1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.notification.app; 18 19 import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE; 20 import static android.provider.Settings.Secure.NOTIFICATION_BUBBLES; 21 22 import android.app.NotificationChannel; 23 import android.content.Context; 24 import android.provider.Settings; 25 26 import androidx.annotation.Nullable; 27 import androidx.fragment.app.FragmentManager; 28 import androidx.preference.Preference; 29 30 import com.android.settings.core.PreferenceControllerMixin; 31 import com.android.settings.notification.BubbleHelper; 32 import com.android.settings.notification.NotificationBackend; 33 import com.android.settingslib.RestrictedSwitchPreference; 34 35 /** 36 * Preference controller for Bubbles. This is used as the app-specific page and conversation 37 * settings. 38 */ 39 public class BubblePreferenceController extends NotificationPreferenceController 40 implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener { 41 42 private static final String TAG = "BubblePrefContr"; 43 private static final String KEY = "bubble_pref"; 44 45 private FragmentManager mFragmentManager; 46 private boolean mIsAppPage; 47 private boolean mHasSentInvalidMsg; 48 private int mNumConversations; 49 private NotificationSettings.DependentFieldListener mListener; 50 BubblePreferenceController(Context context, @Nullable FragmentManager fragmentManager, NotificationBackend backend, boolean isAppPage, @Nullable NotificationSettings.DependentFieldListener listener)51 public BubblePreferenceController(Context context, @Nullable FragmentManager fragmentManager, 52 NotificationBackend backend, boolean isAppPage, 53 @Nullable NotificationSettings.DependentFieldListener listener) { 54 super(context, backend); 55 mFragmentManager = fragmentManager; 56 mIsAppPage = isAppPage; 57 mListener = listener; 58 } 59 60 @Override getPreferenceKey()61 public String getPreferenceKey() { 62 return KEY; 63 } 64 65 @Override isAvailable()66 public boolean isAvailable() { 67 if (!super.isAvailable()) { 68 return false; 69 } 70 if (!mIsAppPage && !isEnabled()) { 71 return false; 72 } 73 if (mChannel != null) { 74 if (isDefaultChannel()) { 75 return true; 76 } else { 77 return mAppRow != null && mAppRow.bubblePreference != BUBBLE_PREFERENCE_NONE; 78 } 79 } 80 return true; 81 } 82 83 @Override isIncludedInFilter()84 boolean isIncludedInFilter() { 85 return mPreferenceFilter.contains(NotificationChannel.EDIT_CONVERSATION); 86 } 87 88 @Override updateState(Preference preference)89 public void updateState(Preference preference) { 90 if (mIsAppPage && mAppRow != null) { 91 mHasSentInvalidMsg = mBackend.isInInvalidMsgState(mAppRow.pkg, mAppRow.uid); 92 mNumConversations = mBackend.getConversations( 93 mAppRow.pkg, mAppRow.uid).getList().size(); 94 // We're on the app specific bubble page which displays a tri-state 95 int backEndPref = mAppRow.bubblePreference; 96 BubblePreference pref = (BubblePreference) preference; 97 pref.setDisabledByAdmin(mAdmin); 98 pref.setSelectedVisibility(!mHasSentInvalidMsg || mNumConversations > 0); 99 if (!isEnabled()) { 100 pref.setSelectedPreference(BUBBLE_PREFERENCE_NONE); 101 } else { 102 pref.setSelectedPreference(backEndPref); 103 } 104 } else if (mChannel != null) { 105 // We're on the channel specific notification page which displays a toggle. 106 RestrictedSwitchPreference switchpref = (RestrictedSwitchPreference) preference; 107 switchpref.setDisabledByAdmin(mAdmin); 108 switchpref.setChecked(mChannel.canBubble() && isEnabled()); 109 } 110 } 111 112 @Override onPreferenceChange(Preference preference, Object newValue)113 public boolean onPreferenceChange(Preference preference, Object newValue) { 114 if (mChannel != null) { 115 // Channel page is toggle 116 mChannel.setAllowBubbles((boolean) newValue); 117 saveChannel(); 118 } else if (mIsAppPage) { 119 // App page is bubble preference 120 BubblePreference pref = (BubblePreference) preference; 121 if (mAppRow != null && mFragmentManager != null) { 122 final int value = (int) newValue; 123 if (!isEnabled() 124 && pref.getSelectedPreference() == BUBBLE_PREFERENCE_NONE) { 125 // if the global setting is off, toggling app level permission requires extra 126 // confirmation 127 new BubbleWarningDialogFragment() 128 .setPkgPrefInfo(mAppRow.pkg, mAppRow.uid, value) 129 .show(mFragmentManager, "dialog"); 130 return false; 131 } else { 132 mAppRow.bubblePreference = value; 133 mBackend.setAllowBubbles(mAppRow.pkg, mAppRow.uid, value); 134 } 135 } 136 if (mListener != null) { 137 mListener.onFieldValueChanged(); 138 } 139 } 140 return true; 141 } 142 isEnabled()143 private boolean isEnabled() { 144 return BubbleHelper.isEnabledSystemWide(mContext); 145 } 146 147 /** 148 * Used in app level prompt that confirms the user is ok with turning on bubbles 149 * globally. If they aren't, undo that. 150 */ revertBubblesApproval(Context context, String pkg, int uid)151 public static void revertBubblesApproval(Context context, String pkg, int uid) { 152 NotificationBackend backend = new NotificationBackend(); 153 backend.setAllowBubbles(pkg, uid, BUBBLE_PREFERENCE_NONE); 154 155 // changing the global settings will cause the observer on the host page to reload 156 // correct preference state 157 Settings.Secure.putInt(context.getContentResolver(), 158 NOTIFICATION_BUBBLES, 159 BubbleHelper.SYSTEM_WIDE_OFF); 160 } 161 162 /** 163 * Apply global bubbles approval 164 */ applyBubblesApproval(Context context, String pkg, int uid, int pref)165 public static void applyBubblesApproval(Context context, String pkg, int uid, int pref) { 166 NotificationBackend backend = new NotificationBackend(); 167 backend.setAllowBubbles(pkg, uid, pref); 168 // changing the global settings will cause the observer on the host page to reload 169 // correct preference state 170 Settings.Secure.putInt(context.getContentResolver(), 171 NOTIFICATION_BUBBLES, 172 BubbleHelper.SYSTEM_WIDE_ON); 173 } 174 } 175