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.ContentResolver; 22 import android.content.Context; 23 import android.content.res.Configuration; 24 import android.content.res.Resources; 25 import android.provider.AlarmClock; 26 import android.text.TextUtils; 27 import android.text.format.DateFormat; 28 import android.util.AttributeSet; 29 import android.util.Log; 30 import android.util.Slog; 31 import android.util.TypedValue; 32 import android.view.View; 33 import android.widget.GridLayout; 34 import android.widget.TextClock; 35 import android.widget.TextView; 36 37 import com.android.internal.widget.LockPatternUtils; 38 39 import java.util.Locale; 40 41 public class KeyguardStatusView extends GridLayout { 42 private static final boolean DEBUG = KeyguardConstants.DEBUG; 43 private static final String TAG = "KeyguardStatusView"; 44 45 private LockPatternUtils mLockPatternUtils; 46 47 private TextView mAlarmStatusView; 48 private TextClock mDateView; 49 private TextClock mClockView; 50 private TextView mOwnerInfo; 51 52 private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() { 53 54 @Override 55 public void onTimeChanged() { 56 refresh(); 57 } 58 59 @Override 60 public void onKeyguardVisibilityChanged(boolean showing) { 61 if (showing) { 62 if (DEBUG) Slog.v(TAG, "refresh statusview showing:" + showing); 63 refresh(); 64 updateOwnerInfo(); 65 } 66 } 67 68 @Override 69 public void onScreenTurnedOn() { 70 setEnableMarquee(true); 71 } 72 73 @Override 74 public void onScreenTurnedOff(int why) { 75 setEnableMarquee(false); 76 } 77 78 @Override 79 public void onUserSwitchComplete(int userId) { 80 refresh(); 81 updateOwnerInfo(); 82 } 83 }; 84 85 public KeyguardStatusView(Context context) { 86 this(context, null, 0); 87 } 88 89 public KeyguardStatusView(Context context, AttributeSet attrs) { 90 this(context, attrs, 0); 91 } 92 93 public KeyguardStatusView(Context context, AttributeSet attrs, int defStyle) { 94 super(context, attrs, defStyle); 95 } 96 97 private void setEnableMarquee(boolean enabled) { 98 if (DEBUG) Log.v(TAG, (enabled ? "Enable" : "Disable") + " transport text marquee"); 99 if (mAlarmStatusView != null) mAlarmStatusView.setSelected(enabled); 100 if (mOwnerInfo != null) mOwnerInfo.setSelected(enabled); 101 } 102 103 @Override 104 protected void onFinishInflate() { 105 super.onFinishInflate(); 106 mAlarmStatusView = (TextView) findViewById(R.id.alarm_status); 107 mDateView = (TextClock) findViewById(R.id.date_view); 108 mClockView = (TextClock) findViewById(R.id.clock_view); 109 mDateView.setShowCurrentUserTime(true); 110 mClockView.setShowCurrentUserTime(true); 111 mOwnerInfo = (TextView) findViewById(R.id.owner_info); 112 mLockPatternUtils = new LockPatternUtils(getContext()); 113 final boolean screenOn = KeyguardUpdateMonitor.getInstance(mContext).isScreenOn(); 114 setEnableMarquee(screenOn); 115 refresh(); 116 updateOwnerInfo(); 117 118 // Disable elegant text height because our fancy colon makes the ymin value huge for no 119 // reason. 120 mClockView.setElegantTextHeight(false); 121 } 122 123 @Override 124 protected void onConfigurationChanged(Configuration newConfig) { 125 super.onConfigurationChanged(newConfig); 126 mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX, 127 getResources().getDimensionPixelSize(R.dimen.widget_big_font_size)); 128 mDateView.setTextSize(TypedValue.COMPLEX_UNIT_PX, 129 getResources().getDimensionPixelSize(R.dimen.widget_label_font_size)); 130 mOwnerInfo.setTextSize(TypedValue.COMPLEX_UNIT_PX, 131 getResources().getDimensionPixelSize(R.dimen.widget_label_font_size)); 132 } 133 134 public void refreshTime() { 135 mDateView.setFormat24Hour(Patterns.dateView); 136 mDateView.setFormat12Hour(Patterns.dateView); 137 138 mClockView.setFormat12Hour(Patterns.clockView12); 139 mClockView.setFormat24Hour(Patterns.clockView24); 140 } 141 142 private void refresh() { 143 AlarmManager.AlarmClockInfo nextAlarm = mLockPatternUtils.getNextAlarm(); 144 Patterns.update(mContext, nextAlarm != null); 145 146 refreshTime(); 147 refreshAlarmStatus(nextAlarm); 148 } 149 150 void refreshAlarmStatus(AlarmManager.AlarmClockInfo nextAlarm) { 151 if (nextAlarm != null) { 152 String alarm = formatNextAlarm(mContext, nextAlarm); 153 mAlarmStatusView.setText(alarm); 154 mAlarmStatusView.setContentDescription( 155 getResources().getString(R.string.keyguard_accessibility_next_alarm, alarm)); 156 mAlarmStatusView.setVisibility(View.VISIBLE); 157 } else { 158 mAlarmStatusView.setVisibility(View.GONE); 159 } 160 } 161 162 public static String formatNextAlarm(Context context, AlarmManager.AlarmClockInfo info) { 163 if (info == null) { 164 return ""; 165 } 166 String skeleton = DateFormat.is24HourFormat(context, ActivityManager.getCurrentUser()) 167 ? "EHm" 168 : "Ehma"; 169 String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton); 170 return DateFormat.format(pattern, info.getTriggerTime()).toString(); 171 } 172 173 private void updateOwnerInfo() { 174 if (mOwnerInfo == null) return; 175 String ownerInfo = getOwnerInfo(); 176 if (!TextUtils.isEmpty(ownerInfo)) { 177 mOwnerInfo.setVisibility(View.VISIBLE); 178 mOwnerInfo.setText(ownerInfo); 179 } else { 180 mOwnerInfo.setVisibility(View.GONE); 181 } 182 } 183 184 @Override 185 protected void onAttachedToWindow() { 186 super.onAttachedToWindow(); 187 KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mInfoCallback); 188 } 189 190 @Override 191 protected void onDetachedFromWindow() { 192 super.onDetachedFromWindow(); 193 KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mInfoCallback); 194 } 195 196 public int getAppWidgetId() { 197 return LockPatternUtils.ID_DEFAULT_STATUS_WIDGET; 198 } 199 200 private String getOwnerInfo() { 201 ContentResolver res = getContext().getContentResolver(); 202 String info = null; 203 final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled(); 204 if (ownerInfoEnabled) { 205 info = mLockPatternUtils.getOwnerInfo(mLockPatternUtils.getCurrentUser()); 206 } 207 return info; 208 } 209 210 @Override 211 public boolean hasOverlappingRendering() { 212 return false; 213 } 214 215 // DateFormat.getBestDateTimePattern is extremely expensive, and refresh is called often. 216 // This is an optimization to ensure we only recompute the patterns when the inputs change. 217 private static final class Patterns { 218 static String dateView; 219 static String clockView12; 220 static String clockView24; 221 static String cacheKey; 222 223 static void update(Context context, boolean hasAlarm) { 224 final Locale locale = Locale.getDefault(); 225 final Resources res = context.getResources(); 226 final String dateViewSkel = res.getString(hasAlarm 227 ? R.string.abbrev_wday_month_day_no_year_alarm 228 : R.string.abbrev_wday_month_day_no_year); 229 final String clockView12Skel = res.getString(R.string.clock_12hr_format); 230 final String clockView24Skel = res.getString(R.string.clock_24hr_format); 231 final String key = locale.toString() + dateViewSkel + clockView12Skel + clockView24Skel; 232 if (key.equals(cacheKey)) return; 233 234 dateView = DateFormat.getBestDateTimePattern(locale, dateViewSkel); 235 236 clockView12 = DateFormat.getBestDateTimePattern(locale, clockView12Skel); 237 // CLDR insists on adding an AM/PM indicator even though it wasn't in the skeleton 238 // format. The following code removes the AM/PM indicator if we didn't want it. 239 if (!clockView12Skel.contains("a")) { 240 clockView12 = clockView12.replaceAll("a", "").trim(); 241 } 242 243 clockView24 = DateFormat.getBestDateTimePattern(locale, clockView24Skel); 244 245 // Use fancy colon. 246 clockView24 = clockView24.replace(':', '\uee01'); 247 clockView12 = clockView12.replace(':', '\uee01'); 248 249 cacheKey = key; 250 } 251 } 252 } 253