DozeParameters.java revision 5753f05e85aa26fb370260e3dcfd44a27f8e43de
1/* 2 * Copyright (C) 2014 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 17package com.android.systemui.statusbar.phone; 18 19import android.content.Context; 20import android.os.SystemProperties; 21import android.text.TextUtils; 22import android.util.Log; 23import android.util.MathUtils; 24import android.util.SparseBooleanArray; 25 26import com.android.systemui.R; 27 28import java.io.PrintWriter; 29import java.util.Arrays; 30import java.util.regex.Matcher; 31import java.util.regex.Pattern; 32 33public class DozeParameters { 34 private static final String TAG = "DozeParameters"; 35 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 36 37 private static final int MAX_DURATION = 60 * 1000; 38 39 private final Context mContext; 40 41 private static PulseSchedule sPulseSchedule; 42 43 private static IntInOutMatcher sPickupSubtypePerformsProxMatcher; 44 45 public DozeParameters(Context context) { 46 mContext = context; 47 } 48 49 public void dump(PrintWriter pw) { 50 pw.println(" DozeParameters:"); 51 pw.print(" getDisplayStateSupported(): "); pw.println(getDisplayStateSupported()); 52 pw.print(" getPulseDuration(pickup=false): "); pw.println(getPulseDuration(false)); 53 pw.print(" getPulseDuration(pickup=true): "); pw.println(getPulseDuration(true)); 54 pw.print(" getPulseInDuration(pickup=false): "); pw.println(getPulseInDuration(false)); 55 pw.print(" getPulseInDuration(pickup=true): "); pw.println(getPulseInDuration(true)); 56 pw.print(" getPulseInVisibleDuration(): "); pw.println(getPulseVisibleDuration()); 57 pw.print(" getPulseOutDuration(): "); pw.println(getPulseOutDuration()); 58 pw.print(" getPulseOnSigMotion(): "); pw.println(getPulseOnSigMotion()); 59 pw.print(" getVibrateOnSigMotion(): "); pw.println(getVibrateOnSigMotion()); 60 pw.print(" getPulseOnPickup(): "); pw.println(getPulseOnPickup()); 61 pw.print(" getVibrateOnPickup(): "); pw.println(getVibrateOnPickup()); 62 pw.print(" getProxCheckBeforePulse(): "); pw.println(getProxCheckBeforePulse()); 63 pw.print(" getPulseOnNotifications(): "); pw.println(getPulseOnNotifications()); 64 pw.print(" getPulseSchedule(): "); pw.println(getPulseSchedule()); 65 pw.print(" getPulseScheduleResets(): "); pw.println(getPulseScheduleResets()); 66 pw.print(" getPickupVibrationThreshold(): "); pw.println(getPickupVibrationThreshold()); 67 pw.print(" getPickupSubtypePerformsProxCheck(): ");pw.println( 68 dumpPickupSubtypePerformsProxCheck()); 69 } 70 71 private String dumpPickupSubtypePerformsProxCheck() { 72 // Refresh sPickupSubtypePerformsProxMatcher 73 getPickupSubtypePerformsProxCheck(0); 74 75 if (sPickupSubtypePerformsProxMatcher == null) { 76 return "fallback: " + mContext.getResources().getBoolean( 77 R.bool.doze_pickup_performs_proximity_check); 78 } else { 79 return "spec: " + sPickupSubtypePerformsProxMatcher.mSpec; 80 } 81 } 82 83 public boolean getDisplayStateSupported() { 84 return getBoolean("doze.display.supported", R.bool.doze_display_state_supported); 85 } 86 87 public int getPulseDuration(boolean pickup) { 88 return getPulseInDuration(pickup) + getPulseVisibleDuration() + getPulseOutDuration(); 89 } 90 91 public int getPulseInDuration(boolean pickup) { 92 return pickup 93 ? getInt("doze.pulse.duration.in.pickup", R.integer.doze_pulse_duration_in_pickup) 94 : getInt("doze.pulse.duration.in", R.integer.doze_pulse_duration_in); 95 } 96 97 public int getPulseVisibleDuration() { 98 return getInt("doze.pulse.duration.visible", R.integer.doze_pulse_duration_visible); 99 } 100 101 public int getPulseOutDuration() { 102 return getInt("doze.pulse.duration.out", R.integer.doze_pulse_duration_out); 103 } 104 105 public boolean getPulseOnSigMotion() { 106 return getBoolean("doze.pulse.sigmotion", R.bool.doze_pulse_on_significant_motion); 107 } 108 109 public boolean getVibrateOnSigMotion() { 110 return SystemProperties.getBoolean("doze.vibrate.sigmotion", false); 111 } 112 113 public boolean getPulseOnPickup() { 114 return getBoolean("doze.pulse.pickup", R.bool.doze_pulse_on_pick_up); 115 } 116 117 public boolean getVibrateOnPickup() { 118 return SystemProperties.getBoolean("doze.vibrate.pickup", false); 119 } 120 121 public boolean getProxCheckBeforePulse() { 122 return getBoolean("doze.pulse.proxcheck", R.bool.doze_proximity_check_before_pulse); 123 } 124 125 public boolean getPulseOnNotifications() { 126 return getBoolean("doze.pulse.notifications", R.bool.doze_pulse_on_notifications); 127 } 128 129 public PulseSchedule getPulseSchedule() { 130 final String spec = getString("doze.pulse.schedule", R.string.doze_pulse_schedule); 131 if (sPulseSchedule == null || !sPulseSchedule.mSpec.equals(spec)) { 132 sPulseSchedule = PulseSchedule.parse(spec); 133 } 134 return sPulseSchedule; 135 } 136 137 public int getPulseScheduleResets() { 138 return getInt("doze.pulse.schedule.resets", R.integer.doze_pulse_schedule_resets); 139 } 140 141 public int getPickupVibrationThreshold() { 142 return getInt("doze.pickup.vibration.threshold", R.integer.doze_pickup_vibration_threshold); 143 } 144 145 private boolean getBoolean(String propName, int resId) { 146 return SystemProperties.getBoolean(propName, mContext.getResources().getBoolean(resId)); 147 } 148 149 private int getInt(String propName, int resId) { 150 int value = SystemProperties.getInt(propName, mContext.getResources().getInteger(resId)); 151 return MathUtils.constrain(value, 0, MAX_DURATION); 152 } 153 154 private String getString(String propName, int resId) { 155 return SystemProperties.get(propName, mContext.getString(resId)); 156 } 157 158 public boolean getPickupSubtypePerformsProxCheck(int subType) { 159 String spec = getString("doze.pickup.proxcheck", 160 R.string.doze_pickup_subtype_performs_proximity_check); 161 162 if (TextUtils.isEmpty(spec)) { 163 // Fall back to non-subtype based property. 164 return mContext.getResources().getBoolean(R.bool.doze_pickup_performs_proximity_check); 165 } 166 167 if (sPickupSubtypePerformsProxMatcher == null 168 || !TextUtils.equals(spec, sPickupSubtypePerformsProxMatcher.mSpec)) { 169 sPickupSubtypePerformsProxMatcher = new IntInOutMatcher(spec); 170 } 171 172 return sPickupSubtypePerformsProxMatcher.isIn(subType); 173 } 174 175 176 /** 177 * Parses a spec of the form `1,2,3,!5,*`. The resulting object will match numbers that are 178 * listed, will not match numbers that are listed with a ! prefix, and will match / not match 179 * unlisted numbers depending on whether * or !* is present. 180 * 181 * * -> match any numbers that are not explicitly listed 182 * !* -> don't match any numbers that are not explicitly listed 183 * 2 -> match 2 184 * !3 -> don't match 3 185 * 186 * It is illegal to specify: 187 * - an empty spec 188 * - a spec containing that are empty, or a lone ! 189 * - a spec for anything other than numbers or * 190 * - multiple terms for the same number / multiple *s 191 */ 192 public static class IntInOutMatcher { 193 private static final String WILDCARD = "*"; 194 private static final char OUT_PREFIX = '!'; 195 196 private final SparseBooleanArray mIsIn; 197 private final boolean mDefaultIsIn; 198 final String mSpec; 199 200 public IntInOutMatcher(String spec) { 201 if (TextUtils.isEmpty(spec)) { 202 throw new IllegalArgumentException("Spec must not be empty"); 203 } 204 205 boolean defaultIsIn = false; 206 boolean foundWildcard = false; 207 208 mSpec = spec; 209 mIsIn = new SparseBooleanArray(); 210 211 for (String itemPrefixed : spec.split(",", -1)) { 212 if (itemPrefixed.length() == 0) { 213 throw new IllegalArgumentException( 214 "Illegal spec, must not have zero-length items: `" + spec + "`"); 215 } 216 boolean isIn = itemPrefixed.charAt(0) != OUT_PREFIX; 217 String item = isIn ? itemPrefixed : itemPrefixed.substring(1); 218 219 if (itemPrefixed.length() == 0) { 220 throw new IllegalArgumentException( 221 "Illegal spec, must not have zero-length items: `" + spec + "`"); 222 } 223 224 if (WILDCARD.equals(item)) { 225 if (foundWildcard) { 226 throw new IllegalArgumentException("Illegal spec, `" + WILDCARD + 227 "` must not appear multiple times in `" + spec + "`"); 228 } 229 defaultIsIn = isIn; 230 foundWildcard = true; 231 } else { 232 int key = Integer.parseInt(item); 233 if (mIsIn.indexOfKey(key) >= 0) { 234 throw new IllegalArgumentException("Illegal spec, `" + key + 235 "` must not appear multiple times in `" + spec + "`"); 236 } 237 mIsIn.put(key, isIn); 238 } 239 } 240 241 if (!foundWildcard) { 242 throw new IllegalArgumentException("Illegal spec, must specify either * or !*"); 243 } 244 245 mDefaultIsIn = defaultIsIn; 246 } 247 248 public boolean isIn(int value) { 249 return (mIsIn.get(value, mDefaultIsIn)); 250 } 251 } 252 253 public static class PulseSchedule { 254 private static final Pattern PATTERN = Pattern.compile("(\\d+?)s", 0); 255 256 private String mSpec; 257 private int[] mSchedule; 258 259 public static PulseSchedule parse(String spec) { 260 if (TextUtils.isEmpty(spec)) return null; 261 try { 262 final PulseSchedule rt = new PulseSchedule(); 263 rt.mSpec = spec; 264 final String[] tokens = spec.split(","); 265 rt.mSchedule = new int[tokens.length]; 266 for (int i = 0; i < tokens.length; i++) { 267 final Matcher m = PATTERN.matcher(tokens[i]); 268 if (!m.matches()) throw new IllegalArgumentException("Bad token: " + tokens[i]); 269 rt.mSchedule[i] = Integer.parseInt(m.group(1)); 270 } 271 if (DEBUG) Log.d(TAG, "Parsed spec [" + spec + "] as: " + rt); 272 return rt; 273 } catch (RuntimeException e) { 274 Log.w(TAG, "Error parsing spec: " + spec, e); 275 return null; 276 } 277 } 278 279 @Override 280 public String toString() { 281 return Arrays.toString(mSchedule); 282 } 283 284 public long getNextTime(long now, long notificationTime) { 285 for (int i = 0; i < mSchedule.length; i++) { 286 final long time = notificationTime + mSchedule[i] * 1000; 287 if (time > now) return time; 288 } 289 return 0; 290 } 291 } 292} 293