1 /*
2  * Copyright (C) 2017 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.launcher3.settings;
17 
18 import static com.android.launcher3.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY;
19 import static com.android.launcher3.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGS;
20 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
21 import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI;
22 
23 import android.app.AlertDialog;
24 import android.app.Dialog;
25 import android.content.ComponentName;
26 import android.content.Context;
27 import android.content.DialogInterface;
28 import android.content.Intent;
29 import android.database.ContentObserver;
30 import android.os.Bundle;
31 import android.provider.Settings;
32 import android.util.AttributeSet;
33 import android.view.View;
34 
35 import androidx.fragment.app.DialogFragment;
36 import androidx.preference.Preference;
37 import androidx.preference.PreferenceViewHolder;
38 
39 import com.android.launcher3.R;
40 import com.android.launcher3.notification.NotificationListener;
41 import com.android.launcher3.util.SettingsCache;
42 
43 /**
44  * A {@link Preference} for indicating notification dots status.
45  * Also has utility methods for updating UI based on dots status changes.
46  */
47 public class NotificationDotsPreference extends Preference
48         implements SettingsCache.OnChangeListener {
49 
50     private boolean mWidgetFrameVisible = false;
51 
52     /** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */
53     private static final String NOTIFICATION_ENABLED_LISTENERS = "enabled_notification_listeners";
54 
55     private final ContentObserver mListenerListObserver =
56             new ContentObserver(MAIN_EXECUTOR.getHandler()) {
57         @Override
58         public void onChange(boolean selfChange) {
59             updateUI();
60         }
61     };
62 
NotificationDotsPreference( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)63     public NotificationDotsPreference(
64             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
65         super(context, attrs, defStyleAttr, defStyleRes);
66     }
67 
NotificationDotsPreference(Context context, AttributeSet attrs, int defStyleAttr)68     public NotificationDotsPreference(Context context, AttributeSet attrs, int defStyleAttr) {
69         super(context, attrs, defStyleAttr);
70     }
71 
NotificationDotsPreference(Context context, AttributeSet attrs)72     public NotificationDotsPreference(Context context, AttributeSet attrs) {
73         super(context, attrs);
74     }
75 
NotificationDotsPreference(Context context)76     public NotificationDotsPreference(Context context) {
77         super(context);
78     }
79 
80     @Override
onAttached()81     public void onAttached() {
82         super.onAttached();
83         SettingsCache.INSTANCE.get(getContext()).register(NOTIFICATION_BADGING_URI, this);
84         getContext().getContentResolver().registerContentObserver(
85                 Settings.Secure.getUriFor(NOTIFICATION_ENABLED_LISTENERS),
86                 false, mListenerListObserver);
87         updateUI();
88 
89         // Update intent
90         Bundle extras = new Bundle();
91         extras.putString(EXTRA_FRAGMENT_ARG_KEY, "notification_badging");
92         setIntent(new Intent("android.settings.NOTIFICATION_SETTINGS")
93                 .putExtra(EXTRA_SHOW_FRAGMENT_ARGS, extras));
94     }
95 
updateUI()96     private void updateUI() {
97         onSettingsChanged(SettingsCache.INSTANCE.get(getContext())
98                 .getValue(NOTIFICATION_BADGING_URI));
99     }
100 
101     @Override
onDetached()102     public void onDetached() {
103         super.onDetached();
104         SettingsCache.INSTANCE.get(getContext()).unregister(NOTIFICATION_BADGING_URI, this);
105         getContext().getContentResolver().unregisterContentObserver(mListenerListObserver);
106 
107     }
108 
setWidgetFrameVisible(boolean isVisible)109     private void setWidgetFrameVisible(boolean isVisible) {
110         if (mWidgetFrameVisible != isVisible) {
111             mWidgetFrameVisible = isVisible;
112             notifyChanged();
113         }
114     }
115 
116     @Override
onBindViewHolder(PreferenceViewHolder holder)117     public void onBindViewHolder(PreferenceViewHolder holder) {
118         super.onBindViewHolder(holder);
119 
120         View widgetFrame = holder.findViewById(android.R.id.widget_frame);
121         if (widgetFrame != null) {
122             widgetFrame.setVisibility(mWidgetFrameVisible ? View.VISIBLE : View.GONE);
123         }
124     }
125 
126     @Override
onSettingsChanged(boolean enabled)127     public void onSettingsChanged(boolean enabled) {
128         int summary = enabled
129                 ? R.string.notification_dots_desc_on
130                 : R.string.notification_dots_desc_off;
131 
132         boolean serviceEnabled = true;
133         if (enabled) {
134             // Check if the listener is enabled or not.
135             String enabledListeners = Settings.Secure.getString(
136                     getContext().getContentResolver(), NOTIFICATION_ENABLED_LISTENERS);
137             ComponentName myListener =
138                     new ComponentName(getContext(), NotificationListener.class);
139             serviceEnabled = enabledListeners != null &&
140                     (enabledListeners.contains(myListener.flattenToString()) ||
141                             enabledListeners.contains(myListener.flattenToShortString()));
142             if (!serviceEnabled) {
143                 summary = R.string.title_missing_notification_access;
144             }
145         }
146         setWidgetFrameVisible(!serviceEnabled);
147         setFragment(serviceEnabled ? null : NotificationAccessConfirmation.class.getName());
148         setSummary(summary);
149     }
150 
151     public static class NotificationAccessConfirmation
152             extends DialogFragment implements DialogInterface.OnClickListener {
153 
154         @Override
onCreateDialog(Bundle savedInstanceState)155         public Dialog onCreateDialog(Bundle savedInstanceState) {
156             final Context context = getActivity();
157             String msg = context.getString(R.string.msg_missing_notification_access,
158                     context.getString(R.string.derived_app_name));
159             return new AlertDialog.Builder(context)
160                     .setTitle(R.string.title_missing_notification_access)
161                     .setMessage(msg)
162                     .setNegativeButton(android.R.string.cancel, null)
163                     .setPositiveButton(R.string.title_change_settings, this)
164                     .create();
165         }
166 
167         @Override
onClick(DialogInterface dialogInterface, int i)168         public void onClick(DialogInterface dialogInterface, int i) {
169             ComponentName cn = new ComponentName(getActivity(), NotificationListener.class);
170             Bundle showFragmentArgs = new Bundle();
171             showFragmentArgs.putString(EXTRA_FRAGMENT_ARG_KEY, cn.flattenToString());
172 
173             Intent intent = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS)
174                     .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
175                     .putExtra(EXTRA_FRAGMENT_ARG_KEY, cn.flattenToString())
176                     .putExtra(EXTRA_SHOW_FRAGMENT_ARGS, showFragmentArgs);
177             getActivity().startActivity(intent);
178         }
179     }
180 }
181