From e154c33da93010da5b4718c88f4a3a25c575e57d Mon Sep 17 00:00:00 2001 From: MrSluffy Date: Fri, 10 Jan 2025 12:14:16 +0800 Subject: [PATCH] enh : refactor carousel imp --- .../ui/popup/WallpaperCarouselView.kt | 210 +++++++----------- 1 file changed, 82 insertions(+), 128 deletions(-) diff --git a/lawnchair/src/app/lawnchair/ui/popup/WallpaperCarouselView.kt b/lawnchair/src/app/lawnchair/ui/popup/WallpaperCarouselView.kt index 6e10e371b8..d88960058e 100644 --- a/lawnchair/src/app/lawnchair/ui/popup/WallpaperCarouselView.kt +++ b/lawnchair/src/app/lawnchair/ui/popup/WallpaperCarouselView.kt @@ -1,17 +1,15 @@ package app.lawnchair.ui.popup -import android.animation.LayoutTransition import android.animation.ValueAnimator import android.annotation.SuppressLint import android.app.WallpaperManager import android.content.Context +import android.graphics.Bitmap import android.graphics.BitmapFactory import android.util.AttributeSet import android.util.Log import android.view.Gravity -import android.view.View import android.view.ViewGroup -import android.view.animation.AccelerateDecelerateInterpolator import android.widget.FrameLayout import android.widget.ImageView import android.widget.LinearLayout @@ -46,82 +44,68 @@ class WallpaperCarouselView @JvmOverloads constructor( private var currentItemIndex = 0 private val iconFrame = IconFrame(context).apply { setIcon(R.drawable.ic_tick) - setBackgroundWithRadius(bgColor = Themes.getColorAccent(context), cornerRadius = 100F) + setBackgroundWithRadius(Themes.getColorAccent(context), 100F) } - private val loadingView: ProgressBar = ProgressBar(context).apply { isIndeterminate = true } + private val loadingView = ProgressBar(context).apply { isIndeterminate = true } init { orientation = HORIZONTAL - setLayoutTransition( - LayoutTransition().apply { - enableTransitionType(LayoutTransition.CHANGING) - setDuration(200L) - }, - ) - addView(loadingView) - val factory = WallpaperViewModelFactory(context) - viewModel = ViewModelProvider(context as ViewModelStoreOwner, factory)[WallpaperViewModel::class.java] + viewModel = ViewModelProvider( + context as ViewModelStoreOwner, + WallpaperViewModelFactory(context), + )[WallpaperViewModel::class.java] observeWallpapers() } private fun observeWallpapers() { viewModel.wallpapers.observe(context as LifecycleOwner) { wallpapers -> - if (wallpapers.isEmpty()) { - visibility = GONE - loadingView.visibility = GONE - } else { - visibility = VISIBLE - displayWallpapers(wallpapers) - } + visibility = if (wallpapers.isEmpty()) GONE else VISIBLE + loadingView.visibility = if (wallpapers.isEmpty()) GONE else VISIBLE + if (wallpapers.isNotEmpty()) displayWallpapers(wallpapers) } } private fun displayWallpapers(wallpapers: List) { removeAllViews() - val isLandscape = deviceProfile.isLandscape || deviceProfile.isTablet - val totalWidth = width.takeIf { it > 0 } ?: (deviceProfile.widthPx * if (isLandscape) 0.5 else 0.8).toInt() + val totalWidth = calculateTotalWidth() val firstItemWidth = totalWidth * 0.4 - val remainingWidth = totalWidth - firstItemWidth - val marginBetweenItems = totalWidth * 0.03 - val itemWidth = (remainingWidth - (marginBetweenItems * (wallpapers.size - 1))) / (wallpapers.size - 1) + val itemWidth = calculateItemWidth(totalWidth, wallpapers.size, firstItemWidth) + val margin = (totalWidth * 0.03).toInt() wallpapers.forEachIndexed { index, wallpaper -> - val cardView = createCardView(index, firstItemWidth, itemWidth, marginBetweenItems, wallpaper) + val cardView = createCardView(index, firstItemWidth, itemWidth, margin, wallpaper) addView(cardView) - - // Load the wallpaper image only if necessary loadWallpaperImage(wallpaper, cardView, index == currentItemIndex) } - loadingView.visibility = GONE } + private fun calculateTotalWidth(): Int { + return width.takeIf { it > 0 } ?: (deviceProfile.widthPx * if (deviceProfile.isLandscape || deviceProfile.isTablet) 0.5 else 0.8).toInt() + } + + private fun calculateItemWidth(totalWidth: Int, itemCount: Int, firstItemWidth: Double): Double { + val remainingWidth = totalWidth - firstItemWidth + val marginBetweenItems = totalWidth * 0.03 + return (remainingWidth - (marginBetweenItems * (itemCount - 1))) / (itemCount - 1) + } + @SuppressLint("ClickableViewAccessibility") private fun createCardView( index: Int, firstItemWidth: Double, itemWidth: Double, - marginBetweenItems: Double, + margin: Int, wallpaper: Wallpaper, ): CardView { return CardView(context).apply { radius = Themes.getDialogCornerRadius(context) / 2 layoutParams = LayoutParams( - when (index) { - currentItemIndex -> firstItemWidth.toInt() - else -> itemWidth.toInt() - }, + if (index == currentItemIndex) firstItemWidth.toInt() else itemWidth.toInt(), LayoutParams.MATCH_PARENT, - ).apply { - setMargins( - if (index > 0) marginBetweenItems.toInt() else 0, - 0, - 0, - 0, - ) - } + ).apply { setMargins(if (index > 0) margin else 0, 0, 0, 0) } setOnTouchListener { _, _ -> if (index != currentItemIndex) { @@ -136,128 +120,98 @@ class WallpaperCarouselView @JvmOverloads constructor( private fun loadWallpaperImage(wallpaper: Wallpaper, cardView: CardView, isCurrent: Boolean) { CoroutineScope(Dispatchers.IO).launch { - val wallpaperFile = File(wallpaper.imagePath) - val bitmap = if (wallpaperFile.exists()) { - BitmapFactory.decodeFile(wallpaper.imagePath) - } else { - null - } + val bitmap = File(wallpaper.imagePath).takeIf { it.exists() }?.let { BitmapFactory.decodeFile(it.path) } + withContext(Dispatchers.Main) { addImageView(cardView, bitmap, isCurrent) } + } + } - withContext(Dispatchers.Main) { - val imageView = ImageView(context).apply { - setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_deepshortcut_placeholder)) - scaleType = ImageView.ScaleType.CENTER_CROP - alpha = 0f - } - cardView.addView(imageView) - - if (bitmap != null) { - imageView.setLayerType(View.LAYER_TYPE_HARDWARE, null) - - imageView.setImageBitmap(bitmap) - imageView.animate() - .alpha(1f) - .setDuration(200L) - .setInterpolator(AccelerateDecelerateInterpolator()) - .withEndAction { - imageView.setLayerType(View.LAYER_TYPE_NONE, null) - if (isCurrent) addIconFrameToCenter(cardView) - } - .start() - } else { - Log.e("WallpaperCarouselView", "File not found: ${wallpaper.imagePath}") - imageView.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_deepshortcut_placeholder)) - } - } + private fun addImageView(cardView: CardView, bitmap: Bitmap?, isCurrent: Boolean) { + val imageView = ImageView(context).apply { + setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_deepshortcut_placeholder)) + scaleType = ImageView.ScaleType.CENTER_CROP + alpha = 0f + } + cardView.addView(imageView) + if (bitmap != null) { + imageView.setImageBitmap(bitmap) + imageView.animate().alpha(1f).setDuration(200L).withEndAction { + if (isCurrent) addIconFrameToCenter(cardView) + }.start() } } private fun setWallpaper(wallpaper: Wallpaper) { val currentCardView = getChildAt(currentItemIndex) as CardView - val loadingSpinner = ProgressBar(context).apply { - isIndeterminate = true - layoutParams = FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT).apply { - gravity = Gravity.CENTER - } - } + val spinner = createLoadingSpinner() currentCardView.removeView(iconFrame) - currentCardView.addView(loadingSpinner) + currentCardView.addView(spinner) CoroutineScope(Dispatchers.IO).launch { try { - val wallpaperManager = WallpaperManager.getInstance(context) - val bitmap = BitmapFactory.decodeFile(wallpaper.imagePath) - - wallpaperManager.setBitmap(bitmap, null, true, WallpaperManager.FLAG_SYSTEM) + WallpaperManager.getInstance(context).setBitmap( + BitmapFactory.decodeFile(wallpaper.imagePath), + null, + true, + WallpaperManager.FLAG_SYSTEM, + ) viewModel.updateWallpaperRank(wallpaper) - - withContext(Dispatchers.Main) { - currentCardView.removeView(loadingSpinner) - addIconFrameToCenter(currentCardView) - } } catch (e: Exception) { Log.e("WallpaperCarouselView", "Failed to set wallpaper: ${e.message}") + } finally { withContext(Dispatchers.Main) { - currentCardView.removeView(loadingSpinner) + currentCardView.removeView(spinner) addIconFrameToCenter(currentCardView) } } } } - private fun addIconFrameToCenter(cardView: CardView) { - if (iconFrame.parent != null) { - (iconFrame.parent as ViewGroup).removeView(iconFrame) - } - - val params = FrameLayout.LayoutParams( - LayoutParams.WRAP_CONTENT, - LayoutParams.WRAP_CONTENT, - ).apply { + private fun createLoadingSpinner() = ProgressBar(context).apply { + isIndeterminate = true + layoutParams = FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT).apply { gravity = Gravity.CENTER } + } - cardView.addView(iconFrame, params) + private fun addIconFrameToCenter(cardView: CardView? = getChildAt(currentItemIndex) as CardView) { + (iconFrame.parent as? ViewGroup)?.removeView(iconFrame) + cardView?.addView( + iconFrame, + FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT).apply { + gravity = Gravity.CENTER + }, + ) } override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, oldh) - if (viewModel.wallpapers.value?.isNotEmpty() == true) { - displayWallpapers(viewModel.wallpapers.value!!) - } + viewModel.wallpapers.value?.let { displayWallpapers(it) } } override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { - val isLandscape = deviceProfile.isLandscape || deviceProfile.isTablet - val valWidth = if (isLandscape) (deviceProfile.widthPx * 0.5).toInt() else (deviceProfile.widthPx * 0.8).toInt() - val width = MeasureSpec.makeMeasureSpec(valWidth, MeasureSpec.EXACTLY) - super.onMeasure(width, heightMeasureSpec) + super.onMeasure( + MeasureSpec.makeMeasureSpec(calculateTotalWidth(), MeasureSpec.EXACTLY), + heightMeasureSpec, + ) } - private fun animateWidthTransition( - newIndex: Int, - firstItemWidth: Double, - itemWidth: Double, - ) { + private fun animateWidthTransition(newIndex: Int, firstItemWidth: Double, itemWidth: Double) { currentItemIndex = newIndex for (i in 0 until childCount) { - val cardView = getChildAt(i) as? CardView ?: continue - val targetWidth = if (i == currentItemIndex) firstItemWidth.toInt() else itemWidth.toInt() - - if (cardView.layoutParams.width != targetWidth) { - val animator = ValueAnimator.ofInt(cardView.layoutParams.width, targetWidth).apply { - duration = 300L - addUpdateListener { animation -> - val animatedValue = animation.animatedValue as Int - cardView.layoutParams = cardView.layoutParams.apply { width = animatedValue } - cardView.requestLayout() + (getChildAt(i) as? CardView)?.let { cardView -> + val targetWidth = if (i == currentItemIndex) firstItemWidth.toInt() else itemWidth.toInt() + if (cardView.layoutParams.width != targetWidth) { + ValueAnimator.ofInt(cardView.layoutParams.width, targetWidth).apply { + duration = 300L + addUpdateListener { + cardView.layoutParams.width = it.animatedValue as Int + cardView.requestLayout() + } + start() } } - animator.start() - } - if (i == currentItemIndex) { - addIconFrameToCenter(cardView) + if (i == currentItemIndex) addIconFrameToCenter(cardView) } } }