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