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