Use dependency injection for most singletons

This commit is contained in:
MM20
2021-10-10 12:15:54 +02:00
parent 023bb2cbb1
commit 087d4fd455
144 changed files with 1789 additions and 1277 deletions
@@ -30,11 +30,6 @@ class CalendarDynamicLauncherIcon(
null
) {
init {
DynamicIconController.getInstance(context).registerIcon(this)
update(context)
}
var currentDay = 0
override fun update(context: Context) {
val calendar = Calendar.getInstance()
@@ -7,6 +7,8 @@ import android.graphics.drawable.LayerDrawable
import android.graphics.drawable.RotateDrawable
import android.os.Build
import androidx.annotation.RequiresApi
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import java.util.*
import kotlin.math.roundToInt
@@ -32,6 +34,7 @@ class ClockDynamicLauncherIcon(
null
) {
init {
foreground.also {
it.setDrawable(secondLayer, ColorDrawable(0))
@@ -40,8 +43,6 @@ class ClockDynamicLauncherIcon(
(it.getDrawable(minuteLayer) as? RotateDrawable)?.fromDegrees = 0f
(it.getDrawable(minuteLayer) as? RotateDrawable)?.toDegrees = 360f
}
DynamicIconController.getInstance(context).registerIcon(this)
update(context)
}
override fun update(context: Context) {
@@ -52,15 +52,7 @@ class DynamicIconController(val context: Context): LifecycleObserver {
}
fun registerIcon(icon: DynamicLauncherIcon) {
icon.update(context)
registeredIcons.add(WeakReference(icon))
}
companion object {
private lateinit var instance: DynamicIconController
fun getInstance(context: Context): DynamicIconController {
if(!::instance.isInitialized) instance = DynamicIconController(context.applicationContext)
return instance
}
}
}
@@ -2,6 +2,8 @@ package de.mm20.launcher2.icons
import android.content.Context
import android.graphics.drawable.Drawable
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
abstract class DynamicLauncherIcon(
foreground: Drawable,
@@ -18,5 +20,6 @@ abstract class DynamicLauncherIcon(
backgroundScale,
autoGenerateBackgroundMode
) {
abstract fun update(context: Context)
}
@@ -14,14 +14,11 @@ import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.LayerDrawable
import android.os.Build
import android.os.UserHandle
import android.util.DisplayMetrics
import android.util.Log
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.drawable.toBitmap
import de.mm20.launcher2.crashreporter.CrashReporter
import de.mm20.launcher2.database.AppDatabase
import de.mm20.launcher2.ktx.dp
import de.mm20.launcher2.ktx.obtainTypedArrayOrNull
import de.mm20.launcher2.ktx.randomElementOrNull
import de.mm20.launcher2.preferences.IconShape
@@ -35,18 +32,21 @@ import java.io.InputStreamReader
import kotlin.math.roundToInt
class IconPackManager private constructor(val context: Context) {
class IconPackManager(
val context: Context,
val dynamicIconController: DynamicIconController
) {
var selectedIconPack: String
get() {
return context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE)
.getString(KEY_ICON_PACK, "")!!
.getString(KEY_ICON_PACK, "")!!
}
set(value) {
Log.d("MM20", "Selected icon pack: $value")
context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE)
.edit()
.putString(KEY_ICON_PACK, value)
.apply()
.edit()
.putString(KEY_ICON_PACK, value)
.apply()
}
@@ -59,7 +59,11 @@ class IconPackManager private constructor(val context: Context) {
return getFromPack(context, activity, size) ?: generateIcon(context, activity, size)
}
private fun getFromPack(context: Context, activity: LauncherActivityInfo, size: Int): LauncherIcon? {
private fun getFromPack(
context: Context,
activity: LauncherActivityInfo,
size: Int
): LauncherIcon? {
val res = try {
context.packageManager.getResourcesForApplication(selectedIconPack)
} catch (e: PackageManager.NameNotFoundException) {
@@ -69,36 +73,42 @@ class IconPackManager private constructor(val context: Context) {
val iconDao = AppDatabase.getInstance(context).iconDao()
val component = ComponentName(activity.applicationInfo.packageName, activity.name)
val icon = iconDao.getIcon(component.flattenToString(), selectedIconPack)
?: return generateIcon(context, activity, size)
?: return generateIcon(context, activity, size)
if (icon.type == "calendar") {
return getIconPackCalendarIcon(context, icon.iconPack, icon.drawable ?: return null)
return getIconPackCalendarIcon(context, icon.iconPack, icon.drawable ?: return null)?.also {
dynamicIconController.registerIcon(it)
}
}
val drawableName = icon.drawable
val resId = res.getIdentifier(drawableName, "drawable", selectedIconPack).takeIf { it != 0 }
?: return generateIcon(context, activity, size)
?: return generateIcon(context, activity, size)
val drawable = ResourcesCompat.getDrawable(res, resId, context.theme) ?: return null
return when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && drawable is AdaptiveIconDrawable -> {
LauncherIcon(
foreground = drawable.foreground,
background = drawable.background,
foregroundScale = 1.5f,
backgroundScale = 1.5f
foreground = drawable.foreground,
background = drawable.background,
foregroundScale = 1.5f,
backgroundScale = 1.5f
)
}
else -> {
LauncherIcon(
foreground = drawable,
foregroundScale = getScale(),
autoGenerateBackgroundMode = LauncherPreferences.instance.legacyIconBg.toInt()
foreground = drawable,
foregroundScale = getScale(),
autoGenerateBackgroundMode = LauncherPreferences.instance.legacyIconBg.toInt()
)
}
}
}
private fun generateIcon(context: Context, activity: LauncherActivityInfo, size: Int): LauncherIcon? {
private fun generateIcon(
context: Context,
activity: LauncherActivityInfo,
size: Int
): LauncherIcon? {
val back = getIconBack()
val upon = getIconUpon()
val mask = getIconMask()
@@ -124,10 +134,12 @@ class IconPackManager private constructor(val context: Context) {
val icon = drawable.toBitmap(width = size, height = size)
inBounds = Rect(0, 0, icon.width, icon.height)
outBounds = Rect((bitmap.width * (1 - scale) * 0.5).roundToInt(),
(bitmap.height * (1 - scale) * 0.5).roundToInt(),
(bitmap.width - bitmap.width * (1 - scale) * 0.5).roundToInt(),
(bitmap.height - bitmap.height * (1 - scale) * 0.5).roundToInt())
outBounds = Rect(
(bitmap.width * (1 - scale) * 0.5).roundToInt(),
(bitmap.height * (1 - scale) * 0.5).roundToInt(),
(bitmap.width - bitmap.width * (1 - scale) * 0.5).roundToInt(),
(bitmap.height - bitmap.height * (1 - scale) * 0.5).roundToInt()
)
canvas.drawBitmap(icon, inBounds, outBounds, paint)
val pack = selectedIconPack
@@ -170,33 +182,39 @@ class IconPackManager private constructor(val context: Context) {
}
return LauncherIcon(
foreground = BitmapDrawable(context.resources, bitmap),
foregroundScale = getScale(),
autoGenerateBackgroundMode = LauncherPreferences.instance.legacyIconBg.toInt()
foreground = BitmapDrawable(context.resources, bitmap),
foregroundScale = getScale(),
autoGenerateBackgroundMode = LauncherPreferences.instance.legacyIconBg.toInt()
)
}
private fun getDefaultIcon(context: Context, activity: LauncherActivityInfo): LauncherIcon? {
if (activity.applicationInfo.packageName == GOOGLE_DESK_CLOCK_PACKAGE_NAME) {
getGoogleDeskClockIcon(context)?.let { return it }
getGoogleDeskClockIcon(context)?.let {
dynamicIconController.registerIcon(it)
return it
}
}
getCalendarIcon(context, activity)?.let {
dynamicIconController.registerIcon(it)
return it
}
getCalendarIcon(context, activity)?.let { return it }
try {
val icon = activity.getIcon(context.resources.displayMetrics.densityDpi) ?: return null
when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && icon is AdaptiveIconDrawable -> {
return LauncherIcon(
foreground = icon.foreground ?: return null,
background = icon.background,
foregroundScale = 1.5f,
backgroundScale = 1.5f
foreground = icon.foreground ?: return null,
background = icon.background,
foregroundScale = 1.5f,
backgroundScale = 1.5f
)
}
else -> {
return LauncherIcon(
foreground = icon,
foregroundScale = getScale(),
autoGenerateBackgroundMode = LauncherPreferences.instance.legacyIconBg.toInt()
foreground = icon,
foregroundScale = getScale(),
autoGenerateBackgroundMode = LauncherPreferences.instance.legacyIconBg.toInt()
)
}
}
@@ -205,7 +223,11 @@ class IconPackManager private constructor(val context: Context) {
}
}
private fun getIconPackCalendarIcon(context: Context, iconPack: String, baseIconName: String): CalendarDynamicLauncherIcon? {
private fun getIconPackCalendarIcon(
context: Context,
iconPack: String,
baseIconName: String
): CalendarDynamicLauncherIcon? {
val resources = try {
context.packageManager.getResourcesForApplication(iconPack)
} catch (e: PackageManager.NameNotFoundException) {
@@ -218,18 +240,21 @@ class IconPackManager private constructor(val context: Context) {
id
}.toIntArray()
return CalendarDynamicLauncherIcon(
context = context,
background = ColorDrawable(0),
foreground = ColorDrawable(0),
foregroundScale = 1.5f,
backgroundScale = 1.5f,
packageName = iconPack,
drawableIds = drawableIds,
autoGenerateBackgroundMode = LauncherPreferences.instance.legacyIconBg.toInt()
context = context,
background = ColorDrawable(0),
foreground = ColorDrawable(0),
foregroundScale = 1.5f,
backgroundScale = 1.5f,
packageName = iconPack,
drawableIds = drawableIds,
autoGenerateBackgroundMode = LauncherPreferences.instance.legacyIconBg.toInt()
)
}
private fun getCalendarIcon(context: Context, activity: LauncherActivityInfo): CalendarDynamicLauncherIcon? {
private fun getCalendarIcon(
context: Context,
activity: LauncherActivityInfo
): CalendarDynamicLauncherIcon? {
val component = ComponentName(activity.applicationInfo.packageName, activity.name)
val pm = context.packageManager
val ai = try {
@@ -240,7 +265,7 @@ class IconPackManager private constructor(val context: Context) {
val resources = pm.getResourcesForActivity(component)
var arrayId = ai.metaData?.getInt("com.teslacoilsw.launcher.calendarIconArray") ?: 0
if (arrayId == 0) arrayId = ai.metaData?.getInt("com.google.android.calendar.dynamic_icons")
?: return null
?: return null
if (arrayId == 0) return null
val typedArray = resources.obtainTypedArrayOrNull(arrayId) ?: return null
if (typedArray.length() != 31) {
@@ -253,43 +278,49 @@ class IconPackManager private constructor(val context: Context) {
}
typedArray.recycle()
return CalendarDynamicLauncherIcon(
context = context,
background = ColorDrawable(0),
foreground = ColorDrawable(0),
foregroundScale = 1.5f,
backgroundScale = 1.5f,
packageName = component.packageName,
drawableIds = drawableIds,
autoGenerateBackgroundMode = LauncherPreferences.instance.legacyIconBg.toInt()
context = context,
background = ColorDrawable(0),
foreground = ColorDrawable(0),
foregroundScale = 1.5f,
backgroundScale = 1.5f,
packageName = component.packageName,
drawableIds = drawableIds,
autoGenerateBackgroundMode = LauncherPreferences.instance.legacyIconBg.toInt()
)
}
private fun getGoogleDeskClockIcon(context: Context): ClockDynamicLauncherIcon? {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return null
val pm = context.packageManager
val appInfo = pm.getApplicationInfo(GOOGLE_DESK_CLOCK_PACKAGE_NAME, PackageManager.GET_META_DATA)
val appInfo =
pm.getApplicationInfo(GOOGLE_DESK_CLOCK_PACKAGE_NAME, PackageManager.GET_META_DATA)
?: return null
val drawable = appInfo.metaData.getInt("com.google.android.apps.nexuslauncher.LEVEL_PER_TICK_ICON_ROUND")
val drawable =
appInfo.metaData.getInt("com.google.android.apps.nexuslauncher.LEVEL_PER_TICK_ICON_ROUND")
val resources = pm.getResourcesForApplication(appInfo)
val baseIcon = try {
ResourcesCompat.getDrawable(resources, drawable, null) as? AdaptiveIconDrawable ?: return null
ResourcesCompat.getDrawable(resources, drawable, null) as? AdaptiveIconDrawable
?: return null
} catch (e: Resources.NotFoundException) {
return null
}
val foreground = baseIcon.foreground as? LayerDrawable ?: return null
val hourLayer = appInfo.metaData.getInt("com.google.android.apps.nexuslauncher.HOUR_LAYER_INDEX")
val minuteLayer = appInfo.metaData.getInt("com.google.android.apps.nexuslauncher.MINUTE_LAYER_INDEX")
val secondLayer = appInfo.metaData.getInt("com.google.android.apps.nexuslauncher.SECOND_LAYER_INDEX")
val hourLayer =
appInfo.metaData.getInt("com.google.android.apps.nexuslauncher.HOUR_LAYER_INDEX")
val minuteLayer =
appInfo.metaData.getInt("com.google.android.apps.nexuslauncher.MINUTE_LAYER_INDEX")
val secondLayer =
appInfo.metaData.getInt("com.google.android.apps.nexuslauncher.SECOND_LAYER_INDEX")
return ClockDynamicLauncherIcon(
context = context,
background = baseIcon.background,
backgroundScale = 1.5f,
foreground = foreground,
foregroundScale = 1.5f,
badgeNumber = 0f,
hourLayer = hourLayer,
minuteLayer = minuteLayer,
secondLayer = secondLayer
context = context,
background = baseIcon.background,
backgroundScale = 1.5f,
foreground = foreground,
foregroundScale = 1.5f,
badgeNumber = 0f,
hourLayer = hourLayer,
minuteLayer = minuteLayer,
secondLayer = secondLayer
)
}
@@ -335,12 +366,6 @@ class IconPackManager private constructor(val context: Context) {
companion object {
const val GOOGLE_DESK_CLOCK_PACKAGE_NAME = "com.google.android.deskclock"
const val GOOGLE_CALENDAR_PACKAGE_NAME = "com.google.android.calendar"
private lateinit var instance: IconPackManager
fun getInstance(context: Context): IconPackManager {
if (!::instance.isInitialized) instance = IconPackManager(context.applicationContext)
return instance
}
}
@Synchronized
@@ -363,9 +388,9 @@ class UpdateIconPacksWorker(val context: Context) {
try {
val packInfo = context.packageManager.getPackageInfo(pack, 0)
val iconPack = IconPack(
name = packInfo.applicationInfo.loadLabel(context.packageManager).toString(),
packageName = pack,
version = packInfo.versionName
name = packInfo.applicationInfo.loadLabel(context.packageManager).toString(),
packageName = pack,
version = packInfo.versionName
)
//if (iconDao.isInstalled(iconPack)) continue
installIconPack(iconPack)
@@ -384,7 +409,9 @@ class UpdateIconPacksWorker(val context: Context) {
intent = Intent("com.novalauncher.THEME")
val novaPacks = pm.queryIntentActivities(intent, 0)
novaPacks.forEach {
if (packs.none { p -> p.activityInfo.packageName == it.activityInfo.packageName }) packs.add(it)
if (packs.none { p -> p.activityInfo.packageName == it.activityInfo.packageName }) packs.add(
it
)
}
packs.sortWith(ResolveInfo.DisplayNameComparator(pm))
return packs
@@ -401,7 +428,10 @@ class UpdateIconPacksWorker(val context: Context) {
else {
val rawId = res.getIdentifier("appfilter", "raw", pkgName)
if (rawId == 0) {
Log.e("MM20", "Icon pack $pkgName has no appfilter.xml, neither in xml nor in raw")
Log.e(
"MM20",
"Icon pack $pkgName has no appfilter.xml, neither in xml nor in raw"
)
return
}
parser = XmlPullParserFactory.newInstance().newPullParser()
@@ -417,33 +447,43 @@ class UpdateIconPacksWorker(val context: Context) {
when (parser.name) {
"item" -> {
val component = parser.getAttributeValue(null, "component")
?: continue@loop
?: continue@loop
val drawable = parser.getAttributeValue(null, "drawable")
?: continue@loop
?: continue@loop
if (component.length <= 14) continue@loop
val componentName = ComponentName.unflattenFromString(component.substring(14, component.lastIndex))
?: continue@loop
val componentName = ComponentName.unflattenFromString(
component.substring(
14,
component.lastIndex
)
)
?: continue@loop
val icon = Icon(
componentName = componentName,
drawable = drawable,
iconPack = pkgName,
type = "app"
componentName = componentName,
drawable = drawable,
iconPack = pkgName,
type = "app"
)
icons.add(icon)
}
"calendar" -> {
val component = parser.getAttributeValue(null, "component")
?: continue@loop
?: continue@loop
val drawable = parser.getAttributeValue(null, "prefix") ?: continue@loop
if (component.length < 14) continue@loop
val componentName = ComponentName.unflattenFromString(component.substring(14, component.lastIndex))
?: continue@loop
val componentName = ComponentName.unflattenFromString(
component.substring(
14,
component.lastIndex
)
)
?: continue@loop
val icon = Icon(
componentName = componentName,
drawable = drawable,
iconPack = pkgName,
type = "calendar"
componentName = componentName,
drawable = drawable,
iconPack = pkgName,
type = "calendar"
)
icons.add(icon)
}
@@ -452,10 +492,10 @@ class UpdateIconPacksWorker(val context: Context) {
if (parser.getAttributeName(i).startsWith("img")) {
val drawable = parser.getAttributeValue(i)
val icon = Icon(
componentName = null,
drawable = drawable,
iconPack = pkgName,
type = "iconback"
componentName = null,
drawable = drawable,
iconPack = pkgName,
type = "iconback"
)
icons.add(icon)
}
@@ -466,10 +506,10 @@ class UpdateIconPacksWorker(val context: Context) {
if (parser.getAttributeName(i).startsWith("img")) {
val drawable = parser.getAttributeValue(i)
val icon = Icon(
componentName = null,
drawable = drawable,
iconPack = pkgName,
type = "iconupon"
componentName = null,
drawable = drawable,
iconPack = pkgName,
type = "iconupon"
)
icons.add(icon)
}
@@ -480,10 +520,10 @@ class UpdateIconPacksWorker(val context: Context) {
if (parser.getAttributeName(i).startsWith("img")) {
val drawable = parser.getAttributeValue(i)
val icon = Icon(
componentName = null,
drawable = drawable,
iconPack = pkgName,
type = "iconmask"
componentName = null,
drawable = drawable,
iconPack = pkgName,
type = "iconmask"
)
icons.add(icon)
}
@@ -491,13 +531,15 @@ class UpdateIconPacksWorker(val context: Context) {
}
"scale" -> {
val scale = parser.getAttributeValue(null, "factor")?.toFloatOrNull()
?: continue@loop
?: continue@loop
iconPack.scale = scale
}
}
}
iconDao.installIconPack(iconPack.toDatabaseEntity(), icons.map { it.toDatabaseEntity() })
iconDao.installIconPack(
iconPack.toDatabaseEntity(),
icons.map { it.toDatabaseEntity() })
(parser as? XmlResourceParser)?.close()
inStream?.close()
@@ -1,17 +1,41 @@
package de.mm20.launcher2.icons
import android.content.BroadcastReceiver
import android.content.Context
import android.util.Log
import android.content.Intent
import android.content.IntentFilter
import android.util.LruCache
import de.mm20.launcher2.search.data.Searchable
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
class IconRepository private constructor(val context: Context) {
class IconRepository(
val context: Context,
val iconPackManager: IconPackManager
) {
private val appReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
requestIconPackListUpdate()
}
}
private val scope = CoroutineScope(Job() + Dispatchers.Main)
init {
requestIconPackListUpdate()
context.registerReceiver(appReceiver, IntentFilter().apply {
addAction(Intent.ACTION_PACKAGE_REPLACED)
addAction(Intent.ACTION_PACKAGE_ADDED)
addAction(Intent.ACTION_PACKAGE_REMOVED)
addAction(Intent.ACTION_MY_PACKAGE_REPLACED)
addAction(Intent.ACTION_PACKAGE_CHANGED)
addDataScheme("package")
})
}
private val cache = LruCache<String, LauncherIcon>(200)
fun getIcon(searchable: Searchable, size: Int): Flow<LauncherIcon> = flow {
@@ -43,7 +67,7 @@ class IconRepository private constructor(val context: Context) {
fun requestIconPackListUpdate() {
scope.launch {
IconPackManager.getInstance(context).updateIconPacks()
iconPackManager.updateIconPacks()
}
}
@@ -54,14 +78,4 @@ class IconRepository private constructor(val context: Context) {
fun clearCache() {
cache.evictAll()
}
companion object {
private lateinit var instance: IconRepository
fun getInstance(context: Context): IconRepository {
if (!::instance.isInitialized) instance = IconRepository(context.applicationContext)
return instance
}
}
}
@@ -0,0 +1,10 @@
package de.mm20.launcher2.icons
import org.koin.android.ext.koin.androidContext
import org.koin.dsl.module
val iconsModule = module {
single { DynamicIconController(androidContext()) }
single { IconPackManager(androidContext(), get()) }
single { IconRepository(androidContext(), get()) }
}