1 /** 2 * Copyright (C) 2018 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 package com.android.settings.core; 17 18 import static android.text.Layout.HYPHENATION_FREQUENCY_NORMAL_FAST; 19 20 import android.annotation.LayoutRes; 21 import android.app.ActivityManager; 22 import android.content.ComponentName; 23 import android.content.Intent; 24 import android.content.pm.PackageManager; 25 import android.content.res.Configuration; 26 import android.content.res.TypedArray; 27 import android.graphics.text.LineBreakConfig; 28 import android.os.Bundle; 29 import android.text.TextUtils; 30 import android.util.Log; 31 import android.view.LayoutInflater; 32 import android.view.View; 33 import android.view.ViewGroup; 34 import android.view.Window; 35 import android.widget.Toolbar; 36 37 import androidx.annotation.NonNull; 38 import androidx.annotation.Nullable; 39 import androidx.coordinatorlayout.widget.CoordinatorLayout; 40 import androidx.fragment.app.FragmentActivity; 41 42 import com.android.settings.R; 43 import com.android.settings.SetupWizardUtils; 44 import com.android.settings.SubSettings; 45 import com.android.settings.core.CategoryMixin.CategoryHandler; 46 import com.android.settingslib.core.lifecycle.HideNonSystemOverlayMixin; 47 import com.android.settingslib.transition.SettingsTransitionHelper.TransitionType; 48 49 import com.google.android.material.appbar.AppBarLayout; 50 import com.google.android.material.appbar.CollapsingToolbarLayout; 51 import com.google.android.material.resources.TextAppearanceConfig; 52 import com.google.android.setupcompat.util.WizardManagerHelper; 53 import com.google.android.setupdesign.transition.TransitionHelper; 54 import com.google.android.setupdesign.util.ThemeHelper; 55 56 /** Base activity for Settings pages */ 57 public class SettingsBaseActivity extends FragmentActivity implements CategoryHandler { 58 59 /** 60 * What type of page transition should be apply. 61 */ 62 public static final String EXTRA_PAGE_TRANSITION_TYPE = "page_transition_type"; 63 64 protected static final boolean DEBUG_TIMING = false; 65 private static final String TAG = "SettingsBaseActivity"; 66 private static final int DEFAULT_REQUEST = -1; 67 private static final float TOOLBAR_LINE_SPACING_MULTIPLIER = 1.1f; 68 69 protected CategoryMixin mCategoryMixin; 70 protected CollapsingToolbarLayout mCollapsingToolbarLayout; 71 protected AppBarLayout mAppBarLayout; 72 private Toolbar mToolbar; 73 74 @Override getCategoryMixin()75 public CategoryMixin getCategoryMixin() { 76 return mCategoryMixin; 77 } 78 79 @Override onCreate(@ullable Bundle savedInstanceState)80 protected void onCreate(@Nullable Bundle savedInstanceState) { 81 final boolean isAnySetupWizard = WizardManagerHelper.isAnySetupWizard(getIntent()); 82 if (isAnySetupWizard) { 83 TransitionHelper.applyForwardTransition(this); 84 TransitionHelper.applyBackwardTransition(this); 85 } 86 super.onCreate(savedInstanceState); 87 if (isFinishing()) { 88 return; 89 } 90 if (isLockTaskModePinned() && !isSettingsRunOnTop()) { 91 Log.w(TAG, "Devices lock task mode pinned."); 92 finish(); 93 } 94 final long startTime = System.currentTimeMillis(); 95 getLifecycle().addObserver(new HideNonSystemOverlayMixin(this)); 96 TextAppearanceConfig.setShouldLoadFontSynchronously(true); 97 98 mCategoryMixin = new CategoryMixin(this); 99 getLifecycle().addObserver(mCategoryMixin); 100 101 final TypedArray theme = getTheme().obtainStyledAttributes(android.R.styleable.Theme); 102 if (!theme.getBoolean(android.R.styleable.Theme_windowNoTitle, false)) { 103 requestWindowFeature(Window.FEATURE_NO_TITLE); 104 } 105 // Apply SetupWizard light theme during setup flow. This is for SubSettings pages. 106 if (isAnySetupWizard && this instanceof SubSettings) { 107 setTheme(SetupWizardUtils.getTheme(this, getIntent())); 108 setTheme(R.style.SettingsPreferenceTheme_SetupWizard); 109 ThemeHelper.trySetDynamicColor(this); 110 } 111 112 if (isToolbarEnabled() && !isAnySetupWizard) { 113 super.setContentView(R.layout.collapsing_toolbar_base_layout); 114 mCollapsingToolbarLayout = findViewById(R.id.collapsing_toolbar); 115 mAppBarLayout = findViewById(R.id.app_bar); 116 if (mCollapsingToolbarLayout != null) { 117 mCollapsingToolbarLayout.setLineSpacingMultiplier(TOOLBAR_LINE_SPACING_MULTIPLIER); 118 mCollapsingToolbarLayout.setHyphenationFrequency(HYPHENATION_FREQUENCY_NORMAL_FAST); 119 mCollapsingToolbarLayout.setStaticLayoutBuilderConfigurer(builder -> 120 builder.setLineBreakConfig( 121 new LineBreakConfig.Builder() 122 .setLineBreakWordStyle( 123 LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE) 124 .build())); 125 } 126 autoSetCollapsingToolbarLayoutScrolling(); 127 } else { 128 super.setContentView(R.layout.settings_base_layout); 129 } 130 131 // This is to hide the toolbar from those pages which don't need a toolbar originally. 132 final Toolbar toolbar = findViewById(R.id.action_bar); 133 if (!isToolbarEnabled() || isAnySetupWizard) { 134 toolbar.setVisibility(View.GONE); 135 return; 136 } 137 setActionBar(toolbar); 138 139 if (DEBUG_TIMING) { 140 Log.d(TAG, "onCreate took " + (System.currentTimeMillis() - startTime) + " ms"); 141 } 142 } 143 144 @Override setActionBar(@ndroidx.annotation.Nullable Toolbar toolbar)145 public void setActionBar(@androidx.annotation.Nullable Toolbar toolbar) { 146 super.setActionBar(toolbar); 147 148 mToolbar = toolbar; 149 } 150 151 @Override onNavigateUp()152 public boolean onNavigateUp() { 153 if (!super.onNavigateUp()) { 154 finishAfterTransition(); 155 } 156 return true; 157 } 158 159 @Override startActivityForResult(Intent intent, int requestCode, @androidx.annotation.Nullable Bundle options)160 public void startActivityForResult(Intent intent, int requestCode, 161 @androidx.annotation.Nullable Bundle options) { 162 final int transitionType = getTransitionType(intent); 163 super.startActivityForResult(intent, requestCode, options); 164 if (transitionType == TransitionType.TRANSITION_SLIDE) { 165 overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out); 166 } else if (transitionType == TransitionType.TRANSITION_FADE) { 167 overridePendingTransition(android.R.anim.fade_in, R.anim.sud_stay); 168 } 169 } 170 171 @Override onPause()172 protected void onPause() { 173 // For accessibility activities launched from setup wizard. 174 if (getTransitionType(getIntent()) == TransitionType.TRANSITION_FADE) { 175 overridePendingTransition(R.anim.sud_stay, android.R.anim.fade_out); 176 } 177 super.onPause(); 178 } 179 180 @Override setContentView(@ayoutRes int layoutResID)181 public void setContentView(@LayoutRes int layoutResID) { 182 final ViewGroup parent = findViewById(R.id.content_frame); 183 if (parent != null) { 184 parent.removeAllViews(); 185 } 186 LayoutInflater.from(this).inflate(layoutResID, parent); 187 } 188 189 @Override setContentView(View view)190 public void setContentView(View view) { 191 ((ViewGroup) findViewById(R.id.content_frame)).addView(view); 192 } 193 194 @Override setContentView(View view, ViewGroup.LayoutParams params)195 public void setContentView(View view, ViewGroup.LayoutParams params) { 196 ((ViewGroup) findViewById(R.id.content_frame)).addView(view, params); 197 } 198 199 @Override setTitle(CharSequence title)200 public void setTitle(CharSequence title) { 201 super.setTitle(title); 202 if (mCollapsingToolbarLayout != null) { 203 mCollapsingToolbarLayout.setTitle(title); 204 } 205 } 206 207 @Override setTitle(int titleId)208 public void setTitle(int titleId) { 209 super.setTitle(getText(titleId)); 210 if (mCollapsingToolbarLayout != null) { 211 mCollapsingToolbarLayout.setTitle(getText(titleId)); 212 } 213 } 214 215 /** 216 * SubSetting page should show a toolbar by default. If the page wouldn't show a toolbar, 217 * override this method and return false value. 218 * 219 * @return ture by default 220 */ isToolbarEnabled()221 protected boolean isToolbarEnabled() { 222 return true; 223 } 224 isLockTaskModePinned()225 private boolean isLockTaskModePinned() { 226 final ActivityManager activityManager = 227 getApplicationContext().getSystemService(ActivityManager.class); 228 return activityManager.getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_PINNED; 229 } 230 isSettingsRunOnTop()231 private boolean isSettingsRunOnTop() { 232 final ActivityManager activityManager = 233 getApplicationContext().getSystemService(ActivityManager.class); 234 final String taskPkgName = activityManager.getRunningTasks(1 /* maxNum */) 235 .get(0 /* index */).baseActivity.getPackageName(); 236 return TextUtils.equals(getPackageName(), taskPkgName); 237 } 238 239 /** 240 * @return whether or not the enabled state actually changed. 241 */ setTileEnabled(ComponentName component, boolean enabled)242 public boolean setTileEnabled(ComponentName component, boolean enabled) { 243 final PackageManager pm = getPackageManager(); 244 int state = pm.getComponentEnabledSetting(component); 245 boolean isEnabled = state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED; 246 if (isEnabled != enabled || state == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) { 247 if (enabled) { 248 mCategoryMixin.removeFromDenylist(component); 249 } else { 250 mCategoryMixin.addToDenylist(component); 251 } 252 pm.setComponentEnabledSetting(component, enabled 253 ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED 254 : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 255 PackageManager.DONT_KILL_APP); 256 return true; 257 } 258 return false; 259 } 260 autoSetCollapsingToolbarLayoutScrolling()261 private void autoSetCollapsingToolbarLayoutScrolling() { 262 if (mAppBarLayout == null) { 263 return; 264 } 265 final CoordinatorLayout.LayoutParams params = 266 (CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams(); 267 final AppBarLayout.Behavior behavior = new AppBarLayout.Behavior(); 268 behavior.setDragCallback( 269 new AppBarLayout.Behavior.DragCallback() { 270 @Override 271 public boolean canDrag(@NonNull AppBarLayout appBarLayout) { 272 // Header can be scrolling while device in landscape mode. 273 return appBarLayout.getResources().getConfiguration().orientation 274 == Configuration.ORIENTATION_LANDSCAPE; 275 } 276 }); 277 params.setBehavior(behavior); 278 } 279 getTransitionType(Intent intent)280 private int getTransitionType(Intent intent) { 281 if (intent == null) { 282 return TransitionType.TRANSITION_NONE; 283 } 284 return intent.getIntExtra(EXTRA_PAGE_TRANSITION_TYPE, TransitionType.TRANSITION_NONE); 285 } 286 } 287