Refactor widgets

This commit is contained in:
MM20
2022-03-29 18:14:07 +02:00
parent 2e43b871c5
commit 956af82f79
21 changed files with 804 additions and 846 deletions
@@ -5,5 +5,5 @@ import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module
val widgetsModule = module {
single { WidgetRepository(androidContext()) }
single<WidgetRepository> { WidgetRepositoryImpl(androidContext(), get()) }
}
@@ -1,32 +1,173 @@
package de.mm20.launcher2.widgets
import android.app.Activity
import android.appwidget.AppWidgetHost
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProviderInfo
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.Build
import de.mm20.launcher2.database.entities.WidgetEntity
import de.mm20.launcher2.ktx.tryStartActivity
data class Widget(
val type: WidgetType,
var data: String,
var height: Int,
val label: String = ""
) {
constructor(entity: WidgetEntity) : this(
type = if (entity.type == "internal") WidgetType.INTERNAL else WidgetType. THIRD_PARTY,
data = entity.data,
height = entity.height,
label = entity.label
)
sealed class Widget {
abstract fun loadLabel(context: Context): String
abstract fun toDatabaseEntity(position: Int = -1): WidgetEntity
open val isConfigurable: Boolean = false
open fun configure(context: Activity, appWidgetHost: AppWidgetHost) {}
fun toDatabaseEntity(position: Int): WidgetEntity {
companion object {
fun fromDatabaseEntity(context: Context, entity: WidgetEntity): Widget? {
if (entity.type == WidgetType.INTERNAL.value) {
return when (entity.data) {
"weather" -> WeatherWidget
"music" -> MusicWidget
"calendar" -> CalendarWidget
else -> null
}
} else {
val widgetId = entity.data.toIntOrNull() ?: return null
val widgetInfo =
AppWidgetManager.getInstance(context).getAppWidgetInfo(widgetId) ?: return null
return ExternalWidget(
height = entity.height,
widgetId = widgetId,
widgetProviderInfo = widgetInfo
)
}
}
}
}
object WeatherWidget : Widget() {
override fun loadLabel(context: Context): String {
return context.getString(R.string.widget_name_weather)
}
override fun toDatabaseEntity(position: Int): WidgetEntity {
return WidgetEntity(
type = if (type == WidgetType.INTERNAL) "internal" else "3rdparty",
label = label,
position = position,
height = height,
data = data
type = WidgetType.INTERNAL.value,
data = "weather",
height = -1,
position = position
)
}
override val isConfigurable: Boolean = true
override fun configure(context: Activity, appWidgetHost: AppWidgetHost) {
val intent = Intent()
intent.component = ComponentName(
context.getPackageName(),
"de.mm20.launcher2.ui.settings.SettingsActivity"
)
intent.putExtra(
"de.mm20.launcher2.settings.ROUTE",
"settings/widgets/weather"
)
context.tryStartActivity(intent)
}
}
object MusicWidget : Widget() {
override fun loadLabel(context: Context): String {
return context.getString(R.string.widget_name_music)
}
override fun toDatabaseEntity(position: Int): WidgetEntity {
return WidgetEntity(
type = WidgetType.INTERNAL.value,
data = "music",
height = -1,
position = position
)
}
override val isConfigurable: Boolean = true
override fun configure(context: Activity, appWidgetHost: AppWidgetHost) {
val intent = Intent()
intent.component = ComponentName(
context.getPackageName(),
"de.mm20.launcher2.ui.settings.SettingsActivity"
)
intent.putExtra(
"de.mm20.launcher2.settings.ROUTE",
"settings/widgets/music"
)
context.tryStartActivity(intent)
}
}
object CalendarWidget : Widget() {
override fun loadLabel(context: Context): String {
return context.getString(R.string.widget_name_calendar)
}
override fun toDatabaseEntity(position: Int): WidgetEntity {
return WidgetEntity(
type = WidgetType.INTERNAL.value,
data = "calendar",
height = -1,
position = position
)
}
override val isConfigurable: Boolean = true
override fun configure(context: Activity, appWidgetHost: AppWidgetHost) {
val intent = Intent()
intent.component = ComponentName(
context.getPackageName(),
"de.mm20.launcher2.ui.settings.SettingsActivity"
)
intent.putExtra(
"de.mm20.launcher2.settings.ROUTE",
"settings/widgets/calendar"
)
context.tryStartActivity(intent)
}
}
class ExternalWidget(
var height: Int,
val widgetId: Int,
val widgetProviderInfo: AppWidgetProviderInfo
) : Widget() {
override fun loadLabel(context: Context): String {
return widgetProviderInfo.loadLabel(context.packageManager)
}
override fun toDatabaseEntity(position: Int): WidgetEntity {
return WidgetEntity(
type = WidgetType.THIRD_PARTY.value,
data = widgetId.toString(),
height = height,
position = position
)
}
override val isConfigurable: Boolean = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
widgetProviderInfo.widgetFeatures and AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE != 0
} else {
false
}
override fun configure(context: Activity, appWidgetHost: AppWidgetHost) {
appWidgetHost.startAppWidgetConfigureActivityForResult(
context,
widgetId,
0,
0,
null
)
}
}
enum class WidgetType {
INTERNAL,
THIRD_PARTY
enum class WidgetType(val value: String) {
INTERNAL("internal"),
THIRD_PARTY("3rdparty")
}
@@ -1,40 +1,79 @@
package de.mm20.launcher2.widgets
import android.content.Context
import de.mm20.launcher2.widgets.R
import de.mm20.launcher2.database.AppDatabase
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import java.util.concurrent.Executors
class WidgetRepository(
val context: Context
) {
interface WidgetRepository {
fun getWidgets(): Flow<List<Widget>>
fun getInternalWidgets(): List<Widget>
fun saveWidgets(widgets: List<Widget>)
fun addWidget(widget: Widget, position: Int)
fun removeWidget(widget: Widget)
fun setWidgetHeight(widget: Widget, newHeight: Int)
}
internal class WidgetRepositoryImpl(
private val context: Context,
private val database: AppDatabase,
) : WidgetRepository {
private val scope = CoroutineScope(Job() + Dispatchers.Default)
fun getWidgets(): Flow<List<Widget>> {
return AppDatabase.getInstance(context).widgetDao()
override fun getWidgets(): Flow<List<Widget>> {
return database.widgetDao()
.getWidgets()
.map { it.map { Widget(it) } }
.map { it.mapNotNull { Widget.fromDatabaseEntity(context, it) } }
}
fun getInternalWidgets(): List<Widget> {
return listOf(
Widget(WidgetType.INTERNAL, "weather", -1, context.getString(R.string.widget_name_weather)),
Widget(WidgetType.INTERNAL, "music", -1, context.getString(R.string.widget_name_music)),
Widget(WidgetType.INTERNAL, "calendar", -1, context.getString(R.string.widget_name_calendar)),
)
override fun getInternalWidgets(): List<Widget> {
return listOf(WeatherWidget, MusicWidget, CalendarWidget)
}
fun saveWidgets(widgets: List<Widget>) {
override fun saveWidgets(widgets: List<Widget>) {
scope.launch {
withContext(Dispatchers.IO) {
AppDatabase.getInstance(context).widgetDao().updateWidgets(widgets.mapIndexed { i, widget -> widget.toDatabaseEntity(i) })
database.widgetDao()
.updateWidgets(widgets.mapIndexed { i, widget -> widget.toDatabaseEntity(i) })
}
}
}
override fun addWidget(widget: Widget, position: Int) {
scope.launch {
withContext(Dispatchers.IO) {
database.widgetDao()
.insert(widget.toDatabaseEntity(position))
}
}
}
override fun removeWidget(widget: Widget) {
scope.launch {
withContext(Dispatchers.IO) {
val ent = widget.toDatabaseEntity()
database.widgetDao().deleteWidget(
ent.type,
ent.data
)
}
}
}
override fun setWidgetHeight(widget: Widget, newHeight: Int) {
scope.launch {
withContext(Dispatchers.IO) {
val ent = widget.toDatabaseEntity()
database.widgetDao().updateHeight(
ent.type,
ent.data,
newHeight
)
}
}
}
}