Add optional translucency to the Widget

This commit is contained in:
Jonas Lochmann 2022-09-12 02:00:00 +02:00
parent 435a29c958
commit a7e2131638
No known key found for this signature in database
GPG key ID: 8B8C9AEE10FA5B36
17 changed files with 2133 additions and 27 deletions

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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
) )
} }

View file

@ -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()

View file

@ -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()
}

View file

@ -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
)

View file

@ -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,10 +36,32 @@ 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()
}
}
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.setPendingIntentTemplate(android.R.id.list, BackgroundActionService.getSwitchToDefaultUserIntent(context))
views.setEmptyView(android.R.id.list, android.R.id.empty) views.setEmptyView(android.R.id.list, android.R.id.empty)
@ -46,12 +70,14 @@ class TimesWidgetProvider: AppWidgetProvider() {
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.runInTransaction {
database.widgetCategory().deleteByWidgetIds(appWidgetIds) 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.runInTransaction {
database.widgetCategory().deleteAll() 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)

View file

@ -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)
) )
} }

View file

@ -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,

View file

@ -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()

View file

@ -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()
}
}

View file

@ -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"

View file

@ -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>

View 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>

View file

@ -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>

View file

@ -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>

View file

@ -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>