diff --git a/res/values/strings.xml b/res/values/strings.xml index 50f9424567e..670a01af812 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -8587,7 +8587,7 @@ Recent conversations - Clear recents + Clear all of the recent ones Clear diff --git a/res/xml/notification_access_permission_details.xml b/res/xml/notification_access_permission_details.xml index d956ce911ac..e0c7c1a2549 100644 --- a/res/xml/notification_access_permission_details.xml +++ b/res/xml/notification_access_permission_details.xml @@ -17,11 +17,18 @@ + + + android:title="@string/notification_access_detail_switch" + settings:controller="com.android.settings.applications.specialaccess.notificationaccess.ApprovalPreferenceController"/> \ No newline at end of file diff --git a/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java b/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java new file mode 100644 index 00000000000..a43b9fd9145 --- /dev/null +++ b/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java @@ -0,0 +1,135 @@ +/* + * 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 diff --git a/src/com/android/settings/applications/specialaccess/notificationaccess/HeaderPreferenceController.java b/src/com/android/settings/applications/specialaccess/notificationaccess/HeaderPreferenceController.java new file mode 100644 index 00000000000..94736e4c21f --- /dev/null +++ b/src/com/android/settings/applications/specialaccess/notificationaccess/HeaderPreferenceController.java @@ -0,0 +1,107 @@ +/* + * 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()); + } + } +} diff --git a/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java b/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java index 58a6d7f5956..c0416a3858c 100644 --- a/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java +++ b/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java @@ -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); @@ -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 @@ -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"); @@ -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 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 */); + } } - 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 - } - // 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); + } + + 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(); } - }); - } - - @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); + } + if (intent != null && intent.hasExtra(Intent.EXTRA_USER_HANDLE)) { + mUserId = ((UserHandle) intent.getParcelableExtra( + Intent.EXTRA_USER_HANDLE)).getIdentifier(); + } else { + mUserId = UserHandle.myUserId(); + } + + 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); + } } + // 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) { - logSpecialPermissionChange(true, cn.getPackageName()); - mNm.setNotificationListenerAccessGranted(cn, false); - AsyncTask.execute(() -> { - if (!mNm.isNotificationPolicyAccessGrantedForPackage( - cn.getPackageName())) { - mNm.removeAutomaticZenRules(cn.getPackageName()); - } - }); - refreshUi(); + final PreferenceScreen screen = getPreferenceScreen(); + ApprovalPreferenceController controller = use(ApprovalPreferenceController.class); + controller.disable(cn); + controller.updateState(screen.findPreference(controller.getPreferenceKey())); } protected void enable(ComponentName cn) { - logSpecialPermissionChange(true, cn.getPackageName()); - mNm.setNotificationListenerAccessGranted(cn, true); - refreshUi(); - } - - protected boolean isServiceEnabled(ComponentName cn) { - return mNm.isNotificationListenerAccessGranted(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; @@ -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 diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetailsTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetailsTest.java deleted file mode 100644 index 26a78d02f07..00000000000 --- a/tests/robotests/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetailsTest.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2019 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 static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; - -import android.content.Context; - -import com.android.internal.logging.nano.MetricsProto; -import com.android.settings.testutils.FakeFeatureFactory; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; - -@RunWith(RobolectricTestRunner.class) -public class NotificationAccessDetailsTest { - - private Context mContext; - private FakeFeatureFactory mFeatureFactory; - private NotificationAccessDetails mFragment; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mFeatureFactory = FakeFeatureFactory.setupForTest(); - mFragment = spy(new NotificationAccessDetails()); - mContext = RuntimeEnvironment.application; - doReturn(mContext).when(mFragment).getContext(); - - } - - @Test - public void logSpecialPermissionChange() { - mFragment.logSpecialPermissionChange(true, "app"); - verify(mFeatureFactory.metricsFeatureProvider).action( - mContext, - MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_NOTIVIEW_ALLOW, - "app"); - - mFragment.logSpecialPermissionChange(false, "app"); - verify(mFeatureFactory.metricsFeatureProvider).action( - mContext, - MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_NOTIVIEW_DENY, - "app"); - } -} diff --git a/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java b/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java new file mode 100644 index 00000000000..064f8134c67 --- /dev/null +++ b/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java @@ -0,0 +1,110 @@ +/* + * 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 static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.NotificationManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; + +import androidx.preference.SwitchPreference; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.testutils.FakeFeatureFactory; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +public class ApprovalPreferenceControllerTest { + + private Context mContext; + private FakeFeatureFactory mFeatureFactory; + @Mock + private NotificationAccessDetails mFragment; + private ApprovalPreferenceController mController; + @Mock + NotificationManager mNm; + @Mock + PackageManager mPm; + PackageInfo mPkgInfo; + ComponentName mCn = new ComponentName("a", "b"); + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mFeatureFactory = FakeFeatureFactory.setupForTest(); + mContext = spy(ApplicationProvider.getApplicationContext()); + doReturn(mContext).when(mFragment).getContext(); + + mPkgInfo = new PackageInfo(); + mPkgInfo.applicationInfo = mock(ApplicationInfo.class); + when(mPkgInfo.applicationInfo.loadLabel(mPm)).thenReturn("LABEL"); + + mController = new ApprovalPreferenceController(mContext, "key"); + mController.setCn(mCn); + mController.setNm(mNm); + mController.setParent(mFragment); + mController.setPkgInfo(mPkgInfo); + } + + @Test + public void updateState_checked() { + when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true); + SwitchPreference pref = new SwitchPreference(mContext); + + mController.updateState(pref); + assertThat(pref.isChecked()).isTrue(); + } + + @Test + public void enable() { + mController.enable(mCn); + verify(mFeatureFactory.metricsFeatureProvider).action( + mContext, + MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_NOTIVIEW_ALLOW, + "a"); + + verify(mNm).setNotificationListenerAccessGranted(mCn, true); + } + + @Test + public void disable() { + mController.disable(mCn); + verify(mFeatureFactory.metricsFeatureProvider).action( + mContext, + MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_NOTIVIEW_ALLOW, + "a"); + + verify(mNm).setNotificationListenerAccessGranted(mCn, false); + } +}