• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /*
2   * Copyright (C) 2014 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  package com.android.settings.security;
17  
18  import static android.app.Activity.RESULT_OK;
19  
20  import android.app.admin.DevicePolicyManager;
21  import android.app.settings.SettingsEnums;
22  import android.content.Context;
23  import android.content.DialogInterface;
24  import android.content.Intent;
25  import android.content.res.Resources;
26  import android.icu.text.MessageFormat;
27  import android.os.Bundle;
28  import android.os.UserHandle;
29  import android.os.UserManager;
30  import android.provider.Settings;
31  import android.widget.CompoundButton;
32  import android.widget.CompoundButton.OnCheckedChangeListener;
33  
34  import androidx.annotation.NonNull;
35  import androidx.appcompat.app.AlertDialog;
36  import androidx.preference.Preference;
37  import androidx.preference.Preference.OnPreferenceChangeListener;
38  import androidx.preference.PreferenceScreen;
39  import androidx.preference.TwoStatePreference;
40  
41  import com.android.internal.widget.LockPatternUtils;
42  import com.android.settings.R;
43  import com.android.settings.SettingsActivity;
44  import com.android.settings.SettingsPreferenceFragment;
45  import com.android.settings.password.ChooseLockGeneric;
46  import com.android.settings.password.ChooseLockSettingsHelper;
47  import com.android.settings.search.BaseSearchIndexProvider;
48  import com.android.settings.widget.SettingsMainSwitchBar;
49  import com.android.settingslib.search.SearchIndexable;
50  import com.android.settingslib.search.SearchIndexableRaw;
51  import com.android.settingslib.widget.FooterPreference;
52  
53  import java.util.List;
54  
55  /**
56   * Screen pinning settings.
57   */
58  @SearchIndexable
59  public class ScreenPinningSettings extends SettingsPreferenceFragment
60          implements OnCheckedChangeListener, DialogInterface.OnClickListener {
61  
62      private static final String KEY_USE_SCREEN_LOCK = "use_screen_lock";
63      private static final String KEY_FOOTER = "screen_pinning_settings_screen_footer";
64      private static final int CHANGE_LOCK_METHOD_REQUEST = 43;
65      private static final int CONFIRM_REQUEST = 1000;
66  
67      private SettingsMainSwitchBar mSwitchBar;
68      private TwoStatePreference mUseScreenLock;
69      private FooterPreference mFooterPreference;
70      private LockPatternUtils mLockPatternUtils;
71      private UserManager mUserManager;
72  
73      @Override
getMetricsCategory()74      public int getMetricsCategory() {
75          return SettingsEnums.SCREEN_PINNING;
76      }
77  
78      @Override
onActivityCreated(Bundle savedInstanceState)79      public void onActivityCreated(Bundle savedInstanceState) {
80          super.onActivityCreated(savedInstanceState);
81  
82          final SettingsActivity activity = (SettingsActivity) getActivity();
83          activity.setTitle(R.string.screen_pinning_title);
84          mLockPatternUtils = new LockPatternUtils(activity);
85          mUserManager = activity.getSystemService(UserManager.class);
86  
87          addPreferencesFromResource(R.xml.screen_pinning_settings);
88          final PreferenceScreen root = getPreferenceScreen();
89          mUseScreenLock = root.findPreference(KEY_USE_SCREEN_LOCK);
90          mFooterPreference = root.findPreference(KEY_FOOTER);
91  
92          mSwitchBar = activity.getSwitchBar();
93          mSwitchBar.setTitle(getContext().getString(R.string.app_pinning_main_switch_title));
94          mSwitchBar.show();
95          mSwitchBar.setChecked(isLockToAppEnabled(getActivity()));
96          mSwitchBar.addOnSwitchChangeListener(this);
97  
98          updateDisplay();
99      }
100  
101      @Override
getHelpResource()102      public int getHelpResource() {
103          return R.string.help_url_screen_pinning;
104      }
105  
106      @Override
onDestroyView()107      public void onDestroyView() {
108          super.onDestroyView();
109  
110          mSwitchBar.removeOnSwitchChangeListener(this);
111          mSwitchBar.hide();
112      }
113  
isLockToAppEnabled(Context context)114      private static boolean isLockToAppEnabled(Context context) {
115          return Settings.System.getInt(context.getContentResolver(),
116                  Settings.System.LOCK_TO_APP_ENABLED, 0) != 0;
117      }
118  
setLockToAppEnabled(boolean isEnabled)119      private void setLockToAppEnabled(boolean isEnabled) {
120          Settings.System.putInt(getContentResolver(), Settings.System.LOCK_TO_APP_ENABLED,
121                  isEnabled ? 1 : 0);
122          if (isEnabled) {
123              // Set the value to match what we have defaulted to in the UI.
124              setScreenLockUsedSetting(isScreenLockUsed());
125          }
126      }
127  
isScreenLockUsed()128      private boolean isScreenLockUsed() {
129          // This functionality should be kept consistent with
130          // com.android.server.wm.LockTaskController (see b/127605586)
131          int defaultValueIfSettingNull = mLockPatternUtils.isSecure(UserHandle.myUserId()) ? 1 : 0;
132          return Settings.Secure.getInt(
133                  getContentResolver(),
134                  Settings.Secure.LOCK_TO_APP_EXIT_LOCKED,
135                  defaultValueIfSettingNull) != 0;
136      }
137  
setScreenLockUsed(boolean isEnabled)138      private boolean setScreenLockUsed(boolean isEnabled) {
139          LockPatternUtils lockPatternUtils = new LockPatternUtils(getActivity());
140          final int passwordQuality = lockPatternUtils
141                  .getKeyguardStoredPasswordQuality(UserHandle.myUserId());
142          if (isEnabled) {
143              if (passwordQuality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
144                  Intent chooseLockIntent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD);
145                  chooseLockIntent.setPackage(getContext().getPackageName());
146                  chooseLockIntent.putExtra(
147                          ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS,
148                          true);
149                  startActivityForResult(chooseLockIntent, CHANGE_LOCK_METHOD_REQUEST);
150                  return false;
151              }
152          }  else {
153              if (passwordQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
154                  final ChooseLockSettingsHelper.Builder builder =
155                          new ChooseLockSettingsHelper.Builder(getActivity(), this);
156                  return builder.setRequestCode(CONFIRM_REQUEST).show();
157              }
158          }
159          setScreenLockUsedSetting(isEnabled);
160          return true;
161      }
162  
setScreenLockUsedSetting(boolean isEnabled)163      private void setScreenLockUsedSetting(boolean isEnabled) {
164          Settings.Secure.putInt(getContentResolver(), Settings.Secure.LOCK_TO_APP_EXIT_LOCKED,
165                  isEnabled ? 1 : 0);
166      }
167  
168      @Override
onActivityResult(int requestCode, int resultCode, Intent data)169      public void onActivityResult(int requestCode, int resultCode, Intent data) {
170          super.onActivityResult(requestCode, resultCode, data);
171          if (requestCode == CHANGE_LOCK_METHOD_REQUEST) {
172              LockPatternUtils lockPatternUtils = new LockPatternUtils(getActivity());
173              boolean validPassQuality = lockPatternUtils.getKeyguardStoredPasswordQuality(
174                      UserHandle.myUserId())
175                      != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
176              setScreenLockUsed(validPassQuality);
177              // Make sure the screen updates.
178              mUseScreenLock.setChecked(validPassQuality);
179          } else if (requestCode == CONFIRM_REQUEST && resultCode == RESULT_OK) {
180              setScreenLockUsedSetting(false);
181          }
182      }
183  
getCurrentSecurityTitle(LockPatternUtils lockPatternUtils)184      private static int getCurrentSecurityTitle(LockPatternUtils lockPatternUtils) {
185          int quality = lockPatternUtils.getKeyguardStoredPasswordQuality(UserHandle.myUserId());
186          switch (quality) {
187              case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
188              case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
189                  return R.string.screen_pinning_unlock_pin;
190              case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
191              case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
192              case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
193              case DevicePolicyManager.PASSWORD_QUALITY_MANAGED:
194                  return R.string.screen_pinning_unlock_password;
195              case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
196                  if (lockPatternUtils.isLockPatternEnabled(UserHandle.myUserId())) {
197                      return R.string.screen_pinning_unlock_pattern;
198                  }
199          }
200          return R.string.screen_pinning_unlock_none;
201      }
202  
203      /**
204       * Listens to the state change of the overall lock-to-app switch.
205       */
206      @Override
onCheckedChanged(CompoundButton buttonView, boolean isChecked)207      public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
208          if (isChecked) {
209              new AlertDialog.Builder(getContext())
210                      .setMessage(R.string.screen_pinning_dialog_message)
211                      .setPositiveButton(R.string.dlg_ok, this)
212                      .setNegativeButton(R.string.dlg_cancel, this)
213                      .setCancelable(false)
214                      .show();
215          } else {
216              setLockToAppEnabled(false);
217              updateDisplay();
218          }
219      }
220  
221      @Override
onClick(DialogInterface dialogInterface, int which)222      public void onClick(DialogInterface dialogInterface, int which) {
223          if (which == DialogInterface.BUTTON_POSITIVE) {
224              setLockToAppEnabled(true);
225          } else {
226              mSwitchBar.setChecked(false);
227          }
228          updateDisplay();
229      }
230  
updateDisplay()231      private void updateDisplay() {
232          if (isLockToAppEnabled(getActivity())) {
233              mUseScreenLock.setEnabled(true);
234              mUseScreenLock.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
235                  @Override
236                  public boolean onPreferenceChange(Preference preference, Object newValue) {
237                      return setScreenLockUsed((boolean) newValue);
238                  }
239              });
240              mUseScreenLock.setChecked(isScreenLockUsed());
241              mUseScreenLock.setTitle(getCurrentSecurityTitle(mLockPatternUtils));
242          } else {
243              mUseScreenLock.setEnabled(false);
244          }
245          mFooterPreference.setSummary(getAppPinningContent());
246      }
247  
isGuestModeSupported()248      private boolean isGuestModeSupported() {
249          return UserManager.supportsMultipleUsers()
250                  && !mUserManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH);
251      }
252  
getAppPinningContent()253      private CharSequence getAppPinningContent() {
254          final int stringResource = isGuestModeSupported()
255                  ? R.string.screen_pinning_guest_user_description
256                  : R.string.screen_pinning_description;
257          return MessageFormat.format(getActivity().getString(stringResource), 1, 2, 3);
258      }
259  
260      /**
261       * For search.
262       *
263       * This page only provides an index for the toggle preference of using screen lock for
264       * unpinning. The preference name will change with various lock configurations. Indexing data
265       * from XML isn't suitable since it uses a static title by default. So, we skip XML indexing
266       * by omitting the XML argument in the constructor and use a dynamic index method instead.
267       */
268      public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
269              new BaseSearchIndexProvider() {
270  
271                  @NonNull
272                  @Override
273                  public List<SearchIndexableRaw> getDynamicRawDataToIndex(@NonNull Context context,
274                          boolean enabled) {
275                      List<SearchIndexableRaw> dynamicRaws =
276                              super.getDynamicRawDataToIndex(context, enabled);
277                      final SearchIndexableRaw raw = new SearchIndexableRaw(context);
278                      final Resources res = context.getResources();
279                      final LockPatternUtils lockPatternUtils = new LockPatternUtils(context);
280                      raw.key = KEY_USE_SCREEN_LOCK;
281                      raw.title = res.getString(getCurrentSecurityTitle(lockPatternUtils));
282                      raw.screenTitle = res.getString(R.string.screen_pinning_title);
283                      dynamicRaws.add(raw);
284                      return dynamicRaws;
285                  }
286              };
287  }
288