diff --git a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java index 7107ff754aa..44982252e2a 100755 --- a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java +++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java @@ -19,9 +19,6 @@ package com.android.settings.applications.appinfo; import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.DialogFragment; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -95,10 +92,6 @@ public class AppInfoDashboardFragment extends DashboardFragment static final int LOADER_STORAGE = 3; static final int LOADER_BATTERY = 4; - // Dialog identifiers used in showDialog - private static final int DLG_BASE = 0; - static final int DLG_CLEAR_INSTANT_APP = DLG_BASE + 1; - public static final String ARG_PACKAGE_NAME = "package"; public static final String ARG_PACKAGE_UID = "uid"; @@ -419,7 +412,9 @@ public class AppInfoDashboardFragment extends DashboardFragment for (Callback callback : mCallbacks) { callback.refreshUi(); } - mAppButtonsPreferenceController.refreshUi(); + if (mAppButtonsPreferenceController.isAvailable()) { + mAppButtonsPreferenceController.refreshUi(); + } if (!mInitialized) { // First time init: are we displaying an uninstalled app? @@ -447,11 +442,6 @@ public class AppInfoDashboardFragment extends DashboardFragment return true; } - @VisibleForTesting - AlertDialog createDialog(int id, int errorCode) { - return mInstantAppButtonPreferenceController.createDialog(id); - } - private void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) { stopListeningToPackageRemove(); // Create new intent to launch Uninstaller activity @@ -561,12 +551,6 @@ public class AppInfoDashboardFragment extends DashboardFragment mFinishing = true; } - void showDialogInner(int id, int moveErrorCode) { - final DialogFragment newFragment = MyAlertDialogFragment.newInstance(id, moveErrorCode); - newFragment.setTargetFragment(this, 0); - newFragment.show(getFragmentManager(), "dialog " + id); - } - @Override public void onRunningStateChanged(boolean running) { // No op. @@ -604,37 +588,6 @@ public class AppInfoDashboardFragment extends DashboardFragment } } - public static class MyAlertDialogFragment extends InstrumentedDialogFragment { - - private static final String ARG_ID = "id"; - - @Override - public int getMetricsCategory() { - return MetricsEvent.DIALOG_APP_INFO_ACTION; - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - final int id = getArguments().getInt(ARG_ID); - final int errorCode = getArguments().getInt("moveError"); - final Dialog dialog = - ((AppInfoDashboardFragment) getTargetFragment()).createDialog(id, errorCode); - if (dialog == null) { - throw new IllegalArgumentException("unknown id " + id); - } - return dialog; - } - - public static MyAlertDialogFragment newInstance(int id, int errorCode) { - final MyAlertDialogFragment dialogFragment = new MyAlertDialogFragment(); - final Bundle args = new Bundle(); - args.putInt(ARG_ID, id); - args.putInt("moveError", errorCode); - dialogFragment.setArguments(args); - return dialogFragment; - } - } - @VisibleForTesting void startListeningToPackageRemove() { if (mListeningToPackageRemove) { diff --git a/src/com/android/settings/applications/appinfo/ButtonActionDialogFragment.java b/src/com/android/settings/applications/appinfo/ButtonActionDialogFragment.java index a3f1bab78c5..7198a54ef88 100644 --- a/src/com/android/settings/applications/appinfo/ButtonActionDialogFragment.java +++ b/src/com/android/settings/applications/appinfo/ButtonActionDialogFragment.java @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ package com.android.settings.applications.appinfo; import android.app.AlertDialog; diff --git a/src/com/android/settings/applications/appinfo/InstantAppButtonDialogFragment.java b/src/com/android/settings/applications/appinfo/InstantAppButtonDialogFragment.java new file mode 100644 index 00000000000..d010918cbeb --- /dev/null +++ b/src/com/android/settings/applications/appinfo/InstantAppButtonDialogFragment.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package com.android.settings.applications.appinfo; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.os.UserHandle; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.settings.R; +import com.android.settings.core.instrumentation.InstrumentedDialogFragment; +import com.android.settings.overlay.FeatureFactory; + +/** + * Fragment to show the dialog for clearing the instant app. + */ +public class InstantAppButtonDialogFragment extends InstrumentedDialogFragment implements + DialogInterface.OnClickListener { + + private static final String ARG_PACKAGE_NAME = "packageName"; + + private String mPackageName; + + public static InstantAppButtonDialogFragment newInstance(String packageName) { + final InstantAppButtonDialogFragment dialogFragment = new InstantAppButtonDialogFragment(); + final Bundle args = new Bundle(1); + args.putString(ARG_PACKAGE_NAME, packageName); + dialogFragment.setArguments(args); + return dialogFragment; + } + + @Override + public int getMetricsCategory() { + return MetricsEvent.DIALOG_APP_INFO_ACTION; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final Bundle arguments = getArguments(); + mPackageName = arguments.getString(ARG_PACKAGE_NAME); + return createDialog(); + } + + @Override + public void onClick(DialogInterface dialog, int which) { + final Context context = getContext(); + final PackageManager packageManager = context.getPackageManager(); + FeatureFactory.getFactory(context).getMetricsFeatureProvider() + .action(context, MetricsEvent.ACTION_SETTINGS_CLEAR_INSTANT_APP, mPackageName); + packageManager.deletePackageAsUser(mPackageName, null, 0, UserHandle.myUserId()); + } + + private AlertDialog createDialog() { + AlertDialog confirmDialog = new AlertDialog.Builder(getContext()) + .setPositiveButton(R.string.clear_instant_app_data, this) + .setNegativeButton(R.string.cancel, null) + .setTitle(R.string.clear_instant_app_data) + .setMessage(R.string.clear_instant_app_confirmation) + .create(); + return confirmDialog; + } + +} + diff --git a/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceController.java b/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceController.java index 4d1dfd9589b..8498669e348 100644 --- a/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceController.java @@ -16,15 +16,13 @@ package com.android.settings.applications.appinfo; -import android.app.AlertDialog; +import android.app.DialogFragment; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.Bundle; -import android.os.UserHandle; import androidx.preference.PreferenceScreen; import android.text.TextUtils; @@ -34,12 +32,10 @@ import android.view.MenuItem; import android.view.View; import android.widget.Button; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; import com.android.settings.applications.AppStoreUtil; import com.android.settings.applications.LayoutPreference; import com.android.settings.core.BasePreferenceController; -import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.applications.AppUtils; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; @@ -50,15 +46,13 @@ import com.android.settingslib.core.lifecycle.events.OnPrepareOptionsMenu; import java.util.List; public class InstantAppButtonsPreferenceController extends BasePreferenceController implements - LifecycleObserver, OnCreateOptionsMenu, OnPrepareOptionsMenu, OnOptionsItemSelected, - DialogInterface.OnClickListener { + LifecycleObserver, OnCreateOptionsMenu, OnPrepareOptionsMenu, OnOptionsItemSelected { private static final String KEY_INSTANT_APP_BUTTONS = "instant_app_buttons"; private static final String META_DATA_DEFAULT_URI = "default-url"; private final AppInfoDashboardFragment mParent; private final String mPackageName; - private final PackageManager mPackageManager; private String mLaunchUri; private LayoutPreference mPreference; private MenuItem mInstallMenu; @@ -68,7 +62,6 @@ public class InstantAppButtonsPreferenceController extends BasePreferenceControl super(context, KEY_INSTANT_APP_BUTTONS); mParent = parent; mPackageName = packageName; - mPackageManager = context.getPackageManager(); mLaunchUri = getDefaultLaunchUri(); if (lifecycle != null) { lifecycle.addObserver(this); @@ -118,27 +111,6 @@ public class InstantAppButtonsPreferenceController extends BasePreferenceControl } } - @Override - public void onClick(DialogInterface dialog, int which) { - FeatureFactory.getFactory(mContext).getMetricsFeatureProvider() - .action(mContext, MetricsEvent.ACTION_SETTINGS_CLEAR_INSTANT_APP, mPackageName); - mPackageManager.deletePackageAsUser( - mPackageName, null, 0, UserHandle.myUserId()); - } - - AlertDialog createDialog(int id) { - if (id == AppInfoDashboardFragment.DLG_CLEAR_INSTANT_APP) { - AlertDialog confirmDialog = new AlertDialog.Builder(mContext) - .setPositiveButton(R.string.clear_instant_app_data, this) - .setNegativeButton(R.string.cancel, null) - .setTitle(R.string.clear_instant_app_data) - .setMessage(mContext.getString(R.string.clear_instant_app_confirmation)) - .create(); - return confirmDialog; - } - return null; - } - private void initButtons(View view) { final Button installButton = view.findViewById(R.id.install); final Button clearDataButton = view.findViewById(R.id.clear_data); @@ -160,8 +132,14 @@ public class InstantAppButtonsPreferenceController extends BasePreferenceControl installButton.setEnabled(false); } } - clearDataButton.setOnClickListener( - v -> mParent.showDialogInner(mParent.DLG_CLEAR_INSTANT_APP, 0)); + clearDataButton.setOnClickListener(v -> showDialog()); + } + + private void showDialog() { + final DialogFragment newFragment = + InstantAppButtonDialogFragment.newInstance(mPackageName); + newFragment.setTargetFragment(mParent, 0); + newFragment.show(mParent.getFragmentManager(), KEY_INSTANT_APP_BUTTONS); } private String getDefaultLaunchUri() { diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonDialogFragmentTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonDialogFragmentTest.java new file mode 100644 index 00000000000..c353b1b6006 --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonDialogFragmentTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.applications.appinfo; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.robolectric.Shadows.shadowOf; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.pm.PackageManager; +import com.android.settings.R; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.shadows.ShadowAlertDialog; +import org.robolectric.shadows.ShadowDialog; +import org.robolectric.util.FragmentTestUtil; + +@RunWith(SettingsRobolectricTestRunner.class) +public class InstantAppButtonDialogFragmentTest { + + private static final String TEST_PACKAGE = "testPackage"; + + private InstantAppButtonDialogFragment mFragment; + private Context mContext; + + @Before + public void setUp() { + mContext = spy(RuntimeEnvironment.application); + mFragment = spy(InstantAppButtonDialogFragment.newInstance(TEST_PACKAGE)); + doReturn(mContext).when(mFragment).getContext(); + } + + @Test + public void onClick_shouldDeleteApp() { + final PackageManager packageManager = mock(PackageManager.class); + when(mContext.getPackageManager()).thenReturn(packageManager); + FragmentTestUtil.startFragment(mFragment); + + mFragment.onClick(null /* dialog */, 0 /* which */); + + verify(packageManager) + .deletePackageAsUser(eq(TEST_PACKAGE), any(), anyInt(), anyInt()); + } + + @Test + public void onCreateDialog_clearAppDialog_shouldShowClearAppDataConfirmation() { + FragmentTestUtil.startFragment(mFragment); + + final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog(); + assertThat(dialog).isNotNull(); + final ShadowAlertDialog shadowDialog = shadowOf(dialog); + + assertThat(shadowDialog.getMessage()).isEqualTo( + mContext.getString(R.string.clear_instant_app_confirmation)); + assertThat(shadowDialog.getTitle()).isEqualTo( + mContext.getString(R.string.clear_instant_app_data)); + assertThat(dialog.getButton(DialogInterface.BUTTON_POSITIVE).getText()).isEqualTo( + mContext.getString(R.string.clear_instant_app_data)); + assertThat(dialog.getButton(DialogInterface.BUTTON_NEGATIVE).getText()).isEqualTo( + mContext.getString(R.string.cancel)); + } +} diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceControllerTest.java index 425b9f0ff19..32cafeb86c2 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceControllerTest.java @@ -27,9 +27,10 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.FragmentManager; +import android.app.FragmentTransaction; import android.content.ComponentName; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; @@ -287,13 +288,16 @@ public class InstantAppButtonsPreferenceControllerTest { } @Test - public void onClick_shouldDeleteApp() { - PackageManager packageManager = mock(PackageManager.class); - ReflectionHelpers.setField(mController, "mPackageManager", packageManager); + public void clickClearAppButton_shouldLaunchInstantAppButtonDialogFragment() { + final FragmentManager fragmentManager = mock(FragmentManager.class); + final FragmentTransaction fragmentTransaction = mock(FragmentTransaction.class); + when(mFragment.getFragmentManager()).thenReturn(fragmentManager); + when(fragmentManager.beginTransaction()).thenReturn(fragmentTransaction); + mController.displayPreference(mScreen); - mController.onClick(mock(DialogInterface.class), DialogInterface.BUTTON_POSITIVE); + mClearAppButton.callOnClick(); - verify(packageManager) - .deletePackageAsUser(eq(TEST_AIA_PACKAGE_NAME), any(), anyInt(),anyInt()); + verify(fragmentTransaction).add(any(InstantAppButtonDialogFragment.class), + eq("instant_app_buttons")); } }