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