KeyguardStatusView.java revision 52414e31cd2e6388ce542f8c5819807c7b8ba7f6
1/* 2 * Copyright (C) 2012 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.keyguard; 18 19import android.app.ActivityManager; 20import android.app.AlarmManager; 21import android.content.Context; 22import android.content.res.Configuration; 23import android.content.res.Resources; 24import android.os.UserHandle; 25import android.text.TextUtils; 26import android.text.format.DateFormat; 27import android.util.AttributeSet; 28import android.util.Log; 29import android.util.Slog; 30import android.util.TypedValue; 31import android.view.View; 32import android.view.ViewGroup; 33import android.widget.GridLayout; 34import android.widget.TextClock; 35import android.widget.TextView; 36 37import com.android.internal.util.ArrayUtils; 38import com.android.internal.widget.LockPatternUtils; 39import com.android.systemui.ChargingView; 40 41import java.util.Locale; 42 43public class KeyguardStatusView extends GridLayout { 44 private static final boolean DEBUG = KeyguardConstants.DEBUG; 45 private static final String TAG = "KeyguardStatusView"; 46 47 private final LockPatternUtils mLockPatternUtils; 48 private final AlarmManager mAlarmManager; 49 50 private TextView mAlarmStatusView; 51 private TextClock mDateView; 52 private TextClock mClockView; 53 private TextView mOwnerInfo; 54 private ViewGroup mClockContainer; 55 private ChargingView mBatteryDoze; 56 57 private View[] mVisibleInDoze; 58 private boolean mPulsing; 59 private boolean mDark; 60 61 private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() { 62 63 @Override 64 public void onTimeChanged() { 65 refresh(); 66 } 67 68 @Override 69 public void onKeyguardVisibilityChanged(boolean showing) { 70 if (showing) { 71 if (DEBUG) Slog.v(TAG, "refresh statusview showing:" + showing); 72 refresh(); 73 updateOwnerInfo(); 74 } 75 } 76 77 @Override 78 public void onStartedWakingUp() { 79 setEnableMarquee(true); 80 } 81 82 @Override 83 public void onFinishedGoingToSleep(int why) { 84 setEnableMarquee(false); 85 } 86 87 @Override 88 public void onUserSwitchComplete(int userId) { 89 refresh(); 90 updateOwnerInfo(); 91 } 92 }; 93 94 public KeyguardStatusView(Context context) { 95 this(context, null, 0); 96 } 97 98 public KeyguardStatusView(Context context, AttributeSet attrs) { 99 this(context, attrs, 0); 100 } 101 102 public KeyguardStatusView(Context context, AttributeSet attrs, int defStyle) { 103 super(context, attrs, defStyle); 104 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 105 mLockPatternUtils = new LockPatternUtils(getContext()); 106 } 107 108 private void setEnableMarquee(boolean enabled) { 109 if (DEBUG) Log.v(TAG, (enabled ? "Enable" : "Disable") + " transport text marquee"); 110 if (mAlarmStatusView != null) mAlarmStatusView.setSelected(enabled); 111 if (mOwnerInfo != null) mOwnerInfo.setSelected(enabled); 112 } 113 114 @Override 115 protected void onFinishInflate() { 116 super.onFinishInflate(); 117 mClockContainer = findViewById(R.id.keyguard_clock_container); 118 mAlarmStatusView = findViewById(R.id.alarm_status); 119 mDateView = findViewById(R.id.date_view); 120 mClockView = findViewById(R.id.clock_view); 121 mDateView.setShowCurrentUserTime(true); 122 mClockView.setShowCurrentUserTime(true); 123 mClockView.setAccessibilityDelegate(new KeyguardClockAccessibilityDelegate(mContext)); 124 mOwnerInfo = findViewById(R.id.owner_info); 125 mBatteryDoze = findViewById(R.id.battery_doze); 126 mVisibleInDoze = new View[]{mBatteryDoze, mClockView}; 127 128 boolean shouldMarquee = KeyguardUpdateMonitor.getInstance(mContext).isDeviceInteractive(); 129 setEnableMarquee(shouldMarquee); 130 refresh(); 131 updateOwnerInfo(); 132 133 // Disable elegant text height because our fancy colon makes the ymin value huge for no 134 // reason. 135 mClockView.setElegantTextHeight(false); 136 } 137 138 @Override 139 protected void onConfigurationChanged(Configuration newConfig) { 140 super.onConfigurationChanged(newConfig); 141 mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX, 142 getResources().getDimensionPixelSize(R.dimen.widget_big_font_size)); 143 // Some layouts like burmese have a different margin for the clock 144 MarginLayoutParams layoutParams = (MarginLayoutParams) mClockView.getLayoutParams(); 145 layoutParams.bottomMargin = getResources().getDimensionPixelSize( 146 R.dimen.bottom_text_spacing_digital); 147 mClockView.setLayoutParams(layoutParams); 148 mDateView.setTextSize(TypedValue.COMPLEX_UNIT_PX, 149 getResources().getDimensionPixelSize(R.dimen.widget_label_font_size)); 150 mOwnerInfo.setTextSize(TypedValue.COMPLEX_UNIT_PX, 151 getResources().getDimensionPixelSize(R.dimen.widget_label_font_size)); 152 } 153 154 public void refreshTime() { 155 mDateView.setFormat24Hour(Patterns.dateView); 156 mDateView.setFormat12Hour(Patterns.dateView); 157 158 mClockView.setFormat12Hour(Patterns.clockView12); 159 mClockView.setFormat24Hour(Patterns.clockView24); 160 } 161 162 private void refresh() { 163 AlarmManager.AlarmClockInfo nextAlarm = 164 mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT); 165 Patterns.update(mContext, nextAlarm != null); 166 167 refreshTime(); 168 refreshAlarmStatus(nextAlarm); 169 } 170 171 void refreshAlarmStatus(AlarmManager.AlarmClockInfo nextAlarm) { 172 if (nextAlarm != null) { 173 String alarm = formatNextAlarm(mContext, nextAlarm); 174 mAlarmStatusView.setText(alarm); 175 mAlarmStatusView.setContentDescription( 176 getResources().getString(R.string.keyguard_accessibility_next_alarm, alarm)); 177 mAlarmStatusView.setVisibility(View.VISIBLE); 178 } else { 179 mAlarmStatusView.setVisibility(View.GONE); 180 } 181 } 182 183 public int getClockBottom() { 184 return mClockView.getBottom() + 185 ((MarginLayoutParams) mClockView.getLayoutParams()).bottomMargin; 186 } 187 188 public static String formatNextAlarm(Context context, AlarmManager.AlarmClockInfo info) { 189 if (info == null) { 190 return ""; 191 } 192 String skeleton = DateFormat.is24HourFormat(context, ActivityManager.getCurrentUser()) 193 ? "EHm" 194 : "Ehma"; 195 String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton); 196 return DateFormat.format(pattern, info.getTriggerTime()).toString(); 197 } 198 199 private void updateOwnerInfo() { 200 if (mOwnerInfo == null) return; 201 String ownerInfo = getOwnerInfo(); 202 if (!TextUtils.isEmpty(ownerInfo)) { 203 mOwnerInfo.setVisibility(View.VISIBLE); 204 mOwnerInfo.setText(ownerInfo); 205 } else { 206 mOwnerInfo.setVisibility(View.GONE); 207 } 208 } 209 210 @Override 211 protected void onAttachedToWindow() { 212 super.onAttachedToWindow(); 213 KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mInfoCallback); 214 } 215 216 @Override 217 protected void onDetachedFromWindow() { 218 super.onDetachedFromWindow(); 219 KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mInfoCallback); 220 } 221 222 private String getOwnerInfo() { 223 String info = null; 224 if (mLockPatternUtils.isDeviceOwnerInfoEnabled()) { 225 // Use the device owner information set by device policy client via 226 // device policy manager. 227 info = mLockPatternUtils.getDeviceOwnerInfo(); 228 } else { 229 // Use the current user owner information if enabled. 230 final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled( 231 KeyguardUpdateMonitor.getCurrentUser()); 232 if (ownerInfoEnabled) { 233 info = mLockPatternUtils.getOwnerInfo(KeyguardUpdateMonitor.getCurrentUser()); 234 } 235 } 236 return info; 237 } 238 239 @Override 240 public boolean hasOverlappingRendering() { 241 return false; 242 } 243 244 // DateFormat.getBestDateTimePattern is extremely expensive, and refresh is called often. 245 // This is an optimization to ensure we only recompute the patterns when the inputs change. 246 private static final class Patterns { 247 static String dateView; 248 static String clockView12; 249 static String clockView24; 250 static String cacheKey; 251 252 static void update(Context context, boolean hasAlarm) { 253 final Locale locale = Locale.getDefault(); 254 final Resources res = context.getResources(); 255 final String dateViewSkel = res.getString(hasAlarm 256 ? R.string.abbrev_wday_month_day_no_year_alarm 257 : R.string.abbrev_wday_month_day_no_year); 258 final String clockView12Skel = res.getString(R.string.clock_12hr_format); 259 final String clockView24Skel = res.getString(R.string.clock_24hr_format); 260 final String key = locale.toString() + dateViewSkel + clockView12Skel + clockView24Skel; 261 if (key.equals(cacheKey)) return; 262 263 dateView = DateFormat.getBestDateTimePattern(locale, dateViewSkel); 264 265 clockView12 = DateFormat.getBestDateTimePattern(locale, clockView12Skel); 266 // CLDR insists on adding an AM/PM indicator even though it wasn't in the skeleton 267 // format. The following code removes the AM/PM indicator if we didn't want it. 268 if (!clockView12Skel.contains("a")) { 269 clockView12 = clockView12.replaceAll("a", "").trim(); 270 } 271 272 clockView24 = DateFormat.getBestDateTimePattern(locale, clockView24Skel); 273 274 // Use fancy colon. 275 clockView24 = clockView24.replace(':', '\uee01'); 276 clockView12 = clockView12.replace(':', '\uee01'); 277 278 cacheKey = key; 279 } 280 } 281 282 public void setDark(boolean dark) { 283 if (mDark == dark) { 284 return; 285 } 286 mDark = dark; 287 288 final int N = mClockContainer.getChildCount(); 289 for (int i = 0; i < N; i++) { 290 View child = mClockContainer.getChildAt(i); 291 if (ArrayUtils.contains(mVisibleInDoze, child)) { 292 continue; 293 } 294 child.setAlpha(dark ? 0 : 1); 295 } 296 updateDozeVisibleViews(); 297 mBatteryDoze.setDark(dark); 298 } 299 300 public void setPulsing(boolean pulsing) { 301 mPulsing = pulsing; 302 updateDozeVisibleViews(); 303 } 304 305 private void updateDozeVisibleViews() { 306 for (View child : mVisibleInDoze) { 307 child.setAlpha(mDark && mPulsing ? 0.8f : 1); 308 } 309 } 310} 311