From 37055382df5f3738c0cce37a7677a4887de51638 Mon Sep 17 00:00:00 2001 From: Ivor Smorenburg Date: Tue, 17 Sep 2024 16:52:13 +0100 Subject: [PATCH] Addressed some comments --- app/src/main/AndroidManifest.xml | 6 +- .../android/HomeAssistantApplication.kt | 3 - .../android/widgets/button/ButtonWidget.kt | 147 +++++++++-- .../button/ButtonWidgetConfigureActivity.kt | 2 +- .../android/widgets/camera/CameraWidget.kt | 246 ++++++++++++------ .../camera/CameraWidgetConfigureActivity.kt | 2 +- .../widgets/template/TemplateWidget.kt | 214 ++++++++++++--- .../TemplateWidgetConfigureActivity.kt | 2 +- app/src/main/res/layout/widget_button.xml | 1 + app/src/main/res/layout/widget_camera.xml | 33 +-- .../main/res/layout/widget_media_controls.xml | 3 +- app/src/main/res/xml/entity_widget_info.xml | 4 +- automotive/src/main/AndroidManifest.xml | 6 +- .../companion/android/util/DisplayUtils.kt | 10 - 14 files changed, 492 insertions(+), 187 deletions(-) delete mode 100644 common/src/main/java/io/homeassistant/companion/android/util/DisplayUtils.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3e55c88586a..79842dffd10 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -112,7 +112,7 @@ - + - + @@ -174,7 +174,7 @@ - + >>() { +class ButtonWidget : AppWidgetProvider() { companion object { private const val TAG = "ButtonWidget" const val CALL_SERVICE = "io.homeassistant.companion.android.widgets.button.ButtonWidget.CALL_SERVICE" private const val CALL_SERVICE_AUTH = "io.homeassistant.companion.android.widgets.button.ButtonWidget.CALL_SERVICE_AUTH" + internal const val RECEIVE_DATA = + "io.homeassistant.companion.android.widgets.button.ButtonWidget.RECEIVE_DATA" internal const val EXTRA_SERVER_ID = "EXTRA_SERVER_ID" internal const val EXTRA_DOMAIN = "EXTRA_DOMAIN" @@ -64,9 +69,91 @@ class ButtonWidget : BaseWidgetProvider authThenCallConfiguredAction(context, appWidgetId) + CALL_SERVICE -> callConfiguredAction(context, appWidgetId) + RECEIVE_DATA -> saveActionCallConfiguration(context, intent.extras, appWidgetId) + Intent.ACTION_SCREEN_ON -> updateAllWidgets(context) + } + } private fun authThenCallConfiguredAction(context: Context, appWidgetId: Int) { Log.d(TAG, "Calling authentication, then configured action") @@ -77,13 +164,13 @@ class ButtonWidget : BaseWidgetProvider>?): RemoteViews { + private fun getWidgetRemoteViews(context: Context, appWidgetId: Int): RemoteViews { // Every time AppWidgetManager.updateAppWidget(...) is called, the button listener // and label need to be re-assigned, or the next time the layout updates // (e.g home screen rotation) the widget will fall back on its default layout // without any click listener being applied - val widget = repository.get(appWidgetId) + val widget = buttonWidgetDao.get(appWidgetId) val auth = widget?.requireAuthentication == true val intent = Intent(context, ButtonWidget::class.java).apply { @@ -99,7 +186,7 @@ class ButtonWidget : BaseWidgetProvider authThenCallConfiguredAction(context, appWidgetId) - CALL_SERVICE -> callConfiguredAction(context, appWidgetId) + private fun setWidgetBackground(views: RemoteViews, widget: ButtonWidgetEntity?) { + when (widget?.backgroundType) { + WidgetBackgroundType.TRANSPARENT -> { + views.setInt(R.id.widgetLayout, "setBackgroundColor", Color.TRANSPARENT) + } + else -> { + views.setInt(R.id.widgetLayout, "setBackgroundResource", R.drawable.widget_button_background) + } } } - override suspend fun getUpdates(serverId: Int, entityIds: List): Flow>> = serverManager.integrationRepository(serverId).getEntityUpdates(entityIds) as Flow>> - private fun setLabelVisibility(views: RemoteViews, widget: ButtonWidgetEntity?) { val labelVisibility = if (widget?.label.isNullOrBlank()) View.GONE else View.VISIBLE views.setViewVisibility(R.id.widgetLabelLayout, labelVisibility) @@ -186,9 +272,9 @@ class ButtonWidget : BaseWidgetProvider>() { +class CameraWidget : AppWidgetProvider() { companion object { private const val TAG = "CameraWidget" - + internal const val RECEIVE_DATA = + "io.homeassistant.companion.android.widgets.camera.CameraWidget.RECEIVE_DATA" internal const val UPDATE_IMAGE = "io.homeassistant.companion.android.widgets.camera.CameraWidget.UPDATE_IMAGE" - internal const val ENTITY_PICTURE_ATTRIBUTE = "entity_picture" - internal const val EXTRA_SERVER_ID = "EXTRA_SERVER_ID" internal const val EXTRA_ENTITY_ID = "EXTRA_ENTITY_ID" internal const val EXTRA_TAP_ACTION = "EXTRA_TAP_ACTION" + private var lastIntent = "" } - private var lastCameraBitmap: Bitmap? = null + @Inject + lateinit var serverManager: ServerManager + + @Inject + lateinit var cameraWidgetDao: CameraWidgetDao + + private val mainScope: CoroutineScope = CoroutineScope(Dispatchers.Main + Job()) + + override fun onUpdate( + context: Context, + appWidgetManager: AppWidgetManager, + appWidgetIds: IntArray + ) { + // There may be multiple widgets active, so update all of them + appWidgetIds.forEach { appWidgetId -> + updateAppWidget( + context, + appWidgetId, + appWidgetManager + ) + } + } + + private fun updateAppWidget( + context: Context, + appWidgetId: Int, + appWidgetManager: AppWidgetManager = AppWidgetManager.getInstance(context) + ) { + if (!context.hasActiveConnection()) { + Log.d(TAG, "Skipping widget update since network connection is not active") + return + } + mainScope.launch { + val views = getWidgetRemoteViews(context, appWidgetId) + appWidgetManager.updateAppWidget(appWidgetId, views) + } + } + + private fun updateAllWidgets(context: Context) { + mainScope.launch { + 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()) + } - override fun getWidgetProvider(context: Context): ComponentName = - ComponentName(context, CameraWidget::class.java) + val cameraWidgetList = dbWidgetList.filter { systemWidgetIds.contains(it.id) } + if (cameraWidgetList.isNotEmpty()) { + Log.d(TAG, "Updating all widgets") + for (item in cameraWidgetList) { + updateAppWidget(context, item.id, appWidgetManager) + } + } + } + } - override suspend fun getWidgetRemoteViews(context: Context, appWidgetId: Int, hasActiveConnection: Boolean, suggestedEntity: Entity<*>?): RemoteViews { + private suspend fun getWidgetRemoteViews(context: Context, appWidgetId: Int): RemoteViews { val updateCameraIntent = Intent(context, CameraWidget::class.java).apply { action = UPDATE_IMAGE putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) } return RemoteViews(context.packageName, R.layout.widget_camera).apply { - val widget = repository.get(appWidgetId) + val widget = cameraWidgetDao.get(appWidgetId) if (widget != null) { - val buildImageUrl = buildImageUrl(widget) - - setViewVisibility( - R.id.widgetCameraError, - if (buildImageUrl.isNullOrEmpty()) { + var entityPictureUrl: String? + try { + entityPictureUrl = retrieveCameraImageUrl(widget.serverId, widget.entityId) + setViewVisibility(R.id.widgetCameraError, View.GONE) + } catch (e: Exception) { + Log.e(TAG, "Failed to fetch entity or entity does not exist", e) + setViewVisibility(R.id.widgetCameraError, View.VISIBLE) + entityPictureUrl = null + } + val baseUrl = serverManager.getServer(widget.serverId)?.connection?.getUrl().toString().removeSuffix("/") + val url = "$baseUrl$entityPictureUrl" + if (entityPictureUrl == null) { + setImageViewResource( + R.id.widgetCameraImage, + R.drawable.app_icon_round + ) + setViewVisibility( + R.id.widgetCameraPlaceholder, View.VISIBLE - } else { + ) + setViewVisibility( + R.id.widgetCameraImage, + View.GONE + ) + } else { + setViewVisibility( + R.id.widgetCameraImage, + View.VISIBLE + ) + setViewVisibility( + R.id.widgetCameraPlaceholder, View.GONE + ) + Log.d(TAG, "Fetching camera image") + Handler(Looper.getMainLooper()).post { + val picasso = Picasso.get() + if (BuildConfig.DEBUG) { + picasso.isLoggingEnabled = true + } + try { + picasso.invalidate(url) + picasso.load(url).resize(getScreenWidth(), 0).onlyScaleDown().into( + this, + R.id.widgetCameraImage, + intArrayOf(appWidgetId) + ) + } catch (e: Exception) { + Log.e(TAG, "Unable to fetch image", e) + } + Log.d(TAG, "Fetch and load complete") } - ) - - updateBitmapCameraImage(context, hasActiveConnection, this, appWidgetId, buildImageUrl) + } val tapWidgetPendingIntent = when (widget.tapAction) { WidgetTapAction.OPEN -> PendingIntent.getActivity( @@ -83,43 +185,37 @@ class CameraWidget : BaseWidgetProvider>() { PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) } - setOnClickPendingIntent(R.id.widgetLayout, tapWidgetPendingIntent) + setOnClickPendingIntent(R.id.widgetCameraImage, tapWidgetPendingIntent) + setOnClickPendingIntent(R.id.widgetCameraPlaceholder, tapWidgetPendingIntent) } } } - private suspend fun buildImageUrl(cameraWidget: CameraWidgetEntity): String? { - val baseUrl = getServerUrl(cameraWidget.serverId) - val entityPictureUrl = retrieveCameraImageUrl(cameraWidget.serverId, cameraWidget.entityId) - entityPictureUrl?.let { - return "$baseUrl$entityPictureUrl" - } - return null - } - private suspend fun retrieveCameraImageUrl(serverId: Int, entityId: String): String? { - val entity: Entity>? - try { - entity = serverManager.integrationRepository(serverId).getEntity(entityId) - return entity?.attributes?.get(ENTITY_PICTURE_ATTRIBUTE)?.toString() - } catch (e: Exception) { - return null - } - } - - private fun getServerUrl(serverId: Int): String { - return serverManager.getServer(serverId)?.connection?.getUrl().toString().removeSuffix("/") + val entity = serverManager.integrationRepository(serverId).getEntity(entityId) + return entity?.attributes?.get("entity_picture")?.toString() } override fun onReceive(context: Context, intent: Intent) { - super.onReceive(context, intent) + lastIntent = intent.action.toString() val appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) + + Log.d( + TAG, + "Broadcast received: " + System.lineSeparator() + + "Broadcast action: " + lastIntent + System.lineSeparator() + + "AppWidgetId: " + appWidgetId + ) + + super.onReceive(context, intent) when (lastIntent) { - UPDATE_IMAGE -> forceUpdateView(context, appWidgetId) + RECEIVE_DATA -> saveEntityConfiguration(context, intent.extras, appWidgetId) + UPDATE_IMAGE -> updateAppWidget(context, appWidgetId) + Intent.ACTION_SCREEN_ON -> updateAllWidgets(context) } } - override fun saveEntityConfiguration(context: Context, extras: Bundle?, appWidgetId: Int) { + private fun saveEntityConfiguration(context: Context, extras: Bundle?, appWidgetId: Int) { if (extras == null) return val serverSelection = if (extras.containsKey(EXTRA_SERVER_ID)) extras.getInt(EXTRA_SERVER_ID) else null @@ -132,13 +228,13 @@ class CameraWidget : BaseWidgetProvider>() { return } - widgetScope?.launch { + mainScope.launch { Log.d( TAG, "Saving camera config data:" + System.lineSeparator() + "entity id: " + entitySelection + System.lineSeparator() ) - repository.add( + cameraWidgetDao.add( CameraWidgetEntity( appWidgetId, serverSelection, @@ -146,43 +242,27 @@ class CameraWidget : BaseWidgetProvider>() { tapActionSelection ) ) - forceUpdateView(context, appWidgetId) + + onUpdate(context, AppWidgetManager.getInstance(context), intArrayOf(appWidgetId)) } } - private fun updateBitmapCameraImage(context: Context, hasActiveConnection: Boolean, views: RemoteViews, appWidgetId: Int, url: String?) { - if (hasActiveConnection && !url.isNullOrEmpty()) { - widgetWorkScope?.launch { - val picasso = Picasso.get() - picasso.isLoggingEnabled = BuildConfig.DEBUG - try { - picasso.invalidate(url) - lastCameraBitmap = picasso.load(url) - .stableKey(url) - .resize(getScreenWidth(), 0) - .onlyScaleDown().get() - - widgetScope?.launch { - views.setViewVisibility(R.id.widgetCameraImage, View.VISIBLE) - views.setViewVisibility(R.id.widgetCameraError, View.GONE) - views.setViewVisibility(R.id.widgetCameraPlaceholder, View.GONE) - views.setImageViewBitmap(R.id.widgetCameraImage, lastCameraBitmap) - AppWidgetManager.getInstance(context).partiallyUpdateAppWidget(appWidgetId, views) - Log.d(TAG, "Fetch and load complete") - } - } catch (e: Exception) { - Log.e(TAG, "Unable to fetch image", e) - } - } - } else { - widgetScope?.launch { - lastCameraBitmap?.let { - views.setImageViewBitmap(R.id.widgetCameraImage, it) - AppWidgetManager.getInstance(context).partiallyUpdateAppWidget(appWidgetId, views) - } - } + override fun onDeleted(context: Context, appWidgetIds: IntArray) { + // When the user deletes the widget, delete the preference associated with it. + mainScope.launch { + cameraWidgetDao.deleteAll(appWidgetIds) } } - override suspend fun getUpdates(serverId: Int, entityIds: List): Flow>> = serverManager.integrationRepository(serverId).getEntityUpdates(entityIds) as Flow>> + override fun onEnabled(context: Context) { + // Enter relevant functionality for when the first widget is created + } + + override fun onDisabled(context: Context) { + // Enter relevant functionality for when the last widget is disabled + } + + private fun getScreenWidth(): Int { + return Resources.getSystem().displayMetrics.widthPixels + } } diff --git a/app/src/main/java/io/homeassistant/companion/android/widgets/camera/CameraWidgetConfigureActivity.kt b/app/src/main/java/io/homeassistant/companion/android/widgets/camera/CameraWidgetConfigureActivity.kt index 34753b57baf..2b4f42bf9a1 100755 --- a/app/src/main/java/io/homeassistant/companion/android/widgets/camera/CameraWidgetConfigureActivity.kt +++ b/app/src/main/java/io/homeassistant/companion/android/widgets/camera/CameraWidgetConfigureActivity.kt @@ -187,7 +187,7 @@ class CameraWidgetConfigureActivity : BaseWidgetConfigureActivity() { - +class TemplateWidget : AppWidgetProvider() { companion object { private const val TAG = "TemplateWidget" + const val UPDATE_VIEW = + "io.homeassistant.companion.android.widgets.template.TemplateWidget.UPDATE_VIEW" + const val RECEIVE_DATA = + "io.homeassistant.companion.android.widgets.template.TemplateWidget.RECEIVE_DATA" + internal const val EXTRA_SERVER_ID = "EXTRA_SERVER_ID" internal const val EXTRA_TEMPLATE = "extra_template" internal const val EXTRA_TEXT_SIZE = "EXTRA_TEXT_SIZE" internal const val EXTRA_BACKGROUND_TYPE = "EXTRA_BACKGROUND_TYPE" internal const val EXTRA_TEXT_COLOR = "EXTRA_TEXT_COLOR" + + private var widgetScope: CoroutineScope? = null + private val widgetTemplates = mutableMapOf() + private val widgetJobs = mutableMapOf() + } + + @Inject + lateinit var serverManager: ServerManager + + @Inject + lateinit var templateWidgetDao: TemplateWidgetDao + + private var thisSetScope = false + private var lastIntent = "" + + init { + setupWidgetScope() + } + + private fun setupWidgetScope() { + if (widgetScope == null || !widgetScope!!.isActive) { + widgetScope = CoroutineScope(Dispatchers.Main + Job()) + thisSetScope = true + } + } + + override fun onUpdate( + context: Context, + appWidgetManager: AppWidgetManager, + appWidgetIds: IntArray + ) { + // There may be multiple widgets active, so update all of them + for (appWidgetId in appWidgetIds) { + widgetScope?.launch { + val views = getWidgetRemoteViews(context, appWidgetId) + appWidgetManager.updateAppWidget(appWidgetId, views) + } + } + } + + override fun onDeleted(context: Context, appWidgetIds: IntArray) { + // When the user deletes the widget, delete the preference associated with it. + widgetScope?.launch { + templateWidgetDao.deleteAll(appWidgetIds) + appWidgetIds.forEach { + widgetTemplates.remove(it) + widgetJobs[it]?.cancel() + widgetJobs.remove(it) + } + } + } + + override fun onReceive(context: Context, intent: Intent) { + lastIntent = intent.action.toString() + val appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) + + super.onReceive(context, intent) + when (lastIntent) { + UPDATE_VIEW -> updateView(context, appWidgetId) + RECEIVE_DATA -> { + saveEntityConfiguration( + context, + intent.extras, + appWidgetId + ) + onScreenOn(context) + } + Intent.ACTION_SCREEN_ON -> onScreenOn(context) + Intent.ACTION_SCREEN_OFF -> onScreenOff() + } + } + + private fun onScreenOn(context: Context) { + setupWidgetScope() + if (!serverManager.isRegistered()) return + widgetScope!!.launch { + updateAllWidgets(context) + + val allWidgets = templateWidgetDao.getAll() + val widgetsWithDifferentTemplate = allWidgets.filter { it.template != widgetTemplates[it.id] } + if (widgetsWithDifferentTemplate.isNotEmpty()) { + if (thisSetScope) { + ContextCompat.registerReceiver( + context.applicationContext, + this@TemplateWidget, + IntentFilter(Intent.ACTION_SCREEN_OFF), + ContextCompat.RECEIVER_NOT_EXPORTED + ) + } + + widgetsWithDifferentTemplate.forEach { widget -> + widgetJobs[widget.id]?.cancel() + + val templateUpdates = + if (serverManager.getServer(widget.serverId) != null) { + serverManager.integrationRepository(widget.serverId).getTemplateUpdates(widget.template) + } else { + null + } + if (templateUpdates != null) { + widgetTemplates[widget.id] = widget.template + widgetJobs[widget.id] = widgetScope!!.launch { + templateUpdates.collect { + onTemplateChanged(context, widget.id, it) + } + } + } else { // Remove data to make it retry on the next update + widgetTemplates.remove(widget.id) + widgetJobs.remove(widget.id) + } + } + } + } } - override fun getWidgetProvider(context: Context): ComponentName = - ComponentName(context, TemplateWidget::class.java) + private fun onScreenOff() { + if (thisSetScope) { + widgetScope?.cancel() + thisSetScope = false + widgetTemplates.clear() + widgetJobs.clear() + } + } - override suspend fun getWidgetRemoteViews(context: Context, appWidgetId: Int, hasActiveConnection: Boolean, suggestedEntity: String?): RemoteViews { + private suspend fun updateAllWidgets( + context: Context + ) { + val systemWidgetIds = AppWidgetManager.getInstance(context) + .getAppWidgetIds(ComponentName(context, TemplateWidget::class.java)) + .toSet() + val dbWidgetIds = templateWidgetDao.getAll().map { it.id } + + val invalidWidgetIds = dbWidgetIds.minus(systemWidgetIds) + if (invalidWidgetIds.isNotEmpty()) { + Log.i(TAG, "Found widgets $invalidWidgetIds in database, but not in AppWidgetManager - sending onDeleted") + onDeleted(context, invalidWidgetIds.toIntArray()) + } + + dbWidgetIds.filter { systemWidgetIds.contains(it) }.forEach { + updateView(context, it) + } + } + + private fun updateView( + context: Context, + appWidgetId: Int, + appWidgetManager: AppWidgetManager = AppWidgetManager.getInstance(context) + ) { + widgetScope?.launch { + val views = getWidgetRemoteViews(context, appWidgetId) + appWidgetManager.updateAppWidget(appWidgetId, views) + } + } + + private suspend fun getWidgetRemoteViews(context: Context, appWidgetId: Int, suggestedTemplate: String? = null): RemoteViews { // Every time AppWidgetManager.updateAppWidget(...) is called, the button listener // and label need to be re-assigned, or the next time the layout updates // (e.g home screen rotation) the widget will fall back on its default layout @@ -53,7 +214,7 @@ class TemplateWidget : BaseWidgetProvider() { putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) } - val widget = repository.get(appWidgetId) + val widget = templateWidgetDao.get(appWidgetId) val useDynamicColors = widget?.backgroundType == WidgetBackgroundType.DYNAMICCOLOR && DynamicColors.isDynamicColorAvailable() return RemoteViews(context.packageName, if (useDynamicColors) R.layout.widget_template_wrapper_dynamiccolor else R.layout.widget_template_wrapper_default).apply { @@ -72,16 +233,15 @@ class TemplateWidget : BaseWidgetProvider() { var textColor = context.getAttribute(R.attr.colorWidgetOnBackground, ContextCompat.getColor(context, commonR.color.colorWidgetButtonLabel)) widget.textColor?.let { textColor = it.toColorInt() } + setInt(R.id.widgetLayout, "setBackgroundColor", Color.TRANSPARENT) setTextColor(R.id.widgetTemplateText, textColor) } - setWidgetBackground(this, R.id.widgetLayout, widget) - // Content - var renderedTemplate: String? = repository.get(appWidgetId)?.lastUpdate ?: context.getString(commonR.string.loading) + var renderedTemplate: String? = templateWidgetDao.get(appWidgetId)?.lastUpdate ?: context.getString(commonR.string.loading) try { - renderedTemplate = suggestedEntity ?: serverManager.integrationRepository(widget.serverId).renderTemplate(widget.template, mapOf()).toString() - repository.updateWidgetLastUpdate( + renderedTemplate = suggestedTemplate ?: serverManager.integrationRepository(widget.serverId).renderTemplate(widget.template, mapOf()).toString() + templateWidgetDao.updateTemplateWidgetLastUpdate( appWidgetId, renderedTemplate ) @@ -105,19 +265,7 @@ class TemplateWidget : BaseWidgetProvider() { } } - override fun onWidgetsViewUpdated(context: Context, appWidgetId: Int, appWidgetManager: AppWidgetManager, remoteViews: RemoteViews, hasActiveConnection: Boolean) { - super.onWidgetsViewUpdated(context, appWidgetId, appWidgetManager, remoteViews, hasActiveConnection) - remoteViews.setViewVisibility( - R.id.widgetTemplateError, - if (!hasActiveConnection) View.VISIBLE else View.GONE - ) - } - - override suspend fun getUpdates(serverId: Int, entityIds: List): Flow? { - return serverManager.integrationRepository(serverId).getTemplateUpdates(entityIds.first()) - } - - override fun saveEntityConfiguration(context: Context, extras: Bundle?, appWidgetId: Int) { + private fun saveEntityConfiguration(context: Context, extras: Bundle?, appWidgetId: Int) { if (extras == null) return val serverId = if (extras.containsKey(EXTRA_SERVER_ID)) extras.getInt(EXTRA_SERVER_ID) else null @@ -133,18 +281,26 @@ class TemplateWidget : BaseWidgetProvider() { } widgetScope?.launch { - repository.add( + templateWidgetDao.add( TemplateWidgetEntity( appWidgetId, serverId, "template_$appWidgetId", template, textSize, - repository.get(appWidgetId)?.lastUpdate ?: ContextCompat.getString(context, commonR.string.loading), + templateWidgetDao.get(appWidgetId)?.lastUpdate ?: "Loading", backgroundTypeSelection, textColorSelection ) ) + onUpdate(context, AppWidgetManager.getInstance(context), intArrayOf(appWidgetId)) + } + } + + private fun onTemplateChanged(context: Context, appWidgetId: Int, template: String?) { + widgetScope?.launch { + val views = getWidgetRemoteViews(context, appWidgetId, template) + AppWidgetManager.getInstance(context).updateAppWidget(appWidgetId, views) } } } diff --git a/app/src/main/java/io/homeassistant/companion/android/widgets/template/TemplateWidgetConfigureActivity.kt b/app/src/main/java/io/homeassistant/companion/android/widgets/template/TemplateWidgetConfigureActivity.kt index 41a373d82ee..78af90b234a 100644 --- a/app/src/main/java/io/homeassistant/companion/android/widgets/template/TemplateWidgetConfigureActivity.kt +++ b/app/src/main/java/io/homeassistant/companion/android/widgets/template/TemplateWidgetConfigureActivity.kt @@ -167,7 +167,7 @@ class TemplateWidgetConfigureActivity : BaseWidgetConfigureActivity - - - - - - + android:layout_height="match_parent" + android:layout_marginStart="4dp" + android:layout_marginTop="4dp" + android:layout_marginBottom="4dp" + android:src="@drawable/app_icon_round" + android:contentDescription="@string/widget_camera_contentdescription" + android:scaleType="fitCenter" /> + android:layout_gravity="center_horizontal|bottom" + android:visibility="gone"/> \ No newline at end of file diff --git a/app/src/main/res/layout/widget_media_controls.xml b/app/src/main/res/layout/widget_media_controls.xml index 28ef61d4fcf..5e90b7d0122 100644 --- a/app/src/main/res/layout/widget_media_controls.xml +++ b/app/src/main/res/layout/widget_media_controls.xml @@ -33,14 +33,13 @@ android:id="@+id/widgetMediaPlaceholder" android:layout_width="wrap_content" android:layout_height="match_parent" - android:padding="30dp" android:layout_marginStart="4dp" android:layout_marginTop="4dp" android:layout_marginBottom="4dp" android:contentDescription="@string/widget_media_image_description" android:adjustViewBounds="true" android:scaleType="fitCenter" - android:src="@android:drawable/ic_media_play" /> + android:src="@drawable/app_icon_round" /> - + - + @@ -182,7 +182,7 @@ - +