/* * 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.notification.zen; import android.app.Application; import android.app.settings.SettingsEnums; import android.content.Context; import android.os.Bundle; import android.os.UserHandle; import androidx.annotation.VisibleForTesting; import androidx.core.text.BidiFormatter; import androidx.fragment.app.Fragment; import androidx.preference.Preference; import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.applications.AppInfoBase; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.core.SubSettingLauncher; import com.android.settings.notification.NotificationBackend; import com.android.settings.notification.app.AppChannelsBypassingDndSettings; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.widget.AppPreference; import java.util.ArrayList; import java.util.List; /** * When clicked, populates the PreferenceScreen with apps that aren't already bypassing DND. The * user can click on these Preferences to allow notification channels from the app to bypass DND. */ public class ZenModeAddBypassingAppsPreferenceController extends AbstractPreferenceController implements PreferenceControllerMixin { private static final String KEY = "zen_mode_non_bypassing_apps_list"; private static final String KEY_ADD = "zen_mode_bypassing_apps_add"; private final NotificationBackend mNotificationBackend; @VisibleForTesting ApplicationsState mApplicationsState; @VisibleForTesting PreferenceScreen mPreferenceScreen; @VisibleForTesting PreferenceCategory mPreferenceCategory; @VisibleForTesting Context mPrefContext; private Preference mAddPreference; private ApplicationsState.Session mAppSession; private Fragment mHostFragment; public ZenModeAddBypassingAppsPreferenceController(Context context, Application app, Fragment host, NotificationBackend notificationBackend) { this(context, app == null ? null : ApplicationsState.getInstance(app), host, notificationBackend); } private ZenModeAddBypassingAppsPreferenceController(Context context, ApplicationsState appState, Fragment host, NotificationBackend notificationBackend) { super(context); mNotificationBackend = notificationBackend; mApplicationsState = appState; mHostFragment = host; } @Override public void displayPreference(PreferenceScreen screen) { mPreferenceScreen = screen; mAddPreference = screen.findPreference(KEY_ADD); mAddPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { mAddPreference.setVisible(false); if (mApplicationsState != null && mHostFragment != null) { mAppSession = mApplicationsState.newSession(mAppSessionCallbacks, mHostFragment.getLifecycle()); } return true; } }); mPrefContext = screen.getContext(); super.displayPreference(screen); } @Override public boolean isAvailable() { return true; } @Override public String getPreferenceKey() { return KEY; } /** * Call this method to trigger the app list to refresh. */ public void updateAppList() { if (mAppSession == null) { return; } ApplicationsState.AppFilter filter = ApplicationsState.FILTER_ALL_ENABLED; List apps = mAppSession.rebuild(filter, ApplicationsState.ALPHA_COMPARATOR); updateAppList(apps); } @VisibleForTesting void updateAppList(List apps) { if (apps == null) { return; } if (mPreferenceCategory == null) { mPreferenceCategory = new PreferenceCategory(mPrefContext); mPreferenceCategory.setTitle(R.string.zen_mode_bypassing_apps_add_header); mPreferenceScreen.addPreference(mPreferenceCategory); } List appsWithNoBypassingDndNotificationChannels = new ArrayList<>(); for (ApplicationsState.AppEntry entry : apps) { String pkg = entry.info.packageName; mApplicationsState.ensureIcon(entry); final int appChannels = mNotificationBackend.getChannelCount(pkg, entry.info.uid); final int appChannelsBypassingDnd = mNotificationBackend .getNotificationChannelsBypassingDnd(pkg, entry.info.uid).getList().size(); if (appChannelsBypassingDnd == 0 && appChannels > 0) { final String key = ZenModeAllBypassingAppsPreferenceController.getKey(pkg); Preference pref = mPreferenceCategory.findPreference(""); if (pref == null) { pref = new AppPreference(mPrefContext); pref.setKey(key); pref.setOnPreferenceClickListener(preference -> { Bundle args = new Bundle(); args.putString(AppInfoBase.ARG_PACKAGE_NAME, entry.info.packageName); args.putInt(AppInfoBase.ARG_PACKAGE_UID, entry.info.uid); new SubSettingLauncher(mContext) .setDestination(AppChannelsBypassingDndSettings.class.getName()) .setArguments(args) .setResultListener(mHostFragment, 0) .setUserHandle(new UserHandle(UserHandle.getUserId(entry.info.uid))) .setSourceMetricsCategory( SettingsEnums.NOTIFICATION_ZEN_MODE_OVERRIDING_APP) .launch(); return true; }); } pref.setTitle(BidiFormatter.getInstance().unicodeWrap(entry.label)); pref.setIcon(entry.icon); appsWithNoBypassingDndNotificationChannels.add(pref); } } if (appsWithNoBypassingDndNotificationChannels.size() == 0) { Preference pref = mPreferenceCategory.findPreference( ZenModeAllBypassingAppsPreferenceController.KEY_NO_APPS); if (pref == null) { pref = new Preference(mPrefContext); pref.setKey(ZenModeAllBypassingAppsPreferenceController.KEY_NO_APPS); pref.setTitle(R.string.zen_mode_bypassing_apps_subtext_none); } mPreferenceCategory.addPreference(pref); } if (ZenModeAllBypassingAppsPreferenceController.hasAppListChanged( appsWithNoBypassingDndNotificationChannels, mPreferenceCategory)) { mPreferenceCategory.removeAll(); for (Preference prefToAdd : appsWithNoBypassingDndNotificationChannels) { mPreferenceCategory.addPreference(prefToAdd); } } } private final ApplicationsState.Callbacks mAppSessionCallbacks = new ApplicationsState.Callbacks() { @Override public void onRunningStateChanged(boolean running) { updateAppList(); } @Override public void onPackageListChanged() { updateAppList(); } @Override public void onRebuildComplete(ArrayList apps) { updateAppList(apps); } @Override public void onPackageIconChanged() { updateAppList(); } @Override public void onPackageSizeChanged(String packageName) { updateAppList(); } @Override public void onAllSizesComputed() { } @Override public void onLauncherInfoChanged() { updateAppList(); } @Override public void onLoadEntriesCompleted() { updateAppList(); } }; }