1 package com.android.tv.settings; 2 3 import static com.android.tv.settings.util.InstrumentationUtils.logEntrySelected; 4 5 import android.app.tvsettings.TvSettingsEnums; 6 import android.content.ActivityNotFoundException; 7 import android.content.ComponentName; 8 import android.content.Context; 9 import android.content.Intent; 10 import android.service.settings.suggestions.Suggestion; 11 import android.util.Log; 12 13 import androidx.annotation.VisibleForTesting; 14 import androidx.preference.Preference; 15 import androidx.preference.PreferenceCategory; 16 17 import com.android.settingslib.core.AbstractPreferenceController; 18 import com.android.settingslib.suggestions.SuggestionControllerMixinCompat; 19 import com.android.settingslib.utils.IconCache; 20 import com.android.tv.settings.R; 21 import com.android.tv.settings.overlay.FlavorUtils; 22 import com.android.tv.settings.suggestions.SuggestionPreference; 23 import com.android.tv.settings.system.SecurityFragment; 24 import com.android.tv.settings.widget.SwitchWithSoundPreference; 25 26 import java.util.ArrayList; 27 import java.util.List; 28 29 public class SuggestionQuickSettingPrefsContainer { 30 @VisibleForTesting static final String KEY_QUICK_SETTINGS = "quick_settings"; 31 private static final String TAG = "SuggestionQuickSettingPrefsContainer"; 32 private static final String KEY_SUGGESTIONS_LIST = "suggestions"; 33 @VisibleForTesting PreferenceCategory mSuggestionsList; 34 IconCache mIconCache; 35 private SuggestionControllerMixinCompat mSuggestionControllerMixin; 36 /** Controllers for the Quick Settings section. */ 37 private List<AbstractPreferenceController> mPreferenceControllers; 38 39 private HotwordSwitchController mHotwordSwitchController; 40 private TakeBugReportController mTakeBugReportController; 41 private PreferenceCategory mQuickSettingsList; 42 private SwitchWithSoundPreference mHotwordSwitch; 43 private Preference mTakeBugReportPreference; 44 private MainFragment mainFragment; 45 SuggestionQuickSettingPrefsContainer(MainFragment mainFragment)46 SuggestionQuickSettingPrefsContainer(MainFragment mainFragment) { 47 this.mainFragment = mainFragment; 48 } 49 onCreate()50 void onCreate() { 51 mIconCache = new IconCache(mainFragment.getContext()); 52 if (!isRestricted()) { 53 ComponentName componentName = new ComponentName("com.android.settings.intelligence", 54 "com.android.settings.intelligence.suggestions.SuggestionService"); 55 mSuggestionControllerMixin = 56 new SuggestionControllerMixinCompat(mainFragment.getContext(), 57 mainFragment, mainFragment.getSettingsLifecycle(), componentName); 58 } 59 } 60 onDestroy()61 void onDestroy() { 62 if (mHotwordSwitchController != null) { 63 mHotwordSwitchController.unregister(); 64 } 65 } 66 showOrHideQuickSettings()67 void showOrHideQuickSettings() { 68 if (shouldShowQuickSettings()) { 69 showQuickSettings(); 70 } else { 71 hideQuickSettings(); 72 } 73 } 74 onCreatePreferences()75 void onCreatePreferences() { 76 if (mHotwordSwitchController != null) { 77 mHotwordSwitchController.init(mainFragment); 78 } 79 } 80 onSuggestionReady(List<Suggestion> data)81 void onSuggestionReady(List<Suggestion> data) { 82 // Suggestion category is handled differently in basic mode 83 if (data == null || data.size() == 0 84 || FlavorUtils.getFeatureFactory(mainFragment.getContext()) 85 .getBasicModeFeatureProvider() 86 .isBasicMode(mainFragment.getContext())) { 87 if (mSuggestionsList != null) { 88 mainFragment.getPreferenceScreen().removePreference(mSuggestionsList); 89 mSuggestionsList = null; 90 } 91 return; 92 } 93 94 if (mSuggestionsList == null) { 95 mSuggestionsList = new PreferenceCategory( 96 mainFragment.getPreferenceManager().getContext()); 97 mSuggestionsList.setKey(KEY_SUGGESTIONS_LIST); 98 mSuggestionsList.setTitle(R.string.header_category_suggestions); 99 mSuggestionsList.setOrder(0); // always at top 100 mainFragment.getPreferenceScreen().addPreference(mSuggestionsList); 101 } 102 updateSuggestionList(data); 103 } 104 105 @VisibleForTesting updateSuggestionList(List<Suggestion> suggestions)106 void updateSuggestionList(List<Suggestion> suggestions) { 107 // Remove suggestions that are not in the new list. 108 for (int i = 0; i < mSuggestionsList.getPreferenceCount(); i++) { 109 SuggestionPreference pref = (SuggestionPreference) mSuggestionsList.getPreference(i); 110 boolean isInNewSuggestionList = false; 111 for (Suggestion suggestion : suggestions) { 112 if (pref.getId().equals(suggestion.getId())) { 113 isInNewSuggestionList = true; 114 break; 115 } 116 } 117 if (!isInNewSuggestionList) { 118 mSuggestionsList.removePreference(pref); 119 } 120 } 121 122 // Add suggestions that are not in the old list and update the existing suggestions. 123 for (Suggestion suggestion : suggestions) { 124 Preference curPref = mainFragment.findPreference( 125 SuggestionPreference.SUGGESTION_PREFERENCE_KEY + suggestion.getId()); 126 if (curPref == null) { 127 SuggestionPreference newSuggPref = 128 new SuggestionPreference(suggestion, 129 mainFragment.getPreferenceManager().getContext(), 130 mSuggestionControllerMixin, mainFragment); 131 newSuggPref.setIcon(mIconCache.getIcon(suggestion.getIcon())); 132 newSuggPref.setTitle(suggestion.getTitle()); 133 newSuggPref.setSummary(suggestion.getSummary()); 134 mSuggestionsList.addPreference(newSuggPref); 135 } else { 136 // Even though the id of suggestion might not change, the details could change. 137 // So we need to update icon, title and summary for the suggestions. 138 curPref.setIcon(mIconCache.getIcon(suggestion.getIcon())); 139 curPref.setTitle(suggestion.getTitle()); 140 curPref.setSummary(suggestion.getSummary()); 141 } 142 } 143 } 144 isRestricted()145 boolean isRestricted() { 146 return SecurityFragment.isRestrictedProfileInEffect(mainFragment.getContext()); 147 } 148 onSuggestionClosed(Preference preference)149 void onSuggestionClosed(Preference preference) { 150 if (mSuggestionsList == null || mSuggestionsList.getPreferenceCount() == 0) { 151 return; 152 } else if (mSuggestionsList.getPreferenceCount() == 1) { 153 mainFragment.getPreferenceScreen().removePreference(mSuggestionsList); 154 } else { 155 mSuggestionsList.removePreference(preference); 156 } 157 } 158 onHotwordStateChanged()159 void onHotwordStateChanged() { 160 if (mHotwordSwitch != null) { 161 mHotwordSwitchController.updateState(mHotwordSwitch); 162 } 163 showOrHideQuickSettings(); 164 } 165 onHotwordEnable()166 void onHotwordEnable() { 167 try { 168 Intent intent = new Intent(HotwordSwitchController.ACTION_HOTWORD_ENABLE); 169 intent.setPackage(HotwordSwitchController.ASSISTANT_PGK_NAME); 170 mainFragment.startActivityForResult(intent, 0); 171 } catch (ActivityNotFoundException e) { 172 Log.w(TAG, "Unable to find hotwording activity.", e); 173 } 174 } 175 onHotwordDisable()176 void onHotwordDisable() { 177 try { 178 Intent intent = new Intent(HotwordSwitchController.ACTION_HOTWORD_DISABLE); 179 intent.setPackage(HotwordSwitchController.ASSISTANT_PGK_NAME); 180 mainFragment.startActivityForResult(intent, 0); 181 } catch (ActivityNotFoundException e) { 182 Log.w(TAG, "Unable to find hotwording activity.", e); 183 } 184 } 185 quickSettingsEnabled()186 private boolean quickSettingsEnabled() { 187 return mainFragment.getContext().getResources().getBoolean( 188 R.bool.config_quick_settings_enabled); 189 } 190 191 /** @return true if there is at least one available item in quick settings. */ shouldShowQuickSettings()192 private boolean shouldShowQuickSettings() { 193 for (AbstractPreferenceController controller : mPreferenceControllers) { 194 if (controller.isAvailable()) { 195 return true; 196 } 197 } 198 return false; 199 } 200 201 202 /** Creates the quick settings category and its children. */ showQuickSettings()203 private void showQuickSettings() { 204 if (mQuickSettingsList != null) { 205 return; 206 } 207 mQuickSettingsList = new PreferenceCategory( 208 mainFragment.getPreferenceManager().getContext()); 209 mQuickSettingsList.setKey(KEY_QUICK_SETTINGS); 210 mQuickSettingsList.setTitle(R.string.header_category_quick_settings); 211 mQuickSettingsList.setOrder(1); // at top, but below suggested settings 212 mainFragment.getPreferenceScreen().addPreference(mQuickSettingsList); 213 if (mHotwordSwitchController != null && mHotwordSwitchController.isAvailable()) { 214 mHotwordSwitch = new SwitchWithSoundPreference( 215 mainFragment.getPreferenceManager().getContext()); 216 mHotwordSwitch.setKey(HotwordSwitchController.KEY_HOTWORD_SWITCH); 217 mHotwordSwitch.setOnPreferenceClickListener(preference -> { 218 logEntrySelected(TvSettingsEnums.QUICK_SETTINGS); 219 return false; 220 }); 221 mHotwordSwitchController.updateState(mHotwordSwitch); 222 mQuickSettingsList.addPreference(mHotwordSwitch); 223 } 224 if (mTakeBugReportController != null && mTakeBugReportController.isAvailable()) { 225 mTakeBugReportPreference = new Preference( 226 mainFragment.getPreferenceManager().getContext()); 227 mTakeBugReportPreference.setKey(TakeBugReportController.KEY_TAKE_BUG_REPORT); 228 mTakeBugReportPreference.setOnPreferenceClickListener(preference -> { 229 logEntrySelected(TvSettingsEnums.QUICK_SETTINGS); 230 return false; 231 }); 232 mTakeBugReportController.updateState(mTakeBugReportPreference); 233 mQuickSettingsList.addPreference(mTakeBugReportPreference); 234 } 235 } 236 237 /** Removes the quick settings category and all its children. */ hideQuickSettings()238 private void hideQuickSettings() { 239 Preference quickSettingsPref = mainFragment.findPreference(KEY_QUICK_SETTINGS); 240 if (quickSettingsPref == null) { 241 return; 242 } 243 mQuickSettingsList.removeAll(); 244 mainFragment.getPreferenceScreen().removePreference(mQuickSettingsList); 245 mQuickSettingsList = null; 246 } 247 onCreatePreferenceControllers(Context context)248 List<AbstractPreferenceController> onCreatePreferenceControllers(Context context) { 249 mPreferenceControllers = new ArrayList<>(2); 250 if (quickSettingsEnabled()) { 251 mHotwordSwitchController = new HotwordSwitchController(context); 252 mTakeBugReportController = new TakeBugReportController(context); 253 mPreferenceControllers.add(mHotwordSwitchController); 254 mPreferenceControllers.add(mTakeBugReportController); 255 } 256 return mPreferenceControllers; 257 } 258 259 } 260