Remove preferences delegates

This commit is contained in:
Koitharu
2022-02-25 19:45:42 +02:00
parent 14be8d4936
commit 94e9fa35e2
18 changed files with 103 additions and 301 deletions

View File

@@ -2,6 +2,7 @@ package org.koitharu.kotatsu.core.prefs
import android.content.Context
import android.content.SharedPreferences
import android.net.Uri
import android.os.Build
import android.provider.Settings
import androidx.appcompat.app.AppCompatDelegate
@@ -13,106 +14,113 @@ import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.channels.trySendBlocking
import kotlinx.coroutines.flow.callbackFlow
import org.koitharu.kotatsu.core.model.ZoomMode
import org.koitharu.kotatsu.utils.delegates.prefs.*
import org.koitharu.kotatsu.utils.ext.toUriOrNull
import java.io.File
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.*
class AppSettings private constructor(private val prefs: SharedPreferences) :
SharedPreferences by prefs {
class AppSettings(context: Context) {
constructor(context: Context) : this(
PreferenceManager.getDefaultSharedPreferences(context)
)
private val prefs = PreferenceManager.getDefaultSharedPreferences(context)
var listMode by EnumPreferenceDelegate(
ListMode::class.java,
KEY_LIST_MODE,
ListMode.DETAILED_LIST
)
var listMode: ListMode
get() = prefs.getString(KEY_LIST_MODE, null)?.findEnumValue(ListMode.values()) ?: ListMode.DETAILED_LIST
set(value) = prefs.edit { putString(KEY_LIST_MODE, value.name) }
var defaultSection by IntEnumPreferenceDelegate(
AppSection::class.java,
KEY_APP_SECTION,
AppSection.HISTORY
)
var defaultSection: AppSection
get() = prefs.getString(KEY_APP_SECTION, null)?.findEnumValue(AppSection.values()) ?: AppSection.HISTORY
set(value) = prefs.edit { putString(KEY_APP_SECTION, value.name) }
val theme by StringIntPreferenceDelegate(
KEY_THEME,
AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
)
val theme: Int
get() = prefs.getString(KEY_THEME, null)?.toIntOrNull() ?: AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
val isDynamicTheme by BoolPreferenceDelegate(KEY_DYNAMIC_THEME, defaultValue = false)
val isDynamicTheme: Boolean
get() = prefs.getBoolean(KEY_DYNAMIC_THEME, false)
val isAmoledTheme by BoolPreferenceDelegate(KEY_THEME_AMOLED, defaultValue = false)
val isAmoledTheme: Boolean
get() = prefs.getBoolean(KEY_THEME_AMOLED, false)
val isToolbarHideWhenScrolling by BoolPreferenceDelegate(KEY_HIDE_TOOLBAR, defaultValue = true)
val isToolbarHideWhenScrolling: Boolean
get() = prefs.getBoolean(KEY_HIDE_TOOLBAR, true)
var gridSize by IntPreferenceDelegate(KEY_GRID_SIZE, defaultValue = 100)
var gridSize: Int
get() = prefs.getInt(KEY_GRID_SIZE, 100)
set(value) = prefs.edit { putInt(KEY_GRID_SIZE, value) }
val readerPageSwitch by StringSetPreferenceDelegate(
KEY_READER_SWITCHERS,
arraySetOf(PAGE_SWITCH_TAPS)
)
val readerPageSwitch: Set<String>
get() = prefs.getStringSet(KEY_READER_SWITCHERS, null) ?: setOf(PAGE_SWITCH_TAPS)
var isTrafficWarningEnabled by BoolPreferenceDelegate(KEY_TRAFFIC_WARNING, defaultValue = true)
var isTrafficWarningEnabled: Boolean
get() = prefs.getBoolean(KEY_TRAFFIC_WARNING, true)
set(value) = prefs.edit { putBoolean(KEY_TRAFFIC_WARNING, value) }
val appUpdateAuto by BoolPreferenceDelegate(KEY_APP_UPDATE_AUTO, defaultValue = true)
val appUpdateAuto: Boolean
get() = prefs.getBoolean(KEY_APP_UPDATE_AUTO, true)
var appUpdate by LongPreferenceDelegate(KEY_APP_UPDATE, defaultValue = 0L)
var appUpdate: Long
get() = prefs.getLong(KEY_APP_UPDATE, 0L)
set(value) = prefs.edit { putLong(KEY_APP_UPDATE, value) }
val trackerNotifications by BoolPreferenceDelegate(
KEY_TRACKER_NOTIFICATIONS,
defaultValue = true
)
val trackerNotifications: Boolean
get() = prefs.getBoolean(KEY_TRACKER_NOTIFICATIONS, true)
var notificationSound by StringPreferenceDelegate(
KEY_NOTIFICATIONS_SOUND,
Settings.System.DEFAULT_NOTIFICATION_URI.toString()
)
var notificationSound: Uri
get() = prefs.getString(KEY_NOTIFICATIONS_SOUND, null)?.toUriOrNull()
?: Settings.System.DEFAULT_NOTIFICATION_URI
set(value) = prefs.edit { putString(KEY_NOTIFICATIONS_SOUND, value.toString()) }
val notificationVibrate by BoolPreferenceDelegate(KEY_NOTIFICATIONS_VIBRATE, false)
val notificationVibrate: Boolean
get() = prefs.getBoolean(KEY_NOTIFICATIONS_VIBRATE, false)
val notificationLight by BoolPreferenceDelegate(KEY_NOTIFICATIONS_LIGHT, true)
val notificationLight: Boolean
get() = prefs.getBoolean(KEY_NOTIFICATIONS_LIGHT, true)
val readerAnimation by BoolPreferenceDelegate(KEY_READER_ANIMATION, false)
val readerAnimation: Boolean
get() = prefs.getBoolean(KEY_READER_ANIMATION, false)
val isPreferRtlReader by BoolPreferenceDelegate(KEY_READER_PREFER_RTL, false)
val isPreferRtlReader: Boolean
get() = prefs.getBoolean(KEY_READER_PREFER_RTL, false)
var historyGrouping by BoolPreferenceDelegate(KEY_HISTORY_GROUPING, true)
var historyGrouping: Boolean
get() = prefs.getBoolean(KEY_HISTORY_GROUPING, true)
set(value) = prefs.edit { putBoolean(KEY_HISTORY_GROUPING, value) }
var isHistoryExcludeNsfw by BoolPreferenceDelegate(KEY_HISTORY_EXCLUDE_NSFW, false)
val isHistoryExcludeNsfw: Boolean
get() = prefs.getBoolean(KEY_HISTORY_EXCLUDE_NSFW, false)
var chaptersReverse by BoolPreferenceDelegate(KEY_REVERSE_CHAPTERS, false)
var chaptersReverse: Boolean
get() = prefs.getBoolean(KEY_REVERSE_CHAPTERS, false)
set(value) = prefs.edit { putBoolean(KEY_REVERSE_CHAPTERS, value) }
val zoomMode by EnumPreferenceDelegate(
ZoomMode::class.java,
KEY_ZOOM_MODE,
ZoomMode.FIT_CENTER
)
val zoomMode: ZoomMode
get() = prefs.getString(KEY_ZOOM_MODE, null)?.findEnumValue(ZoomMode.values()) ?: ZoomMode.FIT_CENTER
val trackSources by StringSetPreferenceDelegate(
KEY_TRACK_SOURCES,
arraySetOf(TRACK_FAVOURITES, TRACK_HISTORY)
)
val trackSources: Set<String>
get() = prefs.getStringSet(KEY_TRACK_SOURCES, null) ?: arraySetOf(TRACK_FAVOURITES, TRACK_HISTORY)
var appPassword by NullableStringPreferenceDelegate(KEY_APP_PASSWORD)
private var sourcesOrderStr by NullableStringPreferenceDelegate(KEY_SOURCES_ORDER)
var appPassword: String?
get() = prefs.getString(KEY_APP_PASSWORD, null)
set(value) = prefs.edit { if (value != null) putString(KEY_APP_PASSWORD, value) else remove(KEY_APP_PASSWORD) }
var sourcesOrder: List<Int>
get() = sourcesOrderStr?.split('|')?.mapNotNull(String::toIntOrNull).orEmpty()
set(value) {
sourcesOrderStr = value.joinToString("|")
get() = prefs.getString(KEY_SOURCES_ORDER, null)
?.split('|')
?.mapNotNull(String::toIntOrNull)
.orEmpty()
set(value) = prefs.edit {
putString(KEY_SOURCES_ORDER, value.joinToString("|"))
}
var hiddenSources by StringSetPreferenceDelegate(KEY_SOURCES_HIDDEN)
var hiddenSources: Set<String>
get() = prefs.getStringSet(KEY_SOURCES_HIDDEN, null) ?: emptySet()
set(value) = prefs.edit { putStringSet(KEY_SOURCES_HIDDEN, value) }
val isSourcesSelected: Boolean
get() = KEY_SOURCES_HIDDEN in prefs
val isPagesNumbersEnabled by BoolPreferenceDelegate(KEY_PAGES_NUMBERS, false)
val isPagesNumbersEnabled: Boolean
get() = prefs.getBoolean(KEY_PAGES_NUMBERS, false)
fun getFallbackStorageDir(): File? {
return prefs.getString(KEY_LOCAL_STORAGE, null)?.let {
@@ -131,7 +139,7 @@ class AppSettings private constructor(private val prefs: SharedPreferences) :
}
}
fun dateFormat(format: String? = prefs.getString(KEY_DATE_FORMAT, "")): DateFormat =
fun getDateFormat(format: String = prefs.getString(KEY_DATE_FORMAT, "").orEmpty()): DateFormat =
when (format) {
"" -> DateFormat.getDateInstance(DateFormat.SHORT)
else -> SimpleDateFormat(format, Locale.getDefault())
@@ -156,6 +164,10 @@ class AppSettings private constructor(private val prefs: SharedPreferences) :
}
}
private fun <E : Enum<E>> String.findEnumValue(values: Array<E>): E? {
return values.find { it.name == this }
}
companion object {
const val PAGE_SWITCH_TAPS = "taps"
@@ -165,7 +177,7 @@ class AppSettings private constructor(private val prefs: SharedPreferences) :
const val TRACK_FAVOURITES = "favourites"
const val KEY_LIST_MODE = "list_mode_2"
const val KEY_APP_SECTION = "app_section"
const val KEY_APP_SECTION = "app_section_2"
const val KEY_THEME = "theme"
const val KEY_DYNAMIC_THEME = "dynamic_theme"
const val KEY_THEME_AMOLED = "amoled_theme"
@@ -215,7 +227,7 @@ class AppSettings private constructor(private val prefs: SharedPreferences) :
val isDynamicColorAvailable: Boolean
get() = DynamicColors.isDynamicColorAvailable() ||
(isSamsung && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
(isSamsung && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
private val isSamsung
get() = Build.MANUFACTURER.equals("samsung", ignoreCase = true)

View File

@@ -1,26 +1,15 @@
package org.koitharu.kotatsu.core.prefs
import android.content.Context
import android.content.SharedPreferences
import org.koitharu.kotatsu.utils.delegates.prefs.LongPreferenceDelegate
import androidx.core.content.edit
class AppWidgetConfig private constructor(
private val prefs: SharedPreferences,
val widgetId: Int
) : SharedPreferences by prefs {
private const val CATEGORY_ID = "cat_id"
var categoryId by LongPreferenceDelegate(CATEGORY_ID, 0L)
class AppWidgetConfig(context: Context, val widgetId: Int) {
companion object {
private val prefs = context.getSharedPreferences("appwidget_$widgetId", Context.MODE_PRIVATE)
private const val CATEGORY_ID = "cat_id"
fun getInstance(context: Context, widgetId: Int) = AppWidgetConfig(
context.getSharedPreferences(
"appwidget_$widgetId",
Context.MODE_PRIVATE
), widgetId
)
}
}
var categoryId: Long
get() = prefs.getLong(CATEGORY_ID, 0L)
set(value) = prefs.edit { putLong(CATEGORY_ID, value) }
}

View File

@@ -164,7 +164,7 @@ class DetailsViewModel(
branch: String?,
): List<ChapterListItem> {
val result = ArrayList<ChapterListItem>(chapters.size)
val dateFormat = settings.dateFormat()
val dateFormat = settings.getDateFormat()
val currentIndex = chapters.indexOfFirst { it.id == currentId }
val firstNewIndex = chapters.size - newCount
val downloadedIds = downloadedChapters?.mapToSet { it.id }
@@ -196,7 +196,7 @@ class DetailsViewModel(
val result = ArrayList<ChapterListItem>(sourceChapters.size)
val currentIndex = sourceChapters.indexOfFirst { it.id == currentId }
val firstNewIndex = sourceChapters.size - newCount
val dateFormat = settings.dateFormat()
val dateFormat = settings.getDateFormat()
for (i in sourceChapters.indices) {
val chapter = sourceChapters[i]
if (chapter.branch != branch) {
@@ -253,4 +253,4 @@ class DetailsViewModel(
}
return groups.maxByOrNull { it.value.size }?.key
}
}
}

View File

@@ -46,7 +46,7 @@ class ChaptersDialog : AlertDialogFragment<DialogChaptersBinding>(),
}
val currentId = arguments?.getLong(ARG_CURRENT_ID, 0L) ?: 0L
val currentPosition = chapters.indexOfFirst { it.id == currentId }
val dateFormat = get<AppSettings>().dateFormat()
val dateFormat = get<AppSettings>().getDateFormat()
binding.recyclerViewChapters.adapter = ChaptersAdapter(this).apply {
setItems(chapters.mapIndexed { index, chapter ->
chapter.toListItem(
@@ -96,4 +96,4 @@ class ChaptersDialog : AlertDialogFragment<DialogChaptersBinding>(),
putLong(ARG_CURRENT_ID, currentId)
}.show(fm, TAG)
}
}
}

View File

@@ -62,7 +62,7 @@ class MainSettingsFragment : BasePreferenceFragment(R.string.settings),
entryValues = arrayOf("", "MM/dd/yy", "dd/MM/yy", "yyyy-MM-dd", "dd MMM yyyy", "MMM dd, yyyy")
val now = Date().time
entries = entryValues.map { value ->
val formattedDate = settings.dateFormat(value.toString()).format(now)
val formattedDate = settings.getDateFormat(value.toString()).format(now)
if (value == "") {
"${context.getString(R.string.system_default)} ($formattedDate)"
} else {
@@ -171,4 +171,4 @@ class MainSettingsFragment : BasePreferenceFragment(R.string.settings),
summary = storage?.getStorageName(context) ?: getString(R.string.not_available)
}
}
}
}

View File

@@ -10,14 +10,13 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BasePreferenceFragment
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.settings.utils.RingtonePickContract
import org.koitharu.kotatsu.utils.ext.toUriOrNull
class NotificationSettingsLegacyFragment : BasePreferenceFragment(R.string.notifications) {
private val ringtonePickContract = registerForActivityResult(
RingtonePickContract(get<Context>().getString(R.string.notification_sound))
) { uri ->
settings.notificationSound = uri?.toString() ?: return@registerForActivityResult
settings.notificationSound = uri ?: return@registerForActivityResult
findPreference<Preference>(AppSettings.KEY_NOTIFICATIONS_SOUND)?.run {
summary = RingtoneManager.getRingtone(context, uri)?.getTitle(context)
?: getString(R.string.silent)
@@ -31,7 +30,7 @@ class NotificationSettingsLegacyFragment : BasePreferenceFragment(R.string.notif
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
findPreference<Preference>(AppSettings.KEY_NOTIFICATIONS_SOUND)?.run {
val uri = settings.notificationSound.toUriOrNull()
val uri = settings.notificationSound
summary = RingtoneManager.getRingtone(context, uri)?.getTitle(context)
?: getString(R.string.silent)
}
@@ -40,10 +39,10 @@ class NotificationSettingsLegacyFragment : BasePreferenceFragment(R.string.notif
override fun onPreferenceTreeClick(preference: Preference): Boolean {
return when (preference.key) {
AppSettings.KEY_NOTIFICATIONS_SOUND -> {
ringtonePickContract.launch(settings.notificationSound.toUriOrNull())
ringtonePickContract.launch(settings.notificationSound)
true
}
else -> super.onPreferenceTreeClick(preference)
}
}
}
}

View File

@@ -183,7 +183,7 @@ class TrackWorker(context: Context, workerParams: WorkerParameters) :
setShortcutId(manga.id.toString())
priority = NotificationCompat.PRIORITY_DEFAULT
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
builder.setSound(settings.notificationSound.toUriOrNull())
builder.setSound(settings.notificationSound)
var defaults = if (settings.notificationLight) {
setLights(colorPrimary, 1000, 5000)
NotificationCompat.DEFAULT_LIGHTS
@@ -298,4 +298,4 @@ class TrackWorker(context: Context, workerParams: WorkerParameters) :
}
}
}
}
}

View File

@@ -1,20 +0,0 @@
package org.koitharu.kotatsu.utils.delegates.prefs
import android.content.SharedPreferences
import androidx.core.content.edit
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class BoolPreferenceDelegate(private val key: String, private val defaultValue: Boolean) :
ReadWriteProperty<SharedPreferences, Boolean> {
override fun getValue(thisRef: SharedPreferences, property: KProperty<*>): Boolean {
return thisRef.getBoolean(key, defaultValue)
}
override fun setValue(thisRef: SharedPreferences, property: KProperty<*>, value: Boolean) {
thisRef.edit {
putBoolean(key, value)
}
}
}

View File

@@ -1,27 +0,0 @@
package org.koitharu.kotatsu.utils.delegates.prefs
import android.content.SharedPreferences
import androidx.core.content.edit
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class EnumPreferenceDelegate<E : Enum<*>>(
private val cls: Class<E>,
private val key: String,
private val defValue: E
) : ReadWriteProperty<SharedPreferences, E> {
override fun getValue(thisRef: SharedPreferences, property: KProperty<*>): E {
val name = thisRef.getString(key, null)
if (name === null) {
return defValue
}
return cls.enumConstants?.find { it.name == name } ?: defValue
}
override fun setValue(thisRef: SharedPreferences, property: KProperty<*>, value: E) {
thisRef.edit {
putString(key, value.name)
}
}
}

View File

@@ -1,28 +0,0 @@
package org.koitharu.kotatsu.utils.delegates.prefs
import android.content.SharedPreferences
import androidx.core.content.edit
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
@Deprecated("")
class IntEnumPreferenceDelegate<E : Enum<*>>(
private val cls: Class<E>,
private val key: String,
private val defValue: E
) : ReadWriteProperty<SharedPreferences, E> {
override fun getValue(thisRef: SharedPreferences, property: KProperty<*>): E {
val ord = thisRef.getInt(key, -1)
if (ord == -1) {
return defValue
}
return cls.enumConstants?.getOrNull(ord) ?: defValue
}
override fun setValue(thisRef: SharedPreferences, property: KProperty<*>, value: E) {
thisRef.edit {
putInt(key, value.ordinal)
}
}
}

View File

@@ -1,20 +0,0 @@
package org.koitharu.kotatsu.utils.delegates.prefs
import android.content.SharedPreferences
import androidx.core.content.edit
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class IntPreferenceDelegate(private val key: String, private val defaultValue: Int) :
ReadWriteProperty<SharedPreferences, Int> {
override fun getValue(thisRef: SharedPreferences, property: KProperty<*>): Int {
return thisRef.getInt(key, defaultValue)
}
override fun setValue(thisRef: SharedPreferences, property: KProperty<*>, value: Int) {
thisRef.edit {
putInt(key, value)
}
}
}

View File

@@ -1,20 +0,0 @@
package org.koitharu.kotatsu.utils.delegates.prefs
import android.content.SharedPreferences
import androidx.core.content.edit
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class LongPreferenceDelegate(private val key: String, private val defaultValue: Long) :
ReadWriteProperty<SharedPreferences, Long> {
override fun getValue(thisRef: SharedPreferences, property: KProperty<*>): Long {
return thisRef.getLong(key, defaultValue)
}
override fun setValue(thisRef: SharedPreferences, property: KProperty<*>, value: Long) {
thisRef.edit {
putLong(key, value)
}
}
}

View File

@@ -1,20 +0,0 @@
package org.koitharu.kotatsu.utils.delegates.prefs
import android.content.SharedPreferences
import androidx.core.content.edit
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class NullableStringPreferenceDelegate(private val key: String) :
ReadWriteProperty<SharedPreferences, String?> {
override fun getValue(thisRef: SharedPreferences, property: KProperty<*>): String? {
return thisRef.getString(key, null)
}
override fun setValue(thisRef: SharedPreferences, property: KProperty<*>, value: String?) {
thisRef.edit {
putString(key, value)
}
}
}

View File

@@ -1,20 +0,0 @@
package org.koitharu.kotatsu.utils.delegates.prefs
import android.content.SharedPreferences
import androidx.core.content.edit
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class StringIntPreferenceDelegate(private val key: String, private val defValue: Int) :
ReadWriteProperty<SharedPreferences, Int> {
override fun getValue(thisRef: SharedPreferences, property: KProperty<*>): Int {
return thisRef.getString(key, defValue.toString())?.toIntOrNull() ?: defValue
}
override fun setValue(thisRef: SharedPreferences, property: KProperty<*>, value: Int) {
thisRef.edit {
putString(key, value.toString())
}
}
}

View File

@@ -1,20 +0,0 @@
package org.koitharu.kotatsu.utils.delegates.prefs
import android.content.SharedPreferences
import androidx.core.content.edit
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class StringPreferenceDelegate(private val key: String, private val defValue: String) :
ReadWriteProperty<SharedPreferences, String> {
override fun getValue(thisRef: SharedPreferences, property: KProperty<*>): String {
return thisRef.getString(key, defValue) ?: defValue
}
override fun setValue(thisRef: SharedPreferences, property: KProperty<*>, value: String) {
thisRef.edit {
putString(key, value)
}
}
}

View File

@@ -1,23 +0,0 @@
package org.koitharu.kotatsu.utils.delegates.prefs
import android.content.SharedPreferences
import androidx.core.content.edit
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class StringSetPreferenceDelegate(
private val key: String,
private val defValue: Set<String> = emptySet()
) :
ReadWriteProperty<SharedPreferences, Set<String>> {
override fun getValue(thisRef: SharedPreferences, property: KProperty<*>): Set<String> {
return thisRef.getStringSet(key, defValue) ?: defValue
}
override fun setValue(thisRef: SharedPreferences, property: KProperty<*>, value: Set<String>) {
thisRef.edit {
putStringSet(key, value)
}
}
}

View File

@@ -50,7 +50,7 @@ class ShelfConfigActivity : BaseActivity<ActivityCategoriesBinding>(),
finishAfterTransition()
return
}
config = AppWidgetConfig.getInstance(this, appWidgetId)
config = AppWidgetConfig(this, appWidgetId)
viewModel.checkedId = config.categoryId
viewModel.content.observe(this, this::onContentChanged)
@@ -118,4 +118,4 @@ class ShelfConfigActivity : BaseActivity<ActivityCategoriesBinding>(),
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids)
sendBroadcast(intent)
}
}
}

View File

@@ -24,7 +24,7 @@ class ShelfListFactory(
) : RemoteViewsService.RemoteViewsFactory {
private val dataSet = ArrayList<Manga>()
private val config = AppWidgetConfig.getInstance(context, widgetId)
private val config = AppWidgetConfig(context, widgetId)
override fun onCreate() {
}
@@ -73,4 +73,4 @@ class ShelfListFactory(
override fun getViewTypeCount() = 1
override fun onDestroy() = Unit
}
}