mirror of
https://codeberg.org/timelimit/timelimit-android.git
synced 2025-10-03 09:49:25 +02:00
Add optional translucency to the Widget
This commit is contained in:
parent
435a29c958
commit
a7e2131638
17 changed files with 2133 additions and 27 deletions
1760
app/schemas/io.timelimit.android.data.RoomDatabase/46.json
Normal file
1760
app/schemas/io.timelimit.android.data.RoomDatabase/46.json
Normal file
File diff suppressed because it is too large
Load diff
|
@ -47,6 +47,7 @@ interface Database {
|
||||||
fun deviceKey(): DeviceKeyDao
|
fun deviceKey(): DeviceKeyDao
|
||||||
fun u2f(): U2FDao
|
fun u2f(): U2FDao
|
||||||
fun widgetCategory(): WidgetCategoryDao
|
fun widgetCategory(): WidgetCategoryDao
|
||||||
|
fun widgetConfig(): WidgetConfigDao
|
||||||
|
|
||||||
fun <T> runInTransaction(block: () -> T): T
|
fun <T> runInTransaction(block: () -> T): T
|
||||||
fun <T> runInUnobservedTransaction(block: () -> T): T
|
fun <T> runInUnobservedTransaction(block: () -> T): T
|
||||||
|
|
|
@ -333,6 +333,12 @@ object DatabaseMigrations {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val MIGRATE_TO_V46 = object: Migration(45, 46) {
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
database.execSQL("CREATE TABLE IF NOT EXISTS `widget_config` (`widget_id` INTEGER NOT NULL, `translucent` INTEGER NOT NULL, PRIMARY KEY(`widget_id`))")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val ALL = arrayOf(
|
val ALL = arrayOf(
|
||||||
MIGRATE_TO_V2,
|
MIGRATE_TO_V2,
|
||||||
MIGRATE_TO_V3,
|
MIGRATE_TO_V3,
|
||||||
|
@ -377,6 +383,7 @@ object DatabaseMigrations {
|
||||||
MIGRATE_TO_V42,
|
MIGRATE_TO_V42,
|
||||||
MIGRATE_TP_V43,
|
MIGRATE_TP_V43,
|
||||||
MIGRATE_TO_V44,
|
MIGRATE_TO_V44,
|
||||||
MIGRATE_TO_V45
|
MIGRATE_TO_V45,
|
||||||
|
MIGRATE_TO_V46
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,8 +59,9 @@ import java.util.concurrent.TimeUnit
|
||||||
CryptContainerKeyResult::class,
|
CryptContainerKeyResult::class,
|
||||||
DevicePublicKey::class,
|
DevicePublicKey::class,
|
||||||
UserU2FKey::class,
|
UserU2FKey::class,
|
||||||
WidgetCategory::class
|
WidgetCategory::class,
|
||||||
], version = 45)
|
WidgetConfig::class
|
||||||
|
], version = 46)
|
||||||
abstract class RoomDatabase: RoomDatabase(), io.timelimit.android.data.Database {
|
abstract class RoomDatabase: RoomDatabase(), io.timelimit.android.data.Database {
|
||||||
companion object {
|
companion object {
|
||||||
private val lock = Object()
|
private val lock = Object()
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package io.timelimit.android.data.dao
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy
|
||||||
|
import androidx.room.Query
|
||||||
|
import io.timelimit.android.data.model.WidgetConfig
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface WidgetConfigDao {
|
||||||
|
@Query("SELECT * FROM widget_config")
|
||||||
|
fun queryAll(): List<WidgetConfig>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM widget_config WHERE widget_id = :widgetId")
|
||||||
|
fun queryByWidgetId(widgetId: Int): WidgetConfig?
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
fun upsert(config: WidgetConfig)
|
||||||
|
|
||||||
|
@Query("DELETE FROM widget_config WHERE widget_id IN (:widgetIds)")
|
||||||
|
fun deleteByWidgetIds(widgetIds: IntArray)
|
||||||
|
|
||||||
|
@Query("DELETE FROM widget_config")
|
||||||
|
fun deleteAll()
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package io.timelimit.android.data.model
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(
|
||||||
|
tableName = "widget_config"
|
||||||
|
)
|
||||||
|
data class WidgetConfig (
|
||||||
|
@ColumnInfo(name = "widget_id")
|
||||||
|
@PrimaryKey
|
||||||
|
val widgetId: Int,
|
||||||
|
val translucent: Boolean
|
||||||
|
)
|
|
@ -26,6 +26,8 @@ import androidx.core.content.getSystemService
|
||||||
import io.timelimit.android.BuildConfig
|
import io.timelimit.android.BuildConfig
|
||||||
import io.timelimit.android.R
|
import io.timelimit.android.R
|
||||||
import io.timelimit.android.async.Threads
|
import io.timelimit.android.async.Threads
|
||||||
|
import io.timelimit.android.coroutines.executeAndWait
|
||||||
|
import io.timelimit.android.coroutines.runAsync
|
||||||
import io.timelimit.android.integration.platform.android.BackgroundActionService
|
import io.timelimit.android.integration.platform.android.BackgroundActionService
|
||||||
import io.timelimit.android.logic.DefaultAppLogic
|
import io.timelimit.android.logic.DefaultAppLogic
|
||||||
|
|
||||||
|
@ -34,24 +36,48 @@ class TimesWidgetProvider: AppWidgetProvider() {
|
||||||
private const val LOG_TAG = "TimesWidgetProvider"
|
private const val LOG_TAG = "TimesWidgetProvider"
|
||||||
|
|
||||||
private fun handleUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
|
private fun handleUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
|
||||||
for (appWidgetId in appWidgetIds) {
|
runAsync {
|
||||||
val views = RemoteViews(context.packageName, R.layout.widget_times)
|
val configs = Threads.database.executeAndWait {
|
||||||
|
try {
|
||||||
|
DefaultAppLogic.with(context).database.widgetConfig()
|
||||||
|
.queryAll()
|
||||||
|
.associateBy { it.widgetId }
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
Log.d(LOG_TAG, "could not query database", ex)
|
||||||
|
}
|
||||||
|
|
||||||
views.setRemoteAdapter(android.R.id.list, TimesWidgetService.intent(context, appWidgetId))
|
emptyMap()
|
||||||
views.setPendingIntentTemplate(android.R.id.list, BackgroundActionService.getSwitchToDefaultUserIntent(context))
|
}
|
||||||
views.setEmptyView(android.R.id.list, android.R.id.empty)
|
}
|
||||||
|
|
||||||
appWidgetManager.updateAppWidget(appWidgetId, views)
|
for (appWidgetId in appWidgetIds) {
|
||||||
|
val config = configs[appWidgetId]
|
||||||
|
val translucent = config?.translucent ?: false
|
||||||
|
|
||||||
|
val views = RemoteViews(
|
||||||
|
context.packageName,
|
||||||
|
if (translucent) R.layout.widget_times_translucent
|
||||||
|
else R.layout.widget_times
|
||||||
|
)
|
||||||
|
|
||||||
|
views.setRemoteAdapter(android.R.id.list, TimesWidgetService.intent(context, appWidgetId, translucent))
|
||||||
|
views.setPendingIntentTemplate(android.R.id.list, BackgroundActionService.getSwitchToDefaultUserIntent(context))
|
||||||
|
views.setEmptyView(android.R.id.list, android.R.id.empty)
|
||||||
|
|
||||||
|
appWidgetManager.updateAppWidget(appWidgetId, views)
|
||||||
|
}
|
||||||
|
|
||||||
|
TimesWidgetService.notifyContentChanges(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
TimesWidgetService.notifyContentChanges(context)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun triggerUpdates(context: Context) {
|
fun triggerUpdates(context: Context, appWidgetIds: IntArray? = null) {
|
||||||
context.getSystemService<AppWidgetManager>()?.also { appWidgetManager ->
|
context.getSystemService<AppWidgetManager>()?.also { appWidgetManager ->
|
||||||
val appWidgetIds = appWidgetManager.getAppWidgetIds(ComponentName(context, TimesWidgetProvider::class.java))
|
val usedAppWidgetIds = appWidgetIds
|
||||||
|
?: appWidgetManager.getAppWidgetIds(ComponentName(context, TimesWidgetProvider::class.java))
|
||||||
|
|
||||||
handleUpdate(context, appWidgetManager, appWidgetIds)
|
handleUpdate(context, appWidgetManager, usedAppWidgetIds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,7 +102,10 @@ class TimesWidgetProvider: AppWidgetProvider() {
|
||||||
|
|
||||||
Threads.database.execute {
|
Threads.database.execute {
|
||||||
try {
|
try {
|
||||||
database.widgetCategory().deleteByWidgetIds(appWidgetIds)
|
database.runInTransaction {
|
||||||
|
database.widgetCategory().deleteByWidgetIds(appWidgetIds)
|
||||||
|
database.widgetConfig().deleteByWidgetIds(appWidgetIds)
|
||||||
|
}
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
Log.d(LOG_TAG, "onDisabled", ex)
|
Log.d(LOG_TAG, "onDisabled", ex)
|
||||||
|
@ -92,7 +121,10 @@ class TimesWidgetProvider: AppWidgetProvider() {
|
||||||
|
|
||||||
Threads.database.execute {
|
Threads.database.execute {
|
||||||
try {
|
try {
|
||||||
database.widgetCategory().deleteAll()
|
database.runInTransaction {
|
||||||
|
database.widgetCategory().deleteAll()
|
||||||
|
database.widgetConfig().deleteAll()
|
||||||
|
}
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
Log.d(LOG_TAG, "onDisabled", ex)
|
Log.d(LOG_TAG, "onDisabled", ex)
|
||||||
|
|
|
@ -36,10 +36,12 @@ import io.timelimit.android.ui.manage.child.category.CategoryItemLeftPadding
|
||||||
class TimesWidgetService: RemoteViewsService() {
|
class TimesWidgetService: RemoteViewsService() {
|
||||||
companion object {
|
companion object {
|
||||||
private const val EXTRA_APP_WIDGET_ID = "appWidgetId"
|
private const val EXTRA_APP_WIDGET_ID = "appWidgetId"
|
||||||
|
private const val EXTRA_TRANSLUCENT = "translucent"
|
||||||
|
|
||||||
fun intent(context: Context, appWidgetId: Int) = Intent(context, TimesWidgetService::class.java)
|
fun intent(context: Context, appWidgetId: Int, translucent: Boolean) = Intent(context, TimesWidgetService::class.java)
|
||||||
.setData(Uri.parse("widget:$appWidgetId"))
|
.setData(Uri.parse("widget:$appWidgetId:$translucent"))
|
||||||
.putExtra(EXTRA_APP_WIDGET_ID, appWidgetId)
|
.putExtra(EXTRA_APP_WIDGET_ID, appWidgetId)
|
||||||
|
.putExtra(EXTRA_TRANSLUCENT, translucent)
|
||||||
|
|
||||||
fun notifyContentChanges(context: Context) {
|
fun notifyContentChanges(context: Context) {
|
||||||
context.getSystemService<AppWidgetManager>()?.also { appWidgetManager ->
|
context.getSystemService<AppWidgetManager>()?.also { appWidgetManager ->
|
||||||
|
@ -68,7 +70,7 @@ class TimesWidgetService: RemoteViewsService() {
|
||||||
notifyContentChanges(this)
|
notifyContentChanges(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createFactory(appWidgetId: Int) = object : RemoteViewsFactory {
|
private fun createFactory(appWidgetId: Int, translucent: Boolean) = object : RemoteViewsFactory {
|
||||||
private var currentItems: List<TimesWidgetItem> = emptyList()
|
private var currentItems: List<TimesWidgetItem> = emptyList()
|
||||||
|
|
||||||
init { onDataSetChanged() }
|
init { onDataSetChanged() }
|
||||||
|
@ -98,11 +100,14 @@ class TimesWidgetService: RemoteViewsService() {
|
||||||
override fun getCount(): Int = currentItems.size
|
override fun getCount(): Int = currentItems.size
|
||||||
|
|
||||||
override fun getViewAt(position: Int): RemoteViews {
|
override fun getViewAt(position: Int): RemoteViews {
|
||||||
|
val categoryItemView = if (translucent) R.layout.widget_times_category_item_translucent
|
||||||
|
else R.layout.widget_times_category_item
|
||||||
|
|
||||||
if (position >= currentItems.size) {
|
if (position >= currentItems.size) {
|
||||||
return RemoteViews(packageName, R.layout.widget_times_category_item)
|
return RemoteViews(packageName, categoryItemView)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createCategoryItem(title: String?, subtitle: String, paddingLeft: Int) = RemoteViews(packageName, R.layout.widget_times_category_item).also { result ->
|
fun createCategoryItem(title: String?, subtitle: String, paddingLeft: Int) = RemoteViews(packageName, categoryItemView).also { result ->
|
||||||
result.setTextViewText(R.id.title, title ?: "")
|
result.setTextViewText(R.id.title, title ?: "")
|
||||||
result.setTextViewText(R.id.subtitle, subtitle)
|
result.setTextViewText(R.id.subtitle, subtitle)
|
||||||
|
|
||||||
|
@ -161,6 +166,7 @@ class TimesWidgetService: RemoteViewsService() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onGetViewFactory(intent: Intent): RemoteViewsFactory = createFactory(
|
override fun onGetViewFactory(intent: Intent): RemoteViewsFactory = createFactory(
|
||||||
intent.getIntExtra(EXTRA_APP_WIDGET_ID, 0)
|
intent.getIntExtra(EXTRA_APP_WIDGET_ID, 0),
|
||||||
|
intent.getBooleanExtra(EXTRA_TRANSLUCENT, false)
|
||||||
)
|
)
|
||||||
}
|
}
|
|
@ -56,6 +56,11 @@ class WidgetConfigActivity: FragmentActivity() {
|
||||||
WidgetConfigFilterDialogFragment().showSafe(supportFragmentManager, WidgetConfigFilterDialogFragment.DIALOG_TAG)
|
WidgetConfigFilterDialogFragment().showSafe(supportFragmentManager, WidgetConfigFilterDialogFragment.DIALOG_TAG)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
is WidgetConfigModel.State.ShowOtherOptions -> {
|
||||||
|
if (supportFragmentManager.findFragmentByTag(WidgetConfigOtherDialogFragment.DIALOG_TAG) == null) {
|
||||||
|
WidgetConfigOtherDialogFragment().showSafe(supportFragmentManager, WidgetConfigOtherDialogFragment.DIALOG_TAG)
|
||||||
|
}
|
||||||
|
}
|
||||||
is WidgetConfigModel.State.Done -> {
|
is WidgetConfigModel.State.Done -> {
|
||||||
setResult(
|
setResult(
|
||||||
RESULT_OK,
|
RESULT_OK,
|
||||||
|
|
|
@ -27,8 +27,10 @@ import io.timelimit.android.data.extensions.sortedCategories
|
||||||
import io.timelimit.android.data.model.Category
|
import io.timelimit.android.data.model.Category
|
||||||
import io.timelimit.android.data.model.UserType
|
import io.timelimit.android.data.model.UserType
|
||||||
import io.timelimit.android.data.model.WidgetCategory
|
import io.timelimit.android.data.model.WidgetCategory
|
||||||
|
import io.timelimit.android.data.model.WidgetConfig
|
||||||
import io.timelimit.android.livedata.castDown
|
import io.timelimit.android.livedata.castDown
|
||||||
import io.timelimit.android.logic.DefaultAppLogic
|
import io.timelimit.android.logic.DefaultAppLogic
|
||||||
|
import io.timelimit.android.ui.widget.TimesWidgetProvider
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class WidgetConfigModel(application: Application): AndroidViewModel(application) {
|
class WidgetConfigModel(application: Application): AndroidViewModel(application) {
|
||||||
|
@ -83,11 +85,16 @@ class WidgetConfigModel(application: Application): AndroidViewModel(application)
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
Threads.database.executeAndWait {
|
val currentConfig = Threads.database.executeAndWait {
|
||||||
database.widgetCategory().deleteByWidgetId(oldState.appWidgetId)
|
database.widgetCategory().deleteByWidgetId(oldState.appWidgetId)
|
||||||
|
|
||||||
|
database.widgetConfig().queryByWidgetId(oldState.appWidgetId)
|
||||||
}
|
}
|
||||||
|
|
||||||
stateInternal.value = State.Done(appWidgetId = oldState.appWidgetId)
|
stateInternal.value = State.ShowOtherOptions(
|
||||||
|
appWidgetId = oldState.appWidgetId,
|
||||||
|
translucent = currentConfig?.translucent ?: false
|
||||||
|
)
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
Log.d(LOG_TAG, "selectModeAll", ex)
|
Log.d(LOG_TAG, "selectModeAll", ex)
|
||||||
|
@ -117,7 +124,7 @@ class WidgetConfigModel(application: Application): AndroidViewModel(application)
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
Threads.database.executeAndWait {
|
val currentConfig = Threads.database.executeAndWait {
|
||||||
val userAndDeviceRelatedData = database.derivedDataDao().getUserAndDeviceRelatedDataSync()
|
val userAndDeviceRelatedData = database.derivedDataDao().getUserAndDeviceRelatedDataSync()
|
||||||
val currentCategoryIds = userAndDeviceRelatedData!!.userRelatedData!!.categoryById.keys
|
val currentCategoryIds = userAndDeviceRelatedData!!.userRelatedData!!.categoryById.keys
|
||||||
|
|
||||||
|
@ -135,9 +142,14 @@ class WidgetConfigModel(application: Application): AndroidViewModel(application)
|
||||||
categoriesToAdd.toList().map { WidgetCategory(oldState.appWidgetId, it) }
|
categoriesToAdd.toList().map { WidgetCategory(oldState.appWidgetId, it) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
database.widgetConfig().queryByWidgetId(oldState.appWidgetId)
|
||||||
}
|
}
|
||||||
|
|
||||||
stateInternal.value = State.Done(appWidgetId = oldState.appWidgetId)
|
stateInternal.value = State.ShowOtherOptions(
|
||||||
|
appWidgetId = oldState.appWidgetId,
|
||||||
|
translucent = currentConfig?.translucent ?: false
|
||||||
|
)
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
Log.d(LOG_TAG, "selectModeAll", ex)
|
Log.d(LOG_TAG, "selectModeAll", ex)
|
||||||
|
@ -148,6 +160,38 @@ class WidgetConfigModel(application: Application): AndroidViewModel(application)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun selectOtherOptions(translucent: Boolean) {
|
||||||
|
val oldState = state.value
|
||||||
|
if (!(oldState is State.ShowOtherOptions)) return
|
||||||
|
stateInternal.value = State.Working
|
||||||
|
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
Threads.database.executeAndWait {
|
||||||
|
database.widgetConfig().upsert(
|
||||||
|
WidgetConfig(
|
||||||
|
widgetId = oldState.appWidgetId,
|
||||||
|
translucent = translucent
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
TimesWidgetProvider.triggerUpdates(
|
||||||
|
context = getApplication(),
|
||||||
|
appWidgetIds = intArrayOf(oldState.appWidgetId)
|
||||||
|
)
|
||||||
|
|
||||||
|
stateInternal.value = State.Done(oldState.appWidgetId)
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
Log.d(LOG_TAG, "selectOtherOptions", ex)
|
||||||
|
}
|
||||||
|
|
||||||
|
stateInternal.value = State.ErrorCancel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun userCancel() {
|
fun userCancel() {
|
||||||
stateInternal.value = State.UserCancel
|
stateInternal.value = State.UserCancel
|
||||||
}
|
}
|
||||||
|
@ -157,6 +201,7 @@ class WidgetConfigModel(application: Application): AndroidViewModel(application)
|
||||||
object Working: State()
|
object Working: State()
|
||||||
data class ShowModeSelection(val appWidgetId: Int, val selectedFilterCategories: Set<String>, val categories: List<Category>): State()
|
data class ShowModeSelection(val appWidgetId: Int, val selectedFilterCategories: Set<String>, val categories: List<Category>): State()
|
||||||
data class ShowCategorySelection(val appWidgetId: Int, val selectedFilterCategories: Set<String>, val categories: List<Category>): State()
|
data class ShowCategorySelection(val appWidgetId: Int, val selectedFilterCategories: Set<String>, val categories: List<Category>): State()
|
||||||
|
data class ShowOtherOptions(val appWidgetId: Int, val translucent: Boolean): State()
|
||||||
data class Done(val appWidgetId: Int): State()
|
data class Done(val appWidgetId: Int): State()
|
||||||
object Unconfigured: State()
|
object Unconfigured: State()
|
||||||
object UserCancel: State()
|
object UserCancel: State()
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package io.timelimit.android.ui.widget.config
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.content.DialogInterface
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.fragment.app.activityViewModels
|
||||||
|
import io.timelimit.android.R
|
||||||
|
|
||||||
|
class WidgetConfigOtherDialogFragment: DialogFragment() {
|
||||||
|
companion object {
|
||||||
|
private const val STATE_TRANSLUCENT = "translucent"
|
||||||
|
|
||||||
|
const val DIALOG_TAG = "WidgetConfigOtherDialogFragment"
|
||||||
|
}
|
||||||
|
|
||||||
|
private val model: WidgetConfigModel by activityViewModels()
|
||||||
|
private var translucent = false
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
model.state.value?.also {
|
||||||
|
if (it is WidgetConfigModel.State.ShowOtherOptions) {
|
||||||
|
translucent = it.translucent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
savedInstanceState?.also { translucent = it.getBoolean(STATE_TRANSLUCENT) }
|
||||||
|
|
||||||
|
model.state.observe(this) {
|
||||||
|
if (!(it is WidgetConfigModel.State.ShowOtherOptions)) dismissAllowingStateLoss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
super.onSaveInstanceState(outState)
|
||||||
|
|
||||||
|
outState.putBoolean(STATE_TRANSLUCENT, translucent)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = AlertDialog.Builder(requireContext(), theme)
|
||||||
|
.setMultiChoiceItems(
|
||||||
|
arrayOf(
|
||||||
|
getString(R.string.widget_config_other_translucent)
|
||||||
|
),
|
||||||
|
booleanArrayOf(
|
||||||
|
translucent
|
||||||
|
)
|
||||||
|
) { _, _, checked ->
|
||||||
|
translucent = checked
|
||||||
|
}
|
||||||
|
.setPositiveButton(R.string.wiazrd_next) { _, _ ->
|
||||||
|
model.selectOtherOptions(translucent)
|
||||||
|
}
|
||||||
|
.create()
|
||||||
|
|
||||||
|
override fun onCancel(dialog: DialogInterface) {
|
||||||
|
super.onCancel(dialog)
|
||||||
|
|
||||||
|
model.userCancel()
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,7 +16,7 @@
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
tools:background="@color/widget_background"
|
tools:background="@color/widgetBackground"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:paddingTop="4dp"
|
android:paddingTop="4dp"
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation version 3 of the License.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
<FrameLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
tools:background="@color/widgetBackgroundTranslucent"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingTop="4dp"
|
||||||
|
android:paddingBottom="4dp"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingRight="16dp">
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/widgetInnerContainer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/topPadding"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="12dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
tools:text="3 m"
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:textAppearanceLarge"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/widgetTextTranslucent"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
tools:text="Erlaubte Apps"
|
||||||
|
android:id="@+id/subtitle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:textAppearanceMedium"
|
||||||
|
android:textColor="@color/widgetTextTranslucent"/>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/bottomPadding"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="12dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</FrameLayout>
|
36
app/src/main/res/layout/widget_times_translucent.xml
Normal file
36
app/src/main/res/layout/widget_times_translucent.xml
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<!--
|
||||||
|
TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation version 3 of the License.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
<FrameLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/widgetBackgroundTranslucent">
|
||||||
|
|
||||||
|
<ListView
|
||||||
|
android:divider="@color/transparent"
|
||||||
|
android:id="@android:id/list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:listSelector="@android:color/transparent"/>
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@android:id/empty"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="?android:attr/progressBarStyleLarge"
|
||||||
|
android:layout_gravity="center" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
|
@ -1618,6 +1618,7 @@
|
||||||
<string name="widget_config_mode_all">alle Kategorien anzeigen</string>
|
<string name="widget_config_mode_all">alle Kategorien anzeigen</string>
|
||||||
<string name="widget_config_mode_filter">sichtbare Kategorien filtern</string>
|
<string name="widget_config_mode_filter">sichtbare Kategorien filtern</string>
|
||||||
<string name="widget_config_error_filter_empty">Sie müssen mindestens eine Kategorie auswählen</string>
|
<string name="widget_config_error_filter_empty">Sie müssen mindestens eine Kategorie auswählen</string>
|
||||||
|
<string name="widget_config_other_translucent">Transparenz aktivieren</string>
|
||||||
|
|
||||||
<string name="wiazrd_next">Weiter</string>
|
<string name="wiazrd_next">Weiter</string>
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,9 @@
|
||||||
<color name="colorPrimaryDark">#00796B</color>
|
<color name="colorPrimaryDark">#00796B</color>
|
||||||
<color name="colorAccent">#1976d2</color>
|
<color name="colorAccent">#1976d2</color>
|
||||||
<color name="widgetBackground">#ffffff</color>
|
<color name="widgetBackground">#ffffff</color>
|
||||||
|
<color name="widgetBackgroundTranslucent">#33000000</color>
|
||||||
<color name="widgetText">#000000</color>
|
<color name="widgetText">#000000</color>
|
||||||
|
<color name="widgetTextTranslucent">#ffffff</color>
|
||||||
|
|
||||||
<color name="transparent">#00000000</color>
|
<color name="transparent">#00000000</color>
|
||||||
<color name="gray">#9E9E9E</color>
|
<color name="gray">#9E9E9E</color>
|
||||||
|
|
|
@ -1666,6 +1666,7 @@
|
||||||
<string name="widget_config_mode_all">Show all Categories</string>
|
<string name="widget_config_mode_all">Show all Categories</string>
|
||||||
<string name="widget_config_mode_filter">Filter visible Categories</string>
|
<string name="widget_config_mode_filter">Filter visible Categories</string>
|
||||||
<string name="widget_config_error_filter_empty">You must select at least one category</string>
|
<string name="widget_config_error_filter_empty">You must select at least one category</string>
|
||||||
|
<string name="widget_config_other_translucent">Enable translucency</string>
|
||||||
|
|
||||||
<string name="wiazrd_next">Next</string>
|
<string name="wiazrd_next">Next</string>
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue