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