blob: 738a8645a01acbda0670a108884a60a96c571a26 [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;
21
Sunny Goyalba406e22018-04-02 11:36:12 -070022import android.annotation.TargetApi;
Rahul Chaturvedi7fc77ca2015-05-19 18:02:16 -070023import android.app.Activity;
Sunny Goyal848cad52017-07-07 03:12:21 -070024import android.app.AlertDialog;
25import android.app.Dialog;
26import android.app.DialogFragment;
27import android.app.FragmentManager;
28import android.content.ComponentName;
Sunny Goyalf48e5922016-05-12 15:36:20 -070029import android.content.ContentResolver;
Sunny Goyal848cad52017-07-07 03:12:21 -070030import android.content.Context;
31import android.content.DialogInterface;
32import android.content.Intent;
Sunny Goyalba406e22018-04-02 11:36:12 -070033import android.os.Build;
Rahul Chaturvedi7fc77ca2015-05-19 18:02:16 -070034import android.os.Bundle;
Sunny Goyalca187462017-03-31 20:09:34 -070035import android.preference.ListPreference;
Rahul Chaturvedi799aa042015-06-01 21:26:41 -040036import android.preference.Preference;
Rahul Chaturvedi7fc77ca2015-05-19 18:02:16 -070037import android.preference.PreferenceFragment;
Sunny Goyalba406e22018-04-02 11:36:12 -070038import android.preference.PreferenceScreen;
Sunny Goyalf48e5922016-05-12 15:36:20 -070039import android.provider.Settings;
Sunny Goyal3fbca152017-11-02 18:55:44 -070040import android.text.TextUtils;
Sunny Goyal3fbca152017-11-02 18:55:44 -070041import android.view.View;
Sunny Goyal3fbca152017-11-02 18:55:44 -070042import android.widget.Adapter;
Sunny Goyalba406e22018-04-02 11:36:12 -070043import android.widget.ListView;
Rahul Chaturvedi7fc77ca2015-05-19 18:02:16 -070044
Sunny Goyalca187462017-03-31 20:09:34 -070045import com.android.launcher3.graphics.IconShapeOverride;
Sunny Goyal848cad52017-07-07 03:12:21 -070046import com.android.launcher3.notification.NotificationListener;
Sunny Goyalba406e22018-04-02 11:36:12 -070047import com.android.launcher3.util.ListViewHighlighter;
Tonyd48710c2017-07-27 23:23:58 -070048import com.android.launcher3.util.SettingsObserver;
Sunny Goyal848cad52017-07-07 03:12:21 -070049import com.android.launcher3.views.ButtonPreference;
Sunny Goyalba406e22018-04-02 11:36:12 -070050
51import java.util.Objects;
Sunny Goyalca187462017-03-31 20:09:34 -070052
Rahul Chaturvedi7fc77ca2015-05-19 18:02:16 -070053/**
Rahul Chaturvedi799aa042015-06-01 21:26:41 -040054 * Settings activity for Launcher. Currently implements the following setting: Allow rotation
Rahul Chaturvedi7fc77ca2015-05-19 18:02:16 -070055 */
56public class SettingsActivity extends Activity {
Tony Wickham2ab84822017-05-12 14:59:09 -070057
58 private static final String ICON_BADGING_PREFERENCE_KEY = "pref_icon_badging";
Tonyd48710c2017-07-27 23:23:58 -070059 /** Hidden field Settings.Secure.NOTIFICATION_BADGING */
60 public static final String NOTIFICATION_BADGING = "notification_badging";
Sunny Goyal848cad52017-07-07 03:12:21 -070061 /** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */
62 private static final String NOTIFICATION_ENABLED_LISTENERS = "enabled_notification_listeners";
Tony Wickham2ab84822017-05-12 14:59:09 -070063
Sunny Goyal3fbca152017-11-02 18:55:44 -070064 private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
65 private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600;
66 private static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted";
67
Rahul Chaturvedi7fc77ca2015-05-19 18:02:16 -070068 @Override
69 protected void onCreate(Bundle savedInstanceState) {
70 super.onCreate(savedInstanceState);
71
Mario Bertschler2de2d672017-06-30 18:51:46 -070072 if (savedInstanceState == null) {
73 // Display the fragment as the main content.
74 getFragmentManager().beginTransaction()
Sunny Goyal3fbca152017-11-02 18:55:44 -070075 .replace(android.R.id.content, getNewFragment())
Mario Bertschler2de2d672017-06-30 18:51:46 -070076 .commit();
77 }
Rahul Chaturvedi7fc77ca2015-05-19 18:02:16 -070078 }
79
Sunny Goyal3fbca152017-11-02 18:55:44 -070080 protected PreferenceFragment getNewFragment() {
81 return new LauncherSettingsFragment();
82 }
83
Rahul Chaturvedi7fc77ca2015-05-19 18:02:16 -070084 /**
85 * This fragment shows the launcher preferences.
86 */
Sunny Goyalf48e5922016-05-12 15:36:20 -070087 public static class LauncherSettingsFragment extends PreferenceFragment {
88
Tony Wickham2ab84822017-05-12 14:59:09 -070089 private IconBadgingObserver mIconBadgingObserver;
Sunny Goyal40253322018-04-19 09:27:38 -070090 private RotationLockObserver mRotationLockObserver;
Sunny Goyalf48e5922016-05-12 15:36:20 -070091
Sunny Goyal3fbca152017-11-02 18:55:44 -070092 private String mPreferenceKey;
93 private boolean mPreferenceHighlighted = false;
94
95 @Override
Rahul Chaturvedi7fc77ca2015-05-19 18:02:16 -070096 public void onCreate(Bundle savedInstanceState) {
97 super.onCreate(savedInstanceState);
Sunny Goyal3fbca152017-11-02 18:55:44 -070098 if (savedInstanceState != null) {
99 mPreferenceHighlighted = savedInstanceState.getBoolean(SAVE_HIGHLIGHTED_KEY);
100 }
101
Sunny Goyalf48e5922016-05-12 15:36:20 -0700102 getPreferenceManager().setSharedPreferencesName(LauncherFiles.SHARED_PREFERENCES_KEY);
Rahul Chaturvedi7fc77ca2015-05-19 18:02:16 -0700103 addPreferencesFromResource(R.xml.launcher_preferences);
Sunny Goyal7779d622015-06-11 16:18:39 -0700104
Tony Wickham2ab84822017-05-12 14:59:09 -0700105 ContentResolver resolver = getActivity().getContentResolver();
106
Sunny Goyal848cad52017-07-07 03:12:21 -0700107 ButtonPreference iconBadgingPref =
108 (ButtonPreference) findPreference(ICON_BADGING_PREFERENCE_KEY);
Hyunyoung Songe24cb632017-09-11 11:18:03 -0700109 if (!Utilities.ATLEAST_OREO) {
Sunny Goyal4179e9b2017-03-08 14:25:09 -0800110 getPreferenceScreen().removePreference(
111 findPreference(SessionCommitReceiver.ADD_ICON_PREFERENCE_KEY));
Tony Wickham2ab84822017-05-12 14:59:09 -0700112 getPreferenceScreen().removePreference(iconBadgingPref);
Tony Wickhamc1385152017-08-14 14:31:03 -0700113 } else if (!getResources().getBoolean(R.bool.notification_badging_enabled)) {
114 getPreferenceScreen().removePreference(iconBadgingPref);
Tony Wickham2ab84822017-05-12 14:59:09 -0700115 } else {
116 // Listen to system notification badge settings while this UI is active.
Sunny Goyal848cad52017-07-07 03:12:21 -0700117 mIconBadgingObserver = new IconBadgingObserver(
118 iconBadgingPref, resolver, getFragmentManager());
Tonyd48710c2017-07-27 23:23:58 -0700119 mIconBadgingObserver.register(NOTIFICATION_BADGING, NOTIFICATION_ENABLED_LISTENERS);
Sunny Goyal4179e9b2017-03-08 14:25:09 -0800120 }
Sunny Goyalca187462017-03-31 20:09:34 -0700121
122 Preference iconShapeOverride = findPreference(IconShapeOverride.KEY_PREFERENCE);
123 if (iconShapeOverride != null) {
124 if (IconShapeOverride.isSupported(getActivity())) {
125 IconShapeOverride.handlePreferenceUi((ListPreference) iconShapeOverride);
126 } else {
127 getPreferenceScreen().removePreference(iconShapeOverride);
128 }
129 }
Sunny Goyal40253322018-04-19 09:27:38 -0700130
131 // Setup allow rotation preference
132 Preference rotationPref = findPreference(ALLOW_ROTATION_PREFERENCE_KEY);
133 if (getResources().getBoolean(R.bool.allow_rotation)) {
134 // Launcher supports rotation by default. No need to show this setting.
135 getPreferenceScreen().removePreference(rotationPref);
136 } else {
137 mRotationLockObserver = new RotationLockObserver(rotationPref, resolver);
138
139 // Register a content observer to listen for system setting changes while
140 // this UI is active.
141 mRotationLockObserver.register(Settings.System.ACCELEROMETER_ROTATION);
142
143 // Initialize the UI once
144 rotationPref.setDefaultValue(getAllowRotationDefaultValue());
145 }
Rahul Chaturvedi7fc77ca2015-05-19 18:02:16 -0700146 }
Rahul Chaturvedi799aa042015-06-01 21:26:41 -0400147
148 @Override
Sunny Goyal3fbca152017-11-02 18:55:44 -0700149 public void onSaveInstanceState(Bundle outState) {
150 super.onSaveInstanceState(outState);
151 outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mPreferenceHighlighted);
152 }
153
154 @Override
155 public void onResume() {
156 super.onResume();
157
158 Intent intent = getActivity().getIntent();
159 mPreferenceKey = intent.getStringExtra(EXTRA_FRAGMENT_ARG_KEY);
160 if (isAdded() && !mPreferenceHighlighted && !TextUtils.isEmpty(mPreferenceKey)) {
161 getView().postDelayed(this::highlightPreference, DELAY_HIGHLIGHT_DURATION_MILLIS);
162 }
163 }
164
165 private void highlightPreference() {
Sunny Goyal3fbca152017-11-02 18:55:44 -0700166 Preference pref = findPreference(mPreferenceKey);
Sunny Goyalba406e22018-04-02 11:36:12 -0700167 if (pref == null || getPreferenceScreen() == null) {
Sunny Goyal3fbca152017-11-02 18:55:44 -0700168 return;
169 }
Sunny Goyalba406e22018-04-02 11:36:12 -0700170 PreferenceScreen screen = getPreferenceScreen();
171 if (Utilities.ATLEAST_OREO) {
172 screen = selectPreferenceRecursive(pref, screen);
173 }
174 if (screen == null) {
175 return;
176 }
177
178 View root = screen.getDialog() != null
179 ? screen.getDialog().getWindow().getDecorView() : getView();
180 ListView list = root.findViewById(android.R.id.list);
181 if (list == null || list.getAdapter() == null) {
182 return;
183 }
184 Adapter adapter = list.getAdapter();
Sunny Goyal3fbca152017-11-02 18:55:44 -0700185
186 // Find the position
187 int position = -1;
188 for (int i = adapter.getCount() - 1; i >= 0; i--) {
189 if (pref == adapter.getItem(i)) {
190 position = i;
191 break;
192 }
193 }
Sunny Goyalba406e22018-04-02 11:36:12 -0700194 new ListViewHighlighter(list, position);
Sunny Goyal3fbca152017-11-02 18:55:44 -0700195 mPreferenceHighlighted = true;
196 }
197
198 @Override
Sunny Goyalf48e5922016-05-12 15:36:20 -0700199 public void onDestroy() {
Tony Wickham2ab84822017-05-12 14:59:09 -0700200 if (mIconBadgingObserver != null) {
Tonyd48710c2017-07-27 23:23:58 -0700201 mIconBadgingObserver.unregister();
Tony Wickham2ab84822017-05-12 14:59:09 -0700202 mIconBadgingObserver = null;
203 }
Sunny Goyal40253322018-04-19 09:27:38 -0700204 if (mRotationLockObserver != null) {
205 mRotationLockObserver.unregister();
206 mRotationLockObserver = null;
207 }
Sunny Goyalf48e5922016-05-12 15:36:20 -0700208 super.onDestroy();
209 }
Sunny Goyalba406e22018-04-02 11:36:12 -0700210
211 @TargetApi(Build.VERSION_CODES.O)
212 private PreferenceScreen selectPreferenceRecursive(
213 Preference pref, PreferenceScreen topParent) {
214 if (!(pref.getParent() instanceof PreferenceScreen)) {
215 return null;
216 }
217
218 PreferenceScreen parent = (PreferenceScreen) pref.getParent();
219 if (Objects.equals(parent.getKey(), topParent.getKey())) {
220 return parent;
221 } else if (selectPreferenceRecursive(parent, topParent) != null) {
222 ((PreferenceScreen) parent.getParent())
223 .onItemClick(null, null, parent.getOrder(), 0);
224 return parent;
225 } else {
226 return null;
227 }
228 }
Sunny Goyalf48e5922016-05-12 15:36:20 -0700229 }
230
Sunny Goyal40253322018-04-19 09:27:38 -0700231 private static class RotationLockObserver extends SettingsObserver.System {
232
233 private final Preference mRotationPref;
234
235 public RotationLockObserver(Preference rotationPref, ContentResolver resolver) {
236 super(resolver);
237 mRotationPref = rotationPref;
238 }
239
240 @Override
241 public void onSettingChanged(boolean enabled) {
242 mRotationPref.setEnabled(enabled);
243 mRotationPref.setSummary(enabled
244 ? R.string.allow_rotation_desc : R.string.allow_rotation_blocked_desc);
245 }
246 }
247
Sunny Goyalf48e5922016-05-12 15:36:20 -0700248 /**
Tony Wickham2ab84822017-05-12 14:59:09 -0700249 * Content observer which listens for system badging setting changes,
250 * and updates the launcher badging setting subtext accordingly.
251 */
Tonyd48710c2017-07-27 23:23:58 -0700252 private static class IconBadgingObserver extends SettingsObserver.Secure
Sunny Goyalb65e13c2017-08-01 11:04:15 -0700253 implements Preference.OnPreferenceClickListener {
Tony Wickham2ab84822017-05-12 14:59:09 -0700254
Sunny Goyal848cad52017-07-07 03:12:21 -0700255 private final ButtonPreference mBadgingPref;
Tony Wickham2ab84822017-05-12 14:59:09 -0700256 private final ContentResolver mResolver;
Sunny Goyal848cad52017-07-07 03:12:21 -0700257 private final FragmentManager mFragmentManager;
Tony Wickham2ab84822017-05-12 14:59:09 -0700258
Sunny Goyal848cad52017-07-07 03:12:21 -0700259 public IconBadgingObserver(ButtonPreference badgingPref, ContentResolver resolver,
260 FragmentManager fragmentManager) {
Tonyd48710c2017-07-27 23:23:58 -0700261 super(resolver);
Tony Wickham2ab84822017-05-12 14:59:09 -0700262 mBadgingPref = badgingPref;
263 mResolver = resolver;
Sunny Goyal848cad52017-07-07 03:12:21 -0700264 mFragmentManager = fragmentManager;
Tony Wickham2ab84822017-05-12 14:59:09 -0700265 }
266
267 @Override
Tonyd48710c2017-07-27 23:23:58 -0700268 public void onSettingChanged(boolean enabled) {
Sunny Goyal848cad52017-07-07 03:12:21 -0700269 int summary = enabled ? R.string.icon_badging_desc_on : R.string.icon_badging_desc_off;
270
271 boolean serviceEnabled = true;
272 if (enabled) {
273 // Check if the listener is enabled or not.
274 String enabledListeners =
275 Settings.Secure.getString(mResolver, NOTIFICATION_ENABLED_LISTENERS);
276 ComponentName myListener =
277 new ComponentName(mBadgingPref.getContext(), NotificationListener.class);
278 serviceEnabled = enabledListeners != null &&
279 (enabledListeners.contains(myListener.flattenToString()) ||
280 enabledListeners.contains(myListener.flattenToShortString()));
281 if (!serviceEnabled) {
282 summary = R.string.title_missing_notification_access;
283 }
284 }
Sunny Goyalb65e13c2017-08-01 11:04:15 -0700285 mBadgingPref.setWidgetFrameVisible(!serviceEnabled);
286 mBadgingPref.setOnPreferenceClickListener(serviceEnabled ? null : this);
Sunny Goyal848cad52017-07-07 03:12:21 -0700287 mBadgingPref.setSummary(summary);
288
289 }
290
291 @Override
Sunny Goyalb65e13c2017-08-01 11:04:15 -0700292 public boolean onPreferenceClick(Preference preference) {
Sunny Goyal848cad52017-07-07 03:12:21 -0700293 new NotificationAccessConfirmation().show(mFragmentManager, "notification_access");
Sunny Goyalb65e13c2017-08-01 11:04:15 -0700294 return true;
Tony Wickham2ab84822017-05-12 14:59:09 -0700295 }
296 }
297
Sunny Goyal848cad52017-07-07 03:12:21 -0700298 public static class NotificationAccessConfirmation
299 extends DialogFragment implements DialogInterface.OnClickListener {
300
301 @Override
302 public Dialog onCreateDialog(Bundle savedInstanceState) {
303 final Context context = getActivity();
304 String msg = context.getString(R.string.msg_missing_notification_access,
305 context.getString(R.string.derived_app_name));
306 return new AlertDialog.Builder(context)
307 .setTitle(R.string.title_missing_notification_access)
308 .setMessage(msg)
309 .setNegativeButton(android.R.string.cancel, null)
310 .setPositiveButton(R.string.title_change_settings, this)
311 .create();
312 }
313
314 @Override
315 public void onClick(DialogInterface dialogInterface, int i) {
316 ComponentName cn = new ComponentName(getActivity(), NotificationListener.class);
317 Intent intent = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS)
318 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
319 .putExtra(":settings:fragment_args_key", cn.flattenToString());
320 getActivity().startActivity(intent);
321 }
322 }
Rahul Chaturvedi7fc77ca2015-05-19 18:02:16 -0700323}