Remote authenticator enrollment finish layout.

This flow will be included in Device Unlock settings with the
Fingerprint and Face Unlock.

Bug: b/293905106
Test: atest RemoteAuthEnrollFinishTest
Change-Id: I5d24e23948e508dcff5216cd63eba8d9fc9ec97f
This commit is contained in:
Justin McClain
2023-08-03 12:09:18 +00:00
parent 317bfca133
commit a3a000426e
8 changed files with 267 additions and 1 deletions

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<com.google.android.setupdesign.GlifLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/setup_wizard_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:icon="@drawable/ic_lock"
app:sucHeaderText="@string/security_settings_remoteauth_enroll_finish_title"
app:sudDescriptionText="@string/security_settings_remoteauth_enroll_finish_description">
<androidx.constraintlayout.widget.ConstraintLayout
style="@style/SudContentFrame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingHorizontal="@dimen/remoteauth_fragment_padding_horizontal">
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/enroll_finish_animation"
android:importantForAccessibility="no"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:lottie_rawRes="@raw/remoteauth_enroll_finish_animation"
app:lottie_loop="true"
app:lottie_autoPlay="true" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.setupdesign.GlifLayout>

File diff suppressed because one or more lines are too long

View File

@@ -169,6 +169,9 @@
<item name="face_preview_scale" format="float" type="dimen">1.0</item> <item name="face_preview_scale" format="float" type="dimen">1.0</item>
<dimen name="face_enroll_intro_illustration_margin_bottom">0dp</dimen> <dimen name="face_enroll_intro_illustration_margin_bottom">0dp</dimen>
<!-- RemoteAuth-->
<dimen name="remoteauth_fragment_padding_horizontal">40dp</dimen>
<!-- Lock pattern view size, align sysui biometric_auth_pattern_view_size --> <!-- Lock pattern view size, align sysui biometric_auth_pattern_view_size -->
<dimen name="biometric_auth_pattern_view_size">348dp</dimen> <dimen name="biometric_auth_pattern_view_size">348dp</dimen>
<dimen name="biometric_auth_pattern_view_max_size">@dimen/biometric_auth_pattern_view_size</dimen> <dimen name="biometric_auth_pattern_view_max_size">@dimen/biometric_auth_pattern_view_size</dimen>

View File

@@ -884,6 +884,20 @@
<!-- Message showing that multiple fingerprints, face, and the current watch is set up. Shown for a menu item that launches fingerprint, face, and active unlock settings or enrollment. [CHAR LIMIT=80]--> <!-- Message showing that multiple fingerprints, face, and the current watch is set up. Shown for a menu item that launches fingerprint, face, and active unlock settings or enrollment. [CHAR LIMIT=80]-->
<string name="security_settings_fingerprint_multiple_face_watch_preference_summary">Face, fingerprints, and <xliff:g id="watch" example="Dani's Watch">%s</xliff:g> added</string> <string name="security_settings_fingerprint_multiple_face_watch_preference_summary">Face, fingerprints, and <xliff:g id="watch" example="Dani's Watch">%s</xliff:g> added</string>
<!-- RemoteAuth unlock enrollment and settings --><skip />
<!-- Strings for RemoteAuth enroll finish page -->
<!-- Title of the dialog that shows when a paired watch has been set up successfully and can be used to unlock the phone [CHAR_LIMIT=45] -->
<string name="security_settings_remoteauth_enroll_finish_title">
You\'re all set!
</string>
<!-- Explains when a paired watch can be used to unlock the phone after it has been set up successfully [CHAR_LIMIT=NONE] -->
<string name="security_settings_remoteauth_enroll_finish_description">
You can now use your watch to unlock this phone when you swipe up on the lock screen or tap a notification
</string>
<!-- Button text to finish enrollment [CHAR LIMIT=30] -->
<string name="security_settings_remoteauth_enroll_finish_btn_next">Done</string>
<!-- Biometric settings --><skip /> <!-- Biometric settings --><skip />
<!-- Title shown for menu item that launches biometric settings. [CHAR LIMIT=66] --> <!-- Title shown for menu item that launches biometric settings. [CHAR LIMIT=66] -->
<string name="security_settings_biometric_preference_title">Fingerprint &amp; Face Unlock</string> <string name="security_settings_biometric_preference_title">Fingerprint &amp; Face Unlock</string>

View File

@@ -0,0 +1,83 @@
/*
* 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.remoteauth
import android.R
import android.graphics.Color
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.ColorInt
import androidx.annotation.IdRes
import androidx.annotation.LayoutRes
import androidx.fragment.app.Fragment
import com.android.settings.Utils
import com.google.android.setupcompat.template.FooterBarMixin
import com.google.android.setupcompat.template.FooterButton
import com.google.android.setupdesign.GlifLayout
/**
* Displays a content view with a sticky footer in the SetupDesign style. Implementations
* must define a primary button, and an optional secondary button.
*
* A layout with a [GlifLayout] must be provided, along with the id of the [GlifLayout].
*/
abstract class RemoteAuthEnrollBase(
@LayoutRes val layoutResId: Int,
@IdRes private val glifLayoutId: Int
) : Fragment(layoutResId) {
protected val primaryFooterButton by lazy { initializePrimaryFooterButton() }
protected val secondaryFooterButton by lazy { initializeSecondaryFooterButton() }
override fun onCreateView(
inflater: LayoutInflater,
viewGroup: ViewGroup?,
savedInstanceArgs: Bundle?
) =
super.onCreateView(inflater, viewGroup, savedInstanceArgs)!!.also { view ->
initializeFooterbarMixin(view)
}
protected fun getGlifLayout(view: View) = view.findViewById<GlifLayout>(glifLayoutId)
/**
* Return a button will be used as the primary footer button.
*/
abstract fun initializePrimaryFooterButton(): FooterButton
/** If non-null, returned button will be used as the secondary footer button. */
abstract fun initializeSecondaryFooterButton(): FooterButton?
private fun initializeFooterbarMixin(view: View) {
val footerBarMixin = getGlifLayout(view).getMixin(FooterBarMixin::class.java)
primaryFooterButton.also { footerBarMixin.primaryButton = it }
secondaryFooterButton?.also { footerBarMixin.secondaryButton = it }
footerBarMixin.getButtonContainer().setBackgroundColor(getBackgroundColor())
}
@ColorInt
private fun getBackgroundColor(): Int {
val stateList = Utils.getColorAttr(context, R.attr.windowBackground)
return stateList?.defaultColor ?: Color.TRANSPARENT
}
private companion object{
const val TAG = "RemoteAuthEnrollBase"
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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.remoteauth.finish
import android.os.Bundle
import android.view.View
import com.android.settings.R
import com.android.settings.remoteauth.RemoteAuthEnrollBase
import com.android.settingslib.widget.LottieColorUtils
import com.airbnb.lottie.LottieAnimationView
import com.google.android.setupcompat.template.FooterButton
/**
* Displays the enrollment finish view.
*/
class RemoteAuthEnrollFinish :
RemoteAuthEnrollBase(
layoutResId = R.layout.remote_auth_enroll_finish,
glifLayoutId = R.id.setup_wizard_layout,
) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
LottieColorUtils.applyDynamicColors(requireContext(), view.findViewById<LottieAnimationView>(R.id.enroll_finish_animation))
}
override fun initializePrimaryFooterButton(): FooterButton {
return FooterButton.Builder(requireContext())
.setText(R.string.security_settings_remoteauth_enroll_finish_btn_next)
.setListener(this::onPrimaryFooterButtonClick)
.setButtonType(FooterButton.ButtonType.NEXT)
.setTheme(R.style.SudGlifButton_Primary)
.build()
}
override fun initializeSecondaryFooterButton(): FooterButton? = null
fun onPrimaryFooterButtonClick(view: View) {
// TODO(b/293906345): Wire up navigation
}
private companion object{
const val TAG = "RemoteAuthEnrollFinish"
}
}

View File

@@ -32,6 +32,7 @@ android_app {
"androidx.core_core", "androidx.core_core",
"androidx.appcompat_appcompat", "androidx.appcompat_appcompat",
"androidx.cardview_cardview", "androidx.cardview_cardview",
"androidx.fragment_fragment-testing",
"androidx.preference_preference", "androidx.preference_preference",
"androidx.recyclerview_recyclerview", "androidx.recyclerview_recyclerview",
"androidx.window_window", "androidx.window_window",
@@ -47,7 +48,7 @@ android_app {
"jsr305", "jsr305",
"settings-contextual-card-protos-lite", "settings-contextual-card-protos-lite",
"settings-log-bridge-protos-lite", "settings-log-bridge-protos-lite",
"settings-telephony-protos-lite", "settings-telephony-protos-lite",
"contextualcards", "contextualcards",
"settings-logtags", "settings-logtags",
"zxing-core-1.7", "zxing-core-1.7",

View File

@@ -0,0 +1,55 @@
/*
* 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.remoteauth.finish
import android.content.Context
import android.os.Bundle
import androidx.fragment.app.testing.launchFragmentInContainer
import androidx.test.core.app.ApplicationProvider
import com.android.settings.R
import com.google.android.setupdesign.GlifLayout
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
@RunWith(RobolectricTestRunner::class)
class RemoteAuthEnrollFinishTest {
private var mContext: Context = ApplicationProvider.getApplicationContext()
@Test
fun testRemoteAuthenticatorEnrollFinish_hasHeader() {
launchFragmentInContainer<RemoteAuthEnrollFinish>(Bundle(), R.style.SudThemeGlif)
.onFragment {
assertThat((it.view as GlifLayout).headerText)
.isEqualTo(mContext.getString(
R.string.security_settings_remoteauth_enroll_finish_title))
}
}
@Test
fun testRemoteAuthenticatorEnrollFinish_hasDescription() {
launchFragmentInContainer<RemoteAuthEnrollFinish>(Bundle(), R.style.SudThemeGlif)
.onFragment {
assertThat((it.view as GlifLayout).descriptionText)
.isEqualTo(mContext.getString(
R.string.security_settings_remoteauth_enroll_finish_description))
}
}
}