Loading res/values/strings.xml +1 −1 Original line number Diff line number Diff line Loading @@ -8587,7 +8587,7 @@ <string name="recent_conversations">Recent conversations</string> <!-- [CHAR LIMIT=20] button title --> <string name="conversation_settings_clear_recents">Clear recents</string> <string name="conversation_settings_clear_recents">Clear all of the recent ones</string> <!-- a11y string --> <string name="clear">Clear</string> res/xml/notification_access_permission_details.xml +8 −1 Original line number Diff line number Diff line Loading @@ -17,11 +17,18 @@ <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" xmlns:settings="http://schemas.android.com/apk/res-auto" android:key="notification_access_permission_detail_settings" android:title="@string/manage_notification_access_title"> <com.android.settingslib.widget.LayoutPreference android:key="pref_app_header" android:layout="@layout/settings_entity_header" settings:controller="com.android.settings.applications.specialaccess.notificationaccess.HeaderPreferenceController"/> <com.android.settings.widget.FilterTouchesSwitchPreference android:key="notification_access_switch" android:title="@string/notification_access_detail_switch"/> android:title="@string/notification_access_detail_switch" settings:controller="com.android.settings.applications.specialaccess.notificationaccess.ApprovalPreferenceController"/> </PreferenceScreen> No newline at end of file src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java 0 → 100644 +135 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.applications.specialaccess.notificationaccess; import android.app.NotificationManager; import android.app.settings.SettingsEnums; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.AsyncTask; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; import androidx.preference.SwitchPreference; import com.android.settings.core.BasePreferenceController; import com.android.settings.overlay.FeatureFactory; public class ApprovalPreferenceController extends BasePreferenceController { private static final String TAG = "ApprovalPrefController"; private PackageInfo mPkgInfo; private ComponentName mCn; private PreferenceFragmentCompat mParent; private NotificationManager mNm; private PackageManager mPm; public ApprovalPreferenceController(Context context, String key) { super(context, key); } public ApprovalPreferenceController setPkgInfo(PackageInfo pkgInfo) { mPkgInfo = pkgInfo; return this; } public ApprovalPreferenceController setCn(ComponentName cn) { mCn = cn; return this; } public ApprovalPreferenceController setParent(PreferenceFragmentCompat parent) { mParent = parent; return this; } public ApprovalPreferenceController setNm(NotificationManager nm) { mNm = nm; return this; } public ApprovalPreferenceController setPm(PackageManager pm) { mPm = pm; return this; } @Override public int getAvailabilityStatus() { return AVAILABLE; } @Override public void updateState(Preference pref) { final SwitchPreference preference = (SwitchPreference) pref; final CharSequence label = mPkgInfo.applicationInfo.loadLabel(mPm); preference.setChecked(isServiceEnabled(mCn)); preference.setOnPreferenceChangeListener((p, newValue) -> { final boolean access = (Boolean) newValue; if (!access) { if (!isServiceEnabled(mCn)) { return true; // already disabled } // show a friendly dialog new FriendlyWarningDialogFragment() .setServiceInfo(mCn, label, mParent) .show(mParent.getFragmentManager(), "friendlydialog"); return false; } else { if (isServiceEnabled(mCn)) { return true; // already enabled } // show a scary dialog new ScaryWarningDialogFragment() .setServiceInfo(mCn, label, mParent) .show(mParent.getFragmentManager(), "dialog"); return false; } }); } public void disable(final ComponentName cn) { logSpecialPermissionChange(true, cn.getPackageName()); mNm.setNotificationListenerAccessGranted(cn, false); AsyncTask.execute(() -> { if (!mNm.isNotificationPolicyAccessGrantedForPackage( cn.getPackageName())) { mNm.removeAutomaticZenRules(cn.getPackageName()); } }); } protected void enable(ComponentName cn) { logSpecialPermissionChange(true, cn.getPackageName()); mNm.setNotificationListenerAccessGranted(cn, true); } protected boolean isServiceEnabled(ComponentName cn) { return mNm.isNotificationListenerAccessGranted(cn); } @VisibleForTesting void logSpecialPermissionChange(boolean enable, String packageName) { final int logCategory = enable ? SettingsEnums.APP_SPECIAL_PERMISSION_NOTIVIEW_ALLOW : SettingsEnums.APP_SPECIAL_PERMISSION_NOTIVIEW_DENY; FeatureFactory.getFactory(mContext).getMetricsFeatureProvider().action(mContext, logCategory, packageName); } } No newline at end of file src/com/android/settings/applications/specialaccess/notificationaccess/HeaderPreferenceController.java 0 → 100644 +107 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.applications.specialaccess.notificationaccess; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.util.IconDrawableFactory; import android.view.View; import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.OnLifecycleEvent; import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.core.BasePreferenceController; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.widget.EntityHeaderController; import com.android.settingslib.applications.AppUtils; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.widget.LayoutPreference; public class HeaderPreferenceController extends BasePreferenceController implements PreferenceControllerMixin, LifecycleObserver { private DashboardFragment mFragment; private EntityHeaderController mHeaderController; private PackageInfo mPackageInfo; private PackageManager mPm; private CharSequence mServiceName; public HeaderPreferenceController(Context context, String key) { super(context, key); } public HeaderPreferenceController setFragment(DashboardFragment fragment) { mFragment = fragment; return this; } public HeaderPreferenceController setPackageInfo(PackageInfo packageInfo) { mPackageInfo = packageInfo; return this; } public HeaderPreferenceController setPm(PackageManager pm) { mPm = pm; return this; } public HeaderPreferenceController setServiceName(CharSequence serviceName) { mServiceName = serviceName; return this; } @Override public int getAvailabilityStatus() { return AVAILABLE; } @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); if (mFragment == null) { return; } LayoutPreference pref = screen.findPreference(getPreferenceKey()); mHeaderController = EntityHeaderController.newInstance( mFragment.getActivity(), mFragment, pref.findViewById(R.id.entity_header)); pref = mHeaderController .setRecyclerView(mFragment.getListView(), mFragment.getSettingsLifecycle()) .setIcon(IconDrawableFactory.newInstance(mFragment.getActivity()) .getBadgedIcon(mPackageInfo.applicationInfo)) .setLabel(mPackageInfo.applicationInfo.loadLabel(mPm)) .setSummary(mServiceName) .setIsInstantApp(AppUtils.isInstant(mPackageInfo.applicationInfo)) .setPackageName(mPackageInfo.packageName) .setUid(mPackageInfo.applicationInfo.uid) .setHasAppInfoLink(true) .setButtonActions(EntityHeaderController.ActionType.ACTION_NONE, EntityHeaderController.ActionType.ACTION_NONE) .done(mFragment.getActivity(), mContext); pref.findViewById(R.id.entity_header).setVisibility(View.VISIBLE); } @OnLifecycleEvent(Lifecycle.Event.ON_START) public void onStart() { if (mHeaderController != null) { mHeaderController.styleActionBar(mFragment.getActivity()); } } } src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java +95 −99 Original line number Diff line number Diff line Loading @@ -16,52 +16,56 @@ package com.android.settings.applications.specialaccess.notificationaccess; import static com.android.settings.applications.AppInfoBase.ARG_PACKAGE_NAME; import android.app.Activity; import android.app.NotificationManager; import android.app.settings.SettingsEnums; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.os.AsyncTask; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.service.notification.NotificationListenerService; import android.util.IconDrawableFactory; import android.util.Log; import android.util.Slog; import androidx.annotation.VisibleForTesting; import androidx.appcompat.app.AlertDialog; import androidx.preference.Preference; import androidx.preference.SwitchPreference; import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.applications.AppInfoBase; import com.android.settings.overlay.FeatureFactory; import com.android.settings.widget.EntityHeaderController; import com.android.settingslib.applications.AppUtils; import com.android.settings.SettingsActivity; import com.android.settings.applications.manageapplications.ManageApplications; import com.android.settings.dashboard.DashboardFragment; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.applications.ApplicationsState; import java.util.ArrayList; import java.util.List; import java.util.Objects; public class NotificationAccessDetails extends AppInfoBase { public class NotificationAccessDetails extends DashboardFragment { private static final String TAG = "NotifAccessDetails"; private static final String SWITCH_PREF_KEY = "notification_access_switch"; private boolean mCreated; private ComponentName mComponentName; private CharSequence mServiceName; protected PackageInfo mPackageInfo; protected int mUserId; protected String mPackageName; protected RestrictedLockUtils.EnforcedAdmin mAppsControlDisallowedAdmin; protected boolean mAppsControlDisallowedBySystem; private boolean mIsNls; private NotificationManager mNm; private PackageManager mPm; @Override public void onCreate(Bundle savedInstanceState) { public void onAttach(Context context) { super.onAttach(context); final Intent intent = getIntent(); if (mComponentName == null && intent != null) { String cn = intent.getStringExtra(Settings.EXTRA_NOTIFICATION_LISTENER_COMPONENT_NAME); Loading @@ -73,38 +77,20 @@ public class NotificationAccessDetails extends AppInfoBase { } } } super.onCreate(savedInstanceState); mNm = getContext().getSystemService(NotificationManager.class); mPm = getPackageManager(); addPreferencesFromResource(R.xml.notification_access_permission_details); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); if (mCreated) { Log.w(TAG, "onActivityCreated: ignoring duplicate call"); return; } mCreated = true; if (mPackageInfo == null) return; retrieveAppEntry(); loadNotificationListenerService(); final Activity activity = getActivity(); final Preference pref = EntityHeaderController .newInstance(activity, this, null /* header */) .setRecyclerView(getListView(), getSettingsLifecycle()) .setIcon(IconDrawableFactory.newInstance(getContext()) .getBadgedIcon(mPackageInfo.applicationInfo)) .setLabel(mPackageInfo.applicationInfo.loadLabel(mPm)) .setSummary(mServiceName) .setIsInstantApp(AppUtils.isInstant(mPackageInfo.applicationInfo)) .setPackageName(mPackageName) .setUid(mPackageInfo.applicationInfo.uid) .setHasAppInfoLink(true) .setButtonActions(EntityHeaderController.ActionType.ACTION_NONE, EntityHeaderController.ActionType.ACTION_NONE) .done(activity, getPrefContext()); getPreferenceScreen().addPreference(pref); use(ApprovalPreferenceController.class) .setPkgInfo(mPackageInfo) .setCn(mComponentName) .setNm(context.getSystemService(NotificationManager.class)) .setPm(context.getPackageManager()) .setParent(this); use(HeaderPreferenceController.class) .setFragment(this) .setPackageInfo(mPackageInfo) .setPm(context.getPackageManager()) .setServiceName(mServiceName); } @Override Loading @@ -112,9 +98,7 @@ public class NotificationAccessDetails extends AppInfoBase { return SettingsEnums.NOTIFICATION_ACCESS_DETAIL; } @Override protected boolean refreshUi() { final Context context = getContext(); if (mComponentName == null) { // No service given Slog.d(TAG, "No component name provided"); Loading @@ -130,72 +114,74 @@ public class NotificationAccessDetails extends AppInfoBase { Slog.d(TAG, "NLSes aren't allowed in work profiles"); return false; } updatePreference(findPreference(SWITCH_PREF_KEY)); return true; } @Override protected AlertDialog createDialog(int id, int errorCode) { return null; } public void updatePreference(SwitchPreference preference) { final CharSequence label = mPackageInfo.applicationInfo.loadLabel(mPm); preference.setChecked(isServiceEnabled(mComponentName)); preference.setOnPreferenceChangeListener((p, newValue) -> { final boolean access = (Boolean) newValue; if (!access) { if (!isServiceEnabled(mComponentName)) { return true; // already disabled } // show a friendly dialog new FriendlyWarningDialogFragment() .setServiceInfo(mComponentName, label, this) .show(getFragmentManager(), "friendlydialog"); return false; } else { if (isServiceEnabled(mComponentName)) { return true; // already enabled public void onResume() { super.onResume(); mAppsControlDisallowedAdmin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced( getActivity(), UserManager.DISALLOW_APPS_CONTROL, mUserId); mAppsControlDisallowedBySystem = RestrictedLockUtilsInternal.hasBaseUserRestriction( getActivity(), UserManager.DISALLOW_APPS_CONTROL, mUserId); if (!refreshUi()) { setIntentAndFinish(true /* appChanged */); } // show a scary dialog new ScaryWarningDialogFragment() .setServiceInfo(mComponentName, label, this) .show(getFragmentManager(), "dialog"); return false; } }); protected void setIntentAndFinish(boolean appChanged) { Log.i(TAG, "appChanged=" + appChanged); Intent intent = new Intent(); intent.putExtra(ManageApplications.APP_CHG, appChanged); SettingsActivity sa = (SettingsActivity) getActivity(); sa.finishPreferencePanel(Activity.RESULT_OK, intent); } @VisibleForTesting void logSpecialPermissionChange(boolean enable, String packageName) { int logCategory = enable ? SettingsEnums.APP_SPECIAL_PERMISSION_NOTIVIEW_ALLOW : SettingsEnums.APP_SPECIAL_PERMISSION_NOTIVIEW_DENY; FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider().action(getContext(), logCategory, packageName); protected void retrieveAppEntry() { final Bundle args = getArguments(); mPackageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null; Intent intent = (args == null) ? getIntent() : (Intent) args.getParcelable("intent"); if (mPackageName == null) { if (intent != null && intent.getData() != null) { mPackageName = intent.getData().getSchemeSpecificPart(); } } if (intent != null && intent.hasExtra(Intent.EXTRA_USER_HANDLE)) { mUserId = ((UserHandle) intent.getParcelableExtra( Intent.EXTRA_USER_HANDLE)).getIdentifier(); } else { mUserId = UserHandle.myUserId(); } public void disable(final ComponentName cn) { logSpecialPermissionChange(true, cn.getPackageName()); mNm.setNotificationListenerAccessGranted(cn, false); AsyncTask.execute(() -> { if (!mNm.isNotificationPolicyAccessGrantedForPackage( cn.getPackageName())) { mNm.removeAutomaticZenRules(cn.getPackageName()); try { mPackageInfo = mPm.getPackageInfoAsUser(mPackageName, PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.GET_SIGNING_CERTIFICATES | PackageManager.GET_PERMISSIONS, mUserId); } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "Exception when retrieving package:" + mPackageName, e); } }); refreshUi(); } protected void enable(ComponentName cn) { logSpecialPermissionChange(true, cn.getPackageName()); mNm.setNotificationListenerAccessGranted(cn, true); refreshUi(); // Dialogs only have access to the parent fragment, not the controller, so pass the information // along to keep business logic out of this file public void disable(final ComponentName cn) { final PreferenceScreen screen = getPreferenceScreen(); ApprovalPreferenceController controller = use(ApprovalPreferenceController.class); controller.disable(cn); controller.updateState(screen.findPreference(controller.getPreferenceKey())); } protected boolean isServiceEnabled(ComponentName cn) { return mNm.isNotificationListenerAccessGranted(cn); protected void enable(ComponentName cn) { final PreferenceScreen screen = getPreferenceScreen(); ApprovalPreferenceController controller = use(ApprovalPreferenceController.class); controller.enable(cn); controller.updateState(screen.findPreference(controller.getPreferenceKey())); } // To save binder calls, load this in the fragment rather than each preference controller protected void loadNotificationListenerService() { mIsNls = false; Loading @@ -218,4 +204,14 @@ public class NotificationAccessDetails extends AppInfoBase { } } } @Override protected int getPreferenceScreenResId() { return R.xml.notification_access_permission_details; } @Override protected String getLogTag() { return TAG; } } No newline at end of file Loading
res/values/strings.xml +1 −1 Original line number Diff line number Diff line Loading @@ -8587,7 +8587,7 @@ <string name="recent_conversations">Recent conversations</string> <!-- [CHAR LIMIT=20] button title --> <string name="conversation_settings_clear_recents">Clear recents</string> <string name="conversation_settings_clear_recents">Clear all of the recent ones</string> <!-- a11y string --> <string name="clear">Clear</string>
res/xml/notification_access_permission_details.xml +8 −1 Original line number Diff line number Diff line Loading @@ -17,11 +17,18 @@ <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" xmlns:settings="http://schemas.android.com/apk/res-auto" android:key="notification_access_permission_detail_settings" android:title="@string/manage_notification_access_title"> <com.android.settingslib.widget.LayoutPreference android:key="pref_app_header" android:layout="@layout/settings_entity_header" settings:controller="com.android.settings.applications.specialaccess.notificationaccess.HeaderPreferenceController"/> <com.android.settings.widget.FilterTouchesSwitchPreference android:key="notification_access_switch" android:title="@string/notification_access_detail_switch"/> android:title="@string/notification_access_detail_switch" settings:controller="com.android.settings.applications.specialaccess.notificationaccess.ApprovalPreferenceController"/> </PreferenceScreen> No newline at end of file
src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java 0 → 100644 +135 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.applications.specialaccess.notificationaccess; import android.app.NotificationManager; import android.app.settings.SettingsEnums; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.AsyncTask; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; import androidx.preference.SwitchPreference; import com.android.settings.core.BasePreferenceController; import com.android.settings.overlay.FeatureFactory; public class ApprovalPreferenceController extends BasePreferenceController { private static final String TAG = "ApprovalPrefController"; private PackageInfo mPkgInfo; private ComponentName mCn; private PreferenceFragmentCompat mParent; private NotificationManager mNm; private PackageManager mPm; public ApprovalPreferenceController(Context context, String key) { super(context, key); } public ApprovalPreferenceController setPkgInfo(PackageInfo pkgInfo) { mPkgInfo = pkgInfo; return this; } public ApprovalPreferenceController setCn(ComponentName cn) { mCn = cn; return this; } public ApprovalPreferenceController setParent(PreferenceFragmentCompat parent) { mParent = parent; return this; } public ApprovalPreferenceController setNm(NotificationManager nm) { mNm = nm; return this; } public ApprovalPreferenceController setPm(PackageManager pm) { mPm = pm; return this; } @Override public int getAvailabilityStatus() { return AVAILABLE; } @Override public void updateState(Preference pref) { final SwitchPreference preference = (SwitchPreference) pref; final CharSequence label = mPkgInfo.applicationInfo.loadLabel(mPm); preference.setChecked(isServiceEnabled(mCn)); preference.setOnPreferenceChangeListener((p, newValue) -> { final boolean access = (Boolean) newValue; if (!access) { if (!isServiceEnabled(mCn)) { return true; // already disabled } // show a friendly dialog new FriendlyWarningDialogFragment() .setServiceInfo(mCn, label, mParent) .show(mParent.getFragmentManager(), "friendlydialog"); return false; } else { if (isServiceEnabled(mCn)) { return true; // already enabled } // show a scary dialog new ScaryWarningDialogFragment() .setServiceInfo(mCn, label, mParent) .show(mParent.getFragmentManager(), "dialog"); return false; } }); } public void disable(final ComponentName cn) { logSpecialPermissionChange(true, cn.getPackageName()); mNm.setNotificationListenerAccessGranted(cn, false); AsyncTask.execute(() -> { if (!mNm.isNotificationPolicyAccessGrantedForPackage( cn.getPackageName())) { mNm.removeAutomaticZenRules(cn.getPackageName()); } }); } protected void enable(ComponentName cn) { logSpecialPermissionChange(true, cn.getPackageName()); mNm.setNotificationListenerAccessGranted(cn, true); } protected boolean isServiceEnabled(ComponentName cn) { return mNm.isNotificationListenerAccessGranted(cn); } @VisibleForTesting void logSpecialPermissionChange(boolean enable, String packageName) { final int logCategory = enable ? SettingsEnums.APP_SPECIAL_PERMISSION_NOTIVIEW_ALLOW : SettingsEnums.APP_SPECIAL_PERMISSION_NOTIVIEW_DENY; FeatureFactory.getFactory(mContext).getMetricsFeatureProvider().action(mContext, logCategory, packageName); } } No newline at end of file
src/com/android/settings/applications/specialaccess/notificationaccess/HeaderPreferenceController.java 0 → 100644 +107 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.applications.specialaccess.notificationaccess; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.util.IconDrawableFactory; import android.view.View; import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.OnLifecycleEvent; import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.core.BasePreferenceController; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.widget.EntityHeaderController; import com.android.settingslib.applications.AppUtils; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.widget.LayoutPreference; public class HeaderPreferenceController extends BasePreferenceController implements PreferenceControllerMixin, LifecycleObserver { private DashboardFragment mFragment; private EntityHeaderController mHeaderController; private PackageInfo mPackageInfo; private PackageManager mPm; private CharSequence mServiceName; public HeaderPreferenceController(Context context, String key) { super(context, key); } public HeaderPreferenceController setFragment(DashboardFragment fragment) { mFragment = fragment; return this; } public HeaderPreferenceController setPackageInfo(PackageInfo packageInfo) { mPackageInfo = packageInfo; return this; } public HeaderPreferenceController setPm(PackageManager pm) { mPm = pm; return this; } public HeaderPreferenceController setServiceName(CharSequence serviceName) { mServiceName = serviceName; return this; } @Override public int getAvailabilityStatus() { return AVAILABLE; } @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); if (mFragment == null) { return; } LayoutPreference pref = screen.findPreference(getPreferenceKey()); mHeaderController = EntityHeaderController.newInstance( mFragment.getActivity(), mFragment, pref.findViewById(R.id.entity_header)); pref = mHeaderController .setRecyclerView(mFragment.getListView(), mFragment.getSettingsLifecycle()) .setIcon(IconDrawableFactory.newInstance(mFragment.getActivity()) .getBadgedIcon(mPackageInfo.applicationInfo)) .setLabel(mPackageInfo.applicationInfo.loadLabel(mPm)) .setSummary(mServiceName) .setIsInstantApp(AppUtils.isInstant(mPackageInfo.applicationInfo)) .setPackageName(mPackageInfo.packageName) .setUid(mPackageInfo.applicationInfo.uid) .setHasAppInfoLink(true) .setButtonActions(EntityHeaderController.ActionType.ACTION_NONE, EntityHeaderController.ActionType.ACTION_NONE) .done(mFragment.getActivity(), mContext); pref.findViewById(R.id.entity_header).setVisibility(View.VISIBLE); } @OnLifecycleEvent(Lifecycle.Event.ON_START) public void onStart() { if (mHeaderController != null) { mHeaderController.styleActionBar(mFragment.getActivity()); } } }
src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java +95 −99 Original line number Diff line number Diff line Loading @@ -16,52 +16,56 @@ package com.android.settings.applications.specialaccess.notificationaccess; import static com.android.settings.applications.AppInfoBase.ARG_PACKAGE_NAME; import android.app.Activity; import android.app.NotificationManager; import android.app.settings.SettingsEnums; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.os.AsyncTask; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.service.notification.NotificationListenerService; import android.util.IconDrawableFactory; import android.util.Log; import android.util.Slog; import androidx.annotation.VisibleForTesting; import androidx.appcompat.app.AlertDialog; import androidx.preference.Preference; import androidx.preference.SwitchPreference; import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.applications.AppInfoBase; import com.android.settings.overlay.FeatureFactory; import com.android.settings.widget.EntityHeaderController; import com.android.settingslib.applications.AppUtils; import com.android.settings.SettingsActivity; import com.android.settings.applications.manageapplications.ManageApplications; import com.android.settings.dashboard.DashboardFragment; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.applications.ApplicationsState; import java.util.ArrayList; import java.util.List; import java.util.Objects; public class NotificationAccessDetails extends AppInfoBase { public class NotificationAccessDetails extends DashboardFragment { private static final String TAG = "NotifAccessDetails"; private static final String SWITCH_PREF_KEY = "notification_access_switch"; private boolean mCreated; private ComponentName mComponentName; private CharSequence mServiceName; protected PackageInfo mPackageInfo; protected int mUserId; protected String mPackageName; protected RestrictedLockUtils.EnforcedAdmin mAppsControlDisallowedAdmin; protected boolean mAppsControlDisallowedBySystem; private boolean mIsNls; private NotificationManager mNm; private PackageManager mPm; @Override public void onCreate(Bundle savedInstanceState) { public void onAttach(Context context) { super.onAttach(context); final Intent intent = getIntent(); if (mComponentName == null && intent != null) { String cn = intent.getStringExtra(Settings.EXTRA_NOTIFICATION_LISTENER_COMPONENT_NAME); Loading @@ -73,38 +77,20 @@ public class NotificationAccessDetails extends AppInfoBase { } } } super.onCreate(savedInstanceState); mNm = getContext().getSystemService(NotificationManager.class); mPm = getPackageManager(); addPreferencesFromResource(R.xml.notification_access_permission_details); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); if (mCreated) { Log.w(TAG, "onActivityCreated: ignoring duplicate call"); return; } mCreated = true; if (mPackageInfo == null) return; retrieveAppEntry(); loadNotificationListenerService(); final Activity activity = getActivity(); final Preference pref = EntityHeaderController .newInstance(activity, this, null /* header */) .setRecyclerView(getListView(), getSettingsLifecycle()) .setIcon(IconDrawableFactory.newInstance(getContext()) .getBadgedIcon(mPackageInfo.applicationInfo)) .setLabel(mPackageInfo.applicationInfo.loadLabel(mPm)) .setSummary(mServiceName) .setIsInstantApp(AppUtils.isInstant(mPackageInfo.applicationInfo)) .setPackageName(mPackageName) .setUid(mPackageInfo.applicationInfo.uid) .setHasAppInfoLink(true) .setButtonActions(EntityHeaderController.ActionType.ACTION_NONE, EntityHeaderController.ActionType.ACTION_NONE) .done(activity, getPrefContext()); getPreferenceScreen().addPreference(pref); use(ApprovalPreferenceController.class) .setPkgInfo(mPackageInfo) .setCn(mComponentName) .setNm(context.getSystemService(NotificationManager.class)) .setPm(context.getPackageManager()) .setParent(this); use(HeaderPreferenceController.class) .setFragment(this) .setPackageInfo(mPackageInfo) .setPm(context.getPackageManager()) .setServiceName(mServiceName); } @Override Loading @@ -112,9 +98,7 @@ public class NotificationAccessDetails extends AppInfoBase { return SettingsEnums.NOTIFICATION_ACCESS_DETAIL; } @Override protected boolean refreshUi() { final Context context = getContext(); if (mComponentName == null) { // No service given Slog.d(TAG, "No component name provided"); Loading @@ -130,72 +114,74 @@ public class NotificationAccessDetails extends AppInfoBase { Slog.d(TAG, "NLSes aren't allowed in work profiles"); return false; } updatePreference(findPreference(SWITCH_PREF_KEY)); return true; } @Override protected AlertDialog createDialog(int id, int errorCode) { return null; } public void updatePreference(SwitchPreference preference) { final CharSequence label = mPackageInfo.applicationInfo.loadLabel(mPm); preference.setChecked(isServiceEnabled(mComponentName)); preference.setOnPreferenceChangeListener((p, newValue) -> { final boolean access = (Boolean) newValue; if (!access) { if (!isServiceEnabled(mComponentName)) { return true; // already disabled } // show a friendly dialog new FriendlyWarningDialogFragment() .setServiceInfo(mComponentName, label, this) .show(getFragmentManager(), "friendlydialog"); return false; } else { if (isServiceEnabled(mComponentName)) { return true; // already enabled public void onResume() { super.onResume(); mAppsControlDisallowedAdmin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced( getActivity(), UserManager.DISALLOW_APPS_CONTROL, mUserId); mAppsControlDisallowedBySystem = RestrictedLockUtilsInternal.hasBaseUserRestriction( getActivity(), UserManager.DISALLOW_APPS_CONTROL, mUserId); if (!refreshUi()) { setIntentAndFinish(true /* appChanged */); } // show a scary dialog new ScaryWarningDialogFragment() .setServiceInfo(mComponentName, label, this) .show(getFragmentManager(), "dialog"); return false; } }); protected void setIntentAndFinish(boolean appChanged) { Log.i(TAG, "appChanged=" + appChanged); Intent intent = new Intent(); intent.putExtra(ManageApplications.APP_CHG, appChanged); SettingsActivity sa = (SettingsActivity) getActivity(); sa.finishPreferencePanel(Activity.RESULT_OK, intent); } @VisibleForTesting void logSpecialPermissionChange(boolean enable, String packageName) { int logCategory = enable ? SettingsEnums.APP_SPECIAL_PERMISSION_NOTIVIEW_ALLOW : SettingsEnums.APP_SPECIAL_PERMISSION_NOTIVIEW_DENY; FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider().action(getContext(), logCategory, packageName); protected void retrieveAppEntry() { final Bundle args = getArguments(); mPackageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null; Intent intent = (args == null) ? getIntent() : (Intent) args.getParcelable("intent"); if (mPackageName == null) { if (intent != null && intent.getData() != null) { mPackageName = intent.getData().getSchemeSpecificPart(); } } if (intent != null && intent.hasExtra(Intent.EXTRA_USER_HANDLE)) { mUserId = ((UserHandle) intent.getParcelableExtra( Intent.EXTRA_USER_HANDLE)).getIdentifier(); } else { mUserId = UserHandle.myUserId(); } public void disable(final ComponentName cn) { logSpecialPermissionChange(true, cn.getPackageName()); mNm.setNotificationListenerAccessGranted(cn, false); AsyncTask.execute(() -> { if (!mNm.isNotificationPolicyAccessGrantedForPackage( cn.getPackageName())) { mNm.removeAutomaticZenRules(cn.getPackageName()); try { mPackageInfo = mPm.getPackageInfoAsUser(mPackageName, PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.GET_SIGNING_CERTIFICATES | PackageManager.GET_PERMISSIONS, mUserId); } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "Exception when retrieving package:" + mPackageName, e); } }); refreshUi(); } protected void enable(ComponentName cn) { logSpecialPermissionChange(true, cn.getPackageName()); mNm.setNotificationListenerAccessGranted(cn, true); refreshUi(); // Dialogs only have access to the parent fragment, not the controller, so pass the information // along to keep business logic out of this file public void disable(final ComponentName cn) { final PreferenceScreen screen = getPreferenceScreen(); ApprovalPreferenceController controller = use(ApprovalPreferenceController.class); controller.disable(cn); controller.updateState(screen.findPreference(controller.getPreferenceKey())); } protected boolean isServiceEnabled(ComponentName cn) { return mNm.isNotificationListenerAccessGranted(cn); protected void enable(ComponentName cn) { final PreferenceScreen screen = getPreferenceScreen(); ApprovalPreferenceController controller = use(ApprovalPreferenceController.class); controller.enable(cn); controller.updateState(screen.findPreference(controller.getPreferenceKey())); } // To save binder calls, load this in the fragment rather than each preference controller protected void loadNotificationListenerService() { mIsNls = false; Loading @@ -218,4 +204,14 @@ public class NotificationAccessDetails extends AppInfoBase { } } } @Override protected int getPreferenceScreenResId() { return R.xml.notification_access_permission_details; } @Override protected String getLogTag() { return TAG; } } No newline at end of file