• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.launcher3;
18 
19 import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
20 import static com.android.launcher3.states.RotationHelper.getAllowRotationDefaultValue;
21 
22 import android.annotation.TargetApi;
23 import android.app.Activity;
24 import android.app.AlertDialog;
25 import android.app.Dialog;
26 import android.app.DialogFragment;
27 import android.app.FragmentManager;
28 import android.content.ComponentName;
29 import android.content.ContentResolver;
30 import android.content.Context;
31 import android.content.DialogInterface;
32 import android.content.Intent;
33 import android.os.Build;
34 import android.os.Bundle;
35 import android.preference.ListPreference;
36 import android.preference.Preference;
37 import android.preference.PreferenceFragment;
38 import android.preference.PreferenceScreen;
39 import android.provider.Settings;
40 import android.text.TextUtils;
41 import android.view.View;
42 import android.widget.Adapter;
43 import android.widget.ListView;
44 
45 import com.android.launcher3.graphics.IconShapeOverride;
46 import com.android.launcher3.notification.NotificationListener;
47 import com.android.launcher3.util.ListViewHighlighter;
48 import com.android.launcher3.util.SettingsObserver;
49 import com.android.launcher3.views.ButtonPreference;
50 
51 import java.util.Objects;
52 
53 /**
54  * Settings activity for Launcher. Currently implements the following setting: Allow rotation
55  */
56 public class SettingsActivity extends Activity {
57 
58     private static final String ICON_BADGING_PREFERENCE_KEY = "pref_icon_badging";
59     /** Hidden field Settings.Secure.NOTIFICATION_BADGING */
60     public static final String NOTIFICATION_BADGING = "notification_badging";
61     /** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */
62     private static final String NOTIFICATION_ENABLED_LISTENERS = "enabled_notification_listeners";
63 
64     private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
65     private static final String EXTRA_SHOW_FRAGMENT_ARGS = ":settings:show_fragment_args";
66     private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600;
67     private static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted";
68 
69     @Override
onCreate(Bundle savedInstanceState)70     protected void onCreate(Bundle savedInstanceState) {
71         super.onCreate(savedInstanceState);
72 
73         if (savedInstanceState == null) {
74             // Display the fragment as the main content.
75             getFragmentManager().beginTransaction()
76                     .replace(android.R.id.content, getNewFragment())
77                     .commit();
78         }
79     }
80 
getNewFragment()81     protected PreferenceFragment getNewFragment() {
82         return new LauncherSettingsFragment();
83     }
84 
85     /**
86      * This fragment shows the launcher preferences.
87      */
88     public static class LauncherSettingsFragment extends PreferenceFragment {
89 
90         private IconBadgingObserver mIconBadgingObserver;
91 
92         private String mPreferenceKey;
93         private boolean mPreferenceHighlighted = false;
94 
95         @Override
onCreate(Bundle savedInstanceState)96         public void onCreate(Bundle savedInstanceState) {
97             super.onCreate(savedInstanceState);
98             if (savedInstanceState != null) {
99                 mPreferenceHighlighted = savedInstanceState.getBoolean(SAVE_HIGHLIGHTED_KEY);
100             }
101 
102             getPreferenceManager().setSharedPreferencesName(LauncherFiles.SHARED_PREFERENCES_KEY);
103             addPreferencesFromResource(R.xml.launcher_preferences);
104 
105             ContentResolver resolver = getActivity().getContentResolver();
106 
107             ButtonPreference iconBadgingPref =
108                     (ButtonPreference) findPreference(ICON_BADGING_PREFERENCE_KEY);
109             if (!Utilities.ATLEAST_OREO) {
110                 getPreferenceScreen().removePreference(
111                         findPreference(SessionCommitReceiver.ADD_ICON_PREFERENCE_KEY));
112                 getPreferenceScreen().removePreference(iconBadgingPref);
113             } else if (!getResources().getBoolean(R.bool.notification_badging_enabled)) {
114                 getPreferenceScreen().removePreference(iconBadgingPref);
115             } else {
116                 // Listen to system notification badge settings while this UI is active.
117                 mIconBadgingObserver = new IconBadgingObserver(
118                         iconBadgingPref, resolver, getFragmentManager());
119                 mIconBadgingObserver.register(NOTIFICATION_BADGING, NOTIFICATION_ENABLED_LISTENERS);
120             }
121 
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             }
130 
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                 // Initialize the UI once
138                 rotationPref.setDefaultValue(getAllowRotationDefaultValue());
139             }
140         }
141 
142         @Override
onSaveInstanceState(Bundle outState)143         public void onSaveInstanceState(Bundle outState) {
144             super.onSaveInstanceState(outState);
145             outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mPreferenceHighlighted);
146         }
147 
148         @Override
onResume()149         public void onResume() {
150             super.onResume();
151 
152             Intent intent = getActivity().getIntent();
153             mPreferenceKey = intent.getStringExtra(EXTRA_FRAGMENT_ARG_KEY);
154             if (isAdded() && !mPreferenceHighlighted && !TextUtils.isEmpty(mPreferenceKey)) {
155                 getView().postDelayed(this::highlightPreference, DELAY_HIGHLIGHT_DURATION_MILLIS);
156             }
157         }
158 
highlightPreference()159         private void highlightPreference() {
160             Preference pref = findPreference(mPreferenceKey);
161             if (pref == null || getPreferenceScreen() == null) {
162                 return;
163             }
164             PreferenceScreen screen = getPreferenceScreen();
165             if (Utilities.ATLEAST_OREO) {
166                 screen = selectPreferenceRecursive(pref, screen);
167             }
168             if (screen == null) {
169                 return;
170             }
171 
172             View root = screen.getDialog() != null
173                     ? screen.getDialog().getWindow().getDecorView() : getView();
174             ListView list = root.findViewById(android.R.id.list);
175             if (list == null || list.getAdapter() == null) {
176                 return;
177             }
178             Adapter adapter = list.getAdapter();
179 
180             // Find the position
181             int position = -1;
182             for (int i = adapter.getCount() - 1; i >= 0; i--) {
183                 if (pref == adapter.getItem(i)) {
184                     position = i;
185                     break;
186                 }
187             }
188             new ListViewHighlighter(list, position);
189             mPreferenceHighlighted = true;
190         }
191 
192         @Override
onDestroy()193         public void onDestroy() {
194             if (mIconBadgingObserver != null) {
195                 mIconBadgingObserver.unregister();
196                 mIconBadgingObserver = null;
197             }
198             super.onDestroy();
199         }
200 
201         @TargetApi(Build.VERSION_CODES.O)
selectPreferenceRecursive( Preference pref, PreferenceScreen topParent)202         private PreferenceScreen selectPreferenceRecursive(
203                 Preference pref, PreferenceScreen topParent) {
204             if (!(pref.getParent() instanceof PreferenceScreen)) {
205                 return null;
206             }
207 
208             PreferenceScreen parent = (PreferenceScreen) pref.getParent();
209             if (Objects.equals(parent.getKey(), topParent.getKey())) {
210                 return parent;
211             } else if (selectPreferenceRecursive(parent, topParent) != null) {
212                 ((PreferenceScreen) parent.getParent())
213                         .onItemClick(null, null, parent.getOrder(), 0);
214                 return parent;
215             } else {
216                 return null;
217             }
218         }
219     }
220 
221     /**
222      * Content observer which listens for system badging setting changes,
223      * and updates the launcher badging setting subtext accordingly.
224      */
225     private static class IconBadgingObserver extends SettingsObserver.Secure
226             implements Preference.OnPreferenceClickListener {
227 
228         private final ButtonPreference mBadgingPref;
229         private final ContentResolver mResolver;
230         private final FragmentManager mFragmentManager;
231 
IconBadgingObserver(ButtonPreference badgingPref, ContentResolver resolver, FragmentManager fragmentManager)232         public IconBadgingObserver(ButtonPreference badgingPref, ContentResolver resolver,
233                 FragmentManager fragmentManager) {
234             super(resolver);
235             mBadgingPref = badgingPref;
236             mResolver = resolver;
237             mFragmentManager = fragmentManager;
238         }
239 
240         @Override
onSettingChanged(boolean enabled)241         public void onSettingChanged(boolean enabled) {
242             int summary = enabled ? R.string.icon_badging_desc_on : R.string.icon_badging_desc_off;
243 
244             boolean serviceEnabled = true;
245             if (enabled) {
246                 // Check if the listener is enabled or not.
247                 String enabledListeners =
248                         Settings.Secure.getString(mResolver, NOTIFICATION_ENABLED_LISTENERS);
249                 ComponentName myListener =
250                         new ComponentName(mBadgingPref.getContext(), NotificationListener.class);
251                 serviceEnabled = enabledListeners != null &&
252                         (enabledListeners.contains(myListener.flattenToString()) ||
253                                 enabledListeners.contains(myListener.flattenToShortString()));
254                 if (!serviceEnabled) {
255                     summary = R.string.title_missing_notification_access;
256                 }
257             }
258             mBadgingPref.setWidgetFrameVisible(!serviceEnabled);
259             mBadgingPref.setOnPreferenceClickListener(serviceEnabled ? null : this);
260             mBadgingPref.setSummary(summary);
261 
262         }
263 
264         @Override
onPreferenceClick(Preference preference)265         public boolean onPreferenceClick(Preference preference) {
266             new NotificationAccessConfirmation().show(mFragmentManager, "notification_access");
267             return true;
268         }
269     }
270 
271     public static class NotificationAccessConfirmation
272             extends DialogFragment implements DialogInterface.OnClickListener {
273 
274         @Override
onCreateDialog(Bundle savedInstanceState)275         public Dialog onCreateDialog(Bundle savedInstanceState) {
276             final Context context = getActivity();
277             String msg = context.getString(R.string.msg_missing_notification_access,
278                     context.getString(R.string.derived_app_name));
279             return new AlertDialog.Builder(context)
280                     .setTitle(R.string.title_missing_notification_access)
281                     .setMessage(msg)
282                     .setNegativeButton(android.R.string.cancel, null)
283                     .setPositiveButton(R.string.title_change_settings, this)
284                     .create();
285         }
286 
287         @Override
onClick(DialogInterface dialogInterface, int i)288         public void onClick(DialogInterface dialogInterface, int i) {
289             ComponentName cn = new ComponentName(getActivity(), NotificationListener.class);
290             Bundle showFragmentArgs = new Bundle();
291             showFragmentArgs.putString(EXTRA_FRAGMENT_ARG_KEY, cn.flattenToString());
292 
293             Intent intent = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS)
294                     .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
295                     .putExtra(EXTRA_FRAGMENT_ARG_KEY, cn.flattenToString())
296                     .putExtra(EXTRA_SHOW_FRAGMENT_ARGS, showFragmentArgs);
297             getActivity().startActivity(intent);
298         }
299     }
300 }
301