1 /* 2 * Copyright (C) 2023 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 com.android.systemui.communal.domain.model 18 19 import android.appwidget.AppWidgetProviderInfo 20 import android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE 21 import android.content.ComponentName 22 import android.content.pm.ApplicationInfo 23 import android.graphics.Bitmap 24 import android.widget.RemoteViews 25 import com.android.systemui.Flags.communalResponsiveGrid 26 import com.android.systemui.communal.shared.model.CommunalContentSize 27 import java.util.UUID 28 29 /** Encapsulates data for a communal content. */ 30 sealed interface CommunalContentModel { 31 /** Unique key across all types of content models. */ 32 val key: String 33 34 /** Size to be rendered in the grid. */ 35 val size: CommunalContentSize 36 37 /** The minimum size content can be resized to. */ 38 val minSize: CommunalContentSize 39 get() = fixedHalfOrResponsiveSize() 40 41 /** 42 * A type of communal content is ongoing / live / ephemeral, and can be sized and ordered 43 * dynamically. 44 */ 45 sealed interface Ongoing : CommunalContentModel { 46 override var size: CommunalContentSize 47 override val minSize 48 get() = 49 if (communalResponsiveGrid()) { 50 CommunalContentSize.Responsive(1) 51 } else { 52 CommunalContentSize.FixedSize.THIRD 53 } 54 55 /** Timestamp in milliseconds of when the content was created. */ 56 val createdTimestampMillis: Long 57 } 58 59 sealed interface WidgetContent : CommunalContentModel { 60 val appWidgetId: Int 61 val rank: Int 62 val componentName: ComponentName 63 64 data class Widget( 65 override val appWidgetId: Int, 66 override val rank: Int, 67 val providerInfo: AppWidgetProviderInfo, 68 val inQuietMode: Boolean, 69 override val size: CommunalContentSize, 70 ) : WidgetContent { 71 override val key = KEY.widget(appWidgetId) 72 override val componentName: ComponentName = providerInfo.provider 73 74 /** Whether this widget can be reconfigured after it has already been added. */ 75 val reconfigurable: Boolean 76 get() = 77 (providerInfo.widgetFeatures and WIDGET_FEATURE_RECONFIGURABLE != 0) && 78 providerInfo.configure != null 79 } 80 81 data class DisabledWidget( 82 override val appWidgetId: Int, 83 override val rank: Int, 84 val providerInfo: AppWidgetProviderInfo, 85 override val size: CommunalContentSize, 86 ) : WidgetContent { 87 override val key = KEY.disabledWidget(appWidgetId) 88 override val componentName: ComponentName = providerInfo.provider 89 90 val appInfo: ApplicationInfo? 91 get() = providerInfo.providerInfo?.applicationInfo 92 } 93 94 data class PendingWidget( 95 override val appWidgetId: Int, 96 override val rank: Int, 97 override val componentName: ComponentName, 98 override val size: CommunalContentSize, 99 val icon: Bitmap? = null, 100 ) : WidgetContent { 101 override val key = KEY.pendingWidget(appWidgetId) 102 } 103 } 104 105 /** A placeholder item representing a new widget being added */ 106 class WidgetPlaceholder : CommunalContentModel { 107 override val key: String = KEY.widgetPlaceholder() 108 // Same as widget size. 109 override val size: CommunalContentSize 110 get() = fixedHalfOrResponsiveSize() 111 } 112 113 /** An empty spacer to reserve space in the grid. */ 114 data class Spacer(override val size: CommunalContentSize) : CommunalContentModel { 115 override val key: String = KEY.spacer() 116 } 117 118 /** A CTA tile in the glanceable hub view mode which can be dismissed. */ 119 class CtaTileInViewMode : CommunalContentModel { 120 override val key: String = KEY.CTA_TILE_IN_VIEW_MODE_KEY 121 // Same as widget size. 122 override val size: CommunalContentSize 123 get() = fixedHalfOrResponsiveSize() 124 } 125 126 class Tutorial(id: Int, override var size: CommunalContentSize) : CommunalContentModel { 127 override val key = KEY.tutorial(id) 128 } 129 130 class Smartspace( 131 smartspaceTargetId: String, 132 val remoteViews: RemoteViews, 133 override val createdTimestampMillis: Long, 134 override var size: CommunalContentSize = fixedHalfOrResponsiveSize(), 135 ) : Ongoing { 136 override val key = KEY.smartspace(smartspaceTargetId) 137 } 138 139 class Umo( 140 override val createdTimestampMillis: Long, 141 override var size: CommunalContentSize = fixedHalfOrResponsiveSize(), 142 override var minSize: CommunalContentSize = fixedHalfOrResponsiveSize(), 143 ) : Ongoing { 144 override val key = KEY.umo() 145 } 146 147 class KEY { 148 companion object { 149 const val CTA_TILE_IN_VIEW_MODE_KEY = "cta_tile_in_view_mode" 150 const val CTA_TILE_IN_EDIT_MODE_KEY = "cta_tile_in_edit_mode" 151 widgetnull152 fun widget(id: Int): String { 153 return "widget_$id" 154 } 155 disabledWidgetnull156 fun disabledWidget(id: Int): String { 157 return "disabled_widget_$id" 158 } 159 pendingWidgetnull160 fun pendingWidget(id: Int): String { 161 return "pending_widget_$id" 162 } 163 widgetPlaceholdernull164 fun widgetPlaceholder(): String { 165 return "widget_placeholder_${UUID.randomUUID()}" 166 } 167 tutorialnull168 fun tutorial(id: Int): String { 169 return "tutorial_$id" 170 } 171 smartspacenull172 fun smartspace(id: String): String { 173 return "smartspace_$id" 174 } 175 umonull176 fun umo(): String { 177 return "umo" 178 } 179 spacernull180 fun spacer(): String { 181 return "spacer_${UUID.randomUUID()}" 182 } 183 } 184 } 185 isWidgetContentnull186 fun isWidgetContent() = this is WidgetContent 187 188 fun isLiveContent() = this is Smartspace || this is Umo 189 } 190 191 private fun fixedHalfOrResponsiveSize() = 192 if (communalResponsiveGrid()) { 193 CommunalContentSize.Responsive(1) 194 } else { 195 CommunalContentSize.FixedSize.HALF 196 } 197