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