1 /*
2  * Copyright (C) 2021 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.app.settings.SettingsEnums;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.util.FeatureFlagUtils;
25 import android.util.Log;
26 
27 import androidx.window.embedding.ActivityFilter;
28 import androidx.window.embedding.ActivityRule;
29 import androidx.window.embedding.EmbeddingAspectRatio;
30 import androidx.window.embedding.RuleController;
31 import androidx.window.embedding.SplitAttributes;
32 import androidx.window.embedding.SplitPairFilter;
33 import androidx.window.embedding.SplitPairRule;
34 import androidx.window.embedding.SplitPlaceholderRule;
35 import androidx.window.embedding.SplitRule;
36 
37 import com.android.settings.R;
38 import com.android.settings.Settings;
39 import com.android.settings.SettingsActivity;
40 import com.android.settings.SubSettings;
41 import com.android.settings.biometrics.face.FaceEnrollIntroduction;
42 import com.android.settings.biometrics.face.FaceEnrollIntroductionInternal;
43 import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling;
44 import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroduction;
45 import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroductionInternal;
46 import com.android.settings.biometrics2.ui.view.FingerprintEnrollmentActivity;
47 import com.android.settings.core.FeatureFlags;
48 import com.android.settings.homepage.DeepLinkHomepageActivity;
49 import com.android.settings.homepage.DeepLinkHomepageActivityInternal;
50 import com.android.settings.homepage.SettingsHomepageActivity;
51 import com.android.settings.overlay.FeatureFactory;
52 import com.android.settings.password.ChooseLockPattern;
53 import com.android.settings.privatespace.PrivateSpaceSetupActivity;
54 import com.android.settings.remoteauth.RemoteAuthActivity;
55 import com.android.settings.remoteauth.RemoteAuthActivityInternal;
56 
57 import java.util.Collection;
58 import java.util.HashSet;
59 import java.util.Set;
60 
61 /** A class to initialize split rules for activity embedding. */
62 public class ActivityEmbeddingRulesController {
63 
64     private static final String TAG = "ActivityEmbeddingCtrl";
65     private static final ComponentName COMPONENT_NAME_WILDCARD = new ComponentName(
66             "*" /* pkg */, "*" /* cls */);
67     private final Context mContext;
68     private final RuleController mRuleController;
69 
ActivityEmbeddingRulesController(Context context)70     public ActivityEmbeddingRulesController(Context context) {
71         mContext = context;
72         mRuleController = RuleController.getInstance(context);
73     }
74 
75     /**
76      * Set up embedding rules to place activities to the right pane.
77      */
initRules()78     public void initRules() {
79         if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(mContext)) {
80             Log.d(TAG, "Not support this feature now");
81             return;
82         }
83 
84         mRuleController.clearRules();
85 
86         // Set a placeholder for home page.
87         registerHomepagePlaceholderRule();
88 
89         registerAlwaysExpandRule();
90     }
91 
92     /** Register a SplitPairRule for 2-pane. */
registerTwoPanePairRule(Context context, ComponentName primaryComponent, ComponentName secondaryComponent, String secondaryIntentAction, SplitRule.FinishBehavior finishPrimaryWithSecondary, SplitRule.FinishBehavior finishSecondaryWithPrimary, boolean clearTop)93     public static void registerTwoPanePairRule(Context context,
94             ComponentName primaryComponent,
95             ComponentName secondaryComponent,
96             String secondaryIntentAction,
97             SplitRule.FinishBehavior finishPrimaryWithSecondary,
98             SplitRule.FinishBehavior finishSecondaryWithPrimary,
99             boolean clearTop) {
100         if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(context)) {
101             return;
102         }
103         final Set<SplitPairFilter> filters = new HashSet<>();
104         filters.add(new SplitPairFilter(primaryComponent, secondaryComponent,
105                 secondaryIntentAction));
106 
107         SplitAttributes attributes = new SplitAttributes.Builder()
108                 .setSplitType(SplitAttributes.SplitType.ratio(
109                         ActivityEmbeddingUtils.getSplitRatio(context)))
110                 .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
111                 .build();
112         SplitPairRule splitPairRule = new SplitPairRule.Builder(filters)
113                 .setFinishPrimaryWithSecondary(finishPrimaryWithSecondary)
114                 .setFinishSecondaryWithPrimary(finishSecondaryWithPrimary)
115                 .setClearTop(clearTop)
116                 .setMinWidthDp(ActivityEmbeddingUtils.getMinCurrentScreenSplitWidthDp(context))
117                 .setMinSmallestWidthDp(
118                         ActivityEmbeddingUtils.getMinSmallestScreenSplitWidthDp(context))
119                 .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW)
120                 .setDefaultSplitAttributes(attributes)
121                 .build();
122         RuleController.getInstance(context).addRule(splitPairRule);
123     }
124 
125     /**
126      * Registers a {@link SplitPairRule} for all classes that Settings homepage can be invoked from.
127      */
registerTwoPanePairRuleForSettingsHome(Context context, ComponentName secondaryComponent, String secondaryIntentAction, boolean finishPrimaryWithSecondary, boolean finishSecondaryWithPrimary, boolean clearTop)128     public static void registerTwoPanePairRuleForSettingsHome(Context context,
129             ComponentName secondaryComponent,
130             String secondaryIntentAction,
131             boolean finishPrimaryWithSecondary,
132             boolean finishSecondaryWithPrimary,
133             boolean clearTop) {
134         if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(context)) {
135             return;
136         }
137 
138         registerTwoPanePairRule(
139                 context,
140                 new ComponentName(context, Settings.class),
141                 secondaryComponent,
142                 secondaryIntentAction,
143                 finishPrimaryWithSecondary ? SplitRule.FinishBehavior.ADJACENT
144                         : SplitRule.FinishBehavior.NEVER,
145                 finishSecondaryWithPrimary ? SplitRule.FinishBehavior.ADJACENT
146                         : SplitRule.FinishBehavior.NEVER,
147                 clearTop);
148 
149         registerTwoPanePairRule(
150                 context,
151                 new ComponentName(context, SettingsHomepageActivity.class),
152                 secondaryComponent,
153                 secondaryIntentAction,
154                 finishPrimaryWithSecondary ? SplitRule.FinishBehavior.ADJACENT
155                         : SplitRule.FinishBehavior.NEVER,
156                 finishSecondaryWithPrimary ? SplitRule.FinishBehavior.ADJACENT
157                         : SplitRule.FinishBehavior.NEVER,
158                 clearTop);
159 
160         // We should finish HomePageActivity altogether even if it shows in single pane for all deep
161         // link cases.
162         registerTwoPanePairRule(
163                 context,
164                 new ComponentName(context, DeepLinkHomepageActivity.class),
165                 secondaryComponent,
166                 secondaryIntentAction,
167                 finishPrimaryWithSecondary ? SplitRule.FinishBehavior.ALWAYS
168                         : SplitRule.FinishBehavior.NEVER,
169                 finishSecondaryWithPrimary ? SplitRule.FinishBehavior.ALWAYS
170                         : SplitRule.FinishBehavior.NEVER,
171                 clearTop);
172 
173         registerTwoPanePairRule(
174                 context,
175                 new ComponentName(context, DeepLinkHomepageActivityInternal.class),
176                 secondaryComponent,
177                 secondaryIntentAction,
178                 finishPrimaryWithSecondary ? SplitRule.FinishBehavior.ALWAYS
179                         : SplitRule.FinishBehavior.NEVER,
180                 finishSecondaryWithPrimary ? SplitRule.FinishBehavior.ALWAYS
181                         : SplitRule.FinishBehavior.NEVER,
182                 clearTop);
183     }
184 
185     /**
186      * Register a new SplitPairRule for Settings home.
187      */
registerTwoPanePairRuleForSettingsHome(Context context, ComponentName secondaryComponent, String secondaryIntentAction, boolean clearTop)188     public static void registerTwoPanePairRuleForSettingsHome(Context context,
189             ComponentName secondaryComponent,
190             String secondaryIntentAction,
191             boolean clearTop) {
192         if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(context)) {
193             return;
194         }
195 
196         registerTwoPanePairRuleForSettingsHome(
197                 context,
198                 secondaryComponent,
199                 secondaryIntentAction,
200                 true /* finishPrimaryWithSecondary */,
201                 true /* finishSecondaryWithPrimary */,
202                 clearTop);
203     }
204 
205     /** Register a SplitPairRule for SubSettings if the device supports 2-pane. */
registerSubSettingsPairRule(Context context, boolean clearTop)206     public static void registerSubSettingsPairRule(Context context, boolean clearTop) {
207         if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(context)) {
208             return;
209         }
210 
211         registerTwoPanePairRuleForSettingsHome(
212                 context,
213                 new ComponentName(context, SubSettings.class),
214                 null /* secondaryIntentAction */,
215                 clearTop);
216 
217         registerTwoPanePairRuleForSettingsHome(
218                 context,
219                 COMPONENT_NAME_WILDCARD,
220                 Intent.ACTION_SAFETY_CENTER,
221                 clearTop
222         );
223     }
224 
registerHomepagePlaceholderRule()225     private void registerHomepagePlaceholderRule() {
226         final Set<ActivityFilter> activityFilters = new HashSet<>();
227         addActivityFilter(activityFilters, SettingsHomepageActivity.class);
228         addActivityFilter(activityFilters, Settings.class);
229 
230         final Intent intent = new Intent(mContext, Settings.NetworkDashboardActivity.class);
231         intent.putExtra(SettingsActivity.EXTRA_IS_SECOND_LAYER_PAGE, true);
232         SplitAttributes attributes = new SplitAttributes.Builder()
233                 .setSplitType(SplitAttributes.SplitType.ratio(
234                         ActivityEmbeddingUtils.getSplitRatio(mContext)))
235                 .build();
236         final SplitPlaceholderRule placeholderRule = new SplitPlaceholderRule.Builder(
237                 activityFilters, intent)
238                 .setMinWidthDp(ActivityEmbeddingUtils.getMinCurrentScreenSplitWidthDp(mContext))
239                 .setMinSmallestWidthDp(
240                         ActivityEmbeddingUtils.getMinSmallestScreenSplitWidthDp(mContext))
241                 .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW)
242                 .setSticky(false)
243                 .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ADJACENT)
244                 .setDefaultSplitAttributes(attributes)
245                 .build();
246 
247         mRuleController.addRule(placeholderRule);
248     }
249 
registerAlwaysExpandRule()250     private void registerAlwaysExpandRule() {
251         final Set<ActivityFilter> activityFilters = new HashSet<>();
252         if (FeatureFlagUtils.isEnabled(mContext, FeatureFlags.SETTINGS_SEARCH_ALWAYS_EXPAND)) {
253             final Intent searchIntent = FeatureFactory.getFeatureFactory()
254                     .getSearchFeatureProvider()
255                     .buildSearchIntent(mContext, SettingsEnums.SETTINGS_HOMEPAGE);
256             addActivityFilter(activityFilters, searchIntent);
257         }
258         addActivityFilter(activityFilters, FingerprintEnrollmentActivity.class);
259         addActivityFilter(activityFilters, FingerprintEnrollmentActivity.InternalActivity.class);
260         addActivityFilter(activityFilters, FingerprintEnrollIntroduction.class);
261         addActivityFilter(activityFilters, FingerprintEnrollIntroductionInternal.class);
262         addActivityFilter(activityFilters, FingerprintEnrollEnrolling.class);
263         addActivityFilter(activityFilters, FaceEnrollIntroductionInternal.class);
264         addActivityFilter(activityFilters, FaceEnrollIntroduction.class);
265         addActivityFilter(activityFilters, RemoteAuthActivity.class);
266         addActivityFilter(activityFilters, RemoteAuthActivityInternal.class);
267         addActivityFilter(activityFilters, ChooseLockPattern.class);
268         addActivityFilter(activityFilters, PrivateSpaceSetupActivity.class);
269         String action = mContext.getString(R.string.config_avatar_picker_action);
270         addActivityFilter(activityFilters, new Intent(action));
271 
272         ActivityRule activityRule = new ActivityRule.Builder(activityFilters).setAlwaysExpand(true)
273                 .build();
274         mRuleController.addRule(activityRule);
275     }
276 
addActivityFilter(Set<ActivityFilter> activityFilters, Intent intent)277     private static void addActivityFilter(Set<ActivityFilter> activityFilters, Intent intent) {
278         activityFilters.add(new ActivityFilter(COMPONENT_NAME_WILDCARD, intent.getAction()));
279     }
280 
addActivityFilter(Collection<ActivityFilter> activityFilters, Class<? extends Activity> activityClass)281     private void addActivityFilter(Collection<ActivityFilter> activityFilters,
282             Class<? extends Activity> activityClass) {
283         activityFilters.add(new ActivityFilter(new ComponentName(mContext, activityClass),
284                 null /* intentAction */));
285     }
286 }
287