blob: a17f6141925c28ea627574b66564a2a43fec696b [file] [log] [blame]
Rahul Chaturvedi7fc77ca2015-05-19 18:02:16 -07001/*
2 * Copyright (C) 2015 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
17package com.android.launcher3;
18
Sunny Goyal40253322018-04-19 09:27:38 -070019import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
20import static com.android.launcher3.states.RotationHelper.getAllowRotationDefaultValue;
Sunny Goyal420d5452018-10-17 10:25:14 -070021import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver;
Sunny Goyal40253322018-04-19 09:27:38 -070022
Sunny Goyalba406e22018-04-02 11:36:12 -070023import android.annotation.TargetApi;
Rahul Chaturvedi7fc77ca2015-05-19 18:02:16 -070024import android.app.Activity;
Sunny Goyal848cad52017-07-07 03:12:21 -070025import android.app.AlertDialog;
26import android.app.Dialog;
27import android.app.DialogFragment;
Sunny Goyal7f920b82018-06-27 15:47:49 -070028import android.app.Fragment;
Sunny Goyal848cad52017-07-07 03:12:21 -070029import android.content.ComponentName;
Sunny Goyalf48e5922016-05-12 15:36:20 -070030import android.content.ContentResolver;
Sunny Goyal848cad52017-07-07 03:12:21 -070031import android.content.Context;
32import android.content.DialogInterface;
33import android.content.Intent;
Sunny Goyalba406e22018-04-02 11:36:12 -070034import android.os.Build;
Rahul Chaturvedi7fc77ca2015-05-19 18:02:16 -070035import android.os.Bundle;
Sunny Goyalca187462017-03-31 20:09:34 -070036import android.preference.ListPreference;
Rahul Chaturvedi799aa042015-06-01 21:26:41 -040037import android.preference.Preference;
Rahul Chaturvedi7fc77ca2015-05-19 18:02:16 -070038import android.preference.PreferenceFragment;
Sunny Goyalba406e22018-04-02 11:36:12 -070039import android.preference.PreferenceScreen;
Sunny Goyalf48e5922016-05-12 15:36:20 -070040import android.provider.Settings;
Sunny Goyal3fbca152017-11-02 18:55:44 -070041import android.text.TextUtils;
Sunny Goyal3fbca152017-11-02 18:55:44 -070042import android.view.View;
Sunny Goyal3fbca152017-11-02 18:55:44 -070043import android.widget.Adapter;
Sunny Goyalba406e22018-04-02 11:36:12 -070044import android.widget.ListView;
Rahul Chaturvedi7fc77ca2015-05-19 18:02:16 -070045
Ryan Lothianfa530cd2018-10-12 14:14:16 -040046import com.android.launcher3.config.FeatureFlags;
Sunny Goyalca187462017-03-31 20:09:34 -070047import com.android.launcher3.graphics.IconShapeOverride;
Sunny Goyal848cad52017-07-07 03:12:21 -070048import com.android.launcher3.notification.NotificationListener;
Sunny Goyalba406e22018-04-02 11:36:12 -070049import com.android.launcher3.util.ListViewHighlighter;
Sunny Goyal420d5452018-10-17 10:25:14 -070050import com.android.launcher3.util.SecureSettingsObserver;
Sunny Goyal848cad52017-07-07 03:12:21 -070051import com.android.launcher3.views.ButtonPreference;
Sunny Goyalba406e22018-04-02 11:36:12 -070052
53import java.util.Objects;
Sunny Goyalca187462017-03-31 20:09:34 -070054
Rahul Chaturvedi7fc77ca2015-05-19 18:02:16 -070055/**
Rahul Chaturvedi799aa042015-06-01 21:26:41 -040056 * Settings activity for Launcher. Currently implements the following setting: Allow rotation
Rahul Chaturvedi7fc77ca2015-05-19 18:02:16 -070057 */
Sunny Goyal7f920b82018-06-27 15:47:49 -070058public class SettingsActivity extends Activity
59 implements PreferenceFragment.OnPreferenceStartFragmentCallback {
Tony Wickham2ab84822017-05-12 14:59:09 -070060
Ryan Lothianfa530cd2018-10-12 14:14:16 -040061 private static final String FLAGS_PREFERENCE_KEY = "flag_toggler";
62
Tony Wickham2ab84822017-05-12 14:59:09 -070063 private static final String ICON_BADGING_PREFERENCE_KEY = "pref_icon_badging";
Sunny Goyal848cad52017-07-07 03:12:21 -070064 /** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */
65 private static final String NOTIFICATION_ENABLED_LISTENERS = "enabled_notification_listeners";
Tony Wickham2ab84822017-05-12 14:59:09 -070066
Sunny Goyal3fbca152017-11-02 18:55:44 -070067 private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
Sunny Goyald47f2d22018-04-18 14:11:37 -070068 private static final String EXTRA_SHOW_FRAGMENT_ARGS = ":settings:show_fragment_args";
Sunny Goyal3fbca152017-11-02 18:55:44 -070069 private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600;
70 private static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted";
71
Rahul Chaturvedi7fc77ca2015-05-19 18:02:16 -070072 @Override
73 protected void onCreate(Bundle savedInstanceState) {
74 super.onCreate(savedInstanceState);
75
Mario Bertschler2de2d672017-06-30 18:51:46 -070076 if (savedInstanceState == null) {
Sunny Goyal7f920b82018-06-27 15:47:49 -070077 Fragment f = Fragment.instantiate(this, getString(R.string.settings_fragment_name));
Mario Bertschler2de2d672017-06-30 18:51:46 -070078 // Display the fragment as the main content.
79 getFragmentManager().beginTransaction()
Sunny Goyal7f920b82018-06-27 15:47:49 -070080 .replace(android.R.id.content, f)
Mario Bertschler2de2d672017-06-30 18:51:46 -070081 .commit();
82 }
Rahul Chaturvedi7fc77ca2015-05-19 18:02:16 -070083 }
84
Sunny Goyal3fbca152017-11-02 18:55:44 -070085 protected PreferenceFragment getNewFragment() {
86 return new LauncherSettingsFragment();
87 }
88
Sunny Goyal7f920b82018-06-27 15:47:49 -070089 @Override
90 public boolean onPreferenceStartFragment(
91 PreferenceFragment preferenceFragment, Preference pref) {
Sunny Goyal5077aef2018-09-17 15:22:13 -070092 if (getFragmentManager().isStateSaved()) {
93 // Sometimes onClick can come after onPause because of being posted on the handler.
94 // Skip starting new fragments in that case.
95 return false;
96 }
Sunny Goyal7f920b82018-06-27 15:47:49 -070097 Fragment f = Fragment.instantiate(this, pref.getFragment(), pref.getExtras());
98 if (f instanceof DialogFragment) {
99 ((DialogFragment) f).show(getFragmentManager(), pref.getKey());
100 } else {
101 getFragmentManager()
102 .beginTransaction()
103 .replace(android.R.id.content, f)
104 .addToBackStack(pref.getKey())
105 .commit();
106 }
107 return true;
108 }
109
Rahul Chaturvedi7fc77ca2015-05-19 18:02:16 -0700110 /**
111 * This fragment shows the launcher preferences.
112 */
Sunny Goyalf48e5922016-05-12 15:36:20 -0700113 public static class LauncherSettingsFragment extends PreferenceFragment {
114
Sunny Goyal420d5452018-10-17 10:25:14 -0700115 private SecureSettingsObserver mIconBadgingObserver;
Sunny Goyalf48e5922016-05-12 15:36:20 -0700116
Sunny Goyal3fbca152017-11-02 18:55:44 -0700117 private String mPreferenceKey;
118 private boolean mPreferenceHighlighted = false;
119
120 @Override
Rahul Chaturvedi7fc77ca2015-05-19 18:02:16 -0700121 public void onCreate(Bundle savedInstanceState) {
122 super.onCreate(savedInstanceState);
Sunny Goyal3fbca152017-11-02 18:55:44 -0700123 if (savedInstanceState != null) {
124 mPreferenceHighlighted = savedInstanceState.getBoolean(SAVE_HIGHLIGHTED_KEY);
125 }
126
Sunny Goyalf48e5922016-05-12 15:36:20 -0700127 getPreferenceManager().setSharedPreferencesName(LauncherFiles.SHARED_PREFERENCES_KEY);
Rahul Chaturvedi7fc77ca2015-05-19 18:02:16 -0700128 addPreferencesFromResource(R.xml.launcher_preferences);
Sunny Goyal7779d622015-06-11 16:18:39 -0700129
Ryan Lothianfa530cd2018-10-12 14:14:16 -0400130 // Only show flag toggler UI if this build variant implements that.
131 Preference flagToggler = findPreference(FLAGS_PREFERENCE_KEY);
132 if (flagToggler != null && !FeatureFlags.showFlagTogglerUi()) {
133 getPreferenceScreen().removePreference(flagToggler);
134 }
135
Tony Wickham2ab84822017-05-12 14:59:09 -0700136 ContentResolver resolver = getActivity().getContentResolver();
137
Sunny Goyal848cad52017-07-07 03:12:21 -0700138 ButtonPreference iconBadgingPref =
139 (ButtonPreference) findPreference(ICON_BADGING_PREFERENCE_KEY);
Hyunyoung Songe24cb632017-09-11 11:18:03 -0700140 if (!Utilities.ATLEAST_OREO) {
Sunny Goyal4179e9b2017-03-08 14:25:09 -0800141 getPreferenceScreen().removePreference(
142 findPreference(SessionCommitReceiver.ADD_ICON_PREFERENCE_KEY));
Tony Wickham2ab84822017-05-12 14:59:09 -0700143 getPreferenceScreen().removePreference(iconBadgingPref);
Tony Wickhamc1385152017-08-14 14:31:03 -0700144 } else if (!getResources().getBoolean(R.bool.notification_badging_enabled)) {
145 getPreferenceScreen().removePreference(iconBadgingPref);
Tony Wickham2ab84822017-05-12 14:59:09 -0700146 } else {
147 // Listen to system notification badge settings while this UI is active.
Sunny Goyal420d5452018-10-17 10:25:14 -0700148 mIconBadgingObserver = newNotificationSettingsObserver(
149 getActivity(), new IconBadgingObserver(iconBadgingPref, resolver));
150 mIconBadgingObserver.register();
151 // Also listen if notification permission changes
152 mIconBadgingObserver.getResolver().registerContentObserver(
153 Settings.Secure.getUriFor(NOTIFICATION_ENABLED_LISTENERS), false,
154 mIconBadgingObserver);
155 mIconBadgingObserver.dispatchOnChange();
Sunny Goyal4179e9b2017-03-08 14:25:09 -0800156 }
Sunny Goyalca187462017-03-31 20:09:34 -0700157
158 Preference iconShapeOverride = findPreference(IconShapeOverride.KEY_PREFERENCE);
159 if (iconShapeOverride != null) {
160 if (IconShapeOverride.isSupported(getActivity())) {
161 IconShapeOverride.handlePreferenceUi((ListPreference) iconShapeOverride);
162 } else {
163 getPreferenceScreen().removePreference(iconShapeOverride);
164 }
165 }
Sunny Goyal40253322018-04-19 09:27:38 -0700166
167 // Setup allow rotation preference
168 Preference rotationPref = findPreference(ALLOW_ROTATION_PREFERENCE_KEY);
169 if (getResources().getBoolean(R.bool.allow_rotation)) {
170 // Launcher supports rotation by default. No need to show this setting.
171 getPreferenceScreen().removePreference(rotationPref);
172 } else {
Sunny Goyal40253322018-04-19 09:27:38 -0700173 // Initialize the UI once
174 rotationPref.setDefaultValue(getAllowRotationDefaultValue());
175 }
Rahul Chaturvedi7fc77ca2015-05-19 18:02:16 -0700176 }
Rahul Chaturvedi799aa042015-06-01 21:26:41 -0400177
178 @Override
Sunny Goyal3fbca152017-11-02 18:55:44 -0700179 public void onSaveInstanceState(Bundle outState) {
180 super.onSaveInstanceState(outState);
181 outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mPreferenceHighlighted);
182 }
183
184 @Override
185 public void onResume() {
186 super.onResume();
187
188 Intent intent = getActivity().getIntent();
189 mPreferenceKey = intent.getStringExtra(EXTRA_FRAGMENT_ARG_KEY);
190 if (isAdded() && !mPreferenceHighlighted && !TextUtils.isEmpty(mPreferenceKey)) {
191 getView().postDelayed(this::highlightPreference, DELAY_HIGHLIGHT_DURATION_MILLIS);
192 }
193 }
194
195 private void highlightPreference() {
Sunny Goyal3fbca152017-11-02 18:55:44 -0700196 Preference pref = findPreference(mPreferenceKey);
Sunny Goyalba406e22018-04-02 11:36:12 -0700197 if (pref == null || getPreferenceScreen() == null) {
Sunny Goyal3fbca152017-11-02 18:55:44 -0700198 return;
199 }
Sunny Goyalba406e22018-04-02 11:36:12 -0700200 PreferenceScreen screen = getPreferenceScreen();
201 if (Utilities.ATLEAST_OREO) {
202 screen = selectPreferenceRecursive(pref, screen);
203 }
204 if (screen == null) {
205 return;
206 }
207
208 View root = screen.getDialog() != null
209 ? screen.getDialog().getWindow().getDecorView() : getView();
210 ListView list = root.findViewById(android.R.id.list);
211 if (list == null || list.getAdapter() == null) {
212 return;
213 }
214 Adapter adapter = list.getAdapter();
Sunny Goyal3fbca152017-11-02 18:55:44 -0700215
216 // Find the position
217 int position = -1;
218 for (int i = adapter.getCount() - 1; i >= 0; i--) {
219 if (pref == adapter.getItem(i)) {
220 position = i;
221 break;
222 }
223 }
Sunny Goyalba406e22018-04-02 11:36:12 -0700224 new ListViewHighlighter(list, position);
Sunny Goyal3fbca152017-11-02 18:55:44 -0700225 mPreferenceHighlighted = true;
226 }
227
228 @Override
Sunny Goyalf48e5922016-05-12 15:36:20 -0700229 public void onDestroy() {
Tony Wickham2ab84822017-05-12 14:59:09 -0700230 if (mIconBadgingObserver != null) {
Tonyd48710c2017-07-27 23:23:58 -0700231 mIconBadgingObserver.unregister();
Tony Wickham2ab84822017-05-12 14:59:09 -0700232 mIconBadgingObserver = null;
233 }
Sunny Goyalf48e5922016-05-12 15:36:20 -0700234 super.onDestroy();
235 }
Sunny Goyalba406e22018-04-02 11:36:12 -0700236
237 @TargetApi(Build.VERSION_CODES.O)
238 private PreferenceScreen selectPreferenceRecursive(
239 Preference pref, PreferenceScreen topParent) {
240 if (!(pref.getParent() instanceof PreferenceScreen)) {
241 return null;
242 }
243
244 PreferenceScreen parent = (PreferenceScreen) pref.getParent();
245 if (Objects.equals(parent.getKey(), topParent.getKey())) {
246 return parent;
247 } else if (selectPreferenceRecursive(parent, topParent) != null) {
248 ((PreferenceScreen) parent.getParent())
249 .onItemClick(null, null, parent.getOrder(), 0);
250 return parent;
251 } else {
252 return null;
253 }
254 }
Sunny Goyalf48e5922016-05-12 15:36:20 -0700255 }
256
257 /**
Tony Wickham2ab84822017-05-12 14:59:09 -0700258 * Content observer which listens for system badging setting changes,
259 * and updates the launcher badging setting subtext accordingly.
260 */
Sunny Goyal420d5452018-10-17 10:25:14 -0700261 private static class IconBadgingObserver implements SecureSettingsObserver.OnChangeListener {
Tony Wickham2ab84822017-05-12 14:59:09 -0700262
Sunny Goyal848cad52017-07-07 03:12:21 -0700263 private final ButtonPreference mBadgingPref;
Tony Wickham2ab84822017-05-12 14:59:09 -0700264 private final ContentResolver mResolver;
265
Sunny Goyal420d5452018-10-17 10:25:14 -0700266 public IconBadgingObserver(ButtonPreference badgingPref, ContentResolver resolver) {
Tony Wickham2ab84822017-05-12 14:59:09 -0700267 mBadgingPref = badgingPref;
268 mResolver = resolver;
269 }
270
271 @Override
Sunny Goyal420d5452018-10-17 10:25:14 -0700272 public void onSettingsChanged(boolean enabled) {
Sunny Goyal848cad52017-07-07 03:12:21 -0700273 int summary = enabled ? R.string.icon_badging_desc_on : R.string.icon_badging_desc_off;
274
275 boolean serviceEnabled = true;
276 if (enabled) {
277 // Check if the listener is enabled or not.
278 String enabledListeners =
279 Settings.Secure.getString(mResolver, NOTIFICATION_ENABLED_LISTENERS);
280 ComponentName myListener =
281 new ComponentName(mBadgingPref.getContext(), NotificationListener.class);
282 serviceEnabled = enabledListeners != null &&
283 (enabledListeners.contains(myListener.flattenToString()) ||
284 enabledListeners.contains(myListener.flattenToShortString()));
285 if (!serviceEnabled) {
286 summary = R.string.title_missing_notification_access;
287 }
288 }
Sunny Goyalb65e13c2017-08-01 11:04:15 -0700289 mBadgingPref.setWidgetFrameVisible(!serviceEnabled);
Sunny Goyal5077aef2018-09-17 15:22:13 -0700290 mBadgingPref.setFragment(
291 serviceEnabled ? null : NotificationAccessConfirmation.class.getName());
Sunny Goyal848cad52017-07-07 03:12:21 -0700292 mBadgingPref.setSummary(summary);
293
294 }
Tony Wickham2ab84822017-05-12 14:59:09 -0700295 }
296
Sunny Goyal848cad52017-07-07 03:12:21 -0700297 public static class NotificationAccessConfirmation
298 extends DialogFragment implements DialogInterface.OnClickListener {
299
300 @Override
301 public Dialog onCreateDialog(Bundle savedInstanceState) {
302 final Context context = getActivity();
303 String msg = context.getString(R.string.msg_missing_notification_access,
304 context.getString(R.string.derived_app_name));
305 return new AlertDialog.Builder(context)
306 .setTitle(R.string.title_missing_notification_access)
307 .setMessage(msg)
308 .setNegativeButton(android.R.string.cancel, null)
309 .setPositiveButton(R.string.title_change_settings, this)
310 .create();
311 }
312
313 @Override
314 public void onClick(DialogInterface dialogInterface, int i) {
315 ComponentName cn = new ComponentName(getActivity(), NotificationListener.class);
Sunny Goyald47f2d22018-04-18 14:11:37 -0700316 Bundle showFragmentArgs = new Bundle();
317 showFragmentArgs.putString(EXTRA_FRAGMENT_ARG_KEY, cn.flattenToString());
318
Sunny Goyal848cad52017-07-07 03:12:21 -0700319 Intent intent = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS)
320 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
Sunny Goyald47f2d22018-04-18 14:11:37 -0700321 .putExtra(EXTRA_FRAGMENT_ARG_KEY, cn.flattenToString())
322 .putExtra(EXTRA_SHOW_FRAGMENT_ARGS, showFragmentArgs);
Sunny Goyal848cad52017-07-07 03:12:21 -0700323 getActivity().startActivity(intent);
324 }
325 }
Rahul Chaturvedi7fc77ca2015-05-19 18:02:16 -0700326}