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