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.qs.pipeline.shared
18 
19 import android.content.ComponentName
20 import android.text.TextUtils
21 import com.android.systemui.qs.external.CustomTile
22 
23 /**
24  * Container for the spec that identifies a tile.
25  *
26  * A tile's [spec] is one of two options:
27  * * `custom(<componentName>)`: A [ComponentName] surrounded by [CustomTile.PREFIX] and terminated
28  *   by `)`, represents a tile provided by an app, corresponding to a `TileService`.
29  * * a string not starting with [CustomTile.PREFIX], representing a tile provided by SystemUI.
30  */
31 sealed class TileSpec private constructor(open val spec: String) {
32 
33     /** Represents a spec that couldn't be parsed into a valid type of tile. */
34     object Invalid : TileSpec("") {
35         override fun toString(): String {
36             return "TileSpec.INVALID"
37         }
38     }
39 
40     /** Container for the spec of a tile provided by SystemUI. */
41     data class PlatformTileSpec
42     internal constructor(
43         override val spec: String,
44     ) : TileSpec(spec)
45 
46     /**
47      * Container for the spec of a tile provided by an app.
48      *
49      * [componentName] indicates the associated `TileService`.
50      */
51     data class CustomTileSpec
52     internal constructor(
53         override val spec: String,
54         val componentName: ComponentName,
55     ) : TileSpec(spec) {
56         override fun toString(): String {
57             return "CustomTileSpec(${componentName.toShortString()})"
58         }
59     }
60 
61     companion object {
62         /** Create a [TileSpec] from the string [spec]. */
63         fun create(spec: String): TileSpec {
64             return if (TextUtils.isEmpty(spec)) {
65                 Invalid
66             } else if (!spec.isCustomTileSpec) {
67                 PlatformTileSpec(spec)
68             } else {
69                 spec.componentName?.let { CustomTileSpec(spec, it) } ?: Invalid
70             }
71         }
72 
73         fun create(component: ComponentName): CustomTileSpec {
74             return CustomTileSpec(CustomTile.toSpec(component), component)
75         }
76 
77         private val String.isCustomTileSpec: Boolean
78             get() = startsWith(CustomTile.PREFIX)
79 
80         private val String.componentName: ComponentName?
81             get() =
82                 if (!isCustomTileSpec) {
83                     null
84                 } else {
85                     if (endsWith(")")) {
86                         val extracted = substring(CustomTile.PREFIX.length, length - 1)
87                         ComponentName.unflattenFromString(extracted)
88                     } else {
89                         null
90                     }
91                 }
92     }
93 }
94