[BiometricsV2] Refactor Fingerprint intro fragment
Refactor FingerprintEnrollIntroFragment to kotlin and add bindView() method for it. Bug: 286197823 Test: atest FingerprintEnrollmentActivityTest Change-Id: I44157bf2c2bea6f49382335438b16aae3e3e5b4c
This commit is contained in:
@@ -81,6 +81,7 @@
|
|||||||
android:id="@+id/footer_message_2"
|
android:id="@+id/footer_message_2"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/security_settings_fingerprint_v2_enroll_introduction_footer_message_2"
|
||||||
style="@style/BiometricEnrollIntroMessage" />
|
style="@style/BiometricEnrollIntroMessage" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
@@ -102,6 +103,7 @@
|
|||||||
android:id="@+id/footer_message_3"
|
android:id="@+id/footer_message_3"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/security_settings_fingerprint_v2_enroll_introduction_footer_message_3"
|
||||||
style="@style/BiometricEnrollIntroMessage" />
|
style="@style/BiometricEnrollIntroMessage" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
@@ -110,6 +112,7 @@
|
|||||||
android:id="@+id/footer_title_1"
|
android:id="@+id/footer_title_1"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/security_settings_fingerprint_enroll_introduction_footer_title_1"
|
||||||
style="@style/BiometricEnrollIntroTitle" />
|
style="@style/BiometricEnrollIntroTitle" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@@ -130,6 +133,7 @@
|
|||||||
android:id="@+id/footer_message_4"
|
android:id="@+id/footer_message_4"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/security_settings_fingerprint_v2_enroll_introduction_footer_message_4"
|
||||||
style="@style/BiometricEnrollIntroMessage" />
|
style="@style/BiometricEnrollIntroMessage" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
@@ -139,7 +143,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
style="@style/BiometricEnrollIntroTitle"
|
style="@style/BiometricEnrollIntroTitle"
|
||||||
android:text="@string/security_settings_face_enroll_introduction_info_title"/>
|
android:text="@string/security_settings_fingerprint_enroll_introduction_footer_title_2"/>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -159,6 +163,7 @@
|
|||||||
android:id="@+id/footer_message_5"
|
android:id="@+id/footer_message_5"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/security_settings_fingerprint_v2_enroll_introduction_footer_message_5"
|
||||||
style="@style/BiometricEnrollIntroMessage" />
|
style="@style/BiometricEnrollIntroMessage" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
@@ -180,6 +185,7 @@
|
|||||||
android:id="@+id/footer_message_6"
|
android:id="@+id/footer_message_6"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/security_settings_fingerprint_v2_enroll_introduction_footer_message_6"
|
||||||
style="@style/BiometricEnrollIntroMessage" />
|
style="@style/BiometricEnrollIntroMessage" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
@@ -1,310 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2022 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.settings.biometrics2.ui.view;
|
|
||||||
|
|
||||||
import static android.app.admin.DevicePolicyResources.Strings.Settings.FINGERPRINT_UNLOCK_DISABLED;
|
|
||||||
|
|
||||||
import static com.android.settings.biometrics2.ui.model.FingerprintEnrollable.FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX;
|
|
||||||
|
|
||||||
import static com.google.android.setupdesign.util.DynamicColorPalette.ColorType.ACCENT;
|
|
||||||
|
|
||||||
import android.app.admin.DevicePolicyManager;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.PorterDuff;
|
|
||||||
import android.graphics.PorterDuffColorFilter;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.text.Html;
|
|
||||||
import android.text.method.LinkMovementMethod;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.View.OnClickListener;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.annotation.StringRes;
|
|
||||||
import androidx.fragment.app.Fragment;
|
|
||||||
import androidx.lifecycle.LiveData;
|
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
|
||||||
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.biometrics2.ui.model.FingerprintEnrollIntroStatus;
|
|
||||||
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel;
|
|
||||||
|
|
||||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
|
||||||
import com.google.android.setupcompat.template.FooterButton;
|
|
||||||
import com.google.android.setupdesign.GlifLayout;
|
|
||||||
import com.google.android.setupdesign.template.RequireScrollMixin;
|
|
||||||
import com.google.android.setupdesign.util.DeviceHelper;
|
|
||||||
import com.google.android.setupdesign.util.DynamicColorPalette;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fingerprint intro onboarding page fragment implementation
|
|
||||||
*/
|
|
||||||
public class FingerprintEnrollIntroFragment extends Fragment {
|
|
||||||
|
|
||||||
private static final String TAG = "FingerprintEnrollIntroFragment";
|
|
||||||
private static final boolean DEBUG = false;
|
|
||||||
|
|
||||||
private FingerprintEnrollIntroViewModel mViewModel = null;
|
|
||||||
|
|
||||||
private View mView = null;
|
|
||||||
private FooterButton mPrimaryFooterButton = null;
|
|
||||||
private FooterButton mSecondaryFooterButton = null;
|
|
||||||
private final OnClickListener mOnNextClickListener = (v) -> mViewModel.onNextButtonClick();
|
|
||||||
private final OnClickListener mOnSkipOrCancelClickListener =
|
|
||||||
(v) -> mViewModel.onSkipOrCancelButtonClick();
|
|
||||||
private ImageView mIconShield = null;
|
|
||||||
private TextView mFooterMessage6 = null;
|
|
||||||
@Nullable private PorterDuffColorFilter mIconColorFilter;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
|
||||||
@Nullable Bundle savedInstanceState) {
|
|
||||||
|
|
||||||
final Context context = inflater.getContext();
|
|
||||||
mView = inflater.inflate(R.layout.fingerprint_enroll_introduction, container, false);
|
|
||||||
|
|
||||||
final ImageView iconFingerprint = mView.findViewById(R.id.icon_fingerprint);
|
|
||||||
final ImageView iconDeviceLocked = mView.findViewById(R.id.icon_device_locked);
|
|
||||||
final ImageView iconTrashCan = mView.findViewById(R.id.icon_trash_can);
|
|
||||||
final ImageView iconInfo = mView.findViewById(R.id.icon_info);
|
|
||||||
mIconShield = mView.findViewById(R.id.icon_shield);
|
|
||||||
final ImageView iconLink = mView.findViewById(R.id.icon_link);
|
|
||||||
iconFingerprint.getDrawable().setColorFilter(getIconColorFilter(context));
|
|
||||||
iconDeviceLocked.getDrawable().setColorFilter(getIconColorFilter(context));
|
|
||||||
iconTrashCan.getDrawable().setColorFilter(getIconColorFilter(context));
|
|
||||||
iconInfo.getDrawable().setColorFilter(getIconColorFilter(context));
|
|
||||||
mIconShield.getDrawable().setColorFilter(getIconColorFilter(context));
|
|
||||||
iconLink.getDrawable().setColorFilter(getIconColorFilter(context));
|
|
||||||
|
|
||||||
final TextView footerMessage2 = mView.findViewById(R.id.footer_message_2);
|
|
||||||
final TextView footerMessage3 = mView.findViewById(R.id.footer_message_3);
|
|
||||||
final TextView footerMessage4 = mView.findViewById(R.id.footer_message_4);
|
|
||||||
final TextView footerMessage5 = mView.findViewById(R.id.footer_message_5);
|
|
||||||
mFooterMessage6 = mView.findViewById(R.id.footer_message_6);
|
|
||||||
footerMessage2.setText(
|
|
||||||
R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_2);
|
|
||||||
footerMessage3.setText(
|
|
||||||
R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_3);
|
|
||||||
footerMessage4.setText(
|
|
||||||
R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_4);
|
|
||||||
footerMessage5.setText(
|
|
||||||
R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_5);
|
|
||||||
mFooterMessage6.setText(
|
|
||||||
R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_6);
|
|
||||||
|
|
||||||
final TextView footerTitle1 = mView.findViewById(R.id.footer_title_1);
|
|
||||||
final TextView footerTitle2 = mView.findViewById(R.id.footer_title_2);
|
|
||||||
footerTitle1.setText(
|
|
||||||
R.string.security_settings_fingerprint_enroll_introduction_footer_title_1);
|
|
||||||
footerTitle2.setText(
|
|
||||||
R.string.security_settings_fingerprint_enroll_introduction_footer_title_2);
|
|
||||||
|
|
||||||
final TextView footerLink = mView.findViewById(R.id.footer_learn_more);
|
|
||||||
footerLink.setMovementMethod(LinkMovementMethod.getInstance());
|
|
||||||
final String footerLinkStr = getContext().getString(
|
|
||||||
R.string.security_settings_fingerprint_v2_enroll_introduction_message_learn_more,
|
|
||||||
Html.FROM_HTML_MODE_LEGACY);
|
|
||||||
footerLink.setText(Html.fromHtml(footerLinkStr));
|
|
||||||
|
|
||||||
// footer buttons
|
|
||||||
|
|
||||||
return mView;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
|
||||||
super.onViewCreated(view, savedInstanceState);
|
|
||||||
|
|
||||||
final Context context = view.getContext();
|
|
||||||
|
|
||||||
if (mViewModel.canAssumeUdfps()) {
|
|
||||||
mFooterMessage6.setVisibility(View.VISIBLE);
|
|
||||||
mIconShield.setVisibility(View.VISIBLE);
|
|
||||||
} else {
|
|
||||||
mFooterMessage6.setVisibility(View.GONE);
|
|
||||||
mIconShield.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
final GlifLayoutHelper glifLayoutHelper = new GlifLayoutHelper(getActivity(), getLayout());
|
|
||||||
if (mViewModel.isBiometricUnlockDisabledByAdmin()
|
|
||||||
&& !mViewModel.isParentalConsentRequired()) {
|
|
||||||
glifLayoutHelper.setHeaderText(
|
|
||||||
R.string.security_settings_fingerprint_enroll_introduction_title_unlock_disabled
|
|
||||||
);
|
|
||||||
glifLayoutHelper.setDescriptionText(getDescriptionDisabledByAdmin(context));
|
|
||||||
} else {
|
|
||||||
glifLayoutHelper.setHeaderText(
|
|
||||||
R.string.security_settings_fingerprint_enroll_introduction_title);
|
|
||||||
glifLayoutHelper.setDescriptionText(getString(
|
|
||||||
R.string.security_settings_fingerprint_enroll_introduction_v3_message,
|
|
||||||
DeviceHelper.getDeviceName(context)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStart() {
|
|
||||||
final Context context = requireContext();
|
|
||||||
final FooterBarMixin footerBarMixin = getFooterBarMixin();
|
|
||||||
initPrimaryFooterButton(context, footerBarMixin);
|
|
||||||
initSecondaryFooterButton(context, footerBarMixin);
|
|
||||||
observePageStatusLiveDataIfNeed();
|
|
||||||
super.onStart();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initPrimaryFooterButton(@NonNull Context context,
|
|
||||||
@NonNull FooterBarMixin footerBarMixin) {
|
|
||||||
if (footerBarMixin.getPrimaryButton() != null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mPrimaryFooterButton = new FooterButton.Builder(context)
|
|
||||||
.setText(R.string.security_settings_fingerprint_enroll_introduction_agree)
|
|
||||||
.setButtonType(FooterButton.ButtonType.OPT_IN)
|
|
||||||
.setTheme(R.style.SudGlifButton_Primary)
|
|
||||||
.build();
|
|
||||||
mPrimaryFooterButton.setOnClickListener(mOnNextClickListener);
|
|
||||||
footerBarMixin.setPrimaryButton(mPrimaryFooterButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initSecondaryFooterButton(@NonNull Context context,
|
|
||||||
@NonNull FooterBarMixin footerBarMixin) {
|
|
||||||
if (footerBarMixin.getSecondaryButton() != null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mSecondaryFooterButton = new FooterButton.Builder(context)
|
|
||||||
.setText(mViewModel.getRequest().isAfterSuwOrSuwSuggestedAction()
|
|
||||||
? R.string.security_settings_fingerprint_enroll_introduction_cancel
|
|
||||||
: R.string.security_settings_fingerprint_enroll_introduction_no_thanks)
|
|
||||||
.setButtonType(FooterButton.ButtonType.NEXT)
|
|
||||||
.setTheme(R.style.SudGlifButton_Primary)
|
|
||||||
.build();
|
|
||||||
mSecondaryFooterButton.setOnClickListener(mOnSkipOrCancelClickListener);
|
|
||||||
footerBarMixin.setSecondaryButton(mSecondaryFooterButton, true /* usePrimaryStyle */);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void observePageStatusLiveDataIfNeed() {
|
|
||||||
final LiveData<FingerprintEnrollIntroStatus> statusLiveData =
|
|
||||||
mViewModel.getPageStatusLiveData();
|
|
||||||
final FingerprintEnrollIntroStatus status = statusLiveData.getValue();
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.e(TAG, "observePageStatusLiveDataIfNeed() requireScrollWithButton, status:"
|
|
||||||
+ status);
|
|
||||||
}
|
|
||||||
if (status != null && (status.hasScrollToBottom()
|
|
||||||
|| status.getEnrollableStatus() == FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX)) {
|
|
||||||
// Update once and do not requireScrollWithButton() again when page has scrolled to
|
|
||||||
// bottom or User has enrolled at least a fingerprint, because if we
|
|
||||||
// requireScrollWithButton() again, primary button will become "More" after scrolling.
|
|
||||||
updateFooterButtons(status);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final RequireScrollMixin requireScrollMixin = getLayout()
|
|
||||||
.getMixin(RequireScrollMixin.class);
|
|
||||||
requireScrollMixin.requireScrollWithButton(getActivity(), mPrimaryFooterButton,
|
|
||||||
getMoreButtonTextRes(), mOnNextClickListener);
|
|
||||||
|
|
||||||
requireScrollMixin.setOnRequireScrollStateChangedListener(
|
|
||||||
scrollNeeded -> mViewModel.setHasScrolledToBottom(!scrollNeeded));
|
|
||||||
statusLiveData.observe(this, this::updateFooterButtons);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAttach(@NonNull Context context) {
|
|
||||||
mViewModel = new ViewModelProvider(getActivity())
|
|
||||||
.get(FingerprintEnrollIntroViewModel.class);
|
|
||||||
super.onAttach(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private PorterDuffColorFilter getIconColorFilter(@NonNull Context context) {
|
|
||||||
if (mIconColorFilter == null) {
|
|
||||||
mIconColorFilter = new PorterDuffColorFilter(
|
|
||||||
DynamicColorPalette.getColor(context, ACCENT),
|
|
||||||
PorterDuff.Mode.SRC_IN);
|
|
||||||
}
|
|
||||||
return mIconColorFilter;
|
|
||||||
}
|
|
||||||
|
|
||||||
private GlifLayout getLayout() {
|
|
||||||
return mView.findViewById(R.id.setup_wizard_layout);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private FooterBarMixin getFooterBarMixin() {
|
|
||||||
final GlifLayout layout = getLayout();
|
|
||||||
return layout.getMixin(FooterBarMixin.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private String getDescriptionDisabledByAdmin(@NonNull Context context) {
|
|
||||||
final int defaultStrId =
|
|
||||||
R.string.security_settings_fingerprint_enroll_introduction_message_unlock_disabled;
|
|
||||||
|
|
||||||
final DevicePolicyManager devicePolicyManager = getActivity()
|
|
||||||
.getSystemService(DevicePolicyManager.class);
|
|
||||||
if (devicePolicyManager != null) {
|
|
||||||
return devicePolicyManager.getResources().getString(FINGERPRINT_UNLOCK_DISABLED,
|
|
||||||
() -> context.getString(defaultStrId));
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "getDescriptionDisabledByAdmin, null device policy manager res");
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateFooterButtons(@NonNull FingerprintEnrollIntroStatus status) {
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.d(TAG, "updateFooterButtons(" + status + ")");
|
|
||||||
}
|
|
||||||
mPrimaryFooterButton.setText(getContext(),
|
|
||||||
status.getEnrollableStatus() == FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX
|
|
||||||
? R.string.done
|
|
||||||
: status.hasScrollToBottom()
|
|
||||||
? R.string.security_settings_fingerprint_enroll_introduction_agree
|
|
||||||
: getMoreButtonTextRes());
|
|
||||||
mSecondaryFooterButton.setVisibility(status.hasScrollToBottom()
|
|
||||||
&& status.getEnrollableStatus() != FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX
|
|
||||||
? View.VISIBLE
|
|
||||||
: View.INVISIBLE);
|
|
||||||
|
|
||||||
final TextView errorTextView = mView.findViewById(R.id.error_text);
|
|
||||||
switch (status.getEnrollableStatus()) {
|
|
||||||
case FINGERPRINT_ENROLLABLE_OK:
|
|
||||||
errorTextView.setText(null);
|
|
||||||
errorTextView.setVisibility(View.GONE);
|
|
||||||
break;
|
|
||||||
case FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX:
|
|
||||||
errorTextView.setText(R.string.fingerprint_intro_error_max);
|
|
||||||
errorTextView.setVisibility(View.VISIBLE);
|
|
||||||
break;
|
|
||||||
case FINGERPRINT_ENROLLABLE_UNKNOWN:
|
|
||||||
// default case, do nothing.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@StringRes
|
|
||||||
private int getMoreButtonTextRes() {
|
|
||||||
return R.string.security_settings_face_enroll_introduction_more;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -0,0 +1,322 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.android.settings.biometrics2.ui.view
|
||||||
|
|
||||||
|
import android.app.admin.DevicePolicyManager
|
||||||
|
import android.app.admin.DevicePolicyResources.Strings.Settings.FINGERPRINT_UNLOCK_DISABLED
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.PorterDuff
|
||||||
|
import android.graphics.PorterDuffColorFilter
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.text.Html
|
||||||
|
import android.text.method.LinkMovementMethod
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import com.android.settings.R
|
||||||
|
import com.android.settings.biometrics2.ui.model.FingerprintEnrollIntroStatus
|
||||||
|
import com.android.settings.biometrics2.ui.model.FingerprintEnrollable.FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX
|
||||||
|
import com.android.settings.biometrics2.ui.model.FingerprintEnrollable.FINGERPRINT_ENROLLABLE_OK
|
||||||
|
import com.android.settings.biometrics2.ui.model.FingerprintEnrollable.FINGERPRINT_ENROLLABLE_UNKNOWN
|
||||||
|
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel
|
||||||
|
import com.google.android.setupcompat.template.FooterBarMixin
|
||||||
|
import com.google.android.setupcompat.template.FooterButton
|
||||||
|
import com.google.android.setupdesign.GlifLayout
|
||||||
|
import com.google.android.setupdesign.template.RequireScrollMixin
|
||||||
|
import com.google.android.setupdesign.util.DeviceHelper
|
||||||
|
import com.google.android.setupdesign.util.DynamicColorPalette
|
||||||
|
import com.google.android.setupdesign.util.DynamicColorPalette.ColorType.ACCENT
|
||||||
|
import java.util.function.Supplier
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fingerprint intro onboarding page fragment implementation
|
||||||
|
*/
|
||||||
|
class FingerprintEnrollIntroFragment : Fragment() {
|
||||||
|
|
||||||
|
private val viewModelProvider: ViewModelProvider
|
||||||
|
get() = ViewModelProvider(requireActivity())
|
||||||
|
|
||||||
|
private var _viewModel: FingerprintEnrollIntroViewModel? = null
|
||||||
|
private val viewModel: FingerprintEnrollIntroViewModel
|
||||||
|
get() {
|
||||||
|
if (_viewModel == null) {
|
||||||
|
_viewModel = viewModelProvider[FingerprintEnrollIntroViewModel::class.java]
|
||||||
|
}
|
||||||
|
return _viewModel!!
|
||||||
|
}
|
||||||
|
|
||||||
|
private var introView: GlifLayout? = null
|
||||||
|
|
||||||
|
private var primaryFooterButton: FooterButton? = null
|
||||||
|
|
||||||
|
private var secondaryFooterButton: FooterButton? = null
|
||||||
|
|
||||||
|
private val onNextClickListener =
|
||||||
|
View.OnClickListener { _: View? -> viewModel.onNextButtonClick() }
|
||||||
|
|
||||||
|
private val onSkipOrCancelClickListener =
|
||||||
|
View.OnClickListener { _: View? -> viewModel.onSkipOrCancelButtonClick() }
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
|
introView = inflater.inflate(
|
||||||
|
R.layout.fingerprint_enroll_introduction,
|
||||||
|
container,
|
||||||
|
false
|
||||||
|
) as GlifLayout
|
||||||
|
return introView!!
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
requireActivity().bindFingerprintEnrollIntroView(
|
||||||
|
view = introView!!,
|
||||||
|
canAssumeUdfps = viewModel.canAssumeUdfps(),
|
||||||
|
isBiometricUnlockDisabledByAdmin = viewModel.isBiometricUnlockDisabledByAdmin,
|
||||||
|
isParentalConsentRequired = viewModel.isParentalConsentRequired,
|
||||||
|
descriptionDisabledByAdminSupplier = { getDescriptionDisabledByAdmin(view.context) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStart() {
|
||||||
|
val context: Context = requireContext()
|
||||||
|
val footerBarMixin: FooterBarMixin = footerBarMixin
|
||||||
|
initPrimaryFooterButton(context, footerBarMixin)
|
||||||
|
initSecondaryFooterButton(context, footerBarMixin)
|
||||||
|
observePageStatusLiveDataIfNeed()
|
||||||
|
super.onStart()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initPrimaryFooterButton(
|
||||||
|
context: Context,
|
||||||
|
footerBarMixin: FooterBarMixin
|
||||||
|
) {
|
||||||
|
if (footerBarMixin.primaryButton != null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
primaryFooterButton = FooterButton.Builder(context)
|
||||||
|
.setText(R.string.security_settings_fingerprint_enroll_introduction_agree)
|
||||||
|
.setButtonType(FooterButton.ButtonType.OPT_IN)
|
||||||
|
.setTheme(R.style.SudGlifButton_Primary)
|
||||||
|
.build()
|
||||||
|
.also {
|
||||||
|
it.setOnClickListener(onNextClickListener)
|
||||||
|
footerBarMixin.primaryButton = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initSecondaryFooterButton(
|
||||||
|
context: Context,
|
||||||
|
footerBarMixin: FooterBarMixin
|
||||||
|
) {
|
||||||
|
if (footerBarMixin.secondaryButton != null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
secondaryFooterButton = FooterButton.Builder(context)
|
||||||
|
.setText(
|
||||||
|
if (viewModel.request.isAfterSuwOrSuwSuggestedAction)
|
||||||
|
R.string.security_settings_fingerprint_enroll_introduction_cancel
|
||||||
|
else
|
||||||
|
R.string.security_settings_fingerprint_enroll_introduction_no_thanks
|
||||||
|
)
|
||||||
|
.setButtonType(FooterButton.ButtonType.NEXT)
|
||||||
|
.setTheme(R.style.SudGlifButton_Primary)
|
||||||
|
.build()
|
||||||
|
.also {
|
||||||
|
it.setOnClickListener(onSkipOrCancelClickListener)
|
||||||
|
footerBarMixin.setSecondaryButton(it, true /* usePrimaryStyle */)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun observePageStatusLiveDataIfNeed() {
|
||||||
|
val statusLiveData: LiveData<FingerprintEnrollIntroStatus> =
|
||||||
|
viewModel.pageStatusLiveData
|
||||||
|
val status: FingerprintEnrollIntroStatus? = statusLiveData.value
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.e(
|
||||||
|
TAG, "observePageStatusLiveDataIfNeed() requireScrollWithButton, status:"
|
||||||
|
+ status
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status != null && (status.hasScrollToBottom()
|
||||||
|
|| status.enrollableStatus === FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX)
|
||||||
|
) {
|
||||||
|
// Update once and do not requireScrollWithButton() again when page has scrolled to
|
||||||
|
// bottom or User has enrolled at least a fingerprint, because if we
|
||||||
|
// requireScrollWithButton() again, primary button will become "More" after scrolling.
|
||||||
|
updateFooterButtons(status)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
introView!!.getMixin(RequireScrollMixin::class.java).let {
|
||||||
|
it.requireScrollWithButton(
|
||||||
|
requireActivity(),
|
||||||
|
primaryFooterButton!!,
|
||||||
|
moreButtonTextRes,
|
||||||
|
onNextClickListener
|
||||||
|
)
|
||||||
|
it.setOnRequireScrollStateChangedListener { scrollNeeded: Boolean ->
|
||||||
|
viewModel.setHasScrolledToBottom(!scrollNeeded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
statusLiveData.observe(this) { newStatus: FingerprintEnrollIntroStatus ->
|
||||||
|
updateFooterButtons(newStatus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAttach(context: Context) {
|
||||||
|
_viewModel = null
|
||||||
|
super.onAttach(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val footerBarMixin: FooterBarMixin
|
||||||
|
get() = introView!!.getMixin(FooterBarMixin::class.java)
|
||||||
|
|
||||||
|
private fun getDescriptionDisabledByAdmin(context: Context): String? {
|
||||||
|
val defaultStrId: Int =
|
||||||
|
R.string.security_settings_fingerprint_enroll_introduction_message_unlock_disabled
|
||||||
|
val devicePolicyManager: DevicePolicyManager = requireActivity()
|
||||||
|
.getSystemService(DevicePolicyManager::class.java)
|
||||||
|
|
||||||
|
return devicePolicyManager.resources.getString(FINGERPRINT_UNLOCK_DISABLED) {
|
||||||
|
context.getString(defaultStrId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateFooterButtons(status: FingerprintEnrollIntroStatus) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "updateFooterButtons($status)")
|
||||||
|
}
|
||||||
|
primaryFooterButton!!.setText(
|
||||||
|
context,
|
||||||
|
if (status.enrollableStatus === FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX)
|
||||||
|
R.string.done
|
||||||
|
else if (status.hasScrollToBottom())
|
||||||
|
R.string.security_settings_fingerprint_enroll_introduction_agree
|
||||||
|
else
|
||||||
|
moreButtonTextRes
|
||||||
|
)
|
||||||
|
secondaryFooterButton!!.visibility =
|
||||||
|
if (status.hasScrollToBottom()
|
||||||
|
&& status.enrollableStatus !== FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX
|
||||||
|
)
|
||||||
|
View.VISIBLE
|
||||||
|
else
|
||||||
|
View.INVISIBLE
|
||||||
|
|
||||||
|
view!!.findViewById<TextView>(R.id.error_text).let {
|
||||||
|
when (status.enrollableStatus) {
|
||||||
|
FINGERPRINT_ENROLLABLE_OK -> {
|
||||||
|
it.text = null
|
||||||
|
it.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX -> {
|
||||||
|
it.setText(R.string.fingerprint_intro_error_max)
|
||||||
|
it.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
FINGERPRINT_ENROLLABLE_UNKNOWN -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:StringRes
|
||||||
|
private val moreButtonTextRes: Int
|
||||||
|
get() = R.string.security_settings_face_enroll_introduction_more
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "FingerprintEnrollIntroFragment"
|
||||||
|
private const val DEBUG = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun FragmentActivity.bindFingerprintEnrollIntroView(
|
||||||
|
view: GlifLayout,
|
||||||
|
canAssumeUdfps: Boolean,
|
||||||
|
isBiometricUnlockDisabledByAdmin: Boolean,
|
||||||
|
isParentalConsentRequired: Boolean,
|
||||||
|
descriptionDisabledByAdminSupplier: Supplier<String?>
|
||||||
|
) {
|
||||||
|
val context = view.context
|
||||||
|
|
||||||
|
val iconFingerprint = view.findViewById<ImageView>(R.id.icon_fingerprint)!!
|
||||||
|
val iconDeviceLocked = view.findViewById<ImageView>(R.id.icon_device_locked)!!
|
||||||
|
val iconTrashCan = view.findViewById<ImageView>(R.id.icon_trash_can)!!
|
||||||
|
val iconInfo = view.findViewById<ImageView>(R.id.icon_info)!!
|
||||||
|
val iconShield = view.findViewById<ImageView>(R.id.icon_shield)!!
|
||||||
|
val iconLink = view.findViewById<ImageView>(R.id.icon_link)!!
|
||||||
|
val footerMessage6 = view.findViewById<TextView>(R.id.footer_message_6)!!
|
||||||
|
|
||||||
|
PorterDuffColorFilter(
|
||||||
|
DynamicColorPalette.getColor(context, ACCENT),
|
||||||
|
PorterDuff.Mode.SRC_IN
|
||||||
|
).let { colorFilter ->
|
||||||
|
iconFingerprint.drawable.colorFilter = colorFilter
|
||||||
|
iconDeviceLocked.drawable.colorFilter = colorFilter
|
||||||
|
iconTrashCan.drawable.colorFilter = colorFilter
|
||||||
|
iconInfo.drawable.colorFilter = colorFilter
|
||||||
|
iconShield.drawable.colorFilter = colorFilter
|
||||||
|
iconLink.drawable.colorFilter = colorFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
view.findViewById<TextView>(R.id.footer_learn_more)!!.let { learnMore ->
|
||||||
|
learnMore.movementMethod = LinkMovementMethod.getInstance()
|
||||||
|
val footerLinkStr: String = context.getString(
|
||||||
|
R.string.security_settings_fingerprint_v2_enroll_introduction_message_learn_more,
|
||||||
|
Html.FROM_HTML_MODE_LEGACY
|
||||||
|
)
|
||||||
|
learnMore.text = Html.fromHtml(footerLinkStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canAssumeUdfps) {
|
||||||
|
footerMessage6.visibility = View.VISIBLE
|
||||||
|
iconShield.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
footerMessage6.visibility = View.GONE
|
||||||
|
iconShield.visibility = View.GONE
|
||||||
|
}
|
||||||
|
val glifLayoutHelper = GlifLayoutHelper(this, view)
|
||||||
|
if (isBiometricUnlockDisabledByAdmin && !isParentalConsentRequired) {
|
||||||
|
glifLayoutHelper.setHeaderText(
|
||||||
|
R.string.security_settings_fingerprint_enroll_introduction_title_unlock_disabled
|
||||||
|
)
|
||||||
|
glifLayoutHelper.setDescriptionText(descriptionDisabledByAdminSupplier.get())
|
||||||
|
} else {
|
||||||
|
glifLayoutHelper.setHeaderText(
|
||||||
|
R.string.security_settings_fingerprint_enroll_introduction_title
|
||||||
|
)
|
||||||
|
glifLayoutHelper.setDescriptionText(
|
||||||
|
getString(
|
||||||
|
R.string.security_settings_fingerprint_enroll_introduction_v3_message,
|
||||||
|
DeviceHelper.getDeviceName(context)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user