• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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