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