• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.car.developeroptions;
18 
19 import android.accessibilityservice.AccessibilityServiceInfo;
20 import android.app.Activity;
21 import android.app.Dialog;
22 import android.app.admin.DevicePolicyManager;
23 import android.app.settings.SettingsEnums;
24 import android.content.Context;
25 import android.content.DialogInterface;
26 import android.content.Intent;
27 import android.content.res.Resources;
28 import android.os.Bundle;
29 import android.util.Log;
30 import android.view.LayoutInflater;
31 import android.view.View;
32 import android.view.ViewGroup;
33 import android.view.accessibility.AccessibilityManager;
34 import android.widget.LinearLayout;
35 import android.widget.TextView;
36 
37 import androidx.appcompat.app.AlertDialog;
38 
39 import com.android.car.developeroptions.core.InstrumentedFragment;
40 import com.android.car.developeroptions.core.instrumentation.InstrumentedDialogFragment;
41 import com.android.car.developeroptions.password.ChooseLockSettingsHelper;
42 
43 import com.google.android.setupcompat.template.FooterBarMixin;
44 import com.google.android.setupcompat.template.FooterButton;
45 import com.google.android.setupdesign.GlifLayout;
46 
47 import java.util.List;
48 
49 public class EncryptionInterstitial extends SettingsActivity {
50     private static final String TAG = EncryptionInterstitial.class.getSimpleName();
51 
52     protected static final String EXTRA_PASSWORD_QUALITY = "extra_password_quality";
53     protected static final String EXTRA_UNLOCK_METHOD_INTENT = "extra_unlock_method_intent";
54     public static final String EXTRA_REQUIRE_PASSWORD = "extra_require_password";
55     private static final int CHOOSE_LOCK_REQUEST = 100;
56 
57     @Override
getIntent()58     public Intent getIntent() {
59         Intent modIntent = new Intent(super.getIntent());
60         modIntent.putExtra(EXTRA_SHOW_FRAGMENT, EncryptionInterstitialFragment.class.getName());
61         return modIntent;
62     }
63 
64     @Override
onApplyThemeResource(Resources.Theme theme, int resid, boolean first)65     protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
66         resid = SetupWizardUtils.getTheme(getIntent());
67         super.onApplyThemeResource(theme, resid, first);
68     }
69 
70     @Override
isValidFragment(String fragmentName)71     protected boolean isValidFragment(String fragmentName) {
72         return EncryptionInterstitialFragment.class.getName().equals(fragmentName);
73     }
74 
createStartIntent(Context ctx, int quality, boolean requirePasswordDefault, Intent unlockMethodIntent)75     public static Intent createStartIntent(Context ctx, int quality,
76             boolean requirePasswordDefault, Intent unlockMethodIntent) {
77         return new Intent(ctx, EncryptionInterstitial.class)
78                 .putExtra(EXTRA_PASSWORD_QUALITY, quality)
79                 .putExtra(EXTRA_SHOW_FRAGMENT_TITLE_RESID, R.string.encryption_interstitial_header)
80                 .putExtra(EXTRA_REQUIRE_PASSWORD, requirePasswordDefault)
81                 .putExtra(EXTRA_UNLOCK_METHOD_INTENT, unlockMethodIntent);
82     }
83 
84     @Override
onCreate(Bundle savedInstance)85     protected void onCreate(Bundle savedInstance) {
86         super.onCreate(savedInstance);
87         LinearLayout layout = (LinearLayout) findViewById(R.id.content_parent);
88         layout.setFitsSystemWindows(false);
89     }
90 
91     public static class EncryptionInterstitialFragment extends InstrumentedFragment {
92 
93         private boolean mPasswordRequired;
94         private Intent mUnlockMethodIntent;
95         private int mRequestedPasswordQuality;
96 
97         @Override
getMetricsCategory()98         public int getMetricsCategory() {
99             return SettingsEnums.ENCRYPTION;
100         }
101 
102         @Override
onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)103         public View onCreateView(
104                 LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
105             return inflater.inflate(R.layout.encryption_interstitial, container, false);
106         }
107 
108         @Override
onViewCreated(View view, Bundle savedInstanceState)109         public void onViewCreated(View view, Bundle savedInstanceState) {
110             super.onViewCreated(view, savedInstanceState);
111 
112             final boolean forFingerprint = getActivity().getIntent().getBooleanExtra(
113                     ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
114             final boolean forFace = getActivity().getIntent()
115                     .getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
116             Intent intent = getActivity().getIntent();
117             mRequestedPasswordQuality = intent.getIntExtra(EXTRA_PASSWORD_QUALITY, 0);
118             mUnlockMethodIntent = intent.getParcelableExtra(EXTRA_UNLOCK_METHOD_INTENT);
119             final int msgId;
120             switch (mRequestedPasswordQuality) {
121                 case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
122                     msgId = forFingerprint ?
123                             R.string.encryption_interstitial_message_pattern_for_fingerprint :
124                             forFace ?
125                             R.string.encryption_interstitial_message_pattern_for_face :
126                             R.string.encryption_interstitial_message_pattern;
127                     break;
128                 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
129                 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
130                     msgId = forFingerprint ?
131                             R.string.encryption_interstitial_message_pin_for_fingerprint :
132                             forFace ?
133                             R.string.encryption_interstitial_message_pin_for_face :
134                             R.string.encryption_interstitial_message_pin;
135                     break;
136                 default:
137                     msgId = forFingerprint ?
138                             R.string.encryption_interstitial_message_password_for_fingerprint :
139                             forFace ?
140                             R.string.encryption_interstitial_message_password_for_face :
141                             R.string.encryption_interstitial_message_password;
142                     break;
143             }
144             TextView message = (TextView) getActivity().findViewById(R.id.encryption_message);
145             message.setText(msgId);
146 
147             setRequirePasswordState(getActivity().getIntent().getBooleanExtra(
148                     EXTRA_REQUIRE_PASSWORD, true));
149 
150             GlifLayout layout = (GlifLayout) view;
151             layout.setHeaderText(getActivity().getTitle());
152 
153             final FooterBarMixin mixin = layout.getMixin(FooterBarMixin.class);
154             mixin.setSecondaryButton(
155                     new FooterButton.Builder(getContext())
156                             .setText(R.string.encryption_interstitial_no)
157                             .setListener(this::onNoButtonClicked)
158                             .setButtonType(FooterButton.ButtonType.SKIP)
159                             .setTheme(R.style.SudGlifButton_Secondary)
160                             .build()
161             );
162 
163             mixin.setPrimaryButton(
164                     new FooterButton.Builder(getContext())
165                             .setText(R.string.encryption_interstitial_yes)
166                             .setListener(this::onYesButtonClicked)
167                             .setButtonType(FooterButton.ButtonType.NEXT)
168                             .setTheme(R.style.SudGlifButton_Primary)
169                             .build()
170             );
171         }
172 
startLockIntent()173         protected void startLockIntent() {
174             if (mUnlockMethodIntent != null) {
175                 mUnlockMethodIntent.putExtra(EXTRA_REQUIRE_PASSWORD, mPasswordRequired);
176                 startActivityForResult(mUnlockMethodIntent, CHOOSE_LOCK_REQUEST);
177             } else {
178                 Log.wtf(TAG, "no unlock intent to start");
179                 finish();
180             }
181         }
182 
183         @Override
onActivityResult(int requestCode, int resultCode, Intent data)184         public void onActivityResult(int requestCode, int resultCode, Intent data) {
185             super.onActivityResult(requestCode, resultCode, data);
186             if (requestCode == CHOOSE_LOCK_REQUEST && resultCode != RESULT_CANCELED) {
187                 getActivity().setResult(resultCode, data);
188                 finish();
189             }
190         }
191 
onYesButtonClicked(View view)192         private void onYesButtonClicked(View view) {
193             final boolean accEn = AccessibilityManager.getInstance(getActivity()).isEnabled();
194             if (accEn && !mPasswordRequired) {
195                 setRequirePasswordState(false); // clear the UI state
196                 AccessibilityWarningDialogFragment.newInstance(mRequestedPasswordQuality)
197                         .show(
198                                 getChildFragmentManager(),
199                                 AccessibilityWarningDialogFragment.TAG);
200             } else {
201                 setRequirePasswordState(true);
202                 startLockIntent();
203             }
204         }
205 
onNoButtonClicked(View view)206         private void onNoButtonClicked(View view) {
207             setRequirePasswordState(false);
208             startLockIntent();
209         }
210 
setRequirePasswordState(boolean required)211         private void setRequirePasswordState(boolean required) {
212             mPasswordRequired = required;
213         }
214 
finish()215         public void finish() {
216             Activity activity = getActivity();
217             if (activity == null) return;
218             if (getFragmentManager().getBackStackEntryCount() > 0) {
219                 getFragmentManager().popBackStack();
220             } else {
221                 activity.finish();
222             }
223         }
224     }
225 
226     public static class AccessibilityWarningDialogFragment extends InstrumentedDialogFragment
227             implements DialogInterface.OnClickListener {
228 
229         public static final String TAG = "AccessibilityWarningDialog";
230 
newInstance(int passwordQuality)231         public static AccessibilityWarningDialogFragment newInstance(int passwordQuality) {
232             AccessibilityWarningDialogFragment fragment = new AccessibilityWarningDialogFragment();
233             Bundle args = new Bundle(1);
234             args.putInt(EXTRA_PASSWORD_QUALITY, passwordQuality);
235             fragment.setArguments(args);
236             return fragment;
237         }
238 
239         @Override
onCreateDialog(Bundle savedInstanceState)240         public Dialog onCreateDialog(Bundle savedInstanceState) {
241             final int titleId;
242             final int messageId;
243             switch (getArguments().getInt(EXTRA_PASSWORD_QUALITY)) {
244                 case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
245                     titleId = R.string.encrypt_talkback_dialog_require_pattern;
246                     messageId = R.string.encrypt_talkback_dialog_message_pattern;
247                     break;
248                 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
249                 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
250                     titleId = R.string.encrypt_talkback_dialog_require_pin;
251                     messageId = R.string.encrypt_talkback_dialog_message_pin;
252                     break;
253                 default:
254                     titleId = R.string.encrypt_talkback_dialog_require_password;
255                     messageId = R.string.encrypt_talkback_dialog_message_password;
256                     break;
257             }
258 
259 
260             final Activity activity = getActivity();
261             List<AccessibilityServiceInfo> list =
262                     AccessibilityManager.getInstance(activity)
263                             .getEnabledAccessibilityServiceList(
264                                     AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
265             final CharSequence exampleAccessibility;
266             if (list.isEmpty()) {
267                 // This should never happen.  But we shouldn't crash
268                 exampleAccessibility = "";
269             } else {
270                 exampleAccessibility = list.get(0).getResolveInfo()
271                         .loadLabel(activity.getPackageManager());
272             }
273             return new AlertDialog.Builder(activity)
274                     .setTitle(titleId)
275                     .setMessage(getString(messageId, exampleAccessibility))
276                     .setCancelable(true)
277                     .setPositiveButton(android.R.string.ok, this)
278                     .setNegativeButton(android.R.string.cancel, this)
279                     .create();
280         }
281 
282         @Override
getMetricsCategory()283         public int getMetricsCategory() {
284             return SettingsEnums.DIALOG_ENCRYPTION_INTERSTITIAL_ACCESSIBILITY;
285         }
286 
287         @Override
onClick(DialogInterface dialog, int which)288         public void onClick(DialogInterface dialog, int which) {
289             EncryptionInterstitialFragment fragment =
290                     (EncryptionInterstitialFragment) getParentFragment();
291             if (fragment != null) {
292                 if (which == DialogInterface.BUTTON_POSITIVE) {
293                     fragment.setRequirePasswordState(true);
294                     fragment.startLockIntent();
295                 } else if (which == DialogInterface.BUTTON_NEGATIVE) {
296                     fragment.setRequirePasswordState(false);
297                 }
298             }
299         }
300     }
301 }
302