From c2e7931ea67ac1d6ad2913fae83e6a0ba2dc5145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Tue, 26 Nov 2019 21:55:04 +0100 Subject: [PATCH] [Config] Implement (basic) new app config storage. --- .../java/pl/szczodrzynski/edziennik/App.java | 5 + .../szczodrzynski/edziennik/MainActivity.kt | 2 +- .../szczodrzynski/edziennik/config/Config.kt | 57 ++++++++++++ .../edziennik/config/ConfigDao.kt | 25 +++++ .../edziennik/config/ConfigEntry.kt | 14 +++ .../edziennik/config/ConfigExtensions.kt | 93 +++++++++++++++++++ .../edziennik/config/ConfigGrades.kt | 9 ++ .../edziennik/config/ConfigMigration.kt | 27 ++++++ .../edziennik/config/ConfigSync.kt | 27 ++++++ .../edziennik/config/ConfigTimetable.kt | 19 ++++ .../edziennik/config/ConfigUI.kt | 32 +++++++ .../edziennik/data/db/AppDb.java | 31 ++++++- .../modules/settings/SettingsNewFragment.java | 8 +- 13 files changed, 342 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/config/Config.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigDao.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigEntry.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigExtensions.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigGrades.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigMigration.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigSync.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigTimetable.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigUI.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/App.java b/app/src/main/java/pl/szczodrzynski/edziennik/App.java index b975aff9..da6fa5f7 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/App.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/App.java @@ -68,6 +68,7 @@ import me.leolin.shortcutbadger.ShortcutBadger; import okhttp3.ConnectionSpec; import okhttp3.OkHttpClient; import okhttp3.TlsVersion; +import pl.szczodrzynski.edziennik.config.Config; import pl.szczodrzynski.edziennik.data.db.AppDb; import pl.szczodrzynski.edziennik.data.db.modules.debuglog.DebugLog; import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore; @@ -145,6 +146,7 @@ public class App extends androidx.multidex.MultiDexApplication implements Config //public Register register; // REGISTER for current profile, read from registerStore public ProfileFull profile; + public Config config; // other stuff public Gson gson; @@ -194,6 +196,8 @@ public class App extends androidx.multidex.MultiDexApplication implements Config gson = new Gson(); networkUtils = new NetworkUtils(this); + config = new Config(db); + Iconics.init(getApplicationContext()); Iconics.registerFont(SzkolnyFont.INSTANCE); @@ -636,6 +640,7 @@ public class App extends androidx.multidex.MultiDexApplication implements Config MainActivity.Companion.setUseOldMessages(profile.getLoginStoreType() == LOGIN_TYPE_MOBIDZIENNIK && appConfig.mobidziennikOldMessages == 1); profileId = profile.getId(); appSharedPrefs.edit().putInt("current_profile_id", profile.getId()).apply(); + config.setProfile(profileId); } else if (!loadedLast) { profileLoadById(profileLastId(), true); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt index d1ae3ed3..4f7ab72c 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt @@ -973,7 +973,7 @@ class MainActivity : AppCompatActivity() { val item = DrawerPrimaryItem() .withIdentifier(target.id.toLong()) .withName(target.name) - .withHiddenInMiniDrawer(!app.appConfig.miniDrawerButtonIds.contains(target.id)) + .withHiddenInMiniDrawer(!app.config.ui.miniMenuButtons.contains(target.id)) .also { if (target.description != null) it.withDescription(target.description!!) } .also { if (target.icon != null) it.withIcon(target.icon!!) } .also { if (target.title != null) it.withAppTitle(getString(target.title!!)) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/config/Config.kt b/app/src/main/java/pl/szczodrzynski/edziennik/config/Config.kt new file mode 100644 index 00000000..0b6d9933 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/config/Config.kt @@ -0,0 +1,57 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-11-26. + */ + +package pl.szczodrzynski.edziennik.config + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import pl.szczodrzynski.edziennik.data.db.AppDb +import kotlin.coroutines.CoroutineContext + +class Config(val db: AppDb) : CoroutineScope { + companion object { + const val DATA_VERSION = 1 + } + + private val job = Job() + override val coroutineContext: CoroutineContext + get() = job + Dispatchers.Default + + private var profileId: Int? = null + val values: HashMap = hashMapOf() + //private val profileValues: HashMap = hashMapOf() + + val ui by lazy { ConfigUI(this) } + val sync by lazy { ConfigSync(this) } + val timetable by lazy { ConfigTimetable(this) } + val grades by lazy { ConfigGrades(this) } + + private var mDataVersion: Int? = null + var dataVersion: Int + get() { mDataVersion = mDataVersion ?: values.get("dataVersion", 0); return mDataVersion ?: 0 } + set(value) { set(-1, "dataVersion", value); mDataVersion = value } + + init { + db.configDao().getAllNow().toHashMap(values) + if (dataVersion < DATA_VERSION) + ConfigMigration(this) + } + fun setProfile(profileId: Int) { + this.profileId = profileId + //profileValues.clear() + //db.configDao().getAllNow(profileId).toHashMap(profileValues) + } + + fun set(profileId: Int, key: String, value: String?) { + //if (profileId == -1) + values[key] = value + /*else + profileValues[key] = value*/ + launch { + db.configDao().add(ConfigEntry(profileId, key, value)) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigDao.kt new file mode 100644 index 00000000..98866bd4 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigDao.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-11-26. + */ + +package pl.szczodrzynski.edziennik.config + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query + +@Dao +interface ConfigDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun add(entry: ConfigEntry) + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun addAll(list: List) + + @Query("SELECT * FROM config WHERE profileId = -1") + fun getAllNow(): List + + @Query("SELECT * FROM config WHERE profileId = :profileId") + fun getAllNow(profileId: Int): List +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigEntry.kt b/app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigEntry.kt new file mode 100644 index 00000000..2e428ee9 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigEntry.kt @@ -0,0 +1,14 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-11-26. + */ + +package pl.szczodrzynski.edziennik.config + +import androidx.room.Entity + +@Entity(tableName = "config", primaryKeys = ["profileId", "key"]) +data class ConfigEntry( + val profileId: Int = -1, + val key: String, + val value: String? +) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigExtensions.kt b/app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigExtensions.kt new file mode 100644 index 00000000..5ce451b0 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigExtensions.kt @@ -0,0 +1,93 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-11-26. + */ + +package pl.szczodrzynski.edziennik.config + +import com.google.gson.* +import com.google.gson.reflect.TypeToken +import pl.szczodrzynski.edziennik.utils.models.Date +import pl.szczodrzynski.edziennik.utils.models.Time + +private val gson = Gson() + +fun Config.set(profileId: Int, key: String, value: Int) { + set(profileId, key, value.toString()) +} +fun Config.set(profileId: Int, key: String, value: Boolean) { + set(profileId, key, value.toString()) +} +fun Config.set(profileId: Int, key: String, value: Long) { + set(profileId, key, value.toString()) +} +fun Config.set(profileId: Int, key: String, value: Float) { + set(profileId, key, value.toString()) +} +fun Config.set(profileId: Int, key: String, value: Date?) { + set(profileId, key, value?.stringY_m_d) +} +fun Config.set(profileId: Int, key: String, value: Time?) { + set(profileId, key, value?.stringValue) +} +fun Config.set(profileId: Int, key: String, value: JsonElement?) { + set(profileId, key, value?.toString()) +} +fun Config.set(profileId: Int, key: String, value: List?) { + set(profileId, key, value?.let { gson.toJson(it) }) +} +fun Config.setStringList(profileId: Int, key: String, value: List?) { + set(profileId, key, value?.let { gson.toJson(it) }) +} +fun Config.setIntList(profileId: Int, key: String, value: List?) { + set(profileId, key, value?.let { gson.toJson(it) }) +} +fun Config.setLongList(profileId: Int, key: String, value: List?) { + set(profileId, key, value?.let { gson.toJson(it) }) +} + +fun HashMap.get(key: String, default: String?): String? { + return this[key] ?: default +} +fun HashMap.get(key: String, default: Boolean): Boolean { + return this[key]?.toBoolean() ?: default +} +fun HashMap.get(key: String, default: Int): Int { + return this[key]?.toIntOrNull() ?: default +} +fun HashMap.get(key: String, default: Long): Long { + return this[key]?.toLongOrNull() ?: default +} +fun HashMap.get(key: String, default: Float): Float { + return this[key]?.toFloatOrNull() ?: default +} +fun HashMap.get(key: String, default: Date?): Date? { + return this[key]?.let { Date.fromY_m_d(it) } ?: default +} +fun HashMap.get(key: String, default: Time?): Time? { + return this[key]?.let { Time.fromHms(it) } ?: default +} +fun HashMap.get(key: String, default: JsonObject?): JsonObject? { + return this[key]?.let { JsonParser().parse(it)?.asJsonObject } ?: default +} +fun HashMap.get(key: String, default: JsonArray?): JsonArray? { + return this[key]?.let { JsonParser().parse(it)?.asJsonArray } ?: default +} +fun HashMap.get(key: String, default: List?): List? { + return this[key]?.let { gson.fromJson>(it, object: TypeToken>(){}.type) } ?: default +} +fun HashMap.getStringList(key: String, default: List?): List? { + return this[key]?.let { gson.fromJson>(it, object: TypeToken>(){}.type) } ?: default +} +fun HashMap.getIntList(key: String, default: List?): List? { + return this[key]?.let { gson.fromJson>(it, object: TypeToken>(){}.type) } ?: default +} +fun HashMap.getLongList(key: String, default: List?): List? { + return this[key]?.let { gson.fromJson>(it, object: TypeToken>(){}.type) } ?: default +} + +fun List.toHashMap(map: HashMap) { + map.clear() + forEach { + map[it.key] = it.value + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigGrades.kt new file mode 100644 index 00000000..3fe6070a --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigGrades.kt @@ -0,0 +1,9 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-11-26. + */ + +package pl.szczodrzynski.edziennik.config + +class ConfigGrades(val config: Config) { + +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigMigration.kt b/app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigMigration.kt new file mode 100644 index 00000000..b41cd31c --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigMigration.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-11-26. + */ + +package pl.szczodrzynski.edziennik.config + +import pl.szczodrzynski.edziennik.MainActivity + +class ConfigMigration(config: Config) { + init { config.apply { + if (dataVersion < 1) { + ui.theme = 1 + sync.enabled = true + sync.interval = 60*60; // seconds + ui.miniMenuButtons = listOf( + MainActivity.DRAWER_ITEM_HOME, + MainActivity.DRAWER_ITEM_TIMETABLE, + MainActivity.DRAWER_ITEM_AGENDA, + MainActivity.DRAWER_ITEM_GRADES, + MainActivity.DRAWER_ITEM_MESSAGES, + MainActivity.DRAWER_ITEM_HOMEWORK, + MainActivity.DRAWER_ITEM_SETTINGS + ) + dataVersion = 1 + } + }} +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigSync.kt b/app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigSync.kt new file mode 100644 index 00000000..7b6c7155 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigSync.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-11-26. + */ + +package pl.szczodrzynski.edziennik.config + +class ConfigSync(val config: Config) { + private var mSyncEnabled: Boolean? = null + var enabled: Boolean + get() { mSyncEnabled = mSyncEnabled ?: config.values.get("syncEnabled", true); return mSyncEnabled ?: true } + set(value) { config.set(-1, "syncEnabled", value); mSyncEnabled = value } + + private var mSyncOnlyWifi: Boolean? = null + var onlyWifi: Boolean + get() { mSyncOnlyWifi = mSyncOnlyWifi ?: config.values.get("syncOnlyWifi", false); return mSyncOnlyWifi ?: notifyAboutUpdates } + set(value) { config.set(-1, "syncOnlyWifi", value); mSyncOnlyWifi = value } + + private var mSyncInterval: Int? = null + var interval: Int + get() { mSyncInterval = mSyncInterval ?: config.values.get("syncInterval", 60*60); return mSyncInterval ?: 60*60 } + set(value) { config.set(-1, "syncInterval", value); mSyncInterval = value } + + private var mNotifyAboutUpdates: Boolean? = null + var notifyAboutUpdates: Boolean + get() { mNotifyAboutUpdates = mNotifyAboutUpdates ?: config.values.get("notifyAboutUpdates", true); return mNotifyAboutUpdates ?: true } + set(value) { config.set(-1, "notifyAboutUpdates", value); mNotifyAboutUpdates = value } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigTimetable.kt b/app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigTimetable.kt new file mode 100644 index 00000000..b1cfab42 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigTimetable.kt @@ -0,0 +1,19 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-11-26. + */ + +package pl.szczodrzynski.edziennik.config + +import pl.szczodrzynski.edziennik.utils.models.Time + +class ConfigTimetable(val config: Config) { + private var mBellSyncMultiplier: Int? = null + var bellSyncMultiplier: Int + get() { mBellSyncMultiplier = mBellSyncMultiplier ?: config.values.get("bellSyncMultiplier", 0); return mBellSyncMultiplier ?: 0 } + set(value) { config.set(-1, "bellSyncMultiplier", value); mBellSyncMultiplier = value } + + private var mBellSyncDiff: Time? = null + var bellSyncDiff: Time? + get() { mBellSyncDiff = mBellSyncDiff ?: config.values.get("bellSyncDiff", null as Time?); return mBellSyncDiff } + set(value) { config.set(-1, "bellSyncDiff", value); mBellSyncDiff = value } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigUI.kt b/app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigUI.kt new file mode 100644 index 00000000..af61a0b4 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigUI.kt @@ -0,0 +1,32 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-11-26. + */ + +package pl.szczodrzynski.edziennik.config + +class ConfigUI(val config: Config) { + private var mTheme: Int? = null + var theme: Int + get() { mTheme = mTheme ?: config.values.get("theme", 1); return mTheme ?: 1 } + set(value) { config.set(-1, "theme", value); mTheme = value } + + private var mHeaderBackground: String? = null + var headerBackground: String? + get() { mHeaderBackground = mHeaderBackground ?: config.values.get("headerBackground", null as String?); return mHeaderBackground } + set(value) { config.set(-1, "headerBg", value); mHeaderBackground = value } + + private var mAppBackground: String? = null + var appBackground: String? + get() { mAppBackground = mAppBackground ?: config.values.get("appBackground", null as String?); return mAppBackground } + set(value) { config.set(-1, "appBg", value); mAppBackground = value } + + private var mMiniMenuVisible: Boolean? = null + var miniMenuVisible: Boolean + get() { mMiniMenuVisible = mMiniMenuVisible ?: config.values.get("miniMenuVisible", false); return mMiniMenuVisible ?: false } + set(value) { config.set(-1, "miniMenuVisible", value); mMiniMenuVisible = value } + + private var mMiniMenuButtons: List? = null + var miniMenuButtons: List + get() { mMiniMenuButtons = mMiniMenuButtons ?: config.values.getIntList("miniMenuButtons", listOf()); return mMiniMenuButtons ?: listOf() } + set(value) { config.set(-1, "miniMenuButtons", value); mMiniMenuButtons = value } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java index 046c78e8..3db151c6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java @@ -10,6 +10,8 @@ import androidx.room.TypeConverters; import androidx.room.migration.Migration; import androidx.sqlite.db.SupportSQLiteDatabase; +import pl.szczodrzynski.edziennik.config.ConfigDao; +import pl.szczodrzynski.edziennik.config.ConfigEntry; import pl.szczodrzynski.edziennik.data.db.converters.ConverterDate; import pl.szczodrzynski.edziennik.data.db.converters.ConverterDateInt; import pl.szczodrzynski.edziennik.data.db.converters.ConverterJsonObject; @@ -105,7 +107,8 @@ import pl.szczodrzynski.edziennik.utils.models.Date; NoticeType.class, AttendanceType.class, pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson.class, - Metadata.class}, version = 64) + ConfigEntry.class, + Metadata.class}, version = 66) @TypeConverters({ ConverterTime.class, ConverterDate.class, @@ -144,6 +147,7 @@ public abstract class AppDb extends RoomDatabase { public abstract NoticeTypeDao noticeTypeDao(); public abstract AttendanceTypeDao attendanceTypeDao(); public abstract TimetableDao timetableDao(); + public abstract ConfigDao configDao(); public abstract MetadataDao metadataDao(); private static volatile AppDb INSTANCE; @@ -763,6 +767,27 @@ public abstract class AppDb extends RoomDatabase { database.execSQL("CREATE INDEX index_lessons_profileId_type_oldDate ON timetable (profileId, type, oldDate);"); } }; + private static final Migration MIGRATION_64_65 = new Migration(64, 65) { + @Override + public void migrate(@NonNull SupportSQLiteDatabase database) { + database.execSQL("CREATE TABLE config (" + + "profileId INTEGER NOT NULL DEFAULT -1," + + "`key` TEXT NOT NULL," + + "value TEXT NOT NULL," + + "PRIMARY KEY(profileId, `key`));"); + } + }; + private static final Migration MIGRATION_65_66 = new Migration(65, 66) { + @Override + public void migrate(@NonNull SupportSQLiteDatabase database) { + database.execSQL("DROP TABLE config;"); + database.execSQL("CREATE TABLE config (" + + "profileId INTEGER NOT NULL DEFAULT -1," + + "`key` TEXT NOT NULL," + + "value TEXT," + + "PRIMARY KEY(profileId, `key`));"); + } + }; public static AppDb getDatabase(final Context context) { @@ -824,7 +849,9 @@ public abstract class AppDb extends RoomDatabase { MIGRATION_60_61, MIGRATION_61_62, MIGRATION_62_63, - MIGRATION_63_64 + MIGRATION_63_64, + MIGRATION_64_65, + MIGRATION_65_66 ) .allowMainThreadQueries() //.fallbackToDestructiveMigration() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java index 832eb77f..9b0693e0 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java @@ -372,7 +372,7 @@ public class SettingsNewFragment extends MaterialAboutFragment { buttonCaptions.add(getString(R.string.menu_settings)); //buttonCaptions.add(getString(R.string.title_debugging)); List selectedIds = new ArrayList<>(); - for (int id: app.appConfig.miniDrawerButtonIds) { + for (int id: app.config.getUi().getMiniMenuButtons()) { selectedIds.add(buttonIds.indexOf(id)); } new MaterialDialog.Builder(activity) @@ -380,16 +380,16 @@ public class SettingsNewFragment extends MaterialAboutFragment { .content(getString(R.string.settings_theme_mini_drawer_buttons_dialog_text)) .items(buttonCaptions) .itemsCallbackMultiChoice(selectedIds.toArray(new Integer[0]), (dialog, which, text) -> { - app.appConfig.miniDrawerButtonIds.clear(); + List list = new ArrayList<>(); for (int index: which) { if (index == -1) continue; // wtf int id = buttonIds.get(index); - app.appConfig.miniDrawerButtonIds.add(id); + list.add(id); } - app.saveConfig("miniDrawerButtonIds"); + app.config.getUi().setMiniMenuButtons(list); activity.setDrawerItems(); activity.getDrawer().updateBadges(); return true;