Add Custom layout to Biometric Prompt
Asked Answered
L

1

6

Is there any way to add our own custom layout to biometric prompt as I have seen various similar threads but there seems to be no solution provided for it yet and fingerprintmanager is deprecated so i don't want to use that.

Lawsuit answered 6/6, 2020 at 6:20 Comment(1)
Have you find any solution?Warehouseman
E
7

I don't think this is possible. The latest version of Android's BiometricPrompt class is designed to display its own custom activity/fragment/dialogue. There is no API that allows a user to modify the layout attached to BiometricPrompt either. Take a look at the source code hosted on Google's repository: https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:biometric/biometric/src/main/java/androidx/biometric/

The only possibility would be to build a biometric authenticator program/class from the ground up (like Google did). But that is an Everest of its own.

Here is Google's code that runs the custom fingerprint dialogue. Notice that it creates a BiometricFragment object:

    /**
     * Shows the biometric prompt to the user and begins authentication.
     *
     * @param info A {@link PromptInfo} object describing the appearance and behavior of the prompt.
     * @param crypto A crypto object to be associated with this authentication.
     */
    private void authenticateInternal(@NonNull PromptInfo info, @Nullable CryptoObject crypto) {
        if (mClientFragmentManager == null) {
            Log.e(TAG, "Unable to start authentication. Client fragment manager was null.");
            return;
        }
        if (mClientFragmentManager.isStateSaved()) {
            Log.e(TAG, "Unable to start authentication. Called after onSaveInstanceState().");
            return;
        }

        final BiometricFragment biometricFragment =
                findOrAddBiometricFragment(mClientFragmentManager);
        biometricFragment.authenticate(info, crypto);
    }

Going to the code for BiometricFragment, we see where the FingerprintDialogFragment dialogue object is displayed to the user at dialog.show(...) towards the end:

    /**
     * Shows the fingerprint dialog UI to the user and begins authentication.
     */
    @SuppressWarnings("deprecation")
    private void showFingerprintDialogForAuthentication() {
        final Context context = requireContext();
        androidx.core.hardware.fingerprint.FingerprintManagerCompat fingerprintManagerCompat =
                androidx.core.hardware.fingerprint.FingerprintManagerCompat.from(context);
        final int errorCode = checkForFingerprintPreAuthenticationErrors(fingerprintManagerCompat);
        if (errorCode != 0) {
            sendErrorAndDismiss(
                    errorCode, ErrorUtils.getFingerprintErrorString(getContext(), errorCode));
            return;
        }

        if (isAdded()) {
            final boolean shouldHideFingerprintDialog =
                    DeviceUtils.shouldHideFingerprintDialog(context, Build.MODEL);
            if (mViewModel.isFingerprintDialogDismissedInstantly() != shouldHideFingerprintDialog) {
                mHandler.postDelayed(
                        new Runnable() {
                            @Override
                            public void run() {
                                mViewModel.setFingerprintDialogDismissedInstantly(
                                        shouldHideFingerprintDialog);
                            }
                        },
                        DISMISS_INSTANTLY_DELAY_MS);
            }

            if (!shouldHideFingerprintDialog) {
                final FingerprintDialogFragment dialog = FingerprintDialogFragment.newInstance();
                dialog.show(getParentFragmentManager(), FINGERPRINT_DIALOG_FRAGMENT_TAG);
            }

            mViewModel.setCanceledFrom(CANCELED_FROM_NONE);
            fingerprintManagerCompat.authenticate(
                    CryptoObjectUtils.wrapForFingerprintManager(mViewModel.getCryptoObject()),
                    0 /* flags */,
                    mViewModel.getCancellationSignalProvider().getFingerprintCancellationSignal(),
                    mViewModel.getAuthenticationCallbackProvider().getFingerprintCallback(),
                    null /* handler */);
        }
    }

Going to Google's FingerprintDialogFrament class, we see where the dialog is created. Notice where View layout inflates the R.layout.fingerprint_dialog_layout towards the top, which is the built-in layout that displays the fingerprint dialog:

     @Override
    @NonNull
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        final AlertDialog.Builder builder = new AlertDialog.Builder(requireContext());
        builder.setTitle(mViewModel.getTitle());

        // We have to use builder.getContext() instead of the usual getContext() in order to get
        // the appropriately themed context for this dialog.
        final View layout = LayoutInflater.from(builder.getContext())
                .inflate(R.layout.fingerprint_dialog_layout, null);

        final TextView subtitleView = layout.findViewById(R.id.fingerprint_subtitle);
        final TextView descriptionView = layout.findViewById(R.id.fingerprint_description);

        final CharSequence subtitle = mViewModel.getSubtitle();
        if (TextUtils.isEmpty(subtitle)) {
            subtitleView.setVisibility(View.GONE);
        } else {
            subtitleView.setVisibility(View.VISIBLE);
            subtitleView.setText(subtitle);
        }

        final CharSequence description = mViewModel.getDescription();
        if (TextUtils.isEmpty(description)) {
            descriptionView.setVisibility(View.GONE);
        } else {
            descriptionView.setVisibility(View.VISIBLE);
            descriptionView.setText(description);
        }

        mFingerprintIcon = layout.findViewById(R.id.fingerprint_icon);
        mHelpMessageView = layout.findViewById(R.id.fingerprint_error);

        final CharSequence negativeButtonText =
                mViewModel.isDeviceCredentialAllowed()
                        ? getString(R.string.confirm_device_credential_password)
                        : mViewModel.getNegativeButtonText();
        builder.setNegativeButton(negativeButtonText, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                mViewModel.setNegativeButtonPressPending(true);
            }
        });

        builder.setView(layout);
        Dialog dialog = builder.create();
        dialog.setCanceledOnTouchOutside(false);
        return dialog;
    }

Here is the code for R.layout.fingerprint_dialog_layout, the built-in fingerprint dialog:

<?xml version="1.0" encoding="utf-8"?>
<!--
  ~ Copyright 2018 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.
  -->

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">

    <LinearLayout android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  android:orientation="vertical">

        <TextView
            android:id="@+id/fingerprint_subtitle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:layout_marginEnd="24dp"
            android:layout_marginStart="24dp"
            android:textColor="?android:attr/textColorSecondary"
            android:textSize="16sp"
            android:singleLine="true"
            android:ellipsize="marquee"
            android:marqueeRepeatLimit="marquee_forever"/>

        <TextView
            android:id="@+id/fingerprint_description"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingTop="0dp"
            android:layout_marginEnd="24dp"
            android:layout_marginStart="24dp"
            android:textColor="?android:attr/textColorSecondary"
            android:textSize="16sp"
            android:maxLines="4"/>

        <ImageView
            android:id="@+id/fingerprint_icon"
            android:layout_width="@dimen/fingerprint_icon_size"
            android:layout_height="@dimen/fingerprint_icon_size"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="32dp"
            android:scaleType="fitXY" />

        <TextView
            android:id="@+id/fingerprint_error"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginEnd="24dp"
            android:layout_marginStart="24dp"
            android:paddingTop="16dp"
            android:paddingBottom="24dp"
            android:textSize="14sp"
            android:gravity="center_horizontal"
            android:accessibilityLiveRegion="polite"
            android:text="@string/fingerprint_dialog_touch_sensor"
            android:textColor="?android:attr/textColorSecondary" />

    </LinearLayout>

</ScrollView>
Eurythermal answered 30/6, 2020 at 12:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.