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.util.InstrumentationUtils.logEntrySelected;
20 
21 import android.app.Fragment;
22 import android.app.tvsettings.TvSettingsEnums;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.pm.PackageManager;
26 import android.content.pm.ResolveInfo;
27 import android.content.res.Resources;
28 import android.media.tv.TvInputInfo;
29 import android.media.tv.TvInputManager;
30 import android.os.Bundle;
31 import android.os.UserHandle;
32 import android.text.TextUtils;
33 import android.util.Log;
34 import android.view.LayoutInflater;
35 import android.view.View;
36 import android.view.ViewGroup;
37 import android.view.inputmethod.InputMethodInfo;
38 
39 import androidx.annotation.Keep;
40 import androidx.annotation.VisibleForTesting;
41 import androidx.leanback.preference.LeanbackSettingsFragment;
42 import androidx.preference.Preference;
43 
44 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
45 import com.android.settingslib.applications.DefaultAppInfo;
46 import com.android.settingslib.development.DevelopmentSettingsEnabler;
47 import com.android.tv.settings.LongClickPreference;
48 import com.android.tv.settings.MainFragment;
49 import com.android.tv.settings.R;
50 import com.android.tv.settings.SettingsPreferenceFragment;
51 import com.android.tv.settings.about.RebootConfirmFragment;
52 import com.android.tv.settings.autofill.AutofillHelper;
53 import com.android.tv.settings.inputmethod.InputMethodHelper;
54 import com.android.tv.settings.system.SecurityFragment;
55 import com.android.tv.twopanelsettings.TwoPanelSettingsFragment;
56 
57 import java.util.List;
58 
59 /**
60  * The "Device Preferences" screen in TV settings.
61  */
62 @Keep
63 public class DevicePrefFragment extends SettingsPreferenceFragment implements
64         LongClickPreference.OnLongClickListener {
65     @VisibleForTesting
66     static final String KEY_DEVELOPER = "developer";
67     @VisibleForTesting
68     static final String KEY_CAST_SETTINGS = "cast";
69     @VisibleForTesting
70     static final String KEY_KEYBOARD = "keyboard";
71     private static final String TAG = "DeviceFragment";
72     private static final String KEY_USAGE = "usageAndDiag";
73     private static final String KEY_INPUTS = "inputs";
74     private static final String KEY_SOUNDS = "sound_effects";
75     private static final String KEY_GOOGLE_SETTINGS = "google_settings";
76     private static final String KEY_HOME_SETTINGS = "home";
77     private static final String KEY_REBOOT = "reboot";
78 
79     private Preference mSoundsPref;
80     private boolean mInputSettingNeeded;
81     private PackageManager mPm;
82 
83     @Override
onCreatePreferences(Bundle savedInstanceState, String rootKey)84     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
85         if (isRestricted()) {
86             setPreferencesFromResource(R.xml.device_restricted, null);
87         } else {
88             setPreferencesFromResource(R.xml.device, null);
89         }
90         mSoundsPref = findPreference(KEY_SOUNDS);
91         final Preference inputPref = findPreference(KEY_INPUTS);
92         if (inputPref != null) {
93             inputPref.setVisible(mInputSettingNeeded);
94         }
95         final LongClickPreference restartPref = findPreference(KEY_REBOOT);
96         if (restartPref != null) {
97             restartPref.setLongClickListener(this);
98         }
99     }
100 
101     @Override
onCreate(Bundle savedInstanceState)102     public void onCreate(Bundle savedInstanceState) {
103         final TvInputManager manager = (TvInputManager) getContext().getSystemService(
104                 Context.TV_INPUT_SERVICE);
105         if (manager != null) {
106             for (final TvInputInfo input : manager.getTvInputList()) {
107                 if (input.isPassthroughInput()) {
108                     mInputSettingNeeded = true;
109                 }
110             }
111         }
112         super.onCreate(savedInstanceState);
113     }
114 
115     @Override
onAttach(Context context)116     public void onAttach(Context context) {
117         super.onAttach(context);
118         mPm = context.getPackageManager();
119     }
120 
121     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)122     public View onCreateView(LayoutInflater inflater, ViewGroup container,
123             Bundle savedInstanceState) {
124         updateDeveloperOptions();
125         updateSounds();
126         updateGoogleSettings();
127         updateCastSettings();
128         updateKeyboardAutofillSettings();
129         hideIfIntentUnhandled(findPreference(KEY_HOME_SETTINGS));
130         hideIfIntentUnhandled(findPreference(KEY_CAST_SETTINGS));
131         hideIfIntentUnhandled(findPreference(KEY_USAGE));
132         return super.onCreateView(inflater, container, savedInstanceState);
133     }
134 
135     @Override
onPreferenceTreeClick(Preference preference)136     public boolean onPreferenceTreeClick(Preference preference) {
137         switch (preference.getKey()) {
138             case KEY_HOME_SETTINGS:
139                 logEntrySelected(TvSettingsEnums.PREFERENCES_HOME_SCREEN);
140                 break;
141             case KEY_GOOGLE_SETTINGS:
142                 logEntrySelected(TvSettingsEnums.PREFERENCES_ASSISTANT);
143                 break;
144             case KEY_CAST_SETTINGS:
145                 logEntrySelected(TvSettingsEnums.PREFERENCES_CHROMECAST_SHELL);
146                 break;
147             case KEY_REBOOT:
148                 logEntrySelected(TvSettingsEnums.SYSTEM_REBOOT);
149                 break;
150         }
151         return super.onPreferenceTreeClick(preference);
152     }
153 
154     @Override
onPreferenceLongClick(Preference preference)155     public boolean onPreferenceLongClick(Preference preference) {
156         if (TextUtils.equals(preference.getKey(), KEY_REBOOT)) {
157             logEntrySelected(TvSettingsEnums.SYSTEM_REBOOT);
158             Fragment fragment = getCallbackFragment();
159             if (fragment instanceof LeanbackSettingsFragment) {
160                 ((LeanbackSettingsFragment) fragment).startImmersiveFragment(
161                         RebootConfirmFragment.newInstance(true /* safeMode */));
162                 return true;
163             } else if (fragment instanceof TwoPanelSettingsFragment) {
164                 ((TwoPanelSettingsFragment) fragment).startImmersiveFragment(
165                         RebootConfirmFragment.newInstance(true /* safeMode */));
166                 return true;
167             }
168         }
169         return false;
170     }
171 
172     @Override
getMetricsCategory()173     public int getMetricsCategory() {
174         return MetricsEvent.SETTINGS_TV_DEVICE_CATEGORY;
175     }
176 
hideIfIntentUnhandled(Preference preference)177     private void hideIfIntentUnhandled(Preference preference) {
178         if (preference == null || !preference.isVisible()) {
179             return;
180         }
181         preference.setVisible(
182                 MainFragment.systemIntentIsHandled(getContext(), preference.getIntent()) != null);
183     }
184 
isRestricted()185     private boolean isRestricted() {
186         return SecurityFragment.isRestrictedProfileInEffect(getContext());
187     }
188 
189     @VisibleForTesting
updateDeveloperOptions()190     void updateDeveloperOptions() {
191         final Preference developerPref = findPreference(KEY_DEVELOPER);
192         if (developerPref == null) {
193             return;
194         }
195 
196         developerPref.setVisible(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(
197                 getContext()));
198     }
199 
updateSounds()200     private void updateSounds() {
201         if (mSoundsPref == null) {
202             return;
203         }
204 
205         Intent soundIntent = new Intent(MainFragment.ACTION_SOUND);
206         final ResolveInfo info = MainFragment.systemIntentIsHandled(getContext(), soundIntent);
207         if (info != null) {
208             mSoundsPref.setVisible(false);
209         }
210     }
211 
updateGoogleSettings()212     private void updateGoogleSettings() {
213         final Preference googleSettingsPref = findPreference(KEY_GOOGLE_SETTINGS);
214         if (googleSettingsPref != null) {
215             final ResolveInfo info = MainFragment.systemIntentIsHandled(getContext(),
216                     googleSettingsPref.getIntent());
217             googleSettingsPref.setVisible(info != null);
218             if (info != null && info.activityInfo != null) {
219                 googleSettingsPref.setIcon(
220                         info.activityInfo.loadIcon(getContext().getPackageManager()));
221                 googleSettingsPref.setTitle(
222                         info.activityInfo.loadLabel(getContext().getPackageManager()));
223             }
224         }
225     }
226 
227     @VisibleForTesting
updateCastSettings()228     void updateCastSettings() {
229         final Preference castPref = findPreference(KEY_CAST_SETTINGS);
230         if (castPref != null) {
231             final ResolveInfo info = MainFragment.systemIntentIsHandled(
232                     getContext(), castPref.getIntent());
233             if (info != null) {
234                 try {
235                     final Context targetContext = getContext()
236                             .createPackageContext(info.resolvePackageName != null
237                                     ? info.resolvePackageName : info.activityInfo.packageName, 0);
238                     castPref.setIcon(targetContext.getDrawable(info.getIconResource()));
239                 } catch (Resources.NotFoundException | PackageManager.NameNotFoundException
240                         | SecurityException e) {
241                     Log.e(TAG, "Cast settings icon not found", e);
242                 }
243                 castPref.setTitle(info.activityInfo.loadLabel(getContext().getPackageManager()));
244             }
245         }
246     }
247 
248     @VisibleForTesting
updateKeyboardAutofillSettings()249     void updateKeyboardAutofillSettings() {
250         final Preference keyboardPref = findPreference(KEY_KEYBOARD);
251 
252         List<DefaultAppInfo> candidates = AutofillHelper.getAutofillCandidates(getContext(),
253                 mPm, UserHandle.myUserId());
254 
255         // Switch title depends on whether there is autofill
256         if (candidates.isEmpty()) {
257             keyboardPref.setTitle(R.string.system_keyboard);
258         } else {
259             keyboardPref.setTitle(R.string.system_keyboard_autofill);
260         }
261 
262         CharSequence summary = "";
263         // append current keyboard to summary
264         String defaultImId = InputMethodHelper.getDefaultInputMethodId(getContext());
265         if (!TextUtils.isEmpty(defaultImId)) {
266             InputMethodInfo info = InputMethodHelper.findInputMethod(defaultImId,
267                     InputMethodHelper.getEnabledSystemInputMethodList(getContext()));
268             if (info != null) {
269                 summary = info.loadLabel(getContext().getPackageManager());
270             }
271 
272         }
273         // append current autofill to summary
274         DefaultAppInfo appInfo = AutofillHelper.getCurrentAutofill(getContext(), candidates);
275         if (appInfo != null) {
276             CharSequence autofillInfo = appInfo.loadLabel();
277             if (summary.length() > 0) {
278                 getContext().getString(R.string.string_concat, summary, autofillInfo);
279             } else {
280                 summary = autofillInfo;
281             }
282         }
283         keyboardPref.setSummary(summary);
284     }
285 
286     @Override
getPageId()287     protected int getPageId() {
288         return TvSettingsEnums.SYSTEM;
289     }
290 }
291