1 /* 2 * Copyright (C) 2018 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.tv.settings.device; 18 19 import static com.android.tv.settings.overlay.FlavorUtils.FLAVOR_CLASSIC; 20 import static com.android.tv.settings.overlay.FlavorUtils.FLAVOR_TWO_PANEL; 21 import static com.android.tv.settings.overlay.FlavorUtils.FLAVOR_VENDOR; 22 import static com.android.tv.settings.overlay.FlavorUtils.FLAVOR_X; 23 import static com.android.tv.settings.util.InstrumentationUtils.logEntrySelected; 24 import static com.android.tv.settings.util.InstrumentationUtils.logToggleInteracted; 25 26 import android.app.tvsettings.TvSettingsEnums; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.pm.PackageManager; 30 import android.content.pm.ResolveInfo; 31 import android.content.res.Resources; 32 import android.media.AudioManager; 33 import android.media.tv.TvInputInfo; 34 import android.media.tv.TvInputManager; 35 import android.os.Bundle; 36 import android.os.UserHandle; 37 import android.provider.Settings; 38 import android.text.TextUtils; 39 import android.util.Log; 40 import android.view.LayoutInflater; 41 import android.view.View; 42 import android.view.ViewGroup; 43 import android.view.inputmethod.InputMethodInfo; 44 45 import androidx.annotation.Keep; 46 import androidx.annotation.VisibleForTesting; 47 import androidx.fragment.app.Fragment; 48 import androidx.leanback.preference.LeanbackSettingsFragmentCompat; 49 import androidx.preference.Preference; 50 import androidx.preference.TwoStatePreference; 51 52 import com.android.settingslib.applications.DefaultAppInfo; 53 import com.android.settingslib.development.DevelopmentSettingsEnabler; 54 import com.android.tv.settings.LongClickPreference; 55 import com.android.tv.settings.MainFragment; 56 import com.android.tv.settings.R; 57 import com.android.tv.settings.SettingsPreferenceFragment; 58 import com.android.tv.settings.about.RebootConfirmFragment; 59 import com.android.tv.settings.autofill.AutofillHelper; 60 import com.android.tv.settings.customization.CustomizationConstants; 61 import com.android.tv.settings.customization.Partner; 62 import com.android.tv.settings.customization.PartnerPreferencesMerger; 63 import com.android.tv.settings.device.eco.PowerAndEnergyFragment; 64 import com.android.tv.settings.inputmethod.InputMethodHelper; 65 import com.android.tv.settings.overlay.FlavorUtils; 66 import com.android.tv.settings.privacy.PrivacyToggle; 67 import com.android.tv.settings.privacy.SensorFragment; 68 import com.android.tv.settings.system.SecurityFragment; 69 import com.android.tv.settings.util.SliceUtils; 70 import com.android.tv.twopanelsettings.TwoPanelSettingsFragment; 71 import com.android.tv.twopanelsettings.slices.SlicePreference; 72 73 import java.util.List; 74 75 /** 76 * The "Device Preferences" screen in TV settings. 77 */ 78 @Keep 79 public class DevicePrefFragment extends SettingsPreferenceFragment implements 80 LongClickPreference.OnLongClickListener { 81 @VisibleForTesting 82 static final String KEY_DEVELOPER = "developer"; 83 @VisibleForTesting 84 static final String KEY_CAST_SETTINGS = "cast"; 85 private static final String KEY_CAST_SETTINGS_SLICE = "cast_settings"; 86 @VisibleForTesting 87 static final String KEY_KEYBOARD = "keyboard"; 88 private static final String TAG = "DeviceFragment"; 89 private static final String KEY_USAGE = "usageAndDiag"; 90 private static final String KEY_INPUTS = "inputs"; 91 private static final String KEY_SOUNDS = "sound_effects"; 92 private static final String KEY_SOUNDS_SWITCH = "sound_effects_switch"; 93 private static final String KEY_GOOGLE_SETTINGS = "google_settings"; 94 private static final String KEY_HOME_SETTINGS = "home"; 95 private static final String KEY_REBOOT = "reboot"; 96 private static final String KEY_MIC = "microphone"; 97 private static final String KEY_CAMERA = "camera"; 98 private static final String KEY_FASTPAIR_SETTINGS_SLICE = "fastpair_slice"; 99 private static final String KEY_OVERLAY_INTERNAL_SETTINGS_SLICE = "overlay_internal"; 100 private static final String KEY_ASSISTANT_BROADCAST = "assistant_broadcast"; 101 private static final String KEY_AMBIENT_SETTINGS = "ambient_settings"; 102 private static final String KEY_ENERGY_SAVER = "energysaver"; 103 private static final String KEY_POWER_AND_ENERGY = "power_and_energy"; 104 private static final String RES_TOP_LEVEL_ASSISTANT_SLICE_URI = "top_level_assistant_slice_uri"; 105 106 private Preference mSoundsPref; 107 private TwoStatePreference mSoundsSwitchPref; 108 private boolean mInputSettingNeeded; 109 private PackageManager mPm; 110 private AudioManager mAudioManager; 111 getPreferenceScreenResId()112 private int getPreferenceScreenResId() { 113 if (isRestricted()) { 114 return R.xml.device_restricted; 115 } 116 switch (FlavorUtils.getFlavor(getContext())) { 117 case FLAVOR_CLASSIC: 118 return R.xml.device; 119 case FLAVOR_TWO_PANEL: 120 return R.xml.device_two_panel; 121 case FLAVOR_X: 122 return R.xml.device_x; 123 case FLAVOR_VENDOR: 124 return R.xml.device_vendor; 125 default: 126 return R.xml.device; 127 } 128 } 129 130 @Override onCreatePreferences(Bundle savedInstanceState, String rootKey)131 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 132 setPreferencesFromResource(getPreferenceScreenResId(), null); 133 if (Partner.getInstance(getContext()).isCustomizationPackageProvided()) { 134 PartnerPreferencesMerger.mergePreferences( 135 getContext(), 136 getPreferenceScreen(), 137 CustomizationConstants.DEVICE_SCREEN 138 ); 139 } 140 mSoundsPref = findPreference(KEY_SOUNDS); 141 mSoundsSwitchPref = findPreference(KEY_SOUNDS_SWITCH); 142 if (mSoundsSwitchPref != null) { 143 mSoundsSwitchPref.setChecked(getSoundEffectsEnabled()); 144 } 145 146 final Preference inputPref = findPreference(KEY_INPUTS); 147 if (inputPref != null) { 148 inputPref.setVisible(mInputSettingNeeded); 149 } 150 final LongClickPreference restartPref = findPreference(KEY_REBOOT); 151 if (restartPref != null) { 152 restartPref.setLongClickListener(this); 153 } 154 155 PrivacyToggle.MIC_TOGGLE.preparePreferenceWithSensorFragment(getContext(), 156 findPreference(KEY_MIC), SensorFragment.TOGGLE_EXTRA); 157 PrivacyToggle.CAMERA_TOGGLE.preparePreferenceWithSensorFragment(getContext(), 158 findPreference(KEY_CAMERA), SensorFragment.TOGGLE_EXTRA); 159 160 final Preference assistantBroadcastPreference = findPreference(KEY_ASSISTANT_BROADCAST); 161 if (assistantBroadcastPreference != null && SliceUtils.isSettingsSliceEnabled( 162 getContext(), 163 ((SlicePreference) assistantBroadcastPreference).getUri(), 164 RES_TOP_LEVEL_ASSISTANT_SLICE_URI)) { 165 assistantBroadcastPreference.setVisible(true); 166 } 167 } 168 169 @Override onCreate(Bundle savedInstanceState)170 public void onCreate(Bundle savedInstanceState) { 171 final TvInputManager manager = (TvInputManager) getContext().getSystemService( 172 Context.TV_INPUT_SERVICE); 173 if (manager != null) { 174 for (final TvInputInfo input : manager.getTvInputList()) { 175 if (input.isPassthroughInput()) { 176 mInputSettingNeeded = true; 177 } 178 } 179 } 180 mAudioManager = getContext().getSystemService(AudioManager.class); 181 super.onCreate(savedInstanceState); 182 } 183 184 @Override onAttach(Context context)185 public void onAttach(Context context) { 186 super.onAttach(context); 187 mPm = context.getPackageManager(); 188 } 189 190 @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)191 public View onCreateView(LayoutInflater inflater, ViewGroup container, 192 Bundle savedInstanceState) { 193 updateDeveloperOptions(); 194 updateSounds(); 195 updateGoogleSettings(); 196 updateCastSettings(); 197 updateInternalSettings(); 198 updateFastpairSettings(); 199 updateKeyboardAutofillSettings(); 200 updateAmbientSettings(); 201 updatePowerAndEnergySettings(); 202 hideIfIntentUnhandled(findPreference(KEY_HOME_SETTINGS)); 203 hideIfIntentUnhandled(findPreference(KEY_CAST_SETTINGS)); 204 hideIfIntentUnhandled(findPreference(KEY_USAGE)); 205 return super.onCreateView(inflater, container, savedInstanceState); 206 } 207 208 @Override onPreferenceTreeClick(Preference preference)209 public boolean onPreferenceTreeClick(Preference preference) { 210 switch (preference.getKey()) { 211 case KEY_HOME_SETTINGS: 212 logEntrySelected(TvSettingsEnums.PREFERENCES_HOME_SCREEN); 213 break; 214 case KEY_GOOGLE_SETTINGS: 215 logEntrySelected(TvSettingsEnums.PREFERENCES_ASSISTANT); 216 break; 217 case KEY_CAST_SETTINGS: 218 logEntrySelected(TvSettingsEnums.PREFERENCES_CHROMECAST_SHELL); 219 break; 220 case KEY_REBOOT: 221 logEntrySelected(TvSettingsEnums.SYSTEM_REBOOT); 222 break; 223 case KEY_SOUNDS_SWITCH: 224 if (mSoundsSwitchPref != null) { 225 logToggleInteracted(TvSettingsEnums.DISPLAY_SOUND_SYSTEM_SOUNDS, 226 mSoundsSwitchPref.isChecked()); 227 setSoundEffectsEnabled(mSoundsSwitchPref.isChecked()); 228 } 229 break; 230 } 231 return super.onPreferenceTreeClick(preference); 232 } 233 234 @Override onPreferenceLongClick(Preference preference)235 public boolean onPreferenceLongClick(Preference preference) { 236 if (TextUtils.equals(preference.getKey(), KEY_REBOOT)) { 237 logEntrySelected(TvSettingsEnums.SYSTEM_REBOOT); 238 Fragment fragment = getCallbackFragment(); 239 if (fragment instanceof LeanbackSettingsFragmentCompat) { 240 ((LeanbackSettingsFragmentCompat) fragment).startImmersiveFragment( 241 RebootConfirmFragment.newInstance(true /* safeMode */)); 242 return true; 243 } else if (fragment instanceof TwoPanelSettingsFragment) { 244 ((TwoPanelSettingsFragment) fragment).startImmersiveFragment( 245 RebootConfirmFragment.newInstance(true /* safeMode */)); 246 return true; 247 } 248 } 249 return false; 250 } 251 getSoundEffectsEnabled()252 public boolean getSoundEffectsEnabled() { 253 return Settings.System.getInt(getActivity().getContentResolver(), 254 Settings.System.SOUND_EFFECTS_ENABLED, 1) != 0; 255 } 256 setSoundEffectsEnabled(boolean enabled)257 private void setSoundEffectsEnabled(boolean enabled) { 258 if (enabled) { 259 mAudioManager.loadSoundEffects(); 260 } else { 261 mAudioManager.unloadSoundEffects(); 262 } 263 Settings.System.putInt(getActivity().getContentResolver(), 264 Settings.System.SOUND_EFFECTS_ENABLED, enabled ? 1 : 0); 265 } 266 hideIfIntentUnhandled(Preference preference)267 private void hideIfIntentUnhandled(Preference preference) { 268 if (preference == null || !preference.isVisible()) { 269 return; 270 } 271 preference.setVisible( 272 MainFragment.systemIntentIsHandled(getContext(), preference.getIntent()) != null); 273 } 274 isRestricted()275 private boolean isRestricted() { 276 return SecurityFragment.isRestrictedProfileInEffect(getContext()); 277 } 278 279 @VisibleForTesting updateDeveloperOptions()280 void updateDeveloperOptions() { 281 final Preference developerPref = findPreference(KEY_DEVELOPER); 282 if (developerPref == null) { 283 return; 284 } 285 286 developerPref.setVisible(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled( 287 getContext())); 288 } 289 updateSounds()290 private void updateSounds() { 291 if (mSoundsPref == null) { 292 return; 293 } 294 295 Intent soundIntent = new Intent(MainFragment.ACTION_SOUND); 296 final ResolveInfo info = MainFragment.systemIntentIsHandled(getContext(), soundIntent); 297 if (info != null) { 298 mSoundsPref.setVisible(false); 299 } 300 } 301 updateGoogleSettings()302 private void updateGoogleSettings() { 303 final Preference googleSettingsPref = findPreference(KEY_GOOGLE_SETTINGS); 304 if (googleSettingsPref != null) { 305 final ResolveInfo info = MainFragment.systemIntentIsHandled(getContext(), 306 googleSettingsPref.getIntent()); 307 googleSettingsPref.setVisible(info != null); 308 if (info != null && info.activityInfo != null) { 309 googleSettingsPref.setIcon( 310 info.activityInfo.loadIcon(getContext().getPackageManager())); 311 googleSettingsPref.setTitle( 312 info.activityInfo.loadLabel(getContext().getPackageManager())); 313 } 314 } 315 } 316 317 @VisibleForTesting updateCastSettings()318 void updateCastSettings() { 319 final Preference castPref = findPreference(KEY_CAST_SETTINGS); 320 final SlicePreference castSlicePref = findPreference(KEY_CAST_SETTINGS_SLICE); 321 if (castPref != null) { 322 final ResolveInfo info = MainFragment.systemIntentIsHandled( 323 getContext(), castPref.getIntent()); 324 if (info != null) { 325 try { 326 final Context targetContext = getContext() 327 .createPackageContext(info.resolvePackageName != null 328 ? info.resolvePackageName : info.activityInfo.packageName, 0); 329 castPref.setIcon(targetContext.getDrawable(info.getIconResource())); 330 } catch (Resources.NotFoundException | PackageManager.NameNotFoundException 331 | SecurityException e) { 332 Log.e(TAG, "Cast settings icon not found", e); 333 } 334 castPref.setTitle(info.activityInfo.loadLabel(getContext().getPackageManager())); 335 } 336 } 337 if (castSlicePref != null) { 338 if (!SliceUtils.isSliceProviderValid(getContext(), castSlicePref.getUri()) 339 || FlavorUtils.getFeatureFactory(getContext()).getBasicModeFeatureProvider() 340 .isBasicMode(getContext())) { 341 castSlicePref.setVisible(false); 342 } 343 } 344 } 345 updateInternalSettings()346 private void updateInternalSettings() { 347 final SlicePreference internalSlicePref = findPreference( 348 KEY_OVERLAY_INTERNAL_SETTINGS_SLICE); 349 if (internalSlicePref != null) { 350 internalSlicePref.setVisible( 351 SliceUtils.isSliceProviderValid(getContext(), internalSlicePref.getUri()) 352 && SliceUtils.isSettingsSliceEnabled(getContext(), 353 internalSlicePref.getUri(), null)); 354 } 355 } 356 357 @VisibleForTesting updateFastpairSettings()358 void updateFastpairSettings() { 359 final SlicePreference fastpairSlicePref = findPreference(KEY_FASTPAIR_SETTINGS_SLICE); 360 if (fastpairSlicePref != null) { 361 if (SliceUtils.isSliceProviderValid(getContext(), fastpairSlicePref.getUri())) { 362 fastpairSlicePref.setVisible(true); 363 } 364 } 365 } 366 367 @VisibleForTesting updateKeyboardAutofillSettings()368 void updateKeyboardAutofillSettings() { 369 final Preference keyboardPref = findPreference(KEY_KEYBOARD); 370 371 List<DefaultAppInfo> candidates = AutofillHelper.getAutofillCandidates(getContext(), 372 mPm, UserHandle.myUserId()); 373 374 // Switch title depends on whether there is autofill 375 if (candidates.isEmpty()) { 376 keyboardPref.setTitle(R.string.system_keyboard); 377 } else { 378 keyboardPref.setTitle(R.string.system_keyboard_autofill); 379 } 380 381 CharSequence summary = ""; 382 // append current keyboard to summary 383 String defaultImId = InputMethodHelper.getDefaultInputMethodId(getContext()); 384 if (!TextUtils.isEmpty(defaultImId)) { 385 InputMethodInfo info = InputMethodHelper.findInputMethod(defaultImId, 386 InputMethodHelper.getEnabledSystemInputMethodList(getContext())); 387 if (info != null) { 388 summary = info.loadLabel(getContext().getPackageManager()); 389 } 390 391 } 392 // append current autofill to summary 393 DefaultAppInfo appInfo = AutofillHelper.getCurrentAutofill(getContext(), candidates); 394 if (appInfo != null) { 395 CharSequence autofillInfo = appInfo.loadLabel(); 396 if (summary.length() > 0) { 397 getContext().getString(R.string.string_concat, summary, autofillInfo); 398 } else { 399 summary = autofillInfo; 400 } 401 } 402 keyboardPref.setSummary(summary); 403 } 404 updateAmbientSettings()405 private void updateAmbientSettings() { 406 final SlicePreference ambientSlicePref = findPreference(KEY_AMBIENT_SETTINGS); 407 if (ambientSlicePref != null) { 408 if (SliceUtils.isSliceProviderValid(getContext(), ambientSlicePref.getUri())) { 409 ambientSlicePref.setVisible(true); 410 } 411 } 412 } 413 updatePowerAndEnergySettings()414 private void updatePowerAndEnergySettings() { 415 final Preference energySaverPref = findPreference(KEY_ENERGY_SAVER); 416 final Preference powerAndEnergyPref = findPreference(KEY_POWER_AND_ENERGY); 417 418 if (energySaverPref == null || powerAndEnergyPref == null) { 419 return; 420 } 421 422 boolean showPowerAndEnergy = 423 !PowerAndEnergyFragment.hasOnlyEnergySaverPreference(getContext()); 424 powerAndEnergyPref.setVisible(showPowerAndEnergy); 425 energySaverPref.setVisible(!showPowerAndEnergy); 426 } 427 428 @Override getPageId()429 protected int getPageId() { 430 return TvSettingsEnums.SYSTEM; 431 } 432 } 433