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