diff options
36 files changed, 749 insertions, 221 deletions
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageDetailsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageDetailsFragment.kt index a16bc1ce4..481543eb6 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageDetailsFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageDetailsFragment.kt @@ -112,8 +112,10 @@ class AutoPermissionUsageDetailsFragment : activity?.finish() return } - if (!requireArguments().containsKey(Intent.EXTRA_PERMISSION_GROUP_NAME) or - (requireArguments().getString(Intent.EXTRA_PERMISSION_GROUP_NAME) == null)) { + if ( + !requireArguments().containsKey(Intent.EXTRA_PERMISSION_GROUP_NAME) or + (requireArguments().getString(Intent.EXTRA_PERMISSION_GROUP_NAME) == null) + ) { DumpableLog.e(LOG_TAG, "Missing argument ${Intent.EXTRA_USER}") activity?.finish() return @@ -128,14 +130,19 @@ class AutoPermissionUsageDetailsFragment : headerLabel = resources.getString( R.string.permission_group_usage_title, - getPermGroupLabel(requireContext(), filterGroup)) + getPermGroupLabel(requireContext(), filterGroup) + ) val context = preferenceManager.getContext() permissionUsages = PermissionUsages(context) roleManager = Utils.getSystemServiceSafe(context, RoleManager::class.java) val usageViewModelFactory = PermissionUsageDetailsViewModelFactoryLegacy( - PermissionControllerApplication.get(), roleManager, filterGroup, sessionId) + PermissionControllerApplication.get(), + roleManager, + filterGroup, + sessionId + ) usageViewModel = ViewModelProvider(this, usageViewModelFactory)[ PermissionUsageDetailsViewModelLegacy::class.java] @@ -157,7 +164,11 @@ class AutoPermissionUsageDetailsFragment : /** Reloads the data to show. */ private fun reloadData() { usageViewModel.loadPermissionUsages( - requireActivity().getLoaderManager(), permissionUsages, this, FILTER_24_HOURS) + requireActivity().getLoaderManager(), + permissionUsages, + this, + FILTER_24_HOURS + ) if (finishedInitialLoad) { setLoading(true) } @@ -176,7 +187,8 @@ class AutoPermissionUsageDetailsFragment : PermissionControllerStatsLog.write( PERMISSION_USAGE_FRAGMENT_INTERACTION, sessionId, - PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SHOW_SYSTEM_CLICKED) + PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SHOW_SYSTEM_CLICKED + ) } showSystem = !showSystem updateAction() @@ -206,19 +218,25 @@ class AutoPermissionUsageDetailsFragment : val uiData = usageViewModel.buildPermissionUsageDetailsUiData( - appPermissionUsages, showSystem, SHOW_7_DAYS) + appPermissionUsages, + showSystem, + SHOW_7_DAYS + ) if (hasSystemApps != uiData.shouldDisplayShowSystemToggle) { hasSystemApps = uiData.shouldDisplayShowSystemToggle updateAction() } - val category = AtomicReference(PreferenceCategory(context)) + val category = AtomicReference(PreferenceCategory(requireContext())) preferenceScreen.addPreference(category.get()) AppDataLoader(context) { renderHistoryPreferences( - uiData.getHistoryPreferenceDataList(), category, preferenceScreen) + uiData.getHistoryPreferenceDataList(), + category, + preferenceScreen + ) setLoading(false) finishedInitialLoad = true @@ -239,7 +257,8 @@ class AutoPermissionUsageDetailsFragment : summary = getString( R.string.permission_group_usage_subtitle_24h, - getPermGroupLabel(requireContext(), filterGroup)) + getPermGroupLabel(requireContext(), filterGroup) + ) isSelectable = false } preferenceScreen.addPreference(preference) @@ -252,7 +271,8 @@ class AutoPermissionUsageDetailsFragment : summary = getString( R.string.manage_permission_summary, - getPermGroupLabel(requireContext(), filterGroup)) + getPermGroupLabel(requireContext(), filterGroup) + ) onPreferenceClickListener = Preference.OnPreferenceClickListener { val intent = @@ -279,12 +299,13 @@ class AutoPermissionUsageDetailsFragment : val currentDateMs = ZonedDateTime.ofInstant( Instant.ofEpochMilli(usageTimestamp), - Clock.system(ZoneId.systemDefault()).zone) + Clock.system(ZoneId.systemDefault()).zone + ) .truncatedTo(ChronoUnit.DAYS) .toEpochSecond() * 1000L if (currentDateMs != previousDateMs) { if (previousDateMs != 0L) { - category.set(PreferenceCategory(context)) + category.set(PreferenceCategory(requireContext())) preferenceScreen.addPreference(category.get()) } if (usageTimestamp > MIDNIGHT_TODAY) { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingDetailsPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingDetailsPreference.kt index 014627c3c..70d0632f9 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingDetailsPreference.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingDetailsPreference.kt @@ -45,7 +45,7 @@ class AppDataSharingDetailsPreference : Preference { } override fun onBindViewHolder(holder: PreferenceViewHolder) { - val noUpdatesMessage = holder.findViewById(R.id.no_updates_message) + val noUpdatesMessage = holder.findViewById(R.id.no_updates_message)!! noUpdatesMessage.isVisible = showNoUpdates super.onBindViewHolder(holder) } diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java index 06e5ed264..a8b16c521 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java @@ -196,20 +196,25 @@ public class DefaultAppChildFragment<PF extends PreferenceFragmentCompat @NonNull CharSequence title, boolean checked, @Nullable ApplicationInfo applicationInfo, @NonNull ArrayMap<String, Preference> oldPreferences, @NonNull PreferenceScreen preferenceScreen, @NonNull Context context) { - TwoStatePreference preference = (TwoStatePreference) oldPreferences.get(key); - if (preference == null) { - preference = requirePreferenceFragment().createApplicationPreference(); + RoleApplicationPreference roleApplicationPreference = + (RoleApplicationPreference) oldPreferences.get(key); + TwoStatePreference preference; + if (roleApplicationPreference == null) { + roleApplicationPreference = requirePreferenceFragment().createApplicationPreference(); + preference = roleApplicationPreference.asTwoStatePreference(); preference.setKey(key); preference.setIcon(icon); preference.setTitle(title); preference.setPersistent(false); preference.setOnPreferenceChangeListener((preference2, newValue) -> false); preference.setOnPreferenceClickListener(this); + } else { + preference = roleApplicationPreference.asTwoStatePreference(); } preference.setChecked(checked); if (applicationInfo != null) { - RoleUiBehaviorUtils.prepareApplicationPreferenceAsUser(mRole, preference, + RoleUiBehaviorUtils.prepareApplicationPreferenceAsUser(mRole, roleApplicationPreference, applicationInfo, mUser, context); } @@ -281,7 +286,7 @@ public class DefaultAppChildFragment<PF extends PreferenceFragmentCompat * @return a new preference for an application */ @NonNull - TwoStatePreference createApplicationPreference(); + RoleApplicationPreference createApplicationPreference(); /** * Create a new preference for the footer. diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java index 3c8af1136..f9a0193bd 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java @@ -167,15 +167,19 @@ public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat RoleItem roleItem = roleItems.get(i); Role role = roleItem.getRole(); - Preference preference = oldPreferences.get(role.getName()); - if (preference == null) { - preference = (Preference) preferenceFragment.createPreference(); + RolePreference rolePreference = (RolePreference) oldPreferences.get(role.getName()); + Preference preference; + if (rolePreference == null) { + rolePreference = preferenceFragment.createPreference(); + preference = rolePreference.asPreference(); preference.setKey(role.getName()); preference.setIconSpaceReserved(true); preference.setTitle(role.getShortLabelResource()); preference.setPersistent(false); preference.setOnPreferenceClickListener(listener); preference.getExtras().putParcelable(Intent.EXTRA_USER, user); + } else { + preference = rolePreference.asPreference(); } List<ApplicationInfo> holderApplicationInfos = roleItem.getHolderApplicationInfos(); @@ -187,8 +191,7 @@ public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat preference.setIcon(Utils.getBadgedIcon(context, holderApplicationInfo)); preference.setSummary(Utils.getAppLabel(holderApplicationInfo, context)); } - RoleUiBehaviorUtils.preparePreferenceAsUser(role, (TwoTargetPreference) preference, - user, context); + RoleUiBehaviorUtils.preparePreferenceAsUser(role, rolePreference, user, context); preferenceGroup.addPreference(preference); } } @@ -283,7 +286,7 @@ public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat * @return a new preference for a default app */ @NonNull - TwoTargetPreference createPreference(); + RolePreference createPreference(); /** * Callback when changes have been made to the {@link PreferenceScreen} of the parent diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleApplicationPreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleApplicationPreference.java new file mode 100644 index 000000000..1b5b27971 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleApplicationPreference.java @@ -0,0 +1,32 @@ +/* + * 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.permissioncontroller.role.ui; + +import androidx.annotation.NonNull; +import androidx.preference.TwoStatePreference; + +/** + * Preference for application being a candidate or holding a role. + */ +public interface RoleApplicationPreference extends UserRestrictionAwarePreference { + + /** + * Get instance of {@code this} as {@link TwoStatePreference}. + */ + @NonNull + TwoStatePreference asTwoStatePreference(); +} diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RolePreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RolePreference.java new file mode 100644 index 000000000..442963ce6 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RolePreference.java @@ -0,0 +1,29 @@ +/* + * 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.permissioncontroller.role.ui; + +import androidx.preference.Preference; + +/** + * Preference used by the default apps list UI. + */ +public interface RolePreference extends TwoTargetPreference, UserRestrictionAwarePreference { + /** + * Return this preference as {@link Preference}. + */ + Preference asPreference(); +} diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/TwoTargetPreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/TwoTargetPreference.java index 23044b833..3a8cd55d3 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/TwoTargetPreference.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/TwoTargetPreference.java @@ -18,6 +18,7 @@ package com.android.permissioncontroller.role.ui; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.preference.Preference; /** * {@link androidx.preference.Preference} with the widget layout as a separate target. @@ -38,6 +39,11 @@ public interface TwoTargetPreference { void setOnSecondTargetClickListener(@Nullable OnSecondTargetClickListener listener); /** + * Return this preference as {@link Preference}. + */ + Preference asPreference(); + + /** * Listener for second target click. */ interface OnSecondTargetClickListener { @@ -49,7 +55,4 @@ public interface TwoTargetPreference { */ void onSecondTargetClick(@NonNull TwoTargetPreference preference); } - - /** @see androidx.preference.Preference#setEnabled(boolean) */ - void setEnabled(boolean enabled); } diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/UserRestrictionAwarePreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/UserRestrictionAwarePreference.java new file mode 100644 index 000000000..e6bc9bab6 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/UserRestrictionAwarePreference.java @@ -0,0 +1,30 @@ +/* + * 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.permissioncontroller.role.ui; + +import androidx.annotation.Nullable; + +/** + * Preference that is aware of user restrictions that can block them. + */ +public interface UserRestrictionAwarePreference { + + /** + * Specifies user restriction that blocks this preference. + */ + void setUserRestriction(@Nullable String userRestriction); +} diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/UserRestrictionAwarePreferenceMixin.java b/PermissionController/src/com/android/permissioncontroller/role/ui/UserRestrictionAwarePreferenceMixin.java new file mode 100644 index 000000000..033507991 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/UserRestrictionAwarePreferenceMixin.java @@ -0,0 +1,69 @@ +/* + * 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.permissioncontroller.role.ui; + +import android.app.admin.DevicePolicyManager; +import android.content.Intent; +import android.provider.Settings; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.preference.Preference; +import androidx.preference.PreferenceViewHolder; + +/** + * Mixin for implementing {@link UserRestrictionAwarePreference}. + */ +public class UserRestrictionAwarePreferenceMixin { + + @NonNull + private final Preference mPreference; + @Nullable + private String mUserRestriction = null; + + public UserRestrictionAwarePreferenceMixin(@NonNull Preference preference) { + mPreference = preference; + } + + /** + * Implementation for {@link UserRestrictionAwarePreference#setUserRestriction}. + */ + public void setUserRestriction(@Nullable String userRestriction) { + mUserRestriction = userRestriction; + mPreference.setEnabled(mUserRestriction == null); + } + + /** + * Call after {@link Preference#onBindViewHolder} to apply blocking effects. + */ + public void onAfterBindViewHolder(@NonNull PreferenceViewHolder holder) { + if (mUserRestriction != null) { + // We set the item view to enabled to make the preference row clickable. + // Normal disabled preferences have the whole view hierarchy disabled, so by making only + // the top-level itemView enabled, we don't change the fact that the whole preference + // still "looks" disabled (see Preference.onBindViewHolder). + // Preference.onBindViewHolder sets the onClickListener as well on each preference, so + // we don't need to unset the listener here (we wouldn't know the correct one anyway). + // This approach is used already by com.android.settingslib.RestrictedPreferenceHelper. + holder.itemView.setEnabled(true); + Intent intent = new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS) + .putExtra(DevicePolicyManager.EXTRA_RESTRICTION, mUserRestriction); + holder.itemView.setOnClickListener( + (view) -> holder.itemView.getContext().startActivity(intent)); + } + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppFragment.java index dbd4c7c03..dc6c03d09 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppFragment.java @@ -23,11 +23,11 @@ import android.os.UserHandle; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.preference.Preference; -import androidx.preference.TwoStatePreference; import com.android.permissioncontroller.R; import com.android.permissioncontroller.auto.AutoSettingsFrameFragment; import com.android.permissioncontroller.role.ui.DefaultAppChildFragment; +import com.android.permissioncontroller.role.ui.RoleApplicationPreference; import com.android.role.controller.model.Role; /** Screen to pick a default app for a particular {@link Role}. */ @@ -90,8 +90,8 @@ public class AutoDefaultAppFragment extends AutoSettingsFrameFragment implements @NonNull @Override - public TwoStatePreference createApplicationPreference() { - return new AutoDefaultAppPreference(requireContext()); + public RoleApplicationPreference createApplicationPreference() { + return new AutoRadioPreference(requireContext()); } @NonNull diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppListFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppListFragment.java index b74caa7ed..081e3153b 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppListFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppListFragment.java @@ -24,7 +24,7 @@ import androidx.annotation.Nullable; import com.android.permissioncontroller.R; import com.android.permissioncontroller.auto.AutoSettingsFrameFragment; import com.android.permissioncontroller.role.ui.DefaultAppListChildFragment; -import com.android.permissioncontroller.role.ui.TwoTargetPreference; +import com.android.permissioncontroller.role.ui.RolePreference; /** Shows various roles for which a default app can be picked. */ public class AutoDefaultAppListFragment extends AutoSettingsFrameFragment implements @@ -57,8 +57,8 @@ public class AutoDefaultAppListFragment extends AutoSettingsFrameFragment implem @NonNull @Override - public TwoTargetPreference createPreference() { - return new AutoSettingsPreference(requireContext()); + public RolePreference createPreference() { + return new AutoRolePreference(requireContext()); } @Override diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppPreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoRadioPreference.java index c91e40561..97a3dad26 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppPreference.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoRadioPreference.java @@ -19,16 +19,23 @@ package com.android.permissioncontroller.role.ui.auto; import android.content.Context; import android.widget.RadioButton; +import androidx.annotation.Nullable; import androidx.core.content.res.TypedArrayUtils; import androidx.preference.PreferenceViewHolder; import androidx.preference.TwoStatePreference; import com.android.permissioncontroller.R; +import com.android.permissioncontroller.role.ui.RoleApplicationPreference; +import com.android.permissioncontroller.role.ui.UserRestrictionAwarePreferenceMixin; /** Preference used to represent apps that can be picked as a default app. */ -public class AutoDefaultAppPreference extends TwoStatePreference { +public class AutoRadioPreference extends TwoStatePreference implements + RoleApplicationPreference { - public AutoDefaultAppPreference(Context context) { + private final UserRestrictionAwarePreferenceMixin mUserRestrictionAwarePreferenceMixin = + new UserRestrictionAwarePreferenceMixin(this); + + public AutoRadioPreference(Context context) { super(context, null, TypedArrayUtils.getAttr(context, R.attr.preferenceStyle, android.R.attr.preferenceStyle)); init(); @@ -45,6 +52,18 @@ public class AutoDefaultAppPreference extends TwoStatePreference { RadioButton radioButton = (RadioButton) holder.findViewById(R.id.radio_button); radioButton.setChecked(isChecked()); + + mUserRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder); + } + + @Override + public void setUserRestriction(@Nullable String userRestriction) { + mUserRestrictionAwarePreferenceMixin.setUserRestriction(userRestriction); + } + + @Override + public AutoRadioPreference asTwoStatePreference() { + return this; } } diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSettingsPreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoRolePreference.java index ba5b17be9..d2f1b6cde 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSettingsPreference.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoRolePreference.java @@ -22,34 +22,57 @@ import android.util.AttributeSet; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.preference.Preference; +import androidx.preference.PreferenceViewHolder; +import com.android.permissioncontroller.role.ui.RolePreference; import com.android.permissioncontroller.role.ui.TwoTargetPreference; +import com.android.permissioncontroller.role.ui.UserRestrictionAwarePreferenceMixin; /** * Preference for use in auto lists. Extends {@link TwoTargetPreference} in order to make sure of * shared logic between phone and auto settings UI. */ -public class AutoSettingsPreference extends Preference implements TwoTargetPreference { +public class AutoRolePreference extends Preference implements RolePreference { - public AutoSettingsPreference(@NonNull Context context, + private UserRestrictionAwarePreferenceMixin mUserRestrictionAwarePreferenceMixin = + new UserRestrictionAwarePreferenceMixin(this); + + public AutoRolePreference(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } - public AutoSettingsPreference(@NonNull Context context, @Nullable AttributeSet attrs, + public AutoRolePreference(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } - public AutoSettingsPreference(@NonNull Context context, @Nullable AttributeSet attrs) { + public AutoRolePreference(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); } - public AutoSettingsPreference(@NonNull Context context) { + public AutoRolePreference(@NonNull Context context) { super(context); } @Override public void setOnSecondTargetClickListener(@Nullable OnSecondTargetClickListener listener) { } + + @Override + public void setUserRestriction(@Nullable String userRestriction) { + mUserRestrictionAwarePreferenceMixin.setUserRestriction(userRestriction); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + + mUserRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder); + } + + @Override + public AutoRolePreference asPreference() { + return this; + } } diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSpecialAppAccessFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSpecialAppAccessFragment.java index c561420da..c37735427 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSpecialAppAccessFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSpecialAppAccessFragment.java @@ -22,11 +22,10 @@ import android.os.Bundle; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.preference.Preference; -import androidx.preference.SwitchPreference; -import androidx.preference.TwoStatePreference; import com.android.permissioncontroller.R; import com.android.permissioncontroller.auto.AutoSettingsFrameFragment; +import com.android.permissioncontroller.role.ui.RoleApplicationPreference; import com.android.permissioncontroller.role.ui.specialappaccess.SpecialAppAccessChildFragment; /** Automotive fragment for displaying special app access for a role. */ @@ -81,8 +80,8 @@ public class AutoSpecialAppAccessFragment extends AutoSettingsFrameFragment impl @NonNull @Override - public TwoStatePreference createApplicationPreference() { - return new SwitchPreference(requireContext()); + public RoleApplicationPreference createApplicationPreference() { + return new AutoSwitchPreference(requireContext()); } @NonNull diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSpecialAppAccessListFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSpecialAppAccessListFragment.java index e2dce4a94..59e6766cc 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSpecialAppAccessListFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSpecialAppAccessListFragment.java @@ -24,7 +24,7 @@ import androidx.annotation.Nullable; import com.android.permissioncontroller.R; import com.android.permissioncontroller.auto.AutoSettingsFrameFragment; -import com.android.permissioncontroller.role.ui.TwoTargetPreference; +import com.android.permissioncontroller.role.ui.RolePreference; import com.android.permissioncontroller.role.ui.specialappaccess.SpecialAppAccessListChildFragment; /** Automotive fragment for the list of role related special app accesses. */ @@ -59,8 +59,8 @@ public class AutoSpecialAppAccessListFragment extends AutoSettingsFrameFragment @NonNull @Override - public TwoTargetPreference createPreference(@NonNull Context context) { - return new AutoSettingsPreference(context); + public RolePreference createPreference(@NonNull Context context) { + return new AutoRolePreference(context); } @Override diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSwitchPreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSwitchPreference.java new file mode 100644 index 000000000..900e58551 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSwitchPreference.java @@ -0,0 +1,76 @@ +/* + * 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.permissioncontroller.role.ui.auto; + +import android.content.Context; +import android.util.AttributeSet; + +import androidx.annotation.AttrRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.StyleRes; +import androidx.preference.PreferenceViewHolder; +import androidx.preference.SwitchPreference; + +import com.android.permissioncontroller.role.ui.RoleApplicationPreference; +import com.android.permissioncontroller.role.ui.UserRestrictionAwarePreferenceMixin; + +/** + * Role application preference represented as a switch. + */ +public class AutoSwitchPreference extends SwitchPreference + implements RoleApplicationPreference { + + private UserRestrictionAwarePreferenceMixin mUserRestrictionAwarePreferenceMixin = + new UserRestrictionAwarePreferenceMixin(this); + + public AutoSwitchPreference(@NonNull Context context, @Nullable AttributeSet attrs, + @AttrRes int defStyleAttr, @StyleRes int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public AutoSwitchPreference(@NonNull Context context, @Nullable AttributeSet attrs, + @StyleRes int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public AutoSwitchPreference(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public AutoSwitchPreference(@NonNull Context context) { + super(context); + } + + @Override + public void setUserRestriction(@Nullable String userRestriction) { + mUserRestrictionAwarePreferenceMixin.setUserRestriction(userRestriction); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + + mUserRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder); + } + + @NonNull + @Override + public AutoSwitchPreference asTwoStatePreference() { + return this; + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldDefaultAppListPreferenceFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldDefaultAppListPreferenceFragment.java index b76b8c561..da920ea7f 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldDefaultAppListPreferenceFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldDefaultAppListPreferenceFragment.java @@ -23,7 +23,7 @@ import androidx.annotation.Nullable; import androidx.preference.PreferenceFragmentCompat; import com.android.permissioncontroller.role.ui.DefaultAppListChildFragment; -import com.android.permissioncontroller.role.ui.TwoTargetPreference; +import com.android.permissioncontroller.role.ui.RolePreference; /** * Handheld preference fragment for the list of default apps. @@ -62,8 +62,8 @@ public class HandheldDefaultAppListPreferenceFragment extends PreferenceFragment @NonNull @Override - public TwoTargetPreference createPreference() { - return new SettingsButtonPreference(requireContext()); + public RolePreference createPreference() { + return new HandheldRolePreference(requireContext()); } @Override diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldDefaultAppPreferenceFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldDefaultAppPreferenceFragment.java index 94c07ef67..b8156590a 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldDefaultAppPreferenceFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldDefaultAppPreferenceFragment.java @@ -24,11 +24,10 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; -import androidx.preference.TwoStatePreference; import com.android.permissioncontroller.role.ui.DefaultAppChildFragment; +import com.android.permissioncontroller.role.ui.RoleApplicationPreference; import com.android.settingslib.widget.FooterPreference; -import com.android.settingslib.widget.SelectorWithWidgetPreference; /** * Handheld preference fragment for a default app. @@ -96,8 +95,8 @@ public class HandheldDefaultAppPreferenceFragment extends PreferenceFragmentComp @NonNull @Override - public TwoStatePreference createApplicationPreference() { - return new SelectorWithWidgetPreference(requireContext()); + public RoleApplicationPreference createApplicationPreference() { + return new HandheldRadioPreference(requireContext()); } @NonNull diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldRadioPreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldRadioPreference.java new file mode 100644 index 000000000..d9ef047d6 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldRadioPreference.java @@ -0,0 +1,75 @@ +/* + * 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.permissioncontroller.role.ui.handheld; + +import android.content.Context; +import android.util.AttributeSet; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.preference.PreferenceViewHolder; + +import com.android.permissioncontroller.role.ui.RoleApplicationPreference; +import com.android.permissioncontroller.role.ui.UserRestrictionAwarePreferenceMixin; +import com.android.settingslib.widget.SelectorWithWidgetPreference; + +/** + * Preference used to represent apps that can be picked as a default app. + */ +public class HandheldRadioPreference extends SelectorWithWidgetPreference implements + RoleApplicationPreference { + + private final UserRestrictionAwarePreferenceMixin mUserRestrictionAwarePreferenceMixin = + new UserRestrictionAwarePreferenceMixin(this); + + public HandheldRadioPreference(@NonNull Context context, + @Nullable AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public HandheldRadioPreference(@NonNull Context context, + @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public HandheldRadioPreference(@NonNull Context context, boolean isCheckbox) { + super(context, isCheckbox); + } + + public HandheldRadioPreference(@NonNull Context context) { + super(context); + } + + @Override + public void setUserRestriction( + @Nullable String userRestriction) { + mUserRestrictionAwarePreferenceMixin.setUserRestriction(userRestriction); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + + mUserRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder); + } + + @NonNull + @Override + public HandheldRadioPreference asTwoStatePreference() { + return this; + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/SettingsButtonPreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldRolePreference.java index f54c9d95d..978fe7d5a 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/SettingsButtonPreference.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldRolePreference.java @@ -28,7 +28,9 @@ import androidx.preference.Preference; import androidx.preference.PreferenceViewHolder; import com.android.permissioncontroller.R; -import com.android.permissioncontroller.role.ui.TwoTargetPreference; +import com.android.permissioncontroller.role.ui.RolePreference; +import com.android.permissioncontroller.role.ui.UserRestrictionAwarePreferenceMixin; +import com.android.settingslib.widget.TwoTargetPreference; /** * {@link Preference} with a settings button. @@ -36,33 +38,35 @@ import com.android.permissioncontroller.role.ui.TwoTargetPreference; * @see com.android.settings.widget.GearPreference */ // Made public for com.android.permissioncontroller.role.ui.specialappaccess.handheld -public class SettingsButtonPreference extends com.android.settingslib.widget.TwoTargetPreference - implements TwoTargetPreference { +public class HandheldRolePreference extends TwoTargetPreference implements RolePreference { + + private final UserRestrictionAwarePreferenceMixin mUserRestrictionAwarePreferenceMixin = + new UserRestrictionAwarePreferenceMixin(this); @Nullable private OnSecondTargetClickListener mOnSecondTargetClickListener; - public SettingsButtonPreference(@NonNull Context context, @Nullable AttributeSet attrs, + public HandheldRolePreference(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(); } - public SettingsButtonPreference(@NonNull Context context, @Nullable AttributeSet attrs, + public HandheldRolePreference(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } - public SettingsButtonPreference(@NonNull Context context, @Nullable AttributeSet attrs) { + public HandheldRolePreference(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } - public SettingsButtonPreference(@NonNull Context context) { + public HandheldRolePreference(@NonNull Context context) { super(context); init(); @@ -89,6 +93,11 @@ public class SettingsButtonPreference extends com.android.settingslib.widget.Two } @Override + public void setUserRestriction(@Nullable String userRestriction) { + mUserRestrictionAwarePreferenceMixin.setUserRestriction(userRestriction); + } + + @Override public void onBindViewHolder(@NonNull PreferenceViewHolder holder) { super.onBindViewHolder(holder); @@ -103,5 +112,12 @@ public class SettingsButtonPreference extends com.android.settingslib.widget.Two } // Make the settings button enabled even if the preference itself is disabled. settingsButton.setEnabled(true); + + mUserRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder); + } + + @Override + public HandheldRolePreference asPreference() { + return this; } } diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessChildFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessChildFragment.java index d75747b52..b95440bbd 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessChildFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessChildFragment.java @@ -37,6 +37,7 @@ import androidx.preference.TwoStatePreference; import com.android.permissioncontroller.permission.utils.Utils; import com.android.permissioncontroller.role.ui.ManageRoleHolderStateLiveData; +import com.android.permissioncontroller.role.ui.RoleApplicationPreference; import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils; import com.android.role.controller.model.Role; import com.android.role.controller.model.Roles; @@ -143,9 +144,12 @@ public class SpecialAppAccessChildFragment<PF extends PreferenceFragmentCompat String key = qualifyingApplicationInfo.packageName + '_' + qualifyingApplicationInfo.uid; - TwoStatePreference preference = (TwoStatePreference) oldPreferences.get(key); - if (preference == null) { - preference = preferenceFragment.createApplicationPreference(); + RoleApplicationPreference roleApplicationPreference = + (RoleApplicationPreference) oldPreferences.get(key); + TwoStatePreference preference; + if (roleApplicationPreference == null) { + roleApplicationPreference = preferenceFragment.createApplicationPreference(); + preference = roleApplicationPreference.asTwoStatePreference(); preference.setKey(key); preference.setIcon(Utils.getBadgedIcon(context, qualifyingApplicationInfo)); preference.setTitle(Utils.getFullAppLabel(qualifyingApplicationInfo, context)); @@ -154,11 +158,13 @@ public class SpecialAppAccessChildFragment<PF extends PreferenceFragmentCompat preference.setOnPreferenceClickListener(this); preference.getExtras().putParcelable(PREFERENCE_EXTRA_APPLICATION_INFO, qualifyingApplicationInfo); + } else { + preference = roleApplicationPreference.asTwoStatePreference(); } preference.setChecked(isHolderPackage); UserHandle user = UserHandle.getUserHandleForUid(qualifyingApplicationInfo.uid); - RoleUiBehaviorUtils.prepareApplicationPreferenceAsUser(mRole, preference, + RoleUiBehaviorUtils.prepareApplicationPreferenceAsUser(mRole, roleApplicationPreference, qualifyingApplicationInfo, user, context); preferenceScreen.addPreference(preference); } @@ -228,7 +234,7 @@ public class SpecialAppAccessChildFragment<PF extends PreferenceFragmentCompat * @return a new preference for an application */ @NonNull - TwoStatePreference createApplicationPreference(); + RoleApplicationPreference createApplicationPreference(); /** * Create a new preference for the footer. diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessListChildFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessListChildFragment.java index ec4de84e1..4b256cef0 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessListChildFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessListChildFragment.java @@ -33,7 +33,7 @@ import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; import com.android.permissioncontroller.role.ui.RoleItem; -import com.android.permissioncontroller.role.ui.TwoTargetPreference; +import com.android.permissioncontroller.role.ui.RolePreference; import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils; import com.android.role.controller.model.Role; import com.android.role.controller.model.Roles; @@ -102,16 +102,20 @@ public class SpecialAppAccessListChildFragment<PF extends PreferenceFragmentComp RoleItem roleItem = roleItems.get(i); Role role = roleItem.getRole(); - Preference preference = oldPreferences.get(role.getName()); - if (preference == null) { - preference = (Preference) preferenceFragment.createPreference(context); + RolePreference rolePreference = (RolePreference) oldPreferences.get(role.getName()); + Preference preference; + if (rolePreference == null) { + rolePreference = preferenceFragment.createPreference(context); + preference = rolePreference.asPreference(); preference.setKey(role.getName()); preference.setIconSpaceReserved(true); preference.setTitle(role.getShortLabelResource()); preference.setPersistent(false); preference.setOnPreferenceClickListener(this); + } else { + preference = rolePreference.asPreference(); } - RoleUiBehaviorUtils.preparePreferenceAsUser(role, (TwoTargetPreference) preference, + RoleUiBehaviorUtils.preparePreferenceAsUser(role, rolePreference, Process.myUserHandle(), context); preferenceScreen.addPreference(preference); @@ -153,7 +157,7 @@ public class SpecialAppAccessListChildFragment<PF extends PreferenceFragmentComp * @return a new preference for a special app access */ @NonNull - TwoTargetPreference createPreference(@NonNull Context context); + RolePreference createPreference(@NonNull Context context); /** * Callback when changes have been made to the {@link PreferenceScreen} of the parent diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/handheld/HandheldSpecialAppAccessListPreferenceFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/handheld/HandheldSpecialAppAccessListPreferenceFragment.java index e0d7884a1..26d858d72 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/handheld/HandheldSpecialAppAccessListPreferenceFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/handheld/HandheldSpecialAppAccessListPreferenceFragment.java @@ -23,8 +23,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.preference.PreferenceFragmentCompat; -import com.android.permissioncontroller.role.ui.TwoTargetPreference; -import com.android.permissioncontroller.role.ui.handheld.SettingsButtonPreference; +import com.android.permissioncontroller.role.ui.RolePreference; +import com.android.permissioncontroller.role.ui.handheld.HandheldRolePreference; import com.android.permissioncontroller.role.ui.specialappaccess.SpecialAppAccessListChildFragment; /** @@ -65,8 +65,8 @@ public class HandheldSpecialAppAccessListPreferenceFragment extends PreferenceFr @NonNull @Override - public TwoTargetPreference createPreference(@NonNull Context context) { - return new SettingsButtonPreference(context); + public RolePreference createPreference(@NonNull Context context) { + return new HandheldRolePreference(context); } @Override diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/handheld/HandheldSpecialAppAccessPreferenceFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/handheld/HandheldSpecialAppAccessPreferenceFragment.java index c1bb0fb23..bfcbefdca 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/handheld/HandheldSpecialAppAccessPreferenceFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/handheld/HandheldSpecialAppAccessPreferenceFragment.java @@ -23,10 +23,9 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; -import androidx.preference.TwoStatePreference; +import com.android.permissioncontroller.role.ui.RoleApplicationPreference; import com.android.permissioncontroller.role.ui.specialappaccess.SpecialAppAccessChildFragment; -import com.android.settingslib.widget.AppSwitchPreference; import com.android.settingslib.widget.FooterPreference; /** @@ -90,8 +89,8 @@ public class HandheldSpecialAppAccessPreferenceFragment extends PreferenceFragme @NonNull @Override - public TwoStatePreference createApplicationPreference() { - return new AppSwitchPreference(requireContext()); + public RoleApplicationPreference createApplicationPreference() { + return new HandheldSwitchPreference(requireContext()); } @NonNull diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/handheld/HandheldSwitchPreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/handheld/HandheldSwitchPreference.java new file mode 100644 index 000000000..1b4dd78a4 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/handheld/HandheldSwitchPreference.java @@ -0,0 +1,74 @@ +/* + * 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.permissioncontroller.role.ui.specialappaccess.handheld; + +import android.content.Context; +import android.util.AttributeSet; + +import androidx.annotation.AttrRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.StyleRes; +import androidx.preference.PreferenceViewHolder; + +import com.android.permissioncontroller.role.ui.RoleApplicationPreference; +import com.android.permissioncontroller.role.ui.UserRestrictionAwarePreferenceMixin; +import com.android.settingslib.widget.AppSwitchPreference; + +/** {@link AppSwitchPreference} that is a role application preference. */ +public class HandheldSwitchPreference extends AppSwitchPreference + implements RoleApplicationPreference { + + private UserRestrictionAwarePreferenceMixin mUserRestrictionAwarePreferenceMixin = + new UserRestrictionAwarePreferenceMixin(this); + + public HandheldSwitchPreference(@NonNull Context context, @Nullable AttributeSet attrs, + @AttrRes int defStyleAttr, @StyleRes int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public HandheldSwitchPreference(@NonNull Context context, @Nullable AttributeSet attrs, + @AttrRes int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public HandheldSwitchPreference(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public HandheldSwitchPreference(@NonNull Context context) { + super(context); + } + + @Override + public void setUserRestriction(@Nullable String userRestriction) { + mUserRestrictionAwarePreferenceMixin.setUserRestriction(userRestriction); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + + mUserRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder); + } + + @NonNull + @Override + public HandheldSwitchPreference asTwoStatePreference() { + return this; + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/role/utils/RoleUiBehaviorUtils.java b/PermissionController/src/com/android/permissioncontroller/role/utils/RoleUiBehaviorUtils.java index 40088c7e0..6081695b5 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/utils/RoleUiBehaviorUtils.java +++ b/PermissionController/src/com/android/permissioncontroller/role/utils/RoleUiBehaviorUtils.java @@ -26,10 +26,11 @@ import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.preference.Preference; import com.android.modules.utils.build.SdkLevel; -import com.android.permissioncontroller.role.ui.TwoTargetPreference; +import com.android.permissioncontroller.role.ui.RoleApplicationPreference; +import com.android.permissioncontroller.role.ui.RolePreference; +import com.android.permissioncontroller.role.ui.UserRestrictionAwarePreference; import com.android.permissioncontroller.role.ui.behavior.RoleUiBehavior; import com.android.role.controller.model.Role; @@ -102,13 +103,9 @@ public final class RoleUiBehaviorUtils { * @see RoleUiBehavior#preparePreferenceAsUser */ public static void preparePreferenceAsUser(@NonNull Role role, - @NonNull TwoTargetPreference preference, @NonNull UserHandle user, + @NonNull RolePreference preference, @NonNull UserHandle user, @NonNull Context context) { - if (SdkLevel.isAtLeastU() && role.isExclusive()) { - final UserManager userManager = context.getSystemService(UserManager.class); - preference.setEnabled(!userManager.hasUserRestrictionForUser( - UserManager.DISALLOW_CONFIG_DEFAULT_APPS, user)); - } + prepareUserRestrictionAwarePreferenceAsUser(role, preference, user, context); RoleUiBehavior uiBehavior = getUiBehavior(role); if (uiBehavior == null) { @@ -134,22 +131,32 @@ public final class RoleUiBehaviorUtils { * @see RoleUiBehavior#prepareApplicationPreferenceAsUser */ public static void prepareApplicationPreferenceAsUser(@NonNull Role role, - @NonNull Preference preference, @NonNull ApplicationInfo applicationInfo, - @NonNull UserHandle user, @NonNull Context context) { - if (SdkLevel.isAtLeastU() && role.isExclusive()) { - final UserManager userManager = context.getSystemService(UserManager.class); - preference.setEnabled(!userManager.hasUserRestrictionForUser( - UserManager.DISALLOW_CONFIG_DEFAULT_APPS, user)); - } + @NonNull RoleApplicationPreference preference, + @NonNull ApplicationInfo applicationInfo, @NonNull UserHandle user, + @NonNull Context context) { + prepareUserRestrictionAwarePreferenceAsUser(role, preference, user, context); RoleUiBehavior uiBehavior = getUiBehavior(role); if (uiBehavior == null) { return; } - uiBehavior.prepareApplicationPreferenceAsUser(role, preference, applicationInfo, user, + uiBehavior.prepareApplicationPreferenceAsUser( + role, preference.asTwoStatePreference(), applicationInfo, user, context); } + private static void prepareUserRestrictionAwarePreferenceAsUser(@NonNull Role role, + @NonNull UserRestrictionAwarePreference preference, @NonNull UserHandle user, + @NonNull Context context) { + if (SdkLevel.isAtLeastU() && role.isExclusive()) { + UserManager userManager = context.getSystemService(UserManager.class); + boolean hasDisallowConfigDefaultApps = userManager.hasUserRestrictionForUser( + UserManager.DISALLOW_CONFIG_DEFAULT_APPS, user); + preference.setUserRestriction(hasDisallowConfigDefaultApps + ? UserManager.DISALLOW_CONFIG_DEFAULT_APPS : null); + } + } + /** * @see RoleUiBehavior#getConfirmationMessage */ diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardAnimator.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardAnimator.kt index a0c7c01b9..2bc83eb10 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardAnimator.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardAnimator.kt @@ -55,35 +55,36 @@ class IssueCardAnimator(val callback: AnimationCallback) { // Ensure AVD is reset before transition starts (resolvedImageView.drawable as AnimatedVectorDrawable).reset() - val defaultIssueContentGroup = holder.findViewById(R.id.default_issue_content) - val resolvedIssueContentGroup = holder.findViewById(R.id.resolved_issue_content) - - val transitionSet = TransitionSet() - .setOrdering(TransitionSet.ORDERING_SEQUENTIAL) - .setInterpolator(linearInterpolator) - .addTransition(hideIssueContentTransition) - .addTransition( - showResolvedImageTransition - .clone() - .addListener( - object : TransitionListenerAdapter() { - override fun onTransitionEnd( - transition: Transition - ) { - super.onTransitionEnd(transition) - startIssueResolvedAnimation( - resolvedIssueContentGroup, - resolvedImageView - ) + val defaultIssueContentGroup = holder.findViewById(R.id.default_issue_content)!! + val resolvedIssueContentGroup = holder.findViewById(R.id.resolved_issue_content)!! + + val transitionSet = + TransitionSet() + .setOrdering(TransitionSet.ORDERING_SEQUENTIAL) + .setInterpolator(linearInterpolator) + .addTransition(hideIssueContentTransition) + .addTransition( + showResolvedImageTransition + .clone() + .addListener( + object : TransitionListenerAdapter() { + override fun onTransitionEnd(transition: Transition) { + super.onTransitionEnd(transition) + startIssueResolvedAnimation( + resolvedIssueContentGroup, + resolvedImageView + ) + } } - }) - ) - .addTransition(showResolvedTextTransition) + ) + ) + .addTransition(showResolvedTextTransition) // Defer transition so that it's called after the root ViewGroup has been laid out. holder.itemView.post { TransitionManager.beginDelayedTransition( - defaultIssueContentGroup.parent as ViewGroup?, transitionSet + defaultIssueContentGroup.parent as ViewGroup?, + transitionSet ) // Setting INVISIBLE rather than GONE to ensure consistent card height between @@ -111,7 +112,8 @@ class IssueCardAnimator(val callback: AnimationCallback) { resolvedImageView ) } - }) + } + ) } private fun makeInvisibleIfVisible(view: View?) { @@ -133,23 +135,27 @@ class IssueCardAnimator(val callback: AnimationCallback) { super.onAnimationEnd(drawable) transitionResolvedIssueUiToHiddenAndMarkComplete(resolvedIssueContentGroup) } - }) + } + ) animatedDrawable.start() } private fun transitionResolvedIssueUiToHiddenAndMarkComplete(resolvedIssueContentGroup: View) { - val hideTransition = hideResolvedUiTransition - .clone() - .setInterpolator(linearInterpolator) - .addListener( - object : TransitionListenerAdapter() { - override fun onTransitionEnd(transition: Transition) { - super.onTransitionEnd(transition) - callback.markIssueResolvedUiCompleted() + val hideTransition = + hideResolvedUiTransition + .clone() + .setInterpolator(linearInterpolator) + .addListener( + object : TransitionListenerAdapter() { + override fun onTransitionEnd(transition: Transition) { + super.onTransitionEnd(transition) + callback.markIssueResolvedUiCompleted() + } } - }) + ) TransitionManager.beginDelayedTransition( - resolvedIssueContentGroup.parent as ViewGroup, hideTransition + resolvedIssueContentGroup.parent as ViewGroup, + hideTransition ) resolvedIssueContentGroup.visibility = View.GONE } @@ -191,10 +197,14 @@ class IssueCardAnimator(val callback: AnimationCallback) { // Using getter due to reliance on DeviceConfig property modification in tests private val hideResolvedUiTransitionDelay - get() = Duration.ofMillis( - DeviceConfig.getLong(DeviceConfig.NAMESPACE_PRIVACY, - PROPERTY_HIDE_RESOLVED_UI_TRANSITION_DELAY_MILLIS, - 400)) + get() = + Duration.ofMillis( + DeviceConfig.getLong( + DeviceConfig.NAMESPACE_PRIVACY, + PROPERTY_HIDE_RESOLVED_UI_TRANSITION_DELAY_MILLIS, + 400 + ) + ) private val linearInterpolator = LinearInterpolator() @@ -207,14 +217,16 @@ class IssueCardAnimator(val callback: AnimationCallback) { .setDuration(0) .addTarget(R.id.resolved_issue_image) - private val showResolvedTextTransition = Fade(Fade.IN) - .setStartDelay(SHOW_RESOLVED_TEXT_TRANSITION_DELAY.toMillis()) - .setDuration(SHOW_RESOLVED_TEXT_TRANSITION_DURATION.toMillis()) - .addTarget(R.id.resolved_issue_text) + private val showResolvedTextTransition = + Fade(Fade.IN) + .setStartDelay(SHOW_RESOLVED_TEXT_TRANSITION_DELAY.toMillis()) + .setDuration(SHOW_RESOLVED_TEXT_TRANSITION_DURATION.toMillis()) + .addTarget(R.id.resolved_issue_text) private val hideResolvedUiTransition - get() = Fade(Fade.OUT) - .setStartDelay(hideResolvedUiTransitionDelay.toMillis()) - .setDuration(HIDE_RESOLVED_UI_TRANSITION_DURATION.toMillis()) + get() = + Fade(Fade.OUT) + .setStartDelay(hideResolvedUiTransitionDelay.toMillis()) + .setDuration(HIDE_RESOLVED_UI_TRANSITION_DURATION.toMillis()) } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PreferenceHighlightManager.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PreferenceHighlightManager.kt index 2acd6b5a3..613493f82 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PreferenceHighlightManager.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PreferenceHighlightManager.kt @@ -59,7 +59,7 @@ internal class PreferenceHighlightManager(private val fragment: PreferenceFragme /** Creates a new [HighlightablePreferenceGroupAdapter] instance */ fun createAdapter( - preferenceScreen: PreferenceScreen?, + preferenceScreen: PreferenceScreen, ): RecyclerView.Adapter<RecyclerView.ViewHolder> { val intent = fragment.getActivity()?.getIntent() preferenceGroupAdapter = diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyBrandChipPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyBrandChipPreference.kt index bf2d0565c..57e4175ca 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyBrandChipPreference.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyBrandChipPreference.kt @@ -42,7 +42,7 @@ internal class SafetyBrandChipPreference(context: Context, attrs: AttributeSet) override fun onBindViewHolder(holder: PreferenceViewHolder) { super.onBindViewHolder(holder) - val brandChipButton = holder.findViewById(R.id.brand_chip) + val brandChipButton = holder.findViewById(R.id.brand_chip)!! brandChipButton.setOnClickListener(brandChipClickListener) } diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt index 365dc0efa..6f146e48c 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt @@ -50,7 +50,7 @@ abstract class SafetyCenterFragment : PreferenceFragmentCompat() { } override fun onCreateAdapter( - preferenceScreen: PreferenceScreen? + preferenceScreen: PreferenceScreen ): RecyclerView.Adapter<RecyclerView.ViewHolder> { /* The scroll-to-result functionality for settings search is currently implemented only for * subpages i.e. non expand-and-collapse type entries. Hence, we check that the flag is diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyGroupPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyGroupPreference.kt index 2a9f6f780..46590448e 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyGroupPreference.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyGroupPreference.kt @@ -45,10 +45,10 @@ class SafetyGroupPreference( layoutResource = R.layout.preference_group } - override fun onBindViewHolder(holder: PreferenceViewHolder?) { + override fun onBindViewHolder(holder: PreferenceViewHolder) { super.onBindViewHolder(holder) - (holder?.itemView as? SafetyEntryGroupView)?.showGroup( + (holder.itemView as? SafetyEntryGroupView)?.showGroup( group, isExpanded, isFirstCard, @@ -56,7 +56,8 @@ class SafetyGroupPreference( getTaskIdForEntry, viewModel, onExpandedListener, - onCollapsedListener) + onCollapsedListener + ) } override fun isSameItem(preference: Preference): Boolean = diff --git a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterNotificationTest.kt b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterNotificationTest.kt index 353f99c04..86bc5f88e 100644 --- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterNotificationTest.kt +++ b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterNotificationTest.kt @@ -31,7 +31,8 @@ import android.safetycenter.SafetySourceIssue import androidx.test.core.app.ApplicationProvider.getApplicationContext import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SdkSuppress -import com.android.compatibility.common.util.UiAutomatorUtils2.getUiDevice +import com.android.compatibility.common.util.DisableAnimationRule +import com.android.compatibility.common.util.FreezeRotationRule import com.android.safetycenter.pendingintents.PendingIntentSender import com.android.safetycenter.testing.Coroutines import com.android.safetycenter.testing.Coroutines.TIMEOUT_SHORT @@ -55,9 +56,9 @@ import com.android.safetycenter.testing.SafetySourceReceiver import com.android.safetycenter.testing.SafetySourceTestData import com.android.safetycenter.testing.SafetySourceTestData.Companion.ISSUE_TYPE_ID import com.android.safetycenter.testing.ShellPermissions.callWithShellPermissionIdentity +import com.android.safetycenter.testing.StatusBarNotificationWithChannel import com.android.safetycenter.testing.SupportsSafetyCenterRule import com.android.safetycenter.testing.TestNotificationListener -import com.android.safetycenter.testing.UiTestHelper.clickNotificationElementWithText import com.android.safetycenter.testing.UiTestHelper.waitSourceIssueDisplayed import com.google.common.truth.Truth.assertThat import java.time.Duration @@ -77,13 +78,15 @@ class SafetyCenterNotificationTest { private val safetyCenterTestConfigs = SafetyCenterTestConfigs(context) private val safetyCenterManager = requireNotNull(context.getSystemService(SafetyCenterManager::class.java)) { - "Could not get system service" + "Could not get SafetyCenterManager" } @get:Rule(order = 1) val supportsSafetyCenterRule = SupportsSafetyCenterRule(context) @get:Rule(order = 2) val safetyCenterTestRule = SafetyCenterTestRule(safetyCenterTestHelper, withNotifications = true) + @get:Rule(order = 3) val disableAnimationRule = DisableAnimationRule() + @get:Rule(order = 4) val freezeRotationRule = FreezeRotationRule() @Before fun enableNotificationsForTestSourceBeforeTest() { @@ -907,7 +910,7 @@ class SafetyCenterNotificationTest { } @Test - fun successNotificationClicked_successNotificationCancelled() { + fun successNotification_notificationHasAutoCancel() { safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, safetySourceTestData.criticalWithResolvingIssueWithSuccessMessage @@ -921,18 +924,16 @@ class SafetyCenterNotificationTest { Response.SetData(safetySourceTestData.information) ) sendActionPendingIntentAndWaitWithPermission(action) - TestNotificationListener.waitForSingleNotificationMatching( - NotificationCharacteristics( - "Issue solved", - "", - actions = emptyList(), + val issueSolvedNotificationWithChannel = + TestNotificationListener.waitForSingleNotificationMatching( + NotificationCharacteristics( + "Issue solved", + "", + actions = emptyList(), + ) ) - ) - - clickNotificationElementWithText("Issue solved") - TestNotificationListener.waitForZeroNotifications() - getUiDevice().pressBack() + assertThat(issueSolvedNotificationWithChannel.hasAutoCancel()).isTrue() } // TODO(b/284271124): Decide what to do with existing notifications when flag flipped off @@ -1021,12 +1022,10 @@ class SafetyCenterNotificationTest { safetySourceTestData.recommendationWithDeviceIssue ) val notificationWithChannel = TestNotificationListener.waitForSingleNotification() - val contentIntent = notificationWithChannel.statusBarNotification.notification.contentIntent - executeBlockAndExit( - launchActivity = { PendingIntentSender.send(contentIntent) }, - block = { waitSourceIssueDisplayed(safetySourceTestData.recommendationDeviceIssue) } - ) + sendContentPendingIntent(notificationWithChannel) { + waitSourceIssueDisplayed(safetySourceTestData.recommendationDeviceIssue) + } } @Test @@ -1042,50 +1041,37 @@ class SafetyCenterNotificationTest { safetySourceTestData.criticalWithResolvingGeneralIssue ) val notificationWithChannel = TestNotificationListener.waitForSingleNotification() - val contentIntent = notificationWithChannel.statusBarNotification.notification.contentIntent - executeBlockAndExit( - launchActivity = { PendingIntentSender.send(contentIntent) }, - block = { - waitSourceIssueDisplayed(safetySourceTestData.criticalResolvingGeneralIssue) - waitSourceIssueDisplayed(safetySourceTestData.recommendationGeneralIssue) - } - ) + sendContentPendingIntent(notificationWithChannel) { + waitSourceIssueDisplayed(safetySourceTestData.criticalResolvingGeneralIssue) + waitSourceIssueDisplayed(safetySourceTestData.recommendationDeviceIssue) + } } @Test - fun sendContentPendingIntent_whenGreenIssue_notificationCancelled() { + fun whenGreenIssue_notificationHasAutoCancel() { safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.informationWithIssue) - TestNotificationListener.waitForSingleNotification() - - clickNotificationElementWithText(safetySourceTestData.informationIssue.summary.toString()) + val notificationWithChannel = TestNotificationListener.waitForSingleNotification() - TestNotificationListener.waitForZeroNotifications() - getUiDevice().pressBack() + assertThat(notificationWithChannel.hasAutoCancel()).isTrue() } @Test - fun sendContentPendingIntent_whenNotGreenIssue_notificationNotCancelled() { + fun whenNotGreenIssue_notificationDoesntHaveAutoCancel() { safetyCenterTestHelper.setData( SINGLE_SOURCE_ID, safetySourceTestData.recommendationWithDeviceIssue ) - TestNotificationListener.waitForSingleNotification() - - clickNotificationElementWithText( - safetySourceTestData.recommendationDeviceIssue.summary.toString() - ) + val notificationWithChannel = TestNotificationListener.waitForSingleNotification() - waitSourceIssueDisplayed(safetySourceTestData.recommendationDeviceIssue) - TestNotificationListener.waitForSingleNotification() - getUiDevice().pressBack() + assertThat(notificationWithChannel.hasAutoCancel()).isFalse() } - companion object { - private val SafetyCenterData.inFlightActions: List<SafetyCenterIssue.Action> + private companion object { + val SafetyCenterData.inFlightActions: List<SafetyCenterIssue.Action> get() = issues.flatMap { it.actions }.filter { it.isInFlight } - private fun sendActionPendingIntentAndWaitWithPermission( + fun sendActionPendingIntentAndWaitWithPermission( action: Notification.Action, timeout: Duration = Coroutines.TIMEOUT_LONG ) { @@ -1097,10 +1083,28 @@ class SafetyCenterNotificationTest { } } - private fun setFlagsForImmediateNotifications(vararg sourceIds: String) { + fun setFlagsForImmediateNotifications(vararg sourceIds: String) { SafetyCenterFlags.notificationsAllowedSources = sourceIds.toSet() SafetyCenterFlags.immediateNotificationBehaviorIssues = sourceIds.map { "$it/$ISSUE_TYPE_ID" }.toSet() } + + fun StatusBarNotificationWithChannel.hasAutoCancel(): Boolean { + val autoCancelMask = + statusBarNotification.notification.flags and Notification.FLAG_AUTO_CANCEL + return autoCancelMask != 0 + } + + fun sendContentPendingIntent( + statusBarNotificationWithChannel: StatusBarNotificationWithChannel, + andExecuteBlock: () -> Unit = {} + ) { + val contentIntent = + statusBarNotificationWithChannel.statusBarNotification.notification.contentIntent + executeBlockAndExit( + launchActivity = { PendingIntentSender.send(contentIntent) }, + block = andExecuteBlock + ) + } } } diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterShellCommandsTest.kt b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterShellCommandsTest.kt index 4057410fa..7c5b41944 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterShellCommandsTest.kt +++ b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterShellCommandsTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * 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. @@ -14,20 +14,23 @@ * limitations under the License. */ -package android.safetycenter.cts +package android.safetycenter.functional +import android.Manifest.permission.INTERACT_ACROSS_USERS +import android.app.ActivityManager import android.content.Context import android.safetycenter.SafetyCenterManager import androidx.test.core.app.ApplicationProvider.getApplicationContext import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.compatibility.common.util.SystemUtil import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.isSafetyCenterEnabledWithPermission +import com.android.safetycenter.testing.ShellPermissions.callWithShellPermissionIdentity import com.android.safetycenter.testing.deviceSupportsSafetyCenter import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith -/** CTS tests for Safety Center's shell commands. */ +/** Tests for Safety Center's shell commands. */ @RunWith(AndroidJUnit4::class) class SafetyCenterShellCommandsTest { private val context: Context = getApplicationContext() @@ -54,6 +57,39 @@ class SafetyCenterShellCommandsTest { assertThat(packageName).isEqualTo(context.packageManager.permissionControllerPackageName) } + @Test + fun clearData_executesSuccessfully() { + executeShellCommand("cmd safety_center clear-data") + } + + @Test + fun refresh_executesSuccessfully() { + val currentUser = + callWithShellPermissionIdentity(INTERACT_ACROSS_USERS) { + ActivityManager.getCurrentUser() + } + executeShellCommand("cmd safety_center refresh --reason OTHER --user $currentUser") + } + + @Test + fun help_containsAllCommands() { + val help = executeShellCommand("cmd safety_center help") + + assertThat(help).contains("help") + assertThat(help).contains("enabled") + assertThat(help).contains("supported") + assertThat(help).contains("package-name") + assertThat(help).contains("clear-data") + assertThat(help).contains("refresh") + } + + @Test + fun dump_containsSafetyCenterService() { + val dump = executeShellCommand("dumpsys safety_center") + + assertThat(dump).contains("SafetyCenterService") + } + private fun executeShellCommand(command: String): String = - SystemUtil.runShellCommand(command).trim() + SystemUtil.runShellCommandOrThrow(command).trim() } diff --git a/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterInteractionLoggingHostTest.kt b/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterInteractionLoggingHostTest.kt index 43884d76d..5fe9e0a2a 100644 --- a/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterInteractionLoggingHostTest.kt +++ b/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterInteractionLoggingHostTest.kt @@ -20,6 +20,7 @@ import android.cts.statsdatom.lib.ConfigUtils import android.cts.statsdatom.lib.ReportUtils import android.safetycenter.hostside.rules.HelperAppRule import android.safetycenter.hostside.rules.RequireSafetyCenterRule +import com.android.compatibility.common.util.ApiLevelUtil import com.android.os.AtomsProto.Atom import com.android.os.AtomsProto.SafetyCenterInteractionReported import com.android.os.AtomsProto.SafetyCenterInteractionReported.Action @@ -27,12 +28,9 @@ import com.android.os.AtomsProto.SafetyCenterInteractionReported.ViewType import com.android.tradefed.testtype.DeviceJUnit4ClassRunner import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test import com.google.common.truth.Truth.assertThat -import com.android.compatibility.common.util.ApiLevelUtil -import org.junit.Assume.assumeTrue - import org.junit.After +import org.junit.Assume.assumeTrue import org.junit.Before -import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -106,26 +104,21 @@ class SafetyCenterInteractionLoggingHostTest : BaseHostJUnit4Test() { } @Test - @Ignore - // TODO(b/278202773): Fix/de-flake this test fun openSubpageFromHomepage_recordsEventWithSafetyCenterNavigationSource() { assumeAtLeastUpsideDownCake("Safety Center subpages require Android U+") helperAppRule.runTest(TEST_CLASS_NAME, testMethodName = "openSubpageFromHomepage") val safetyCenterViewedAtoms = getInteractionReportedAtoms(Action.SAFETY_CENTER_VIEWED) + val subpageViewedEvent = safetyCenterViewedAtoms.find { it.viewType == ViewType.SUBPAGE } - assertThat(safetyCenterViewedAtoms.map { it.viewType }) - .containsExactly(ViewType.FULL, ViewType.SUBPAGE, ViewType.FULL) - .inOrder() - assertThat(safetyCenterViewedAtoms[1].navigationSource) + assertThat(subpageViewedEvent).isNotNull() + assertThat(subpageViewedEvent!!.navigationSource) .isEqualTo(SafetyCenterInteractionReported.NavigationSource.SAFETY_CENTER) assertThat(safetyCenterViewedAtoms.map { it.sessionId }.distinct()).hasSize(1) } @Test - @Ignore - // TODO(b/278202773): Fix/de-flake this test fun openSubpageFromSettingsSearch_recordsEventWithSettingsNavigationSource() { assumeAtLeastUpsideDownCake("Safety Center subpages require Android U+") diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/Coroutines.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/Coroutines.kt index 18402e286..6c6336da3 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/Coroutines.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/Coroutines.kt @@ -19,7 +19,6 @@ package com.android.safetycenter.testing import android.util.Log import androidx.test.platform.app.InstrumentationRegistry import java.time.Duration -import kotlin.comparisons.minOf import kotlinx.coroutines.DEBUG_PROPERTY_NAME import kotlinx.coroutines.DEBUG_PROPERTY_VALUE_AUTO import kotlinx.coroutines.DEBUG_PROPERTY_VALUE_ON @@ -39,7 +38,7 @@ object Coroutines { /** A long timeout, to be used for actions that are expected to complete. */ val TIMEOUT_LONG: Duration - get() = minOf(TEST_TIMEOUT.dividedBy(2), Duration.ofMinutes(1)) + get() = TEST_TIMEOUT.dividedBy(2) /** A short timeout, to be used for actions that are expected not to complete. */ val TIMEOUT_SHORT: Duration = Duration.ofSeconds(1) diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/UiTestHelper.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/UiTestHelper.kt index 24a2d2ea2..915780d4e 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/UiTestHelper.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/UiTestHelper.kt @@ -187,12 +187,6 @@ object UiTestHelper { waitDisplayed(By.text(MORE_ISSUES_LABEL)) { it.click() } } - /** Clicks on a notification element that contains the given text. */ - fun clickNotificationElementWithText(text: String) { - getUiDevice().openNotification() - waitDisplayed(By.text(text)) { it.click() } - } - /** Enables or disables animations based on [enabled]. */ fun setAnimationsEnabled(enabled: Boolean) { val scale = |