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