Automatically delete 'stale'/old widgets from database (#2702)

Automatically delete stale widgets from database

 - Automatically removes widgets that are no longer bound to the widget provider but that are still in the database -> 'stale'/old widgets will no longer cause unnecessary updates
This commit is contained in:
Joris Pelgröm 2022-07-29 23:46:02 +02:00 committed by GitHub
parent 2e7c72456c
commit 1cf3506d4b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 59 additions and 95 deletions

View file

@ -1,14 +1,10 @@
package io.homeassistant.companion.android.widgets
import android.appwidget.AppWidgetManager
import android.content.Context
import android.view.View
import android.widget.Toast
import androidx.lifecycle.lifecycleScope
import io.homeassistant.companion.android.BaseActivity
import io.homeassistant.companion.android.common.R
import io.homeassistant.companion.android.database.widget.WidgetDao
import kotlinx.coroutines.launch
abstract class BaseWidgetConfigureActivity : BaseActivity() {
@ -16,34 +12,6 @@ abstract class BaseWidgetConfigureActivity : BaseActivity() {
abstract val dao: WidgetDao
protected val onDeleteWidget = View.OnClickListener { deleteConfirmation(it.context) }
private fun deleteConfirmation(context: Context) {
val builder: android.app.AlertDialog.Builder = android.app.AlertDialog.Builder(context)
builder.setTitle(R.string.confirm_delete_this_widget_title)
builder.setMessage(R.string.confirm_delete_this_widget_message)
builder.setPositiveButton(
R.string.confirm_positive
) { dialog, _ ->
lifecycleScope.launch {
dao.delete(appWidgetId)
dialog.dismiss()
finish()
}
}
builder.setNegativeButton(
R.string.confirm_negative
) { dialog, _ -> // Do nothing
dialog.dismiss()
}
val alert: android.app.AlertDialog? = builder.create()
alert?.show()
}
protected fun showAddWidgetError() {
Toast.makeText(applicationContext, R.string.widget_creation_error, Toast.LENGTH_LONG).show()
}

View file

@ -2,9 +2,11 @@ package io.homeassistant.companion.android.widgets
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.RemoteViews
import io.homeassistant.companion.android.common.data.integration.Entity
import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
@ -92,7 +94,22 @@ abstract class BaseWidgetProvider : AppWidgetProvider() {
private suspend fun updateAllWidgets(
context: Context
) {
getAllWidgetIds(context).forEach {
val widgetProvider = getWidgetProvider(context)
val systemWidgetIds = AppWidgetManager.getInstance(context)
.getAppWidgetIds(widgetProvider)
.toSet()
val dbWidgetIds = getAllWidgetIds(context)
val invalidWidgetIds = dbWidgetIds.minus(systemWidgetIds)
if (invalidWidgetIds.isNotEmpty()) {
Log.i(
widgetProvider.shortClassName,
"Found widgets $invalidWidgetIds in database, but not in AppWidgetManager - sending onDeleted"
)
onDeleted(context, invalidWidgetIds.toIntArray())
}
dbWidgetIds.filter { systemWidgetIds.contains(it) }.forEach {
updateView(context, it)
}
}
@ -108,6 +125,7 @@ abstract class BaseWidgetProvider : AppWidgetProvider() {
}
}
abstract fun getWidgetProvider(context: Context): ComponentName
abstract suspend fun getWidgetRemoteViews(context: Context, appWidgetId: Int, suggestedEntity: Entity<Map<String, Any>>? = null): RemoteViews
abstract suspend fun getAllWidgetIds(context: Context): List<Int>
abstract fun saveEntityConfiguration(context: Context, extras: Bundle?, appWidgetId: Int)

View file

@ -3,6 +3,7 @@ package io.homeassistant.companion.android.widgets.button
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.graphics.Color
@ -85,10 +86,21 @@ class ButtonWidget : AppWidgetProvider() {
private fun updateAllWidgets(context: Context) {
mainScope.launch {
val buttonWidgetEntityList = buttonWidgetDao.getAll()
val appWidgetManager = AppWidgetManager.getInstance(context)
val systemWidgetIds = appWidgetManager.getAppWidgetIds(ComponentName(context, ButtonWidget::class.java))
val dbWidgetList = buttonWidgetDao.getAll()
val invalidWidgetIds = dbWidgetList
.filter { !systemWidgetIds.contains(it.id) }
.map { it.id }
if (invalidWidgetIds.isNotEmpty()) {
Log.i(TAG, "Found widgets $invalidWidgetIds in database, but not in AppWidgetManager - sending onDeleted")
onDeleted(context, invalidWidgetIds.toIntArray())
}
val buttonWidgetEntityList = dbWidgetList.filter { systemWidgetIds.contains(it.id) }
if (buttonWidgetEntityList.isNotEmpty()) {
Log.d(TAG, "Updating all widgets")
val appWidgetManager = AppWidgetManager.getInstance(context)
for (item in buttonWidgetEntityList) {
val views = getWidgetRemoteViews(context, item.id)

View file

@ -223,8 +223,6 @@ class ButtonWidgetConfigureActivity : BaseWidgetConfigureActivity(), IconDialog.
buttonWidget.textColor?.let { it.toColorInt() == ContextCompat.getColor(this, commonR.color.colorWidgetButtonLabelBlack) } ?: false
binding.addButton.setText(commonR.string.update_widget)
binding.deleteButton.visibility = VISIBLE
binding.deleteButton.setOnClickListener(onDeleteWidget)
} else {
binding.backgroundType.setSelection(0)
}

View file

@ -3,6 +3,7 @@ package io.homeassistant.companion.android.widgets.camera
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.net.ConnectivityManager
@ -84,10 +85,21 @@ class CameraWidget : AppWidgetProvider() {
private fun updateAllWidgets(context: Context) {
mainScope.launch {
val cameraWidgetList = cameraWidgetDao.getAll()
val appWidgetManager = AppWidgetManager.getInstance(context)
val systemWidgetIds = appWidgetManager.getAppWidgetIds(ComponentName(context, CameraWidget::class.java))
val dbWidgetList = cameraWidgetDao.getAll()
val invalidWidgetIds = dbWidgetList
.filter { !systemWidgetIds.contains(it.id) }
.map { it.id }
if (invalidWidgetIds.isNotEmpty()) {
Log.i(TAG, "Found widgets $invalidWidgetIds in database, but not in AppWidgetManager - sending onDeleted")
onDeleted(context, invalidWidgetIds.toIntArray())
}
val cameraWidgetList = dbWidgetList.filter { systemWidgetIds.contains(it.id) }
if (cameraWidgetList.isNotEmpty()) {
Log.d(TAG, "Updating all widgets")
val appWidgetManager = AppWidgetManager.getInstance(context)
for (item in cameraWidgetList) {
updateAppWidget(context, item.id, appWidgetManager)
}

View file

@ -98,8 +98,6 @@ class CameraWidgetConfigureActivity : BaseWidgetConfigureActivity() {
if (cameraWidget != null) {
binding.widgetTextConfigEntityId.setText(cameraWidget.entityId)
binding.addButton.setText(commonR.string.update_widget)
binding.deleteButton.visibility = View.VISIBLE
binding.deleteButton.setOnClickListener(onDeleteWidget)
val entity = runBlocking {
try {
integrationUseCase.getEntity(cameraWidget.entityId)

View file

@ -2,6 +2,7 @@ package io.homeassistant.companion.android.widgets.entity
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.graphics.Color
@ -46,6 +47,9 @@ class EntityWidget : BaseWidgetProvider() {
@Inject
lateinit var staticWidgetDao: StaticWidgetDao
override fun getWidgetProvider(context: Context): ComponentName =
ComponentName(context, EntityWidget::class.java)
override suspend fun getWidgetRemoteViews(context: Context, appWidgetId: Int, suggestedEntity: Entity<Map<String, Any>>?): RemoteViews {
val intent = Intent(context, EntityWidget::class.java).apply {
action = UPDATE_VIEW

View file

@ -166,8 +166,6 @@ class EntityWidgetConfigureActivity : BaseWidgetConfigureActivity() {
staticWidget.textColor?.let { it.toColorInt() == ContextCompat.getColor(this, commonR.color.colorWidgetButtonLabelBlack) } ?: false
binding.addButton.setText(commonR.string.update_widget)
binding.deleteButton.visibility = VISIBLE
binding.deleteButton.setOnClickListener(onDeleteWidget)
} else {
binding.backgroundType.setSelection(0)
}

View file

@ -2,6 +2,7 @@ package io.homeassistant.companion.android.widgets.media_player_controls
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
@ -71,6 +72,9 @@ class MediaPlayerControlsWidget : BaseWidgetProvider() {
@Inject
lateinit var mediaPlayCtrlWidgetDao: MediaPlayerControlsWidgetDao
override fun getWidgetProvider(context: Context): ComponentName =
ComponentName(context, MediaPlayerControlsWidget::class.java)
override fun onUpdate(
context: Context,
appWidgetManager: AppWidgetManager,

View file

@ -143,8 +143,6 @@ class MediaPlayerControlsWidgetConfigureActivity : BaseWidgetConfigureActivity()
if (entities != null)
selectedEntities.addAll(entities)
binding.addButton.setText(commonR.string.update_widget)
binding.deleteButton.visibility = View.VISIBLE
binding.deleteButton.setOnClickListener(onDeleteWidget)
}
val entityAdapter = SingleItemArrayAdapter<Entity<Any>>(this) { it?.entityId ?: "" }

View file

@ -2,6 +2,7 @@ package io.homeassistant.companion.android.widgets.template
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.graphics.Color
@ -46,6 +47,9 @@ class TemplateWidget : BaseWidgetProvider() {
}
}
override fun getWidgetProvider(context: Context): ComponentName =
ComponentName(context, TemplateWidget::class.java)
override suspend fun getAllWidgetIds(context: Context): List<Int> {
return templateWidgetDao.getAll().map { it.id }
}

View file

@ -114,9 +114,6 @@ class TemplateWidgetConfigureActivity : BaseWidgetConfigureActivity() {
templateWidget.textColor?.let { it.toColorInt() == ContextCompat.getColor(this, android.R.color.white) } ?: true
binding.textColorBlack.isChecked =
templateWidget.textColor?.let { it.toColorInt() == ContextCompat.getColor(this, commonR.color.colorWidgetButtonLabelBlack) } ?: false
binding.deleteButton.visibility = View.VISIBLE
binding.deleteButton.setOnClickListener(onDeleteWidget)
} else {
binding.backgroundType.setSelection(0)
}

View file

@ -171,14 +171,5 @@
android:layout_marginTop="8dp"
android:text="@string/add_widget" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/delete_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginTop="8dp"
android:visibility="gone"
android:text="@string/delete_widget" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>

View file

@ -46,14 +46,5 @@
android:layout_marginTop="8dp"
android:text="@string/add_widget" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/delete_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginTop="8dp"
android:visibility="gone"
android:text="@string/delete_widget" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>

View file

@ -130,13 +130,5 @@
android:layout_gravity="end"
android:layout_marginTop="8dp"
android:text="@string/add_widget" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/delete_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginTop="8dp"
android:visibility="gone"
android:text="@string/delete_widget" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>

View file

@ -235,15 +235,6 @@
android:layout_gravity="end"
android:layout_marginTop="8dp"
android:text="@string/add_widget" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/delete_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginTop="8dp"
android:visibility="gone"
android:text="@string/delete_widget" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>

View file

@ -118,15 +118,6 @@
android:layout_gravity="end"
android:layout_marginTop="8dp"
android:text="@string/add_widget" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/delete_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginTop="8dp"
android:visibility="gone"
android:text="@string/delete_widget" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>

View file

@ -119,8 +119,6 @@
<string name="confirm_delete_all_notification_title">Confirm deleting all notifications</string>
<string name="confirm_delete_this_notification_message">Are you sure? This cannot be undone</string>
<string name="confirm_delete_this_notification_title">Confirm deleting this notification</string>
<string name="confirm_delete_this_widget_message">Are you sure? This cannot be undone. If you accidentally deleted the wrong widget you will need to remove the bad widget from the home screen and create a new one.</string>
<string name="confirm_delete_this_widget_title">Confirm deleting this widget</string>
<string name="confirm_negative">NO</string>
<string name="confirm_positive">YES</string>
<string name="continue_on_phone">Continue on your phone</string>
@ -138,7 +136,6 @@
<string name="delete_all_notifications">Delete all notifications from history</string>
<string name="delete_shortcut">Delete Shortcut</string>
<string name="delete_this_notification">Delete this notification from history</string>
<string name="delete_widget">Delete Widget</string>
<string name="description">Description</string>
<string name="details">Details</string>
<string name="developer_tools">Developer Tools</string>