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.pm.ApplicationInfo
22 import android.graphics.Bitmap
23 import android.widget.RemoteViews
24 import com.android.systemui.communal.shared.model.CommunalContentSize
25 import com.android.systemui.communal.widgets.CommunalAppWidgetHost
26 import java.util.UUID
27 
28 /** Encapsulates data for a communal content. */
29 sealed interface CommunalContentModel {
30     /** Unique key across all types of content models. */
31     val key: String
32 
33     /** Size to be rendered in the grid. */
34     val size: CommunalContentSize
35 
36     /**
37      * A type of communal content is ongoing / live / ephemeral, and can be sized and ordered
38      * dynamically.
39      */
40     sealed interface Ongoing : CommunalContentModel {
41         override var size: CommunalContentSize
42 
43         /** Timestamp in milliseconds of when the content was created. */
44         val createdTimestampMillis: Long
45     }
46 
47     sealed interface WidgetContent : CommunalContentModel {
48         val appWidgetId: Int
49 
50         data class Widget(
51             override val appWidgetId: Int,
52             val providerInfo: AppWidgetProviderInfo,
53             val appWidgetHost: CommunalAppWidgetHost,
54             val inQuietMode: Boolean,
55         ) : WidgetContent {
56             override val key = KEY.widget(appWidgetId)
57             // Widget size is always half.
58             override val size = CommunalContentSize.HALF
59 
60             /** Whether this widget can be reconfigured after it has already been added. */
61             val reconfigurable: Boolean
62                 get() =
63                     (providerInfo.widgetFeatures and WIDGET_FEATURE_RECONFIGURABLE != 0) &&
64                         providerInfo.configure != null
65         }
66 
67         data class DisabledWidget(
68             override val appWidgetId: Int,
69             val providerInfo: AppWidgetProviderInfo
70         ) : WidgetContent {
71             override val key = KEY.disabledWidget(appWidgetId)
72             // Widget size is always half.
73             override val size = CommunalContentSize.HALF
74 
75             val appInfo: ApplicationInfo?
76                 get() = providerInfo.providerInfo?.applicationInfo
77         }
78 
79         data class PendingWidget(
80             override val appWidgetId: Int,
81             val packageName: String,
82             val icon: Bitmap? = null,
83         ) : WidgetContent {
84             override val key = KEY.pendingWidget(appWidgetId)
85             // Widget size is always half.
86             override val size = CommunalContentSize.HALF
87         }
88     }
89 
90     /** A placeholder item representing a new widget being added */
91     class WidgetPlaceholder : CommunalContentModel {
92         override val key: String = KEY.widgetPlaceholder()
93         // Same as widget size.
94         override val size = CommunalContentSize.HALF
95     }
96 
97     /** A CTA tile in the glanceable hub view mode which can be dismissed. */
98     class CtaTileInViewMode : CommunalContentModel {
99         override val key: String = KEY.CTA_TILE_IN_VIEW_MODE_KEY
100         // Same as widget size.
101         override val size = CommunalContentSize.HALF
102     }
103 
104     class Tutorial(
105         id: Int,
106         override var size: CommunalContentSize,
107     ) : CommunalContentModel {
108         override val key = KEY.tutorial(id)
109     }
110 
111     class Smartspace(
112         smartspaceTargetId: String,
113         val remoteViews: RemoteViews,
114         override val createdTimestampMillis: Long,
115         override var size: CommunalContentSize = CommunalContentSize.HALF,
116     ) : Ongoing {
117         override val key = KEY.smartspace(smartspaceTargetId)
118     }
119 
120     class Umo(
121         override val createdTimestampMillis: Long,
122         override var size: CommunalContentSize = CommunalContentSize.HALF,
123     ) : Ongoing {
124         override val key = KEY.umo()
125     }
126 
127     class KEY {
128         companion object {
129             const val CTA_TILE_IN_VIEW_MODE_KEY = "cta_tile_in_view_mode"
130             const val CTA_TILE_IN_EDIT_MODE_KEY = "cta_tile_in_edit_mode"
131 
widgetnull132             fun widget(id: Int): String {
133                 return "widget_$id"
134             }
135 
disabledWidgetnull136             fun disabledWidget(id: Int): String {
137                 return "disabled_widget_$id"
138             }
139 
pendingWidgetnull140             fun pendingWidget(id: Int): String {
141                 return "pending_widget_$id"
142             }
143 
widgetPlaceholdernull144             fun widgetPlaceholder(): String {
145                 return "widget_placeholder_${UUID.randomUUID()}"
146             }
147 
tutorialnull148             fun tutorial(id: Int): String {
149                 return "tutorial_$id"
150             }
151 
smartspacenull152             fun smartspace(id: String): String {
153                 return "smartspace_$id"
154             }
155 
umonull156             fun umo(): String {
157                 return "umo"
158             }
159         }
160     }
161 
isWidgetContentnull162     fun isWidgetContent() = this is WidgetContent
163 
164     fun isLiveContent() = this is Smartspace || this is Umo
165 }
166