1 /*
2 * Copyright (C) 2022 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.settingslib.spa.slice
18
19 import android.app.Activity
20 import android.app.PendingIntent
21 import android.content.BroadcastReceiver
22 import android.content.ComponentName
23 import android.content.Context
24 import android.content.Intent
25 import android.net.Uri
26 import android.os.Bundle
27 import com.android.settingslib.spa.framework.common.SettingsEntry
28 import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
29 import com.android.settingslib.spa.framework.util.KEY_DESTINATION
30 import com.android.settingslib.spa.framework.util.KEY_HIGHLIGHT_ENTRY
31 import com.android.settingslib.spa.framework.util.SESSION_SLICE
32 import com.android.settingslib.spa.framework.util.SPA_INTENT_RESERVED_KEYS
33 import com.android.settingslib.spa.framework.util.appendSpaParams
34 import com.android.settingslib.spa.framework.util.getDestination
35 import com.android.settingslib.spa.framework.util.getEntryId
36
37 // Defines SliceUri, which contains special query parameters:
38 // -- KEY_DESTINATION: The route that this slice is navigated to.
39 // -- KEY_HIGHLIGHT_ENTRY: The entry id of this slice
40 // Other parameters can considered as runtime parameters.
41 // Use {entryId, runtimeParams} as the unique Id of this Slice.
42 typealias SliceUri = Uri
43
getEntryIdnull44 fun SliceUri.getEntryId(): String? {
45 return getQueryParameter(KEY_HIGHLIGHT_ENTRY)
46 }
47
getDestinationnull48 fun SliceUri.getDestination(): String? {
49 return getQueryParameter(KEY_DESTINATION)
50 }
51
getRuntimeArgumentsnull52 fun SliceUri.getRuntimeArguments(): Bundle {
53 val params = Bundle()
54 for (queryName in queryParameterNames) {
55 if (SPA_INTENT_RESERVED_KEYS.contains(queryName)) continue
56 params.putString(queryName, getQueryParameter(queryName))
57 }
58 return params
59 }
60
SliceUrinull61 fun SliceUri.getSliceId(): String? {
62 val entryId = getEntryId() ?: return null
63 val params = getRuntimeArguments()
64 return "${entryId}_$params"
65 }
66
appendSpaParamsnull67 fun Uri.Builder.appendSpaParams(
68 destination: String? = null,
69 entryId: String? = null,
70 runtimeArguments: Bundle? = null
71 ): Uri.Builder {
72 if (destination != null) appendQueryParameter(KEY_DESTINATION, destination)
73 if (entryId != null) appendQueryParameter(KEY_HIGHLIGHT_ENTRY, entryId)
74 if (runtimeArguments != null) {
75 for (key in runtimeArguments.keySet()) {
76 appendQueryParameter(key, runtimeArguments.getString(key, ""))
77 }
78 }
79 return this
80 }
81
fromEntrynull82 fun Uri.Builder.fromEntry(
83 entry: SettingsEntry,
84 authority: String?,
85 runtimeArguments: Bundle? = null
86 ): Uri.Builder {
87 if (authority == null) return this
88 val sp = entry.containerPage()
89 return scheme("content").authority(authority).appendSpaParams(
90 destination = sp.buildRoute(),
91 entryId = entry.id,
92 runtimeArguments = runtimeArguments
93 )
94 }
95
createBroadcastPendingIntentnull96 fun SliceUri.createBroadcastPendingIntent(): PendingIntent? {
97 val context = SpaEnvironmentFactory.instance.appContext
98 val sliceBroadcastClass =
99 SpaEnvironmentFactory.instance.sliceBroadcastReceiverClass ?: return null
100 val entryId = getEntryId() ?: return null
101 return createBroadcastPendingIntent(context, sliceBroadcastClass, entryId)
102 }
103
createBrowsePendingIntentnull104 fun SliceUri.createBrowsePendingIntent(): PendingIntent? {
105 val context = SpaEnvironmentFactory.instance.appContext
106 val browseActivityClass = SpaEnvironmentFactory.instance.browseActivityClass ?: return null
107 val destination = getDestination() ?: return null
108 val entryId = getEntryId()
109 return createBrowsePendingIntent(context, browseActivityClass, destination, entryId)
110 }
111
createBrowsePendingIntentnull112 fun Intent.createBrowsePendingIntent(): PendingIntent? {
113 val context = SpaEnvironmentFactory.instance.appContext
114 val browseActivityClass = SpaEnvironmentFactory.instance.browseActivityClass ?: return null
115 val destination = getDestination() ?: return null
116 val entryId = getEntryId()
117 return createBrowsePendingIntent(context, browseActivityClass, destination, entryId)
118 }
119
createBrowsePendingIntentnull120 private fun createBrowsePendingIntent(
121 context: Context,
122 browseActivityClass: Class<out Activity>,
123 destination: String,
124 entryId: String?
125 ): PendingIntent {
126 val intent = Intent().setComponent(ComponentName(context, browseActivityClass))
127 .appendSpaParams(destination, entryId, SESSION_SLICE)
128 .apply {
129 // Set both extra and data (which is a Uri) in Slice Intent:
130 // 1) extra is used in SPA navigation framework
131 // 2) data is used in Slice framework
132 data = Uri.Builder().appendSpaParams(destination, entryId).build()
133 flags = Intent.FLAG_ACTIVITY_NEW_TASK
134 }
135
136 return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
137 }
138
createBroadcastPendingIntentnull139 private fun createBroadcastPendingIntent(
140 context: Context,
141 sliceBroadcastClass: Class<out BroadcastReceiver>,
142 entryId: String
143 ): PendingIntent {
144 val intent = Intent().setComponent(ComponentName(context, sliceBroadcastClass))
145 .apply { data = Uri.Builder().appendSpaParams(entryId = entryId).build() }
146 return PendingIntent.getBroadcast(
147 context, 0 /* requestCode */, intent,
148 PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_MUTABLE
149 )
150 }
151