• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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