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