fixup : pop-up wallpaper carousel

- fixed item duplication
This commit is contained in:
MrSluffy
2025-01-09 14:05:41 +08:00
parent 343703f1f5
commit ff73a51817
7 changed files with 108 additions and 27 deletions

View File

@@ -26,6 +26,7 @@ import app.lawnchair.wallpaper.service.Wallpaper
import com.android.launcher3.R
import com.android.launcher3.util.Themes
import com.android.launcher3.views.ActivityContext
import java.io.File
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -129,13 +130,25 @@ class WallpaperCarouselView @JvmOverloads constructor(
addView(cardView)
CoroutineScope(Dispatchers.IO).launch {
val bitmap = BitmapFactory.decodeFile(wallpaper.imagePath)
withContext(Dispatchers.Main) {
(cardView.getChildAt(0) as? ImageView)?.apply {
setImageBitmap(bitmap)
val wallpaperFile = File(wallpaper.imagePath)
if (wallpaperFile.exists()) {
val bitmap = BitmapFactory.decodeFile(wallpaper.imagePath)
withContext(Dispatchers.Main) {
(cardView.getChildAt(0) as? ImageView)?.apply {
setImageBitmap(bitmap)
}
if (index == currentItemIndex) {
addIconFrameToCenter(cardView)
}
}
if (index == currentItemIndex) {
addIconFrameToCenter(cardView)
} else {
Log.e("WallpaperCarouselView", "File not found: ${wallpaper.imagePath}")
withContext(Dispatchers.Main) {
(cardView.getChildAt(0) as? ImageView)?.apply {
setImageDrawable(
ContextCompat.getDrawable(context, R.drawable.ic_deepshortcut_placeholder),
)
}
}
}
}
@@ -163,6 +176,8 @@ class WallpaperCarouselView @JvmOverloads constructor(
wallpaperManager.setBitmap(bitmap, null, true, WallpaperManager.FLAG_SYSTEM)
viewModel.updateWallpaperRank(wallpaper)
withContext(Dispatchers.Main) {
currentCardView.removeView(loadingSpinner)
addIconFrameToCenter(currentCardView)

View File

@@ -5,17 +5,13 @@ import android.content.Context
import app.lawnchair.util.MainThreadInitializedObject
import app.lawnchair.util.requireSystemService
import app.lawnchair.wallpaper.WallpaperColorsCompat.Companion.HINT_SUPPORTS_DARK_THEME
import app.lawnchair.wallpaper.service.WallpaperService
import com.android.launcher3.Utilities
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
sealed class WallpaperManagerCompat(val context: Context) {
private val listeners = mutableListOf<OnColorsChangedListener>()
private val colorHints: Int get() = wallpaperColors?.colorHints ?: 0
protected val wallpaperManager: WallpaperManager = context.requireSystemService()
val wallpaperManager: WallpaperManager = context.requireSystemService()
abstract val wallpaperColors: WallpaperColorsCompat?
@@ -33,10 +29,6 @@ sealed class WallpaperManagerCompat(val context: Context) {
listeners.toTypedArray().forEach {
it.onColorsChanged()
}
CoroutineScope(Dispatchers.IO).launch {
WallpaperService(context).saveWallpaper(wallpaperManager)
}
}
interface OnColorsChangedListener {

View File

@@ -1,28 +1,65 @@
package app.lawnchair.wallpaper.model
import android.app.WallpaperManager
import android.content.Context
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import app.lawnchair.wallpaper.WallpaperManagerCompat
import app.lawnchair.wallpaper.service.Wallpaper
import app.lawnchair.wallpaper.service.WallpaperDatabase
import app.lawnchair.wallpaper.service.WallpaperService
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
class WallpaperViewModel(context: Context) : ViewModel() {
private val dao = WallpaperDatabase.INSTANCE.get(context).wallpaperDao()
private val service = WallpaperService.INSTANCE.get(context)
private val wallpaperManagerCompat = WallpaperManagerCompat.INSTANCE.get(context)
private val _wallpapers = MutableLiveData<List<Wallpaper>>()
val wallpapers: LiveData<List<Wallpaper>> = _wallpapers
private val mutex = Mutex()
private val listener = object : WallpaperManagerCompat.OnColorsChangedListener {
override fun onColorsChanged() {
viewModelScope.launch {
mutex.withLock {
saveWallpaper(wallpaperManagerCompat.wallpaperManager)
}
}
}
}
init {
loadTopWallpapers()
wallpaperManagerCompat.addOnChangeListener(listener)
}
private suspend fun saveWallpaper(wallpaperManager: WallpaperManager) {
service.saveWallpaper(wallpaperManager)
refreshWallpapers()
}
private suspend fun refreshWallpapers() {
val topWallpapers = dao.getTopWallpapers()
_wallpapers.postValue(topWallpapers)
}
private fun loadTopWallpapers() {
viewModelScope.launch {
val topWallpapers = dao.getTopWallpapers()
_wallpapers.postValue(topWallpapers)
mutex.withLock {
refreshWallpapers()
}
}
}
suspend fun updateWallpaperRank(wallpaper: Wallpaper) {
service.updateWallpaperRank(wallpaper)
loadTopWallpapers()
}
}

View File

@@ -9,4 +9,5 @@ data class Wallpaper(
val imagePath: String,
val rank: Int,
val timestamp: Long,
val checksum: String? = null,
)

View File

@@ -17,6 +17,9 @@ interface WallpaperDao {
@Query("UPDATE wallpapers SET rank = rank + 1 WHERE rank >= :rank")
suspend fun updateRank(rank: Int)
@Query("UPDATE wallpapers SET rank = :rank, timestamp = :timestamp WHERE id = :id")
suspend fun updateWallpaper(id: Long, rank: Int, timestamp: Long)
@Query("DELETE FROM wallpapers WHERE id = :id")
suspend fun deleteWallpaper(id: Long)

View File

@@ -3,11 +3,13 @@ package app.lawnchair.wallpaper.service
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.migration.Migration
import androidx.sqlite.db.SimpleSQLiteQuery
import androidx.sqlite.db.SupportSQLiteDatabase
import app.lawnchair.util.MainThreadInitializedObject
import kotlinx.coroutines.runBlocking
@Database(entities = [Wallpaper::class], version = 1, exportSchema = false)
@Database(entities = [Wallpaper::class], version = 2, exportSchema = false)
abstract class WallpaperDatabase : RoomDatabase() {
abstract fun wallpaperDao(): WallpaperDao
@@ -23,12 +25,17 @@ abstract class WallpaperDatabase : RoomDatabase() {
}
companion object {
private val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE wallpapers ADD COLUMN checksum TEXT DEFAULT 'undefined'")
}
}
val INSTANCE = MainThreadInitializedObject { context ->
Room.databaseBuilder(
context,
WallpaperDatabase::class.java,
"wallpaper_database",
).build()
).addMigrations(MIGRATION_1_2).build()
}
}
}

View File

@@ -10,6 +10,7 @@ import com.android.launcher3.util.MainThreadInitializedObject
import com.android.launcher3.util.SafeCloseable
import java.io.File
import java.io.FileOutputStream
import java.security.MessageDigest
import kotlinx.coroutines.runBlocking
class WallpaperService(val context: Context) : SafeCloseable {
@@ -29,35 +30,60 @@ class WallpaperService(val context: Context) : SafeCloseable {
}
}
private fun calculateChecksum(imageData: ByteArray): String {
return MessageDigest.getInstance("MD5")
.digest(imageData)
.joinToString("") { "%02x".format(it) }
}
private suspend fun saveWallpaper(imageData: ByteArray) {
val timestamp = System.currentTimeMillis()
val topWallpapers = dao.getTopWallpapers()
val imagePath = saveImageToAppStorage(imageData)
if (topWallpapers.size < 4) {
val wallpaper = Wallpaper(imagePath = imagePath, rank = topWallpapers.size, timestamp = timestamp)
val checksum = calculateChecksum(imageData)
val existingWallpapers = dao.getTopWallpapers()
if (existingWallpapers.any { it.checksum == checksum }) {
Log.d("WallpaperService", "Wallpaper already exists with checksum: $checksum")
return
}
if (existingWallpapers.size < 4) {
val wallpaper = Wallpaper(imagePath = imagePath, rank = existingWallpapers.size, timestamp = timestamp, checksum = checksum)
dao.insert(wallpaper)
} else {
val lowestRankedWallpaper = topWallpapers.minByOrNull { it.timestamp }
val lowestRankedWallpaper = existingWallpapers.minByOrNull { it.timestamp }
if (lowestRankedWallpaper != null) {
dao.deleteWallpaper(lowestRankedWallpaper.id)
deleteWallpaperFile(lowestRankedWallpaper.imagePath)
}
for (wallpaper in topWallpapers) {
for (wallpaper in existingWallpapers) {
if (wallpaper.rank >= (lowestRankedWallpaper?.rank ?: 0)) {
dao.updateRank(wallpaper.rank)
}
}
val wallpaper = Wallpaper(imagePath = imagePath, rank = 0, timestamp = timestamp)
val wallpaper = Wallpaper(imagePath = imagePath, rank = 0, timestamp = timestamp, checksum = checksum)
dao.insert(wallpaper)
}
}
suspend fun updateWallpaperRank(selectedWallpaper: Wallpaper) {
val topWallpapers = dao.getTopWallpapers()
val currentTime = System.currentTimeMillis()
dao.updateWallpaper(selectedWallpaper.id, rank = 0, timestamp = currentTime)
for (wallpaper in topWallpapers) {
if (wallpaper.id != selectedWallpaper.id) {
dao.updateRank(wallpaper.rank)
}
}
}
fun getTopWallpapers(): List<Wallpaper> = runBlocking {
val wallpapers = dao.getTopWallpapers()
wallpapers.ifEmpty { emptyList() }