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; 18 19 import android.app.AlertDialog; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.DialogInterface; 23 import android.content.DialogInterface.OnDismissListener; 24 import android.content.pm.ServiceInfo; 25 import android.content.res.ColorStateList; 26 import android.net.Uri; 27 import android.service.notification.ZenModeConfig; 28 import android.service.notification.ZenModeConfig.EventInfo; 29 import android.service.notification.ZenModeConfig.ScheduleInfo; 30 import android.text.Editable; 31 import android.text.TextUtils; 32 import android.text.TextWatcher; 33 import android.util.ArraySet; 34 import android.util.Log; 35 import android.util.TypedValue; 36 import android.view.LayoutInflater; 37 import android.view.View; 38 import android.widget.EditText; 39 import android.widget.RadioButton; 40 import android.widget.RadioGroup; 41 42 import com.android.settings.R; 43 44 import java.util.List; 45 46 public abstract class ZenRuleNameDialog { 47 private static final String TAG = ZenModeSettings.TAG; 48 private static final boolean DEBUG = ZenModeSettings.DEBUG; 49 50 private final AlertDialog mDialog; 51 private final EditText mEditText; 52 private final View mWarning; 53 private final RadioGroup mTypes; 54 private final ColorStateList mWarningTint; 55 private final ColorStateList mOriginalTint; 56 private final String mOriginalRuleName; 57 private final ArraySet<String> mExistingNames; 58 private final ServiceListing mServiceListing; 59 private final RuleInfo[] mExternalRules = new RuleInfo[3]; 60 private final boolean mIsNew; 61 62 public ZenRuleNameDialog(Context context, ServiceListing serviceListing, String ruleName, 63 ArraySet<String> existingNames) { 64 mServiceListing = serviceListing; 65 mIsNew = ruleName == null; 66 mOriginalRuleName = ruleName; 67 mWarningTint = ColorStateList.valueOf(context.getColor(R.color.zen_rule_name_warning)); 68 final View v = LayoutInflater.from(context).inflate(R.layout.zen_rule_name, null, false); 69 mEditText = (EditText) v.findViewById(R.id.rule_name); 70 mWarning = v.findViewById(R.id.rule_name_warning); 71 if (!mIsNew) { 72 mEditText.setText(ruleName); 73 } 74 TypedValue outValue = new TypedValue(); 75 context.getTheme().resolveAttribute(android.R.attr.colorAccent, outValue, true); 76 mOriginalTint = ColorStateList.valueOf(outValue.data); 77 mEditText.setSelectAllOnFocus(true); 78 mTypes = (RadioGroup) v.findViewById(R.id.rule_types); 79 if (mServiceListing != null) { 80 bindType(R.id.rule_type_schedule, defaultNewSchedule()); 81 bindType(R.id.rule_type_event, defaultNewEvent()); 82 bindExternalRules(); 83 mServiceListing.addCallback(mServiceListingCallback); 84 mServiceListing.reload(); 85 } else { 86 mTypes.setVisibility(View.GONE); 87 } 88 mDialog = new AlertDialog.Builder(context) 89 .setTitle(mIsNew ? R.string.zen_mode_add_rule : R.string.zen_mode_rule_name) 90 .setView(v) 91 .setPositiveButton(R.string.okay, new DialogInterface.OnClickListener() { 92 @Override 93 public void onClick(DialogInterface dialog, int which) { 94 final String newName = trimmedText(); 95 if (!mIsNew && mOriginalRuleName != null 96 && mOriginalRuleName.equalsIgnoreCase(newName)) { 97 return; // no change to an existing rule, just dismiss 98 } 99 onOk(newName, selectedRuleInfo()); 100 } 101 }) 102 .setOnDismissListener(new OnDismissListener() { 103 @Override 104 public void onDismiss(DialogInterface dialog) { 105 if (mServiceListing != null) { 106 mServiceListing.removeCallback(mServiceListingCallback); 107 } 108 } 109 }) 110 .setNegativeButton(R.string.cancel, null) 111 .create(); 112 mEditText.addTextChangedListener(new TextWatcher() { 113 @Override 114 public void onTextChanged(CharSequence s, int start, int before, int count) { 115 // noop 116 } 117 118 @Override 119 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 120 // noop 121 } 122 123 @Override 124 public void afterTextChanged(Editable s) { 125 updatePositiveButtonAndWarning(); 126 } 127 }); 128 mExistingNames = new ArraySet<String>(existingNames.size()); 129 for (String existingName : existingNames) { 130 mExistingNames.add(existingName.toLowerCase()); 131 } 132 } 133 134 abstract public void onOk(String ruleName, RuleInfo ruleInfo); 135 136 public void show() { 137 mDialog.show(); 138 updatePositiveButtonAndWarning(); 139 } 140 141 private void bindType(int id, RuleInfo ri) { 142 final RadioButton rb = (RadioButton) mTypes.findViewById(id); 143 if (ri == null) { 144 rb.setVisibility(View.GONE); 145 return; 146 } 147 rb.setVisibility(View.VISIBLE); 148 if (ri.caption != null) { 149 rb.setText(ri.caption); 150 } 151 rb.setTag(ri); 152 } 153 154 private RuleInfo selectedRuleInfo() { 155 final int id = mTypes.getCheckedRadioButtonId(); 156 if (id == -1) return null; 157 final RadioButton rb = (RadioButton) mTypes.findViewById(id); 158 return (RuleInfo) rb.getTag(); 159 } 160 161 private String trimmedText() { 162 return mEditText.getText() == null ? null : mEditText.getText().toString().trim(); 163 } 164 165 private void updatePositiveButtonAndWarning() { 166 final String name = trimmedText(); 167 final boolean validName = !TextUtils.isEmpty(name) 168 && (name.equalsIgnoreCase(mOriginalRuleName) 169 || !mExistingNames.contains(name.toLowerCase())); 170 mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(validName); 171 final boolean showWarning = !TextUtils.isEmpty(name) && !validName; 172 mWarning.setVisibility(showWarning ? View.VISIBLE : View.INVISIBLE); 173 mEditText.setBackgroundTintList(showWarning ? mWarningTint : mOriginalTint); 174 } 175 176 private static RuleInfo defaultNewSchedule() { 177 final ScheduleInfo schedule = new ScheduleInfo(); 178 schedule.days = ZenModeConfig.ALL_DAYS; 179 schedule.startHour = 22; 180 schedule.endHour = 7; 181 final RuleInfo rt = new RuleInfo(); 182 rt.settingsAction = ZenModeScheduleRuleSettings.ACTION; 183 rt.defaultConditionId = ZenModeConfig.toScheduleConditionId(schedule); 184 return rt; 185 } 186 187 private static RuleInfo defaultNewEvent() { 188 final EventInfo event = new EventInfo(); 189 event.calendar = null; // any calendar 190 event.reply = EventInfo.REPLY_ANY_EXCEPT_NO; 191 final RuleInfo rt = new RuleInfo(); 192 rt.settingsAction = ZenModeEventRuleSettings.ACTION; 193 rt.defaultConditionId = ZenModeConfig.toEventConditionId(event); 194 return rt; 195 } 196 197 private void bindExternalRules() { 198 bindType(R.id.rule_type_3, mExternalRules[0]); 199 bindType(R.id.rule_type_4, mExternalRules[1]); 200 bindType(R.id.rule_type_5, mExternalRules[2]); 201 } 202 203 private final ServiceListing.Callback mServiceListingCallback = new ServiceListing.Callback() { 204 @Override 205 public void onServicesReloaded(List<ServiceInfo> services) { 206 if (DEBUG) Log.d(TAG, "Services reloaded: count=" + services.size()); 207 mExternalRules[0] = mExternalRules[1] = mExternalRules[2] = null; 208 int i = 0; 209 for (ServiceInfo si : services) { 210 final RuleInfo ri = ZenModeExternalRuleSettings.getRuleInfo(si); 211 if (ri != null) { 212 mExternalRules[i] = ri; 213 i++; 214 if (i == mExternalRules.length) { 215 break; 216 } 217 } 218 } 219 bindExternalRules(); 220 } 221 }; 222 223 public static class RuleInfo { 224 public String caption; 225 public String settingsAction; 226 public Uri defaultConditionId; 227 public ComponentName serviceComponent; 228 public ComponentName configurationActivity; 229 } 230 231 }