1 /* 2 * Copyright 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.glance.appwidget.demos 18 19 import android.appwidget.AppWidgetManager 20 import android.content.Context 21 import android.util.Log 22 import android.widget.RemoteViews 23 import androidx.compose.runtime.Composable 24 import androidx.compose.ui.graphics.Color 25 import androidx.compose.ui.unit.dp 26 import androidx.compose.ui.unit.sp 27 import androidx.glance.GlanceId 28 import androidx.glance.GlanceModifier 29 import androidx.glance.LocalSize 30 import androidx.glance.appwidget.GlanceAppWidget 31 import androidx.glance.appwidget.GlanceAppWidgetReceiver 32 import androidx.glance.appwidget.SizeMode 33 import androidx.glance.appwidget.background 34 import androidx.glance.appwidget.provideContent 35 import androidx.glance.layout.Alignment 36 import androidx.glance.layout.Box 37 import androidx.glance.layout.Column 38 import androidx.glance.layout.fillMaxSize 39 import androidx.glance.layout.fillMaxWidth 40 import androidx.glance.layout.padding 41 import androidx.glance.layout.wrapContentHeight 42 import androidx.glance.text.FontWeight 43 import androidx.glance.text.Text 44 import androidx.glance.text.TextAlign 45 import androidx.glance.text.TextStyle 46 import kotlin.math.roundToInt 47 48 /** 49 * Demonstrates different methods of handling errors. A widget can use either the default Glance 50 * error ui, provide a custom layout, or ignore the error and not make any change to the ui state. 51 */ 52 class ErrorUiAppWidget : GlanceAppWidget() { 53 override val sizeMode: SizeMode = SizeMode.Exact 54 provideGlancenull55 override suspend fun provideGlance(context: Context, id: GlanceId) { 56 val repo = ErrorUiAppWidgetConfigurationRepo(context = context, glanceId = id) 57 58 provideContent { 59 val errorBehavior = repo.getOnErrorBehavior() 60 61 Content(errorBehavior) 62 } 63 } 64 providePreviewnull65 override suspend fun providePreview(context: Context, widgetCategory: Int) { 66 provideContent { Content() } 67 } 68 69 @Composable Contentnull70 private fun Content( 71 errorBehavior: OnErrorBehavior = OnErrorBehavior.Default, 72 ) { 73 val size = LocalSize.current 74 Column( 75 modifier = 76 GlanceModifier.fillMaxSize() 77 .background(day = Color.LightGray, night = Color.DarkGray) 78 .padding(8.dp), 79 ) { 80 Text( 81 "Error UI Demo. Method: $errorBehavior", 82 modifier = GlanceModifier.fillMaxWidth().wrapContentHeight(), 83 style = 84 TextStyle( 85 fontWeight = FontWeight.Bold, 86 fontSize = 18.sp, 87 textAlign = TextAlign.Center 88 ) 89 ) 90 Box( 91 modifier = GlanceModifier.fillMaxWidth().defaultWeight(), 92 contentAlignment = Alignment.Center 93 ) { 94 Text( 95 "Error UI triggers if width or height reach 400 dp in any orientation.", 96 style = TextStyle(fontWeight = FontWeight.Medium, fontSize = 15.sp) 97 ) 98 check(size.width < 400.dp && size.height < 400.dp) { "Too large now!" } 99 } 100 Text( 101 " Current size: ${size.width.value.roundToInt()} dp x " + 102 "${size.height.value.roundToInt()} dp" 103 ) 104 } 105 } 106 onCompositionErrornull107 override fun onCompositionError( 108 context: Context, 109 glanceId: GlanceId, 110 appWidgetId: Int, 111 throwable: Throwable 112 ) { 113 fun showCustomError() { 114 // Optionally, a custom error view can also be created. 115 val rv = RemoteViews(context.packageName, R.layout.error_ui_app_widget_on_error_layout) 116 rv.setTextViewText( 117 R.id.error_text_view, 118 "Error was thrown. \nThis is a custom view \nError Message: `${throwable.message}`" 119 ) 120 AppWidgetManager.getInstance(context).updateAppWidget(appWidgetId, rv) 121 } 122 123 val repo = ErrorUiAppWidgetConfigurationRepo(context = context, glanceId = glanceId) 124 125 when (repo.getOnErrorBehavior()) { 126 OnErrorBehavior.Default -> 127 super.onCompositionError(context, glanceId, appWidgetId, throwable) 128 OnErrorBehavior.Custom -> showCustomError() 129 OnErrorBehavior.Ignore -> Unit // do nothing beyond the logging 130 } 131 132 // onCreateErrorLayout is a good place to perform logging. 133 Log.w("Error App", "onCreateErrorLayout called.") 134 } 135 } 136 137 class ErrorUiAppWidgetReceiver : GlanceAppWidgetReceiver() { 138 override val glanceAppWidget: GlanceAppWidget = ErrorUiAppWidget() 139 } 140