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