Fix kotlin nullable errors that were exposed by setting the retention of android.annotation.NonNull and android.annotation.Nullable to class retention. Bug: 294110802 Test: builds Change-Id: I6aa0516fa4f6443b6d4dff873baf3b08ff9189f0
162 lines
6.4 KiB
Kotlin
162 lines
6.4 KiB
Kotlin
/*
|
|
* 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.introduction
|
|
|
|
import android.content.Context
|
|
import android.util.AttributeSet
|
|
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.VisibleForTesting
|
|
import androidx.constraintlayout.widget.ConstraintLayout
|
|
import androidx.recyclerview.widget.LinearLayoutManager
|
|
import androidx.recyclerview.widget.RecyclerView
|
|
import androidx.viewpager2.widget.MarginPageTransformer
|
|
import androidx.viewpager2.widget.ViewPager2
|
|
|
|
import com.airbnb.lottie.LottieAnimationView
|
|
|
|
import com.android.settings.R
|
|
import com.android.settingslib.widget.LottieColorUtils
|
|
|
|
class IntroductionImageCarousel : ConstraintLayout {
|
|
private val carousel: ViewPager2 by lazy { requireViewById<ViewPager2>(R.id.image_carousel) }
|
|
private val progressIndicator: RecyclerView by lazy {
|
|
requireViewById<RecyclerView>(R.id.carousel_progress_indicator)
|
|
}
|
|
private val backArrow: ImageView by lazy {
|
|
requireViewById<ImageView>(R.id.carousel_back_arrow)
|
|
}
|
|
private val forwardArrow: ImageView by lazy {
|
|
requireViewById<ImageView>(R.id.carousel_forward_arrow)
|
|
}
|
|
private val progressIndicatorAdapter = ProgressIndicatorAdapter()
|
|
// The index of the current animation we are on
|
|
private var currentPage = 0
|
|
set(value) {
|
|
val pageRange = 0..(ANIMATION_LIST.size - 1)
|
|
field = value.coerceIn(pageRange)
|
|
backArrow.isEnabled = field > pageRange.start
|
|
forwardArrow.isEnabled = field < pageRange.endInclusive
|
|
carousel.setCurrentItem(field)
|
|
progressIndicatorAdapter.currentIndex = field
|
|
}
|
|
|
|
private val onPageChangeCallback =
|
|
object : ViewPager2.OnPageChangeCallback() {
|
|
override fun onPageSelected(position: Int) {
|
|
currentPage = position
|
|
}
|
|
}
|
|
constructor(context: Context) : super(context)
|
|
constructor(context: Context, attrSet: AttributeSet?) : super(context, attrSet)
|
|
|
|
init {
|
|
LayoutInflater.from(context).inflate(R.layout.remote_auth_introduction_image_carousel, this)
|
|
|
|
with(carousel) {
|
|
setPageTransformer(
|
|
MarginPageTransformer(
|
|
context.resources.getDimension(R.dimen.remoteauth_introduction_fragment_padding_horizontal).toInt()
|
|
)
|
|
)
|
|
adapter = ImageCarouselAdapter()
|
|
registerOnPageChangeCallback(onPageChangeCallback)
|
|
}
|
|
|
|
with(progressIndicator) {
|
|
layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
|
|
adapter = progressIndicatorAdapter
|
|
}
|
|
|
|
backArrow.setOnClickListener { currentPage-- }
|
|
forwardArrow.setOnClickListener { currentPage++ }
|
|
}
|
|
|
|
fun unregister() {
|
|
carousel.unregisterOnPageChangeCallback(onPageChangeCallback)
|
|
}
|
|
|
|
private class AnimationViewHolder(val context: Context, itemView: View) : RecyclerView.ViewHolder(itemView) {
|
|
val animationView = itemView.requireViewById<LottieAnimationView>(R.id.explanation_animation)
|
|
val descriptionText = itemView.requireViewById<TextView>(R.id.carousel_text)
|
|
}
|
|
|
|
/** Adapter for the onboarding animations. */
|
|
private class ImageCarouselAdapter : RecyclerView.Adapter<AnimationViewHolder>() {
|
|
|
|
override fun getItemCount() = ANIMATION_LIST.size
|
|
|
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
|
AnimationViewHolder(parent.context, LayoutInflater.from(parent.context).inflate(R.layout.remote_auth_introduction_image_carousel_item, parent, false))
|
|
|
|
override fun onBindViewHolder(holder: AnimationViewHolder, position: Int) {
|
|
with(holder.animationView) {
|
|
setAnimation(ANIMATION_LIST[position].first)
|
|
LottieColorUtils.applyDynamicColors(holder.context, this)
|
|
}
|
|
holder.descriptionText.setText(ANIMATION_LIST[position].second)
|
|
with(holder.itemView) {
|
|
// This makes sure that the proper description text instead of a generic "Page" label is
|
|
// verbalized by Talkback when switching to a new page on the ViewPager2.
|
|
contentDescription = context.getString(ANIMATION_LIST[position].second)
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Adapter for icons indicating carousel progress. */
|
|
private class ProgressIndicatorAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
|
|
|
var currentIndex: Int = 0
|
|
set(value) {
|
|
val previousIndex = field
|
|
field = value.coerceIn(0, getItemCount() - 1)
|
|
notifyItemChanged(previousIndex)
|
|
notifyItemChanged(field)
|
|
}
|
|
|
|
override fun getItemCount() = ANIMATION_LIST.size
|
|
|
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
|
object :
|
|
RecyclerView.ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.remote_auth_introduction_image_carousel_progress_icon, parent, false)) {}
|
|
|
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
|
holder.itemView.isSelected = position == currentIndex
|
|
}
|
|
}
|
|
companion object {
|
|
@VisibleForTesting
|
|
val ANIMATION_LIST =
|
|
listOf(
|
|
Pair(
|
|
R.raw.remoteauth_explanation_swipe_animation,
|
|
R.string.security_settings_remoteauth_enroll_introduction_animation_swipe_up
|
|
),
|
|
Pair(
|
|
R.raw.remoteauth_explanation_notification_animation,
|
|
R.string.security_settings_remoteauth_enroll_introduction_animation_tap_notification
|
|
),
|
|
)
|
|
const val TAG = "RemoteAuthCarousel"
|
|
}
|
|
}
|