From e1a2c82db37551db768274e04d5f2592f64ff9f0 Mon Sep 17 00:00:00 2001 From: josephpv Date: Thu, 21 Dec 2023 18:57:49 +0000 Subject: [PATCH] Add Private space Delete settings page inside PS settings page Contains implementation of settings page for Delete private space controller inside private space settings page. - On selecting to delete private space displays list of account signed in to private profile and deletes the private space after authentication. - Shows a toast message after private space is deleted - Adds dependency for setupdesgin loading layout to show a loading screen while deletion of private space is in progress. Recording Link : b/318383729 go/ss/4Aq3rmbSGHMHesK.png Bug: 318383729 Test: atest DeletePrivateSpaceControllerTest Change-Id: Ia1730915e2469b47823c507f9ef6cd8f63c99baf --- Android.bp | 1 + AndroidManifest.xml | 5 + res/layout/private_space_confirm_deletion.xml | 30 +++ res/layout/private_space_delete.xml | 68 +++++++ res/navigation/private_space_delete_nav.xml | 31 ++++ res/values/strings.xml | 16 +- res/xml/private_space_settings.xml | 2 +- .../core/gateway/SettingsGateway.java | 4 + .../DeletePrivateSpaceController.java | 90 --------- .../privatespace/PrivateSpaceMaintainer.java | 3 +- .../delete/DeletePrivateSpaceController.java | 53 ++++++ .../delete/PrivateSpaceDeleteActivity.java | 50 +++++ .../delete/PrivateSpaceDeleteFragment.java | 172 ++++++++++++++++++ .../PrivateSpaceDeletionProgressFragment.java | 143 +++++++++++++++ .../DeletePrivateSpaceControllerTest.java | 108 ----------- .../DeletePrivateSpaceControllerTest.java | 74 ++++++++ .../PrivateSpaceDeleteFragmentTest.java | 46 +++++ ...vateSpaceDeletionProgressFragmentTest.java | 132 ++++++++++++++ 18 files changed, 826 insertions(+), 202 deletions(-) create mode 100644 res/layout/private_space_confirm_deletion.xml create mode 100644 res/layout/private_space_delete.xml create mode 100644 res/navigation/private_space_delete_nav.xml delete mode 100644 src/com/android/settings/privatespace/DeletePrivateSpaceController.java create mode 100644 src/com/android/settings/privatespace/delete/DeletePrivateSpaceController.java create mode 100644 src/com/android/settings/privatespace/delete/PrivateSpaceDeleteActivity.java create mode 100644 src/com/android/settings/privatespace/delete/PrivateSpaceDeleteFragment.java create mode 100644 src/com/android/settings/privatespace/delete/PrivateSpaceDeletionProgressFragment.java delete mode 100644 tests/unit/src/com/android/settings/privatespace/DeletePrivateSpaceControllerTest.java create mode 100644 tests/unit/src/com/android/settings/privatespace/delete/DeletePrivateSpaceControllerTest.java create mode 100644 tests/unit/src/com/android/settings/privatespace/delete/PrivateSpaceDeleteFragmentTest.java create mode 100644 tests/unit/src/com/android/settings/privatespace/delete/PrivateSpaceDeletionProgressFragmentTest.java diff --git a/Android.bp b/Android.bp index eef73a43dd3..737c16cc3f6 100644 --- a/Android.bp +++ b/Android.bp @@ -109,6 +109,7 @@ android_library { "statslog-settings", "androidx.test.rules", "telephony_flags_core_java_lib", + "setupdesign-lottie-loading-layout", ], plugins: ["androidx.room_room-compiler-plugin"], diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 195c44ecd23..c8f35ae561c 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -5070,6 +5070,11 @@ + + + + + + + \ No newline at end of file diff --git a/res/layout/private_space_delete.xml b/res/layout/private_space_delete.xml new file mode 100644 index 00000000000..9fc36157a3b --- /dev/null +++ b/res/layout/private_space_delete.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/navigation/private_space_delete_nav.xml b/res/navigation/private_space_delete_nav.xml new file mode 100644 index 00000000000..b8850b7f348 --- /dev/null +++ b/res/navigation/private_space_delete_nav.xml @@ -0,0 +1,31 @@ + + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index b760f68e872..78900bc1584 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1280,8 +1280,20 @@ On System - - Delete Private Space + + Delete private space + + Delete private space? + + Your private space will be removed from your device. All private apps and data will be deleted. You can’t undo this action. + + The following accounts will be removed from your private space: + + Delete + + Deleting private space\u2026 + + This will take a few moments Private Space successfully deleted diff --git a/res/xml/private_space_settings.xml b/res/xml/private_space_settings.xml index 244c792c7e5..a3dfbf22906 100644 --- a/res/xml/private_space_settings.xml +++ b/res/xml/private_space_settings.xml @@ -65,7 +65,7 @@ diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java index 8fee052b19f..b4de8ccce95 100644 --- a/src/com/android/settings/core/gateway/SettingsGateway.java +++ b/src/com/android/settings/core/gateway/SettingsGateway.java @@ -169,6 +169,8 @@ import com.android.settings.print.PrintJobSettingsFragment; import com.android.settings.print.PrintSettingsFragment; import com.android.settings.privacy.PrivacyControlsFragment; import com.android.settings.privacy.PrivacyDashboardFragment; +import com.android.settings.privatespace.delete.PrivateSpaceDeleteFragment; +import com.android.settings.privatespace.delete.PrivateSpaceDeletionProgressFragment; import com.android.settings.privatespace.onelock.PrivateSpaceBiometricSettings; import com.android.settings.regionalpreferences.RegionalPreferencesEntriesFragment; import com.android.settings.safetycenter.MoreSecurityPrivacyFragment; @@ -271,6 +273,8 @@ public class SettingsGateway { CombinedBiometricSettings.class.getName(), CombinedBiometricProfileSettings.class.getName(), PrivateSpaceBiometricSettings.class.getName(), + PrivateSpaceDeleteFragment.class.getName(), + PrivateSpaceDeletionProgressFragment.class.getName(), SwipeToNotificationSettings.class.getName(), DoubleTapPowerSettings.class.getName(), DoubleTapScreenSettings.class.getName(), diff --git a/src/com/android/settings/privatespace/DeletePrivateSpaceController.java b/src/com/android/settings/privatespace/DeletePrivateSpaceController.java deleted file mode 100644 index 98bf82705be..00000000000 --- a/src/com/android/settings/privatespace/DeletePrivateSpaceController.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2023 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.privatespace; - -import static com.android.settings.privatespace.PrivateSpaceMaintainer.ErrorDeletingPrivateSpace.DELETE_PS_ERROR_INTERNAL; -import static com.android.settings.privatespace.PrivateSpaceMaintainer.ErrorDeletingPrivateSpace.DELETE_PS_ERROR_NONE; -import static com.android.settings.privatespace.PrivateSpaceMaintainer.ErrorDeletingPrivateSpace.DELETE_PS_ERROR_NO_PRIVATE_SPACE; - -import android.content.Context; -import android.text.TextUtils; -import android.util.Log; -import android.widget.Toast; - -import androidx.preference.Preference; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.settings.R; -import com.android.settings.core.BasePreferenceController; - -/** Controller to delete the private space from the PS Settings page */ -public class DeletePrivateSpaceController extends BasePreferenceController { - private static final String TAG = "DeletePrivateSpaceController"; - private final PrivateSpaceMaintainer mPrivateSpaceMaintainer; - - static class Injector { - PrivateSpaceMaintainer injectPrivateSpaceMaintainer(Context context) { - return PrivateSpaceMaintainer.getInstance(context); - } - } - - public DeletePrivateSpaceController(Context context, String preferenceKey) { - this(context, preferenceKey, new Injector()); - } - - DeletePrivateSpaceController(Context context, String preferenceKey, Injector injector) { - super(context, preferenceKey); - mPrivateSpaceMaintainer = injector.injectPrivateSpaceMaintainer(context); - } - - @Override - public int getAvailabilityStatus() { - return android.os.Flags.allowPrivateProfile() ? AVAILABLE : UNSUPPORTED_ON_DEVICE; - } - - @Override - public boolean handlePreferenceTreeClick(Preference preference) { - if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) { - return false; - } - - PrivateSpaceMaintainer.ErrorDeletingPrivateSpace error = - mPrivateSpaceMaintainer.deletePrivateSpace(); - if (error == DELETE_PS_ERROR_NONE) { - showSuccessfulDeletionToast(); - } else if (error == DELETE_PS_ERROR_INTERNAL) { - showDeletionInternalErrorToast(); - } else if (error == DELETE_PS_ERROR_NO_PRIVATE_SPACE) { - // Ideally this should never happen as PS Settings is not available when there's no - // Private Profile. - Log.e(TAG, "Unexpected attempt to delete non-existent PS"); - } - return super.handlePreferenceTreeClick(preference); - } - - /** Shows a toast saying that the private space was deleted */ - @VisibleForTesting - public void showSuccessfulDeletionToast() { - Toast.makeText(mContext, R.string.private_space_deleted, Toast.LENGTH_SHORT).show(); - } - - /** Shows a toast saying that the private space could not be deleted */ - @VisibleForTesting - public void showDeletionInternalErrorToast() { - Toast.makeText(mContext, R.string.private_space_delete_failed, Toast.LENGTH_SHORT).show(); - } -} diff --git a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java index 8d6831406f9..a2831478a8b 100644 --- a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java +++ b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java @@ -75,7 +75,8 @@ public class PrivateSpaceMaintainer { * *

This method should be used by the Private Space Setup Flow ONLY. */ - final synchronized boolean createPrivateSpace() { + @VisibleForTesting + public final synchronized boolean createPrivateSpace() { if (!Flags.allowPrivateProfile()) { return false; } diff --git a/src/com/android/settings/privatespace/delete/DeletePrivateSpaceController.java b/src/com/android/settings/privatespace/delete/DeletePrivateSpaceController.java new file mode 100644 index 00000000000..af4535ec43a --- /dev/null +++ b/src/com/android/settings/privatespace/delete/DeletePrivateSpaceController.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2023 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.privatespace.delete; + +import android.content.Context; +import android.content.Intent; + +import androidx.annotation.NonNull; +import androidx.preference.Preference; + +import com.android.settings.core.BasePreferenceController; + +/** Controller to delete the private space from the PS Settings page */ +public class DeletePrivateSpaceController extends BasePreferenceController { + private static final String TAG = "PrivateSpaceDeleteCtrl"; + + public DeletePrivateSpaceController(@NonNull Context context, @NonNull String preferenceKey) { + super(context, preferenceKey); + } + + @Override + public int getAvailabilityStatus() { + return android.os.Flags.allowPrivateProfile() ? AVAILABLE : UNSUPPORTED_ON_DEVICE; + } + + @Override + public boolean handlePreferenceTreeClick(@NonNull Preference preference) { + if (mPreferenceKey.equals(preference.getKey())) { + startPrivateSpaceDeleteActivity(); + return true; + } + return false; + } + + private void startPrivateSpaceDeleteActivity() { + final Intent intent = new Intent(mContext, PrivateSpaceDeleteActivity.class); + mContext.startActivity(intent); + } +} diff --git a/src/com/android/settings/privatespace/delete/PrivateSpaceDeleteActivity.java b/src/com/android/settings/privatespace/delete/PrivateSpaceDeleteActivity.java new file mode 100644 index 00000000000..a4109b868bc --- /dev/null +++ b/src/com/android/settings/privatespace/delete/PrivateSpaceDeleteActivity.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2024 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.privatespace.delete; + +import android.app.settings.SettingsEnums; +import android.os.Bundle; + +import androidx.navigation.fragment.NavHostFragment; + +import com.android.settings.R; +import com.android.settings.SetupWizardUtils; +import com.android.settings.core.InstrumentedActivity; + +import com.google.android.setupdesign.util.ThemeHelper; + +public class PrivateSpaceDeleteActivity extends InstrumentedActivity { + @Override + public int getMetricsCategory() { + return SettingsEnums.PRIVATE_SPACE_SETTINGS; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + if (!android.os.Flags.allowPrivateProfile()) { + return; + } + setTheme(SetupWizardUtils.getTheme(this, getIntent())); + ThemeHelper.trySetDynamicColor(this); + super.onCreate(savedInstanceState); + setContentView(R.layout.privatespace_setup_root); + NavHostFragment navHostFragment = + (NavHostFragment) + getSupportFragmentManager().findFragmentById(R.id.ps_nav_host_fragment); + navHostFragment.getNavController().setGraph(R.navigation.private_space_delete_nav); + } +} diff --git a/src/com/android/settings/privatespace/delete/PrivateSpaceDeleteFragment.java b/src/com/android/settings/privatespace/delete/PrivateSpaceDeleteFragment.java new file mode 100644 index 00000000000..7dd3a5b0102 --- /dev/null +++ b/src/com/android/settings/privatespace/delete/PrivateSpaceDeleteFragment.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2023 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.privatespace.delete; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.app.Activity; +import android.app.settings.SettingsEnums; +import android.content.Context; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.UserHandle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.navigation.fragment.NavHostFragment; + +import com.android.settings.R; +import com.android.settings.core.InstrumentedFragment; +import com.android.settings.password.ChooseLockSettingsHelper; +import com.android.settings.privatespace.PrivateSpaceMaintainer; +import com.android.settingslib.accounts.AuthenticatorHelper; + +import com.google.android.setupcompat.template.FooterBarMixin; +import com.google.android.setupcompat.template.FooterButton; +import com.google.android.setupdesign.GlifLayout; + +/** Fragment to delete private space that lists the accounts logged in to the private profile. */ +public class PrivateSpaceDeleteFragment extends InstrumentedFragment { + private static final String TAG = "PrivateSpaceDeleteFrag"; + private View mContentView; + private static final int CREDENTIAL_CONFIRM_REQUEST = 1; + @Nullable private UserHandle mPrivateUserHandle; + + @Override + public void onCreate(@Nullable Bundle icicle) { + if (android.os.Flags.allowPrivateProfile()) { + super.onCreate(icicle); + } + } + + @Override + public void onStart() { + super.onStart(); + if (PrivateSpaceMaintainer.getInstance(getContext()).isPrivateSpaceLocked()) { + getActivity().finish(); + } + } + + @Override + public int getMetricsCategory() { + return SettingsEnums.PRIVATE_SPACE_SETTINGS; + } + + private View.OnClickListener startAuthenticationForDelete() { + return v -> { + final ChooseLockSettingsHelper.Builder builder = + new ChooseLockSettingsHelper.Builder(getActivity(), this); + if (mPrivateUserHandle != null) { + builder.setRequestCode(CREDENTIAL_CONFIRM_REQUEST) + .setUserId(mPrivateUserHandle.getIdentifier()) + .show(); + } else { + Log.e(TAG, "Private space user handle cannot be null"); + getActivity().finish(); + } + }; + } + + @NonNull + @Override + public View onCreateView( + @NonNull LayoutInflater inflater, + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + mPrivateUserHandle = + PrivateSpaceMaintainer.getInstance(getContext()).getPrivateProfileHandle(); + if (mPrivateUserHandle == null) { + Log.e(TAG, "Private space user handle cannot be null"); + getActivity().finish(); + } + mContentView = inflater.inflate(R.layout.private_space_delete, container, false); + final GlifLayout layout = mContentView.findViewById(R.id.private_space_delete_layout); + final FooterBarMixin mixin = layout.getMixin(FooterBarMixin.class); + final Activity activity = getActivity(); + mixin.setPrimaryButton( + new FooterButton.Builder(activity) + .setText(R.string.private_space_delete_button_label) + .setListener(startAuthenticationForDelete()) + .setButtonType(FooterButton.ButtonType.OTHER) + .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary) + .build()); + mixin.setSecondaryButton( + new FooterButton.Builder(activity) + .setText(android.R.string.cancel) + .setListener(view -> activity.onBackPressed()) + .setButtonType(FooterButton.ButtonType.CANCEL) + .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Secondary) + .build()); + + loadPrivateProfileAccountList(); + return mContentView; + } + + private void loadPrivateProfileAccountList() { + View accountsLabel = mContentView.findViewById(R.id.accounts_label); + LinearLayout contents = (LinearLayout) mContentView.findViewById(R.id.accounts); + contents.removeAllViews(); + + Context context = getActivity(); + + AccountManager accountManager = AccountManager.get(context); + + LayoutInflater inflater = context.getSystemService(LayoutInflater.class); + + final AuthenticatorHelper helper = + new AuthenticatorHelper(context, mPrivateUserHandle, null); + final String[] accountTypes = helper.getEnabledAccountTypes(); + + for (String type : accountTypes) { + final String accountType = type; + final Account[] accounts = + accountManager.getAccountsByTypeAsUser(accountType, mPrivateUserHandle); + Drawable icon = helper.getDrawableForType(getContext(), accountType); + if (icon == null) { + icon = context.getPackageManager().getDefaultActivityIcon(); + } + for (Account account : accounts) { + View child = inflater.inflate(R.layout.main_clear_account, contents, false); + child.findViewById(android.R.id.icon).setImageDrawable(icon); + child.findViewById(android.R.id.title).setText(account.name); + contents.addView(child); + } + } + + if (contents.getChildCount() > 0) { + accountsLabel.setVisibility(View.VISIBLE); + contents.setVisibility(View.VISIBLE); + } + } + + @Override + public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == CREDENTIAL_CONFIRM_REQUEST && resultCode == Activity.RESULT_OK) { + NavHostFragment.findNavController(PrivateSpaceDeleteFragment.this) + .navigate(R.id.action_authenticate_delete); + } + } +} diff --git a/src/com/android/settings/privatespace/delete/PrivateSpaceDeletionProgressFragment.java b/src/com/android/settings/privatespace/delete/PrivateSpaceDeletionProgressFragment.java new file mode 100644 index 00000000000..3a166413565 --- /dev/null +++ b/src/com/android/settings/privatespace/delete/PrivateSpaceDeletionProgressFragment.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2024 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.privatespace.delete; + +import static com.android.settings.privatespace.PrivateSpaceMaintainer.ErrorDeletingPrivateSpace.DELETE_PS_ERROR_INTERNAL; +import static com.android.settings.privatespace.PrivateSpaceMaintainer.ErrorDeletingPrivateSpace.DELETE_PS_ERROR_NONE; + +import android.app.settings.SettingsEnums; +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; + +import androidx.activity.OnBackPressedCallback; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.settings.R; +import com.android.settings.core.InstrumentedFragment; +import com.android.settings.privatespace.PrivateSpaceMaintainer; + +/** Fragment to show loading animation screen while deleting private space. */ +public class PrivateSpaceDeletionProgressFragment extends InstrumentedFragment { + private static final String TAG = "PrivateSpaceDeleteProg"; + private static final int PRIVATE_SPACE_DELETE_POST_DELAY_MS = 1000; + private Handler mHandler; + private PrivateSpaceMaintainer mPrivateSpaceMaintainer; + private Runnable mDeletePrivateSpace = + new Runnable() { + @Override + public void run() { + deletePrivateSpace(); + getActivity().finish(); + } + }; + + static class Injector { + PrivateSpaceMaintainer injectPrivateSpaceMaintainer(Context context) { + return PrivateSpaceMaintainer.getInstance(context); + } + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + if (android.os.Flags.allowPrivateProfile()) { + super.onCreate(savedInstanceState); + } + } + + @Override + public int getMetricsCategory() { + return SettingsEnums.PRIVATE_SPACE_SETTINGS; + } + + @NonNull + @Override + public View onCreateView( + @NonNull LayoutInflater inflater, + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + mPrivateSpaceMaintainer = + new PrivateSpaceDeletionProgressFragment.Injector() + .injectPrivateSpaceMaintainer(getActivity().getApplicationContext()); + if (!mPrivateSpaceMaintainer.doesPrivateSpaceExist()) { + // Ideally this should never happen as PS Settings is not available when there's no + // Private Profile. + Log.e(TAG, "Unexpected attempt to delete non-existent PS"); + getActivity().finish(); + } + View contentView = + inflater.inflate(R.layout.private_space_confirm_deletion, container, false); + OnBackPressedCallback callback = + new OnBackPressedCallback(true /* enabled by default */) { + @Override + public void handleOnBackPressed() { + // Handle the back button event. We intentionally don't want to allow back + // button to work in this screen during the setup flow. + } + }; + requireActivity().getOnBackPressedDispatcher().addCallback(this, callback); + mHandler = new Handler(Looper.getMainLooper()); + // Ensures screen visibility to user by introducing a 1-second delay before deleting private + // space. + mHandler.postDelayed(mDeletePrivateSpace, PRIVATE_SPACE_DELETE_POST_DELAY_MS); + return contentView; + } + + @Override + public void onDestroy() { + mHandler.removeCallbacks(mDeletePrivateSpace); + super.onDestroy(); + } + + /** Deletes private space and shows a toast message */ + @VisibleForTesting + public void deletePrivateSpace() { + PrivateSpaceMaintainer.ErrorDeletingPrivateSpace error = + mPrivateSpaceMaintainer.deletePrivateSpace(); + if (error == DELETE_PS_ERROR_NONE) { + showSuccessfulDeletionToast(); + } else if (error == DELETE_PS_ERROR_INTERNAL) { + showDeletionInternalErrorToast(); + } + } + + @VisibleForTesting + public void setPrivateSpaceMaintainer(@NonNull Injector injector) { + mPrivateSpaceMaintainer = injector.injectPrivateSpaceMaintainer(getActivity()); + } + + /** Shows a toast saying that the private space was deleted */ + @VisibleForTesting + public void showSuccessfulDeletionToast() { + Toast.makeText(getContext(), R.string.private_space_deleted, Toast.LENGTH_SHORT).show(); + } + + /** Shows a toast saying that the private space could not be deleted */ + @VisibleForTesting + public void showDeletionInternalErrorToast() { + Toast.makeText(getContext(), R.string.private_space_delete_failed, Toast.LENGTH_SHORT) + .show(); + } +} diff --git a/tests/unit/src/com/android/settings/privatespace/DeletePrivateSpaceControllerTest.java b/tests/unit/src/com/android/settings/privatespace/DeletePrivateSpaceControllerTest.java deleted file mode 100644 index 8fb3eae9dab..00000000000 --- a/tests/unit/src/com/android/settings/privatespace/DeletePrivateSpaceControllerTest.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2023 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.privatespace; - -import static com.android.settings.core.BasePreferenceController.AVAILABLE; -import static com.android.settings.privatespace.PrivateSpaceMaintainer.ErrorDeletingPrivateSpace.DELETE_PS_ERROR_INTERNAL; -import static com.android.settings.privatespace.PrivateSpaceMaintainer.ErrorDeletingPrivateSpace.DELETE_PS_ERROR_NONE; -import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.verify; - -import android.content.Context; - -import androidx.preference.Preference; -import androidx.test.core.app.ApplicationProvider; -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; - -@RunWith(AndroidJUnit4.class) -public class DeletePrivateSpaceControllerTest { - @Mock private PrivateSpaceMaintainer mPrivateSpaceMaintainer; - @Mock private Context mContext; - - private Preference mPreference; - private DeletePrivateSpaceController mDeletePrivateSpaceController; - - /** Required setup before a test. */ - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mContext = ApplicationProvider.getApplicationContext(); - final String preferenceKey = "private_space_delete"; - - mPreference = new Preference(ApplicationProvider.getApplicationContext()); - mPreference.setKey(preferenceKey); - - mDeletePrivateSpaceController = - new DeletePrivateSpaceController( - mContext, - preferenceKey, - new DeletePrivateSpaceController.Injector() { - @Override - PrivateSpaceMaintainer injectPrivateSpaceMaintainer(Context context) { - return mPrivateSpaceMaintainer; - } - }); - } - - /** Tests that the controller is always available. */ - @Test - public void getAvailabilityStatus_returnsAvailable() { - assertThat(mDeletePrivateSpaceController.getAvailabilityStatus()).isEqualTo(AVAILABLE); - } - - /** Tests that on click it attempts to delete the PS. */ - @Test - public void handlePreferenceTreeClick_attemptsToDeletePrivateSpace() { - doReturn(DELETE_PS_ERROR_NONE).when(mPrivateSpaceMaintainer).deletePrivateSpace(); - DeletePrivateSpaceController spy = Mockito.spy(mDeletePrivateSpaceController); - doNothing().when(spy).showSuccessfulDeletionToast(); - spy.handlePreferenceTreeClick(mPreference); - - verify(mPrivateSpaceMaintainer).deletePrivateSpace(); - } - - /** Tests that on deletion of PS relevant toast is shown. */ - @Test - public void handlePreferenceTreeClick_onDeletion_showsDeletedToast() { - doReturn(DELETE_PS_ERROR_NONE).when(mPrivateSpaceMaintainer).deletePrivateSpace(); - DeletePrivateSpaceController spy = Mockito.spy(mDeletePrivateSpaceController); - doNothing().when(spy).showSuccessfulDeletionToast(); - spy.handlePreferenceTreeClick(mPreference); - - verify(spy).showSuccessfulDeletionToast(); - } - - /** Tests that on failing to delete the PS relevant toast is shown. */ - @Test - public void handlePreferenceTreeClick_onDeletionError_showsDeletionFailedToast() { - doReturn(DELETE_PS_ERROR_INTERNAL).when(mPrivateSpaceMaintainer).deletePrivateSpace(); - DeletePrivateSpaceController spy = Mockito.spy(mDeletePrivateSpaceController); - doNothing().when(spy).showDeletionInternalErrorToast(); - spy.handlePreferenceTreeClick(mPreference); - - verify(spy).showDeletionInternalErrorToast(); - } -} diff --git a/tests/unit/src/com/android/settings/privatespace/delete/DeletePrivateSpaceControllerTest.java b/tests/unit/src/com/android/settings/privatespace/delete/DeletePrivateSpaceControllerTest.java new file mode 100644 index 00000000000..371ca240039 --- /dev/null +++ b/tests/unit/src/com/android/settings/privatespace/delete/DeletePrivateSpaceControllerTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2024 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.privatespace.delete; + +import static com.android.settings.core.BasePreferenceController.AVAILABLE; +import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.platform.test.flag.junit.SetFlagsRule; + +import androidx.preference.Preference; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +public class DeletePrivateSpaceControllerTest { + @Mock private Context mContext; + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private Preference mPreference; + private DeletePrivateSpaceController mDeletePrivateSpaceController; + + /** Required setup before a test. */ + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = ApplicationProvider.getApplicationContext(); + final String preferenceKey = "private_space_delete"; + + mPreference = new Preference(ApplicationProvider.getApplicationContext()); + mPreference.setKey(preferenceKey); + + mDeletePrivateSpaceController = new DeletePrivateSpaceController(mContext, preferenceKey); + } + + /** Tests that the controller is available when private space flag is enabled. */ + @Test + public void getAvailabilityStatus_whenPrivateFlagEnabled_returnsAvailable() { + mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE); + + assertThat(mDeletePrivateSpaceController.getAvailabilityStatus()).isEqualTo(AVAILABLE); + } + + /** Tests that the controller is not available when private space flag is disabled. */ + @Test + public void getAvailabilityStatus_whenPrivateFlagDisabled_returnsUnsupportedOnDevice() { + mSetFlagsRule.disableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE); + + assertThat(mDeletePrivateSpaceController.getAvailabilityStatus()) + .isEqualTo(UNSUPPORTED_ON_DEVICE); + } +} diff --git a/tests/unit/src/com/android/settings/privatespace/delete/PrivateSpaceDeleteFragmentTest.java b/tests/unit/src/com/android/settings/privatespace/delete/PrivateSpaceDeleteFragmentTest.java new file mode 100644 index 00000000000..16ccbc451fc --- /dev/null +++ b/tests/unit/src/com/android/settings/privatespace/delete/PrivateSpaceDeleteFragmentTest.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2024 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.privatespace.delete; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.spy; + +import android.app.settings.SettingsEnums; +import android.os.Flags; +import android.platform.test.flag.junit.SetFlagsRule; + +import androidx.test.annotation.UiThreadTest; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class PrivateSpaceDeleteFragmentTest { + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private PrivateSpaceDeleteFragment mFragment; + + @Test + @UiThreadTest + public void verifyMetricsConstant() { + mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE); + mFragment = spy(new PrivateSpaceDeleteFragment()); + assertThat(mFragment.getMetricsCategory()).isEqualTo(SettingsEnums.PRIVATE_SPACE_SETTINGS); + } +} diff --git a/tests/unit/src/com/android/settings/privatespace/delete/PrivateSpaceDeletionProgressFragmentTest.java b/tests/unit/src/com/android/settings/privatespace/delete/PrivateSpaceDeletionProgressFragmentTest.java new file mode 100644 index 00000000000..5c2ef23564b --- /dev/null +++ b/tests/unit/src/com/android/settings/privatespace/delete/PrivateSpaceDeletionProgressFragmentTest.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2024 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.privatespace.delete; + +import static com.android.settings.privatespace.PrivateSpaceMaintainer.ErrorDeletingPrivateSpace.DELETE_PS_ERROR_INTERNAL; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.app.settings.SettingsEnums; +import android.content.Context; +import android.os.Flags; +import android.platform.test.flag.junit.SetFlagsRule; + +import androidx.test.annotation.UiThreadTest; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.android.settings.privatespace.PrivateSpaceMaintainer; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +public class PrivateSpaceDeletionProgressFragmentTest { + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private Context mContext; + private PrivateSpaceDeletionProgressFragment mFragment; + private PrivateSpaceMaintainer mPrivateSpaceMaintainer; + @Mock private PrivateSpaceMaintainer mPrivateSpaceMaintainerMock; + + @UiThreadTest + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mContext = ApplicationProvider.getApplicationContext(); + mFragment = new PrivateSpaceDeletionProgressFragment(); + PrivateSpaceDeletionProgressFragment.Injector injector = + new PrivateSpaceDeletionProgressFragment.Injector() { + @Override + public PrivateSpaceMaintainer injectPrivateSpaceMaintainer(Context context) { + return mPrivateSpaceMaintainer; + } + }; + mPrivateSpaceMaintainer = PrivateSpaceMaintainer.getInstance(mContext); + mFragment.setPrivateSpaceMaintainer(injector); + } + + @After + public void tearDown() { + mPrivateSpaceMaintainer.deletePrivateSpace(); + } + + @Test + @UiThreadTest + public void verifyMetricsConstant() { + mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE); + assertThat(mFragment.getMetricsCategory()).isEqualTo(SettingsEnums.PRIVATE_SPACE_SETTINGS); + } + + /** Tests that deletePrivateSpace() deletes the private space. */ + @Test + @UiThreadTest + public void deletePrivateSpace_deletesPS() { + PrivateSpaceDeletionProgressFragment spyFragment = spy(mFragment); + doNothing().when(spyFragment).showSuccessfulDeletionToast(); + mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE); + + mPrivateSpaceMaintainer.createPrivateSpace(); + spyFragment.deletePrivateSpace(); + assertThat(mPrivateSpaceMaintainer.doesPrivateSpaceExist()).isFalse(); + } + + /** Tests that on deletion of the private space relevant toast is shown. */ + @Test + @UiThreadTest + public void deletePrivateSpace_onDeletion_showsDeletedToast() { + PrivateSpaceDeletionProgressFragment spyFragment = spy(mFragment); + doNothing().when(spyFragment).showSuccessfulDeletionToast(); + mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE); + + mPrivateSpaceMaintainer.createPrivateSpace(); + spyFragment.deletePrivateSpace(); + verify(spyFragment).showSuccessfulDeletionToast(); + } + + /** Tests that on failing to delete the private space relevant toast is shown. */ + @Test + @UiThreadTest + public void deletePrivateSpace_onDeletionError_showsDeletionFailedToast() { + PrivateSpaceDeletionProgressFragment spyFragment = + spy(new PrivateSpaceDeletionProgressFragment()); + PrivateSpaceDeletionProgressFragment.Injector injector = + new PrivateSpaceDeletionProgressFragment.Injector() { + @Override + PrivateSpaceMaintainer injectPrivateSpaceMaintainer(Context context) { + return mPrivateSpaceMaintainerMock; + } + }; + spyFragment.setPrivateSpaceMaintainer(injector); + doReturn(DELETE_PS_ERROR_INTERNAL).when(mPrivateSpaceMaintainerMock).deletePrivateSpace(); + doNothing().when(spyFragment).showDeletionInternalErrorToast(); + mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE); + + spyFragment.deletePrivateSpace(); + + verify(spyFragment).showDeletionInternalErrorToast(); + } +}