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.settings.notification.zen; 18 19 import android.app.Dialog; 20 import android.app.NotificationManager; 21 import android.app.settings.SettingsEnums; 22 import android.content.Context; 23 import android.content.DialogInterface; 24 import android.content.pm.ApplicationInfo; 25 import android.content.pm.ComponentInfo; 26 import android.content.pm.PackageManager; 27 import android.graphics.drawable.Drawable; 28 import android.os.AsyncTask; 29 import android.os.Bundle; 30 import android.service.notification.ZenModeConfig; 31 import android.util.Log; 32 import android.view.LayoutInflater; 33 import android.view.View; 34 import android.widget.ImageView; 35 import android.widget.LinearLayout; 36 import android.widget.TextView; 37 38 import androidx.appcompat.app.AlertDialog; 39 import androidx.fragment.app.Fragment; 40 41 import com.android.settings.R; 42 import com.android.settings.core.instrumentation.InstrumentedDialogFragment; 43 import com.android.settings.utils.ZenServiceListing; 44 45 import java.lang.ref.WeakReference; 46 import java.text.Collator; 47 import java.util.Comparator; 48 import java.util.Set; 49 import java.util.TreeSet; 50 51 public class ZenRuleSelectionDialog extends InstrumentedDialogFragment { 52 private static final String TAG = "ZenRuleSelectionDialog"; 53 private static final boolean DEBUG = ZenModeSettings.DEBUG; 54 55 private static ZenServiceListing mServiceListing; 56 protected static PositiveClickListener mPositiveClickListener; 57 58 private static Context mContext; 59 private static PackageManager mPm; 60 private static NotificationManager mNm; 61 private LinearLayout mRuleContainer; 62 63 /** 64 * The interface we expect a listener to implement. 65 */ 66 public interface PositiveClickListener { onSystemRuleSelected(ZenRuleInfo ruleInfo, Fragment parent)67 void onSystemRuleSelected(ZenRuleInfo ruleInfo, Fragment parent); onExternalRuleSelected(ZenRuleInfo ruleInfo, Fragment parent)68 void onExternalRuleSelected(ZenRuleInfo ruleInfo, Fragment parent); 69 } 70 71 @Override getMetricsCategory()72 public int getMetricsCategory() { 73 return SettingsEnums.NOTIFICATION_ZEN_MODE_RULE_SELECTION_DIALOG; 74 } 75 show(Context context, Fragment parent, PositiveClickListener listener, ZenServiceListing serviceListing)76 public static void show(Context context, Fragment parent, PositiveClickListener 77 listener, ZenServiceListing serviceListing) { 78 mPositiveClickListener = listener; 79 mContext = context; 80 mPm = mContext.getPackageManager(); 81 mNm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); 82 mServiceListing = serviceListing; 83 84 ZenRuleSelectionDialog dialog = new ZenRuleSelectionDialog(); 85 dialog.setTargetFragment(parent, 0); 86 dialog.show(parent.getFragmentManager(), TAG); 87 } 88 89 @Override onCreateDialog(Bundle savedInstanceState)90 public Dialog onCreateDialog(Bundle savedInstanceState) { 91 final View v = LayoutInflater.from(getContext()).inflate(R.layout.zen_rule_type_selection, 92 null, false); 93 94 mRuleContainer = (LinearLayout) v.findViewById(R.id.rule_container); 95 if (mServiceListing != null) { 96 bindType(defaultNewEvent()); 97 bindType(defaultNewSchedule()); 98 mServiceListing.addZenCallback(mServiceListingCallback); 99 mServiceListing.reloadApprovedServices(); 100 } 101 return new AlertDialog.Builder(getContext()) 102 .setTitle(R.string.zen_mode_choose_rule_type) 103 .setView(v) 104 .setNegativeButton(R.string.cancel, null) 105 .create(); 106 } 107 108 @Override onDismiss(DialogInterface dialog)109 public void onDismiss(DialogInterface dialog) { 110 super.onDismiss(dialog); 111 if (mServiceListing != null) { 112 mServiceListing.removeZenCallback(mServiceListingCallback); 113 } 114 } 115 bindType(final ZenRuleInfo ri)116 private void bindType(final ZenRuleInfo ri) { 117 try { 118 ApplicationInfo info = mPm.getApplicationInfo(ri.packageName, 0); 119 final LinearLayout v = (LinearLayout) LayoutInflater.from(mContext).inflate( 120 R.layout.zen_rule_type, null, false); 121 122 ImageView iconView = v.findViewById(R.id.icon); 123 ((TextView) v.findViewById(R.id.title)).setText(ri.title); 124 if (!ri.isSystem) { 125 LoadIconTask task = new LoadIconTask(iconView); 126 task.execute(info); 127 128 TextView subtitle = (TextView) v.findViewById(R.id.subtitle); 129 subtitle.setText(info.loadLabel(mPm)); 130 subtitle.setVisibility(View.VISIBLE); 131 } else { 132 if (ZenModeConfig.isValidScheduleConditionId(ri.defaultConditionId)) { 133 iconView.setImageDrawable(mContext.getDrawable(R.drawable.ic_timelapse)); 134 } else if (ZenModeConfig.isValidEventConditionId(ri.defaultConditionId)) { 135 iconView.setImageDrawable(mContext.getDrawable(R.drawable.ic_event)); 136 } 137 } 138 v.setOnClickListener(new View.OnClickListener() { 139 @Override 140 public void onClick(View v) { 141 dismiss(); 142 if (ri.isSystem) { 143 mPositiveClickListener.onSystemRuleSelected(ri, getTargetFragment()); 144 } else { 145 mPositiveClickListener.onExternalRuleSelected(ri, getTargetFragment()); 146 } 147 } 148 }); 149 mRuleContainer.addView(v); 150 } catch (PackageManager.NameNotFoundException e) { 151 // Omit rule. 152 } 153 } 154 defaultNewSchedule()155 private ZenRuleInfo defaultNewSchedule() { 156 final ZenModeConfig.ScheduleInfo schedule = new ZenModeConfig.ScheduleInfo(); 157 schedule.days = ZenModeConfig.ALL_DAYS; 158 schedule.startHour = 22; 159 schedule.endHour = 7; 160 final ZenRuleInfo rt = new ZenRuleInfo(); 161 rt.settingsAction = ZenModeScheduleRuleSettings.ACTION; 162 rt.title = mContext.getString(R.string.zen_schedule_rule_type_name); 163 rt.packageName = ZenModeConfig.getEventConditionProvider().getPackageName(); 164 rt.defaultConditionId = ZenModeConfig.toScheduleConditionId(schedule); 165 rt.serviceComponent = ZenModeConfig.getScheduleConditionProvider(); 166 rt.isSystem = true; 167 return rt; 168 } 169 defaultNewEvent()170 private ZenRuleInfo defaultNewEvent() { 171 final ZenModeConfig.EventInfo event = new ZenModeConfig.EventInfo(); 172 event.calName = null; // any calendar 173 event.calendarId = null; 174 event.reply = ZenModeConfig.EventInfo.REPLY_ANY_EXCEPT_NO; 175 final ZenRuleInfo rt = new ZenRuleInfo(); 176 rt.settingsAction = ZenModeEventRuleSettings.ACTION; 177 rt.title = mContext.getString(R.string.zen_event_rule_type_name); 178 rt.packageName = ZenModeConfig.getScheduleConditionProvider().getPackageName(); 179 rt.defaultConditionId = ZenModeConfig.toEventConditionId(event); 180 rt.serviceComponent = ZenModeConfig.getEventConditionProvider(); 181 rt.isSystem = true; 182 return rt; 183 } 184 bindExternalRules(Set<ZenRuleInfo> externalRuleTypes)185 private void bindExternalRules(Set<ZenRuleInfo> externalRuleTypes) { 186 for (ZenRuleInfo ri : externalRuleTypes) { 187 bindType(ri); 188 } 189 } 190 191 private final ZenServiceListing.Callback mServiceListingCallback = new 192 ZenServiceListing.Callback() { 193 @Override 194 public void onComponentsReloaded(Set<ComponentInfo> componentInfos) { 195 if (DEBUG) Log.d(TAG, "Reloaded: count=" + componentInfos.size()); 196 197 Set<ZenRuleInfo> externalRuleTypes = new TreeSet<>(RULE_TYPE_COMPARATOR); 198 for (ComponentInfo ci : componentInfos) { 199 final ZenRuleInfo ri = AbstractZenModeAutomaticRulePreferenceController. 200 getRuleInfo(mPm, ci); 201 if (ri != null && ri.configurationActivity != null 202 && mNm.isNotificationPolicyAccessGrantedForPackage(ri.packageName) 203 && (ri.ruleInstanceLimit <= 0 || ri.ruleInstanceLimit 204 >= (mNm.getRuleInstanceCount(ci.getComponentName()) + 1))) { 205 externalRuleTypes.add(ri); 206 } 207 } 208 bindExternalRules(externalRuleTypes); 209 } 210 }; 211 212 private static final Comparator<ZenRuleInfo> RULE_TYPE_COMPARATOR = 213 new Comparator<ZenRuleInfo>() { 214 private final Collator mCollator = Collator.getInstance(); 215 216 @Override 217 public int compare(ZenRuleInfo lhs, ZenRuleInfo rhs) { 218 int byAppName = mCollator.compare(lhs.packageLabel, rhs.packageLabel); 219 if (byAppName != 0) { 220 return byAppName; 221 } else { 222 return mCollator.compare(lhs.title, rhs.title); 223 } 224 } 225 }; 226 227 private class LoadIconTask extends AsyncTask<ApplicationInfo, Void, Drawable> { 228 private final WeakReference<ImageView> viewReference; 229 LoadIconTask(ImageView view)230 public LoadIconTask(ImageView view) { 231 viewReference = new WeakReference<>(view); 232 } 233 234 @Override doInBackground(ApplicationInfo... params)235 protected Drawable doInBackground(ApplicationInfo... params) { 236 return params[0].loadIcon(mPm); 237 } 238 239 @Override onPostExecute(Drawable icon)240 protected void onPostExecute(Drawable icon) { 241 if (icon != null) { 242 final ImageView view = viewReference.get(); 243 if (view != null) { 244 view.setImageDrawable(icon); 245 } 246 } 247 } 248 } 249 }