• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.settings.activityembedding
18 
19 import android.app.Activity
20 import android.content.ActivityNotFoundException
21 import android.content.Context
22 import android.content.Intent
23 import android.content.pm.UserInfo
24 import android.provider.Settings
25 import android.util.Log
26 import com.android.settings.SettingsActivity
27 import com.android.settings.SettingsActivity.EXTRA_IS_DEEPLINK_HOME_STARTED_FROM_SEARCH
28 import com.android.settings.Utils
29 import com.android.settings.flags.Flags
30 import com.android.settings.homepage.DeepLinkHomepageActivityInternal
31 import com.android.settings.homepage.SettingsHomepageActivity
32 import com.android.settings.password.PasswordUtils
33 import com.android.settingslib.spaprivileged.framework.common.userManager
34 
35 object EmbeddedDeepLinkUtils {
36     private const val TAG = "EmbeddedDeepLinkUtils"
37 
38     @JvmStatic
Contextnull39     fun Context.tryStartMultiPaneDeepLink(
40         intent: Intent,
41         highlightMenuKey: String? = null,
42     ): Boolean {
43         intent.putExtra(
44             SettingsActivity.EXTRA_INITIAL_CALLING_PACKAGE,
45             PasswordUtils.getCallingAppPackageName(activityToken),
46         )
47         val trampolineIntent: Intent
48         if (intent.getBooleanExtra(SettingsActivity.EXTRA_IS_FROM_SLICE, false)) {
49             // Get menu key for slice deep link case.
50             var sliceHighlightMenuKey: String? = intent.getStringExtra(
51                 Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY
52             )
53             if (sliceHighlightMenuKey.isNullOrEmpty()) {
54                 sliceHighlightMenuKey = highlightMenuKey
55             }
56             trampolineIntent = getTrampolineIntent(intent, sliceHighlightMenuKey)
57             trampolineIntent.setClass(this, DeepLinkHomepageActivityInternal::class.java)
58         } else {
59             trampolineIntent = getTrampolineIntent(intent, highlightMenuKey)
60         }
61         return startTrampolineIntent(trampolineIntent)
62     }
63 
64     /**
65      * Returns the deep link trampoline intent for large screen devices.
66      */
67     @JvmStatic
getTrampolineIntentnull68     fun getTrampolineIntent(intent: Intent, highlightMenuKey: String?): Intent {
69         val detailIntent = Intent(intent)
70         // Guard against the arbitrary Intent injection.
71         if (detailIntent.selector != null) {
72             detailIntent.setSelector(null)
73         }
74         // It's a deep link intent, SettingsHomepageActivity will set SplitPairRule and start it.
75         return Intent(Settings.ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY).apply {
76             setPackage(Utils.SETTINGS_PACKAGE_NAME)
77             replaceExtras(detailIntent)
78 
79             // Relay detail intent data to prevent failure of Intent#ParseUri.
80             // If Intent#getData() is not null, Intent#toUri will return an Uri which has the scheme
81             // of Intent#getData() and it may not be the scheme of an Intent.
82             putExtra(
83                 SettingsHomepageActivity.EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_DATA,
84                 detailIntent.data
85             )
86             detailIntent.setData(null)
87             putExtra(
88                 Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI,
89                 detailIntent.toUri(Intent.URI_INTENT_SCHEME)
90             )
91             putExtra(
92                 Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY,
93                 highlightMenuKey
94             )
95             addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT)
96         }
97     }
98 
99     /**
100      * Returns the deep link trampoline intent for settings search results for large screen devices.
101      */
102     @JvmStatic
getTrampolineIntentForSearchResultnull103     fun getTrampolineIntentForSearchResult(
104         context: Context,
105         intent: Intent,
106         highlightMenuKey: String?
107     ): Intent {
108         return getTrampolineIntent(intent, highlightMenuKey).apply {
109             if (Flags.settingsSearchResultDeepLinkInSameTask()) {
110                 // Ensure the deep link intent does not include FLAG_ACTIVITY_NEW_TASK which
111                 // causes the search result deep link to open in a separate window.
112                 removeFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
113                 putExtra(EXTRA_IS_DEEPLINK_HOME_STARTED_FROM_SEARCH, true)
114             } else {
115                 addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
116             }
117 
118             setClass(context, DeepLinkHomepageActivityInternal::class.java)
119         }
120     }
121 
122     /**
123      * Returns whether the user is a sub profile.
124      */
125     @JvmStatic
isSubProfilenull126     fun isSubProfile(userInfo: UserInfo): Boolean =
127         userInfo.isManagedProfile || userInfo.isPrivateProfile
128 
129     private fun Context.startTrampolineIntent(trampolineIntent: Intent): Boolean = try {
130         val userInfo = userManager.getUserInfo(user.identifier)
131         if (isSubProfile(userInfo)) {
132             trampolineIntent.setClass(this, DeepLinkHomepageActivityInternal::class.java)
133                 .putExtra(SettingsActivity.EXTRA_USER_HANDLE, user)
134             startActivityAsUser(
135                 trampolineIntent,
136                 userManager.getProfileParent(userInfo.id).userHandle
137             )
138         } else {
139             startActivity(trampolineIntent)
140         }
141         true
142     } catch (e: ActivityNotFoundException) {
143         Log.e(TAG, "Deep link homepage is not available to show 2-pane UI")
144         false
145     }
146 }
147