/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.policy.impl;
import com.android.internal.R;
import com.android.internal.telephony.IccCard;
import com.android.internal.telephony.IccCard.State;
import com.android.internal.widget.DigitalClock;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.TransportControlView;
import com.android.internal.policy.impl.KeyguardUpdateMonitor.InfoCallbackImpl;
import com.android.internal.policy.impl.KeyguardUpdateMonitor.SimStateCallback;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import libcore.util.MutableInt;
import android.content.ContentResolver;
import android.content.Context;
import android.content.ContentUris;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.provider.Settings;
import android.provider.CalendarContract;
import android.provider.CalendarContract.Calendars;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.format.DateFormat;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.ViewFlipper;
/***
* Manages a number of views inside of LockScreen layouts. See below for a list of widgets
*
*/
class KeyguardStatusViewManager implements OnClickListener {
private static final boolean DEBUG = false;
private static final String TAG = "KeyguardStatusView";
public static final int LOCK_ICON = 0; // R.drawable.ic_lock_idle_lock;
public static final int ALARM_ICON = R.drawable.ic_lock_idle_alarm;
public static final int CHARGING_ICON = 0; //R.drawable.ic_lock_idle_charging;
public static final int BATTERY_LOW_ICON = 0; //R.drawable.ic_lock_idle_low_battery;
public static final int BATTERY_ICON = 0; //insert a R.drawable icon if you want it to show up
private static final long INSTRUCTION_RESET_DELAY = 2000; // time until instruction text resets
private static final int INSTRUCTION_TEXT = 10;
private static final int CARRIER_TEXT = 11;
private static final int CARRIER_HELP_TEXT = 12;
private static final int HELP_MESSAGE_TEXT = 13;
private static final int OWNER_INFO = 14;
private static final int BATTERY_INFO = 15;
private static final int WEATHER_INFO = 16;
private static final int CALENDAR_INFO = 17;
private static final int COLOR_WHITE = 0xFFFFFFFF;
public static final String EXTRA_CITY = "city";
public static final String EXTRA_FORECAST_DATE = "forecast_date";
public static final String EXTRA_CONDITION = "condition";
public static final String EXTRA_TEMP = "temp";
public static final String EXTRA_HUMIDITY = "humidity";
public static final String EXTRA_WIND = "wind";
public static final String EXTRA_LOW = "todays_low";
public static final String EXTRA_HIGH = "todays_high";
private boolean mLockAlwaysBattery;
private StatusMode mStatus;
private String mDateFormatString;
private TransientTextManager mTransientTextManager;
// Views that this class controls.
// NOTE: These may be null in some LockScreen screens and should protect from NPE
private TextView mCarrierView;
private TextView mDateView;
private TextView mStatus1View;
private TextView mOwnerInfoView;
private TextView mAlarmStatusView;
private TransportControlView mTransportView;
private WeatherPanel mWeatherPanelView;
private WeatherText mWeatherTextView;
private ViewFlipper mCalendarView;
private boolean mCalendarUsingColors = true;
private ArrayList<EventBundle> mCalendarEvents = null;
// Top-level container view for above views
private View mContainer;
// are we showing battery information?
private boolean mShowingBatteryInfo = false;
private Intent mWeatherInfo = null; // being tricky
// last known plugged in state
private boolean mPluggedIn = false;
// last known battery level
private int mBatteryLevel = 100;
// last known SIM state
protected State mSimState;
private LockPatternUtils mLockPatternUtils;
private KeyguardUpdateMonitor mUpdateMonitor;
private Button mEmergencyCallButton;
private boolean mEmergencyButtonEnabledBecauseSimLocked;
// Shadowed text values
private CharSequence mCarrierText;
private CharSequence mCarrierHelpText;
private String mHelpMessageText;
private String mInstructionText;
private CharSequence mOwnerInfoText;
private boolean mShowingStatus;
private KeyguardScreenCallback mCallback;
private final boolean mEmergencyCallButtonEnabledInScreen;
private CharSequence mPlmn;
private CharSequence mSpn;
protected int mPhoneState;
private DigitalClock mDigitalClock;
private class TransientTextManager {
private TextView mTextView;
private class Data {
final int icon;
final CharSequence text;
Data(CharSequence t, int i) {
text = t;
icon = i;
}
};
private ArrayList<Data> mMessages = new ArrayList<Data>(5);
TransientTextManager(TextView textView) {
mTextView = textView;
}
/* Show given message with icon for up to duration ms. Newer messages override older ones.
* The most recent message with the longest duration is shown as messages expire until
* nothing is left, in which case the text/icon is defined by a call to
* getAltTextMessage() */
void post(final CharSequence message, final int icon, long duration) {
if (mTextView == null) {
return;
}
mTextView.setText(message);
mTextView.setCompoundDrawablesWithIntrinsicBounds(icon, 0, 0, 0);
final Data data = new Data(message, icon);
mContainer.postDelayed(new Runnable() {
public void run() {
mMessages.remove(data);
int last = mMessages.size() - 1;
final CharSequence lastText;
final int lastIcon;
if (last > 0) {
final Data oldData = mMessages.get(last);
lastText = oldData.text;
lastIcon = oldData.icon;
} else {
final MutableInt tmpIcon = new MutableInt(0);
lastText = getAltTextMessage(tmpIcon);
lastIcon = tmpIcon.value;
}
mTextView.setText(lastText);
mTextView.setCompoundDrawablesWithIntrinsicBounds(lastIcon, 0, 0, 0);
}
}, duration);
}
};
/**
*
* @param view the containing view of all widgets
* @param updateMonitor the update monitor to use
* @param lockPatternUtils lock pattern util object
* @param callback used to invoke emergency dialer
* @param emergencyButtonEnabledInScreen whether emergency button is enabled by default
*/
public KeyguardStatusViewManager(View view, KeyguardUpdateMonitor updateMonitor,
LockPatternUtils lockPatternUtils, KeyguardScreenCallback callback,
boolean emergencyButtonEnabledInScreen) {
if (DEBUG) Log.v(TAG, "KeyguardStatusViewManager()");
mContainer = view;
mDateFormatString = getContext().getString(R.string.abbrev_wday_month_day_no_year);
mLockPatternUtils = lockPatternUtils;
mUpdateMonitor = updateMonitor;
mCallback = callback;
mCarrierView = (TextView) findViewById(R.id.carrier);
mDateView = (TextView) findViewById(R.id.date);
mStatus1View = (TextView) findViewById(R.id.status1);
mAlarmStatusView = (TextView) findViewById(R.id.alarm_status);
mOwnerInfoView = (TextView) findViewById(R.id.propertyOf);
mTransportView = (TransportControlView) findViewById(R.id.transport);
mEmergencyCallButton = (Button) findViewById(R.id.emergencyCallButton);
mEmergencyCallButtonEnabledInScreen = emergencyButtonEnabledInScreen;
mDigitalClock = (DigitalClock) findViewById(R.id.time);
mWeatherPanelView = (WeatherPanel) findViewById(R.id.weatherpanel);
mWeatherTextView = (WeatherText) findViewById(R.id.weather);
mCalendarView = (ViewFlipper) findViewById(R.id.calendar);
// Hide transport control view until we know we need to show it.
if (mTransportView != null) {
mTransportView.setVisibility(View.GONE);
}
if (mEmergencyCallButton != null) {
mEmergencyCallButton.setText(R.string.lockscreen_emergency_call);
mEmergencyCallButton.setOnClickListener(this);
mEmergencyCallButton.setFocusable(false); // touch only!
}
mTransientTextManager = new TransientTextManager(mCarrierView);
mUpdateMonitor.registerInfoCallback(mInfoCallback);
mUpdateMonitor.registerSimStateCallback(mSimStateCallback);
resetStatusInfo();
refreshDate();
updateOwnerInfo();
updateColors();
// Required to get Marquee to work.
final View scrollableViews[] = { mCarrierView, mDateView, mStatus1View, mOwnerInfoView,
mAlarmStatusView, mWeatherTextView, mCalendarView, mWeatherPanelView };
for (View v : scrollableViews) {
if (v != null) {
v.setSelected(true);
}
}
}
private boolean inWidgetMode() {
return mTransportView != null && mTransportView.getVisibility() == View.VISIBLE;
}
void setInstructionText(String string) {
mInstructionText = string;
update(INSTRUCTION_TEXT, string);
}
void setCarrierText(CharSequence string) {
mCarrierText = string;
update(CARRIER_TEXT, string);
}
void setOwnerInfo(CharSequence string) {
mOwnerInfoText = string;
update(OWNER_INFO, string);
}
/**
* Sets the carrier help text message, if view is present. Carrier help text messages are
* typically for help dealing with SIMS and connectivity.
*
* @param resId resource id of the message
*/
public void setCarrierHelpText(int resId) {
mCarrierHelpText = getText(resId);
update(CARRIER_HELP_TEXT, mCarrierHelpText);
}
private CharSequence getText(int resId) {
return resId == 0 ? null : getContext().getText(resId);
}
/**
* Unlock help message. This is typically for help with unlock widgets, e.g. "wrong password"
* or "try again."
*
* @param textResId
* @param lockIcon
*/
public void setHelpMessage(int textResId, int lockIcon) {
final CharSequence tmp = getText(textResId);
mHelpMessageText = tmp == null ? null : tmp.toString();
update(HELP_MESSAGE_TEXT, mHelpMessageText);
}
private void update(int what, CharSequence string) {
if (inWidgetMode()) {
if (DEBUG) Log.v(TAG, "inWidgetMode() is true");
// Use Transient text for messages shown while widget is shown.
switch (what) {
case INSTRUCTION_TEXT:
case CARRIER_HELP_TEXT:
case HELP_MESSAGE_TEXT:
case BATTERY_INFO:
mTransientTextManager.post(string, 0, INSTRUCTION_RESET_DELAY);
break;
case WEATHER_INFO:
updateWeatherInfo();
break;
case CALENDAR_INFO:
updateCalendar();
updateColors();
break;
case OWNER_INFO:
case CARRIER_TEXT:
default:
if (DEBUG) Log.w(TAG, "Not showing message id " + what + ", str=" + string);
}
} else {
// dont update everything 7 times, filter based on "what"
switch (what) {
case INSTRUCTION_TEXT:
case CARRIER_HELP_TEXT:
case HELP_MESSAGE_TEXT:
case BATTERY_INFO:
updateStatus1();
break;
case OWNER_INFO:
updateOwnerInfo();
break;
case CARRIER_TEXT:
updateCarrierText();
break;
case WEATHER_INFO:
updateWeatherInfo();
break;
case CALENDAR_INFO:
updateCalendar();
updateColors();
break;
default:
;
}
}
}
public void onPause() {
if (DEBUG) Log.v(TAG, "onPause()");
mUpdateMonitor.removeCallback(mInfoCallback);
mUpdateMonitor.removeCallback(mSimStateCallback);
}
/** {@inheritDoc} */
public void onResume() {
if (DEBUG) Log.v(TAG, "onResume()");
// First update the clock, if present.
if (mDigitalClock != null) {
mDigitalClock.updateTime();
}
mUpdateMonitor.registerInfoCallback(mInfoCallback);
mUpdateMonitor.registerSimStateCallback(mSimStateCallback);
resetStatusInfo();
// Issue the biometric unlock failure message in a centralized place
// TODO: we either need to make the Face Unlock multiple failures string a more general
// 'biometric unlock' or have each biometric unlock handle this on their own.
if (mUpdateMonitor.getMaxBiometricUnlockAttemptsReached()) {
setInstructionText(getContext().getString(R.string.faceunlock_multiple_failures));
}
}
void resetStatusInfo() {
mInstructionText = null;
mShowingBatteryInfo = mUpdateMonitor.shouldShowBatteryInfo();
mPluggedIn = mUpdateMonitor.isDevicePluggedIn();
mBatteryLevel = mUpdateMonitor.getBatteryLevel();
mWeatherInfo = mUpdateMonitor.getWeather();
updateStatusLines(true);
}
/**
* Update the status lines based on these rules:
* AlarmStatus: Alarm state always gets it's own line.
* Status1 is shared between help, battery status and generic unlock instructions,
* prioritized in that order.
* @param showStatusLines status lines are shown if true
*/
void updateStatusLines(boolean showStatusLines) {
if (DEBUG) Log.v(TAG, "updateStatusLines(" + showStatusLines + ")");
mShowingStatus = showStatusLines;
updateAlarmInfo();
updateWeatherInfo();
updateCalendar();
updateOwnerInfo();
updateStatus1();
updateCarrierText();
updateColors();
}
private void updateAlarmInfo() {
if (mAlarmStatusView != null) {
String nextAlarm = mLockPatternUtils.getNextAlarm();
boolean showAlarm = mShowingStatus && !TextUtils.isEmpty(nextAlarm);
mAlarmStatusView.setText(nextAlarm);
mAlarmStatusView.setCompoundDrawablesWithIntrinsicBounds(ALARM_ICON, 0, 0, 0);
mAlarmStatusView.setVisibility(showAlarm ? View.VISIBLE : View.GONE);
}
}
private void updateOwnerInfo() {
final ContentResolver res = getContext().getContentResolver();
final boolean ownerInfoEnabled = Settings.Secure.getInt(res,
Settings.Secure.LOCK_SCREEN_OWNER_INFO_ENABLED, 1) != 0;
mOwnerInfoText = ownerInfoEnabled ?
Settings.Secure.getString(res, Settings.Secure.LOCK_SCREEN_OWNER_INFO) : null;
if (mOwnerInfoView != null) {
mOwnerInfoView.setText(mOwnerInfoText);
mOwnerInfoView.setVisibility(TextUtils.isEmpty(mOwnerInfoText) ? View.GONE:View.VISIBLE);
}
}
private void updateWeatherInfo() {
final ContentResolver res = getContext().getContentResolver();
final boolean weatherInfoEnabled = (Settings.System.getBoolean(res,
Settings.System.LOCKSCREEN_WEATHER, false)
&& (Settings.System.getBoolean(res, Settings.System.USE_WEATHER, false))
&& mShowingStatus);
final boolean weatherLocationEnabled = Settings.System.getBoolean(res,
Settings.System.WEATHER_SHOW_LOCATION, false);
final int weatherInfoType = Settings.System.getInt(res,
Settings.System.LOCKSCREEN_WEATHER_TYPE, 0);
if (weatherInfoEnabled) {
if (weatherInfoType == 0) {
if (mWeatherPanelView != null && mWeatherInfo != null) {
mWeatherPanelView.updateWeather(mWeatherInfo);
mWeatherPanelView.setVisibility(weatherInfoEnabled ? View.VISIBLE : View.GONE);
}
if (mWeatherTextView != null) {
mWeatherTextView.setVisibility(View.GONE);
}
} else {
if (mWeatherTextView != null && mWeatherInfo != null) {
mWeatherTextView.updateWeather(mWeatherInfo);
mWeatherTextView.setVisibility(weatherInfoEnabled ? View.VISIBLE : View.GONE);
}
if (mWeatherPanelView != null) {
mWeatherPanelView.setVisibility(View.GONE);
}
}
} else {
if (mWeatherPanelView != null) {
mWeatherPanelView.setVisibility(View.GONE);
}
if (mWeatherTextView != null) {
mWeatherTextView.setVisibility(View.GONE);
}
}
}
private void updateCalendar() {
ContentResolver resolver = getContext().getContentResolver();
String calendarSources = Settings.System.getString(resolver,
Settings.System.LOCKSCREEN_CALENDAR_SOURCES);
if (calendarSources == null)
return;
boolean multipleEventsEnabled = (Settings.System.getBoolean(resolver,
Settings.System.LOCKSCREEN_CALENDAR_FLIP, false));
int interval = Settings.System.getInt(resolver,
Settings.System.LOCKSCREEN_CALENDAR_INTERVAL, 2500);
long range = Settings.System.getLong(resolver,
Settings.System.LOCKSCREEN_CALENDAR_RANGE, 86400000);
boolean hideOnGoing = (Settings.System.getBoolean(resolver,
Settings.System.LOCKSCREEN_CALENDAR_HIDE_ONGOING, false));
mCalendarUsingColors = (Settings.System.getBoolean(resolver,
Settings.System.LOCKSCREEN_CALENDAR_USE_COLORS, false));
boolean calendarEventsEnabled = (Settings.System.getBoolean(resolver,
Settings.System.LOCKSCREEN_CALENDAR, false) && mShowingStatus
&& !calendarSources.isEmpty());
if (calendarEventsEnabled) {
if (mCalendarEvents == null) {
try {
getCalendarEvents(resolver, calendarSources, multipleEventsEnabled, hideOnGoing, range);
} catch (Exception e) {
e.printStackTrace();
}
}
} else if (!calendarEventsEnabled && mCalendarView != null) {
mCalendarView.setVisibility(View.GONE);
return;
} else {
return;
}
if (mCalendarView != null) {
mCalendarView.removeAllViews();
Log.d(TAG, "we have " + String.valueOf(mCalendarEvents.size()) + " event(s)");
int dateWidth = (int) (findViewById(R.id.time).getWidth() * 1.2);
for (EventBundle e : mCalendarEvents) {
String title = e.title + (e.dayString.isEmpty() ? " " : ", ");
String details = e.dayString
+ ((e.allDay) ? "" : " " + DateFormat.format(
DateFormat.is24HourFormat(getContext()) ? "kk:mm"
: "hh:mm a", e.begin).toString())
+ (!e.location.isEmpty() ? " (" + e.location + ")" : "");
CalendarEntry cEntry = new CalendarEntry(getContext(), title, details, dateWidth);
if (mCalendarUsingColors) {
cEntry.setColor(e.color);
}
mCalendarView.addView(cEntry);
mCalendarView.requestLayout();
}
if (!multipleEventsEnabled || mCalendarEvents.size() <= 1) {
mCalendarView.stopFlipping();
} else {
mCalendarView.setFlipInterval(interval);
mCalendarView.startFlipping();
}
mCalendarView.setVisibility(View.VISIBLE);
}
}
private void updateStatus1() {
if (mStatus1View != null) {
MutableInt icon = new MutableInt(0);
CharSequence string = getPriorityTextMessage(icon);
mStatus1View.setText(string);
mStatus1View.setCompoundDrawablesWithIntrinsicBounds(icon.value, 0, 0, 0);
mStatus1View.setVisibility(mShowingStatus ? View.VISIBLE : View.GONE);
}
}
private void updateCarrierText() {
if (!inWidgetMode() && mCarrierView != null) {
mCarrierView.setText(mCarrierText);
}
}
private CharSequence getAltTextMessage(MutableInt icon) {
// If we have replaced the status area with a single widget, then this code
// prioritizes what to show in that space when all transient messages are gone.
CharSequence string = null;
mLockAlwaysBattery = Settings.System.getInt(getContext().getContentResolver(),
Settings.System.LOCKSCREEN_BATTERY, 0) == 1;
if (mShowingBatteryInfo || mLockAlwaysBattery) {
// Battery status
if (mPluggedIn) {
// Charging or charged
if (mUpdateMonitor.isDeviceCharged()) {
string = getContext().getString(R.string.lockscreen_charged);
} else {
string = getContext().getString(R.string.lockscreen_plugged_in, mBatteryLevel);
}
icon.value = CHARGING_ICON;
} else if (mBatteryLevel < KeyguardUpdateMonitor.LOW_BATTERY_THRESHOLD) {
// Battery is low
string = getContext().getString(R.string.lockscreen_low_battery);
icon.value = BATTERY_LOW_ICON;
if (mLockAlwaysBattery) {
// Show battery at low percent
string = getContext().getString(R.string.lockscreen_always_low_battery, mBatteryLevel);
icon.value = BATTERY_LOW_ICON;
}
} else if (mLockAlwaysBattery) {
// Always show battery
string = getContext().getString(R.string.lockscreen_always_battery, mBatteryLevel);
icon.value = BATTERY_ICON;
}
} else {
string = mCarrierText;
}
return string;
}
private CharSequence getPriorityTextMessage(MutableInt icon) {
CharSequence string = null;
mLockAlwaysBattery = Settings.System.getInt(getContext().getContentResolver(),
Settings.System.LOCKSCREEN_BATTERY, 0) == 1;
if (!TextUtils.isEmpty(mInstructionText)) {
// Instructions only
string = mInstructionText;
icon.value = LOCK_ICON;
} else if (mShowingBatteryInfo || mLockAlwaysBattery) {
// Battery status
if (mPluggedIn) {
// Charging or charged
if (mUpdateMonitor.isDeviceCharged()) {
string = getContext().getString(R.string.lockscreen_charged);
} else {
string = getContext().getString(R.string.lockscreen_plugged_in, mBatteryLevel);
}
icon.value = CHARGING_ICON;
} else if (mBatteryLevel < KeyguardUpdateMonitor.LOW_BATTERY_THRESHOLD) {
// Battery is low
string = getContext().getString(R.string.lockscreen_low_battery);
icon.value = BATTERY_LOW_ICON;
if (mLockAlwaysBattery) {
// Show battery at low percent
string = getContext().getString(R.string.lockscreen_always_low_battery, mBatteryLevel);
icon.value = BATTERY_LOW_ICON;
}
} else if (mLockAlwaysBattery) {
// Always show battery
string = getContext().getString(R.string.lockscreen_always_battery, mBatteryLevel);
icon.value = BATTERY_ICON;
}
} else if (!inWidgetMode() && mOwnerInfoView == null && mOwnerInfoText != null) {
// OwnerInfo shows in status if we don't have a dedicated widget
string = mOwnerInfoText;
}
return string;
}
void refreshDate() {
if (mDateView != null) {
mDateView.setText(DateFormat.format(mDateFormatString, new Date()));
}
}
/**
* Determine the current status of the lock screen given the sim state and other stuff.
*/
public StatusMode getStatusForIccState(IccCard.State simState) {
// Since reading the SIM may take a while, we assume it is present until told otherwise.
if (simState == null) {
return StatusMode.Normal;
}
final boolean missingAndNotProvisioned = (!mUpdateMonitor.isDeviceProvisioned()
&& (simState == IccCard.State.ABSENT || simState == IccCard.State.PERM_DISABLED));
// Assume we're NETWORK_LOCKED if not provisioned
simState = missingAndNotProvisioned ? State.NETWORK_LOCKED : simState;
switch (simState) {
case ABSENT:
return StatusMode.SimMissing;
case NETWORK_LOCKED:
return StatusMode.SimMissingLocked;
case NOT_READY:
return StatusMode.SimMissing;
case PIN_REQUIRED:
return StatusMode.SimLocked;
case PUK_REQUIRED:
return StatusMode.SimPukLocked;
case READY:
return StatusMode.Normal;
case PERM_DISABLED:
return StatusMode.SimPermDisabled;
case UNKNOWN:
return StatusMode.SimMissing;
}
return StatusMode.SimMissing;
}
private Context getContext() {
return mContainer.getContext();
}
/**
* Update carrier text, carrier help and emergency button to match the current status based
* on SIM state.
*
* @param simState
*/
private void updateCarrierStateWithSimStatus(State simState) {
if (DEBUG) Log.d(TAG, "updateCarrierTextWithSimStatus(), simState = " + simState);
CharSequence carrierText = null;
int carrierHelpTextId = 0;
mEmergencyButtonEnabledBecauseSimLocked = false;
mStatus = getStatusForIccState(simState);
mSimState = simState;
switch (mStatus) {
case Normal:
carrierText = makeCarierString(mPlmn, mSpn);
break;
case NetworkLocked:
carrierText = makeCarrierStringOnEmergencyCapable(
getContext().getText(R.string.lockscreen_network_locked_message),
mPlmn);
carrierHelpTextId = R.string.lockscreen_instructions_when_pattern_disabled;
break;
case SimMissing:
// Shows "No SIM card | Emergency calls only" on devices that are voice-capable.
// This depends on mPlmn containing the text "Emergency calls only" when the radio
// has some connectivity. Otherwise, it should be null or empty and just show
// "No SIM card"
carrierText = makeCarrierStringOnEmergencyCapable(
getContext().getText(R.string.lockscreen_missing_sim_message_short),
mPlmn);
carrierHelpTextId = R.string.lockscreen_missing_sim_instructions_long;
break;
case SimPermDisabled:
carrierText = getContext().getText(
R.string.lockscreen_permanent_disabled_sim_message_short);
carrierHelpTextId = R.string.lockscreen_permanent_disabled_sim_instructions;
mEmergencyButtonEnabledBecauseSimLocked = true;
break;
case SimMissingLocked:
carrierText = makeCarrierStringOnEmergencyCapable(
getContext().getText(R.string.lockscreen_missing_sim_message_short),
mPlmn);
carrierHelpTextId = R.string.lockscreen_missing_sim_instructions;
mEmergencyButtonEnabledBecauseSimLocked = true;
break;
case SimLocked:
carrierText = makeCarrierStringOnEmergencyCapable(
getContext().getText(R.string.lockscreen_sim_locked_message),
mPlmn);
mEmergencyButtonEnabledBecauseSimLocked = true;
break;
case SimPukLocked:
carrierText = makeCarrierStringOnEmergencyCapable(
getContext().getText(R.string.lockscreen_sim_puk_locked_message),
mPlmn);
if (!mLockPatternUtils.isPukUnlockScreenEnable()) {
// This means we're showing the PUK unlock screen
mEmergencyButtonEnabledBecauseSimLocked = true;
}
break;
}
String customLabel;
customLabel = Settings.System.getString(getContext().getContentResolver(),
Settings.System.CUSTOM_CARRIER_LABEL);
if(customLabel == null || customLabel.length() == 0)
setCarrierText(carrierText);
else
setCarrierText(customLabel);
setCarrierHelpText(carrierHelpTextId);
updateEmergencyCallButtonState(mPhoneState);
}
/*
* Add emergencyCallMessage to carrier string only if phone supports emergency calls.
*/
private CharSequence makeCarrierStringOnEmergencyCapable(
CharSequence simMessage, CharSequence emergencyCallMessage) {
if (mLockPatternUtils.isEmergencyCallCapable()) {
return makeCarierString(simMessage, emergencyCallMessage);
}
return simMessage;
}
private View findViewById(int id) {
return mContainer.findViewById(id);
}
/**
* The status of this lock screen. Primarily used for widgets on LockScreen.
*/
enum StatusMode {
/**
* Normal case (sim card present, it's not locked)
*/
Normal(true),
/**
* The sim card is 'network locked'.
*/
NetworkLocked(true),
/**
* The sim card is missing.
*/
SimMissing(false),
/**
* The sim card is missing, and this is the device isn't provisioned, so we don't let
* them get past the screen.
*/
SimMissingLocked(false),
/**
* The sim card is PUK locked, meaning they've entered the wrong sim unlock code too many
* times.
*/
SimPukLocked(false),
/**
* The sim card is locked.
*/
SimLocked(true),
/**
* The sim card is permanently disabled due to puk unlock failure
*/
SimPermDisabled(false);
private final boolean mShowStatusLines;
StatusMode(boolean mShowStatusLines) {
this.mShowStatusLines = mShowStatusLines;
}
/**
* @return Whether the status lines (battery level and / or next alarm) are shown while
* in this state. Mostly dictated by whether this is room for them.
*/
public boolean shouldShowStatusLines() {
return mShowStatusLines;
}
}
private void updateEmergencyCallButtonState(int phoneState) {
if (mEmergencyCallButton != null) {
boolean enabledBecauseSimLocked =
mLockPatternUtils.isEmergencyCallEnabledWhileSimLocked()
&& mEmergencyButtonEnabledBecauseSimLocked;
boolean shown = mEmergencyCallButtonEnabledInScreen || enabledBecauseSimLocked;
mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton,
phoneState, shown);
}
}
private InfoCallbackImpl mInfoCallback = new InfoCallbackImpl() {
@Override
public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn,
int batteryLevel) {
mShowingBatteryInfo = showBatteryInfo;
mPluggedIn = pluggedIn;
mBatteryLevel = batteryLevel;
final MutableInt tmpIcon = new MutableInt(0);
update(BATTERY_INFO, getAltTextMessage(tmpIcon));
}
public void onRefreshWeatherInfo(Intent weatherIntent) {
mWeatherInfo = weatherIntent;
update(WEATHER_INFO, null);
}
public void onRefreshCalendarInfo() {
update(CALENDAR_INFO, null);
}
@Override
public void onTimeChanged() {
refreshDate();
}
@Override
public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) {
mPlmn = plmn;
mSpn = spn;
updateCarrierStateWithSimStatus(mSimState);
}
@Override
public void onPhoneStateChanged(int phoneState) {
mPhoneState = phoneState;
updateEmergencyCallButtonState(phoneState);
}
};
private SimStateCallback mSimStateCallback = new SimStateCallback() {
public void onSimStateChanged(State simState) {
updateCarrierStateWithSimStatus(simState);
}
};
public void onClick(View v) {
if (v == mEmergencyCallButton) {
mCallback.takeEmergencyCallAction();
}
}
/**
* Performs concentenation of PLMN/SPN
* @param plmn
* @param spn
* @return
*/
private static CharSequence makeCarierString(CharSequence plmn, CharSequence spn) {
final boolean plmnValid = !TextUtils.isEmpty(plmn);
final boolean spnValid = !TextUtils.isEmpty(spn);
if (plmnValid && spnValid) {
return plmn + "|" + spn;
} else if (plmnValid) {
return plmn;
} else if (spnValid) {
return spn;
} else {
return "";
}
}
public void updateColors() {
if (DEBUG) Log.d(TAG, "Lets update the colors");
ContentResolver resolver = getContext().getContentResolver();
int color = Settings.System.getInt(resolver,
Settings.System.LOCKSCREEN_CUSTOM_TEXT_COLOR, COLOR_WHITE);
// carrier view
try {
mCarrierView.setTextColor(color);
if (DEBUG) Log.d(TAG, String.format("Setting mCarrierView text color to %d", color));
} catch (NullPointerException ne) {
if (DEBUG) ne.printStackTrace();
}
// date view
try {
mDateView.setTextColor(color);
if (DEBUG) Log.d(TAG, String.format("Setting mDateView DATE text color to %d", color));
} catch (NullPointerException ne) {
if (DEBUG) ne.printStackTrace();
}
// status view
try {
mStatus1View.setTextColor(color);
if (DEBUG) Log.d(TAG, String.format("Setting mStatus1View DATE text color to %d", color));
} catch (NullPointerException ne) {
if (DEBUG) ne.printStackTrace();
}
// weather view
try {
mWeatherTextView.setTextColor(color);
if (DEBUG)
Log.d(TAG, String.format("Setting mWeatherTextView DATE text color to %d", color));
} catch (NullPointerException ne) {
if (DEBUG)
ne.printStackTrace();
}
// weatherpanel view
try {
mWeatherPanelView.setTextColor(color);
if (DEBUG) Log.d(TAG, String.format("Setting mWeatherPanelView DATE text color to %d", color));
} catch (NullPointerException ne) {
if (DEBUG) ne.printStackTrace();
}
// calendar view
try {
if (!mCalendarUsingColors) {
for (int i = 0; i < mCalendarView.getChildCount(); i++) {
((CalendarEntry) mCalendarView.getChildAt(i)).setColor(color);
}
if (DEBUG)
Log.d(TAG, String.format("Setting mCalendarView DATE text color to %d", color));
}
} catch (NullPointerException ne) {
if (DEBUG)
ne.printStackTrace();
}
// owner info view
try {
mOwnerInfoView.setTextColor(color);
if (DEBUG) Log.d(TAG, String.format("Setting mOwnerInfoView DATE text color to %d", color));
} catch (NullPointerException ne) {
if (DEBUG) ne.printStackTrace();
}
// alarm status view
try {
mAlarmStatusView.setTextColor(color);
if (DEBUG) Log.d(TAG, String.format("Setting mAlarmStatusView DATE text color to %d", color));
} catch (NullPointerException ne) {
if (DEBUG) ne.printStackTrace();
}
}
private void getCalendarEvents(ContentResolver resolver, String sources,
boolean multipleEvents, boolean hideOnGoing, long range) {
mCalendarEvents = new ArrayList<EventBundle>();
Calendar now = Calendar.getInstance();
Uri.Builder builder = CalendarContract.Instances.CONTENT_URI.buildUpon();
ContentUris.appendId(builder, now.getTimeInMillis());
ContentUris.appendId(builder, now.getTimeInMillis() + range);
String selection = "(( " + CalendarContract.Instances.CALENDAR_ID
+ " IN ( " + sources + " ))"
+ (hideOnGoing ? " AND ( " + CalendarContract.Instances.BEGIN
+ " > " + now.getTimeInMillis() + " ))" : ")");
Cursor eventCur = resolver.query(builder.build(), new String[] {
CalendarContract.Instances.TITLE,
CalendarContract.Instances.BEGIN,
CalendarContract.Instances.EVENT_LOCATION,
CalendarContract.Instances.ALL_DAY,
CalendarContract.Instances.CALENDAR_COLOR
}, selection, null,
CalendarContract.Instances.START_DAY + " ASC, "
+ CalendarContract.Instances.START_MINUTE + " ASC");
int events = eventCur.getCount();
if (events > 0) {
eventCur.moveToFirst();
do {
mCalendarEvents.add(new EventBundle(eventCur.getString(0),
eventCur.getLong(1), eventCur.getString(2),
now, (eventCur.getInt(3) != 0), eventCur.getInt(4)));
if (!multipleEvents)
break;
} while (eventCur.moveToNext());
}
eventCur.close();
}
private class EventBundle {
public String title;
public Calendar begin;
public String location;
public String dayString;
public boolean allDay = false;
public int color;
EventBundle(String s, long b, String l, Calendar now, boolean a, int c) {
title = s;
begin = Calendar.getInstance();
if (a) {
begin.setTimeInMillis(b - begin.get(Calendar.ZONE_OFFSET) - begin.get(Calendar.DST_OFFSET));
allDay = true;
} else {
begin.setTimeInMillis(b);
}
location = (l == null) ? "" : l;
int beginDay = begin.get(Calendar.DAY_OF_YEAR);
int today = now.get(Calendar.DAY_OF_YEAR);
if (beginDay == today) { // today
dayString = "";
} else if (today + 1 == beginDay || (today >= 365 && beginDay == 1)) { // tomorrow
dayString = getContext().getString(R.string.lockscreen_calendar_tomorrow);
} else { // another day of week
dayString = begin.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.SHORT,
Locale.getDefault());
}
allDay = a;
color = c;
}
}
}