blob: 8899ac323cef42c2d58ebd05c7c874680cf62fb8 [file] [log] [blame]
/*
* Copyright (C) 2008 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.widget.LockPatternUtils;
import com.android.internal.widget.RotarySelector;
import android.content.Context;
import android.text.format.DateFormat;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.*;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.media.AudioManager;
import com.android.internal.telephony.IccCard;
import java.util.Date;
import java.text.SimpleDateFormat;
/**
* The screen within {@link LockPatternKeyguardView} that shows general
* information about the device depending on its state, and how to get
* past it, as applicable.
*/
class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateMonitor.InfoCallback,
KeyguardUpdateMonitor.SimStateCallback, KeyguardUpdateMonitor.ConfigurationChangeCallback,
RotarySelector.OnDialTriggerListener {
static private final boolean DBG = false;
static private final String TAG = "LockScreen";
private Status mStatus = Status.Normal;
private final LockPatternUtils mLockPatternUtils;
private final KeyguardUpdateMonitor mUpdateMonitor;
private final KeyguardScreenCallback mCallback;
private TextView mCarrier;
private RotarySelector mRotary;
private TextView mTime;
private TextView mDate;
private TextView mStatus1;
private TextView mStatus2;
private TextView mScreenLocked;
private Button mEmergencyCallButton;
// are we showing battery information?
private boolean mShowingBatteryInfo = false;
// last known plugged in state
private boolean mPluggedIn = false;
// last known battery level
private int mBatteryLevel = 100;
private String mNextAlarm = null;
private Drawable mAlarmIcon = null;
private String mCharging = null;
private Drawable mChargingIcon = null;
private boolean mSilentMode;
private AudioManager mAudioManager;
private java.text.DateFormat mDateFormat;
private java.text.DateFormat mTimeFormat;
private boolean mCreatedInPortrait;
/**
* The status of this lock screen.
*/
enum Status {
/**
* 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);
private final boolean mShowStatusLines;
Status(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 showStatusLines() {
return mShowStatusLines;
}
}
/**
* @param context Used to setup the view.
* @param lockPatternUtils Used to know the state of the lock pattern settings.
* @param updateMonitor Used to register for updates on various keyguard related
* state, and query the initial state at setup.
* @param callback Used to communicate back to the host keyguard view.
*/
LockScreen(Context context, LockPatternUtils lockPatternUtils,
KeyguardUpdateMonitor updateMonitor,
KeyguardScreenCallback callback) {
super(context);
mLockPatternUtils = lockPatternUtils;
mUpdateMonitor = updateMonitor;
mCallback = callback;
mCreatedInPortrait = updateMonitor.isInPortrait();
final LayoutInflater inflater = LayoutInflater.from(context);
if (mCreatedInPortrait) {
inflater.inflate(R.layout.keyguard_screen_rotary_unlock, this, true);
} else {
inflater.inflate(R.layout.keyguard_screen_rotary_unlock_land, this, true);
}
mShowingBatteryInfo = updateMonitor.shouldShowBatteryInfo();
mPluggedIn = updateMonitor.isDevicePluggedIn();
mBatteryLevel = updateMonitor.getBatteryLevel();
mCarrier = (TextView) findViewById(R.id.carrier);
mTime = (TextView) findViewById(R.id.time);
mDate = (TextView) findViewById(R.id.date);
mStatus1 = (TextView) findViewById(R.id.status1);
mStatus2 = (TextView) findViewById(R.id.status2);
mEmergencyCallButton = (Button) findViewById(R.id.emergencyCallButton);
mEmergencyCallButton.setText(R.string.lockscreen_emergency_call);
mScreenLocked = (TextView) findViewById(R.id.screenLocked);
mRotary = (RotarySelector) findViewById(R.id.rotary);
mEmergencyCallButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mCallback.takeEmergencyCallAction();
}
});
setFocusable(true);
setFocusableInTouchMode(true);
setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
mStatus = getCurrentStatus(updateMonitor.getSimState());
updateLayout(mStatus);
refreshBatteryStringAndIcon();
refreshAlarmDisplay();
mTimeFormat = DateFormat.getTimeFormat(getContext());
mDateFormat = getLockScreenDateFormat();
refreshTimeAndDateDisplay();
updateStatusLines();
updateMonitor.registerInfoCallback(this);
updateMonitor.registerSimStateCallback(this);
updateMonitor.registerConfigurationChangeCallback(this);
mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
mSilentMode = mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT;
mRotary.setOnDialTriggerListener(this);
mRotary.setLeftHandleResource(R.drawable.ic_jog_dial_unlock);
mRotary.setRightHandleResource(mSilentMode ?
R.drawable.ic_jog_dial_sound_off :
R.drawable.ic_jog_dial_sound_on);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_MENU) {
mCallback.goToUnlockScreen();
}
return false;
}
/** {@inheritDoc} */
public void onDialTrigger(View v, int whichHandle) {
if (whichHandle == RotarySelector.OnDialTriggerListener.LEFT_HANDLE) {
mCallback.goToUnlockScreen();
} else if (whichHandle == RotarySelector.OnDialTriggerListener.RIGHT_HANDLE) {
// toggle silent mode
mSilentMode = !mSilentMode;
mAudioManager.setRingerMode(mSilentMode ? AudioManager.RINGER_MODE_SILENT
: AudioManager.RINGER_MODE_NORMAL);
final int handleIcon = mSilentMode ?
R.drawable.ic_jog_dial_sound_off :
R.drawable.ic_jog_dial_sound_on;
final int toastIcon = mSilentMode ?
R.drawable.ic_lock_ringer_off :
R.drawable.ic_lock_ringer_on;
mRotary.setRightHandleResource(handleIcon);
String message = mSilentMode ?
getContext().getString(R.string.global_action_silent_mode_on_status) :
getContext().getString(R.string.global_action_silent_mode_off_status);
toastMessage(mScreenLocked, message, toastIcon);
mCallback.pokeWakelock();
}
}
/**
* Displays a message in a text view and then removes it.
* @param textView The text view.
* @param text The text.
* @param iconResourceId The left hand icon.
*/
private void toastMessage(final TextView textView, final String text, final int iconResourceId) {
if (mPendingR1 != null) {
textView.removeCallbacks(mPendingR1);
mPendingR1 = null;
}
if (mPendingR2 != null) {
textView.removeCallbacks(mPendingR2);
mPendingR2 = null;
}
mPendingR1 = new Runnable() {
public void run() {
textView.setText(text);
textView.setCompoundDrawablesWithIntrinsicBounds(iconResourceId, 0, 0, 0);
textView.setCompoundDrawablePadding(4);
}
};
textView.postDelayed(mPendingR1, 0);
mPendingR2 = new Runnable() {
public void run() {
textView.setText("");
textView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
}
};
textView.postDelayed(mPendingR2, 3500);
}
private Runnable mPendingR1;
private Runnable mPendingR2;
private void refreshAlarmDisplay() {
mNextAlarm = mLockPatternUtils.getNextAlarm();
if (mNextAlarm != null) {
mAlarmIcon = getContext().getResources().getDrawable(R.drawable.ic_lock_idle_alarm);
}
updateStatusLines();
}
/** {@inheritDoc} */
public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn,
int batteryLevel) {
if (DBG) Log.d(TAG, "onRefreshBatteryInfo(" + showBatteryInfo + ", " + pluggedIn + ")");
mShowingBatteryInfo = showBatteryInfo;
mPluggedIn = pluggedIn;
mBatteryLevel = batteryLevel;
refreshBatteryStringAndIcon();
updateStatusLines();
}
private void refreshBatteryStringAndIcon() {
if (!mShowingBatteryInfo) {
mCharging = null;
return;
}
if (mChargingIcon == null) {
mChargingIcon =
getContext().getResources().getDrawable(R.drawable.ic_lock_idle_charging);
}
if (mPluggedIn) {
if (mBatteryLevel >= 100) {
mCharging = getContext().getString(R.string.lockscreen_charged);
} else {
mCharging = getContext().getString(R.string.lockscreen_plugged_in, mBatteryLevel);
}
} else {
mCharging = getContext().getString(R.string.lockscreen_low_battery);
}
}
/** {@inheritDoc} */
public void onTimeChanged() {
refreshTimeAndDateDisplay();
}
private void refreshTimeAndDateDisplay() {
Date now = new Date();
mTime.setText(mTimeFormat.format(now));
mDate.setText(mDateFormat.format(now));
}
/**
* @return A localized format like "Fri, Sep 18, 2009"
*/
private java.text.DateFormat getLockScreenDateFormat() {
SimpleDateFormat adjusted = null;
try {
// this call gives us the localized order
final SimpleDateFormat dateFormat = (SimpleDateFormat)
java.text.DateFormat.getDateInstance(java.text.DateFormat.FULL);
adjusted = new SimpleDateFormat(dateFormat.toPattern()
.replace("MMMM", "MMM") // we want "Sep", not "September"
.replace("EEEE", "EEE")); // we want "Fri", no "Friday"
} catch (ClassCastException e) {
// in case the library implementation changes and this throws a class cast exception
// or anything else that is funky
Log.e("LockScreen", "couldn't finnagle our custom date format :(", e);
return java.text.DateFormat.getDateInstance(java.text.DateFormat.MEDIUM);
}
return adjusted;
}
private void updateStatusLines() {
if (!mStatus.showStatusLines()
|| (mCharging == null && mNextAlarm == null)) {
mStatus1.setVisibility(View.INVISIBLE);
mStatus2.setVisibility(View.INVISIBLE);
} else if (mCharging != null && mNextAlarm == null) {
// charging only
mStatus1.setVisibility(View.VISIBLE);
mStatus2.setVisibility(View.INVISIBLE);
mStatus1.setText(mCharging);
mStatus1.setCompoundDrawablesWithIntrinsicBounds(mChargingIcon, null, null, null);
} else if (mNextAlarm != null && mCharging == null) {
// next alarm only
mStatus1.setVisibility(View.VISIBLE);
mStatus2.setVisibility(View.INVISIBLE);
mStatus1.setText(mNextAlarm);
mStatus1.setCompoundDrawablesWithIntrinsicBounds(mAlarmIcon, null, null, null);
} else if (mCharging != null && mNextAlarm != null) {
// both charging and next alarm
mStatus1.setVisibility(View.VISIBLE);
mStatus2.setVisibility(View.VISIBLE);
mStatus1.setText(mCharging);
mStatus1.setCompoundDrawablesWithIntrinsicBounds(mChargingIcon, null, null, null);
mStatus2.setText(mNextAlarm);
mStatus2.setCompoundDrawablesWithIntrinsicBounds(mAlarmIcon, null, null, null);
}
}
/** {@inheritDoc} */
public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) {
if (DBG) Log.d(TAG, "onRefreshCarrierInfo(" + plmn + ", " + spn + ")");
updateLayout(mStatus);
}
private void putEmergencyBelow(int viewId) {
final RelativeLayout.LayoutParams layoutParams =
(RelativeLayout.LayoutParams) mEmergencyCallButton.getLayoutParams();
layoutParams.addRule(RelativeLayout.BELOW, viewId);
mEmergencyCallButton.setLayoutParams(layoutParams);
}
/**
* Determine the current status of the lock screen given the sim state and other stuff.
*/
private Status getCurrentStatus(IccCard.State simState) {
boolean missingAndNotProvisioned = (!mUpdateMonitor.isDeviceProvisioned()
&& simState == IccCard.State.ABSENT);
if (missingAndNotProvisioned) {
return Status.SimMissingLocked;
}
switch (simState) {
case ABSENT:
return Status.SimMissing;
case NETWORK_LOCKED:
return Status.SimMissingLocked;
case NOT_READY:
return Status.SimMissing;
case PIN_REQUIRED:
return Status.SimLocked;
case PUK_REQUIRED:
return Status.SimPukLocked;
case READY:
return Status.Normal;
case UNKNOWN:
return Status.SimMissing;
}
return Status.SimMissing;
}
/**
* Update the layout to match the current status.
*/
private void updateLayout(Status status) {
switch (status) {
case Normal:
// text
mCarrier.setText(
getCarrierString(
mUpdateMonitor.getTelephonyPlmn(),
mUpdateMonitor.getTelephonySpn()));
// mScreenLocked.setText(R.string.lockscreen_screen_locked);
// layout
mScreenLocked.setVisibility(View.VISIBLE);
mRotary.setVisibility(View.VISIBLE);
mEmergencyCallButton.setVisibility(View.GONE);
break;
case NetworkLocked:
// text
mCarrier.setText(R.string.lockscreen_network_locked_message);
mScreenLocked.setText(R.string.lockscreen_instructions_when_pattern_disabled);
// layout
mScreenLocked.setVisibility(View.VISIBLE);
mRotary.setVisibility(View.VISIBLE);
mEmergencyCallButton.setVisibility(View.GONE);
break;
case SimMissing:
// text
mCarrier.setText(R.string.lockscreen_missing_sim_message_short);
mScreenLocked.setText(R.string.lockscreen_instructions_when_pattern_disabled);
// layout
mScreenLocked.setVisibility(View.INVISIBLE);
mRotary.setVisibility(View.VISIBLE);
mEmergencyCallButton.setVisibility(View.VISIBLE);
putEmergencyBelow(R.id.divider);
break;
case SimMissingLocked:
// text
mCarrier.setText(R.string.lockscreen_missing_sim_message_short);
mScreenLocked.setText(R.string.lockscreen_missing_sim_instructions);
// layout
mScreenLocked.setVisibility(View.VISIBLE);
mRotary.setVisibility(View.GONE);
mEmergencyCallButton.setVisibility(View.VISIBLE);
putEmergencyBelow(R.id.screenLocked);
break;
case SimLocked:
// text
mCarrier.setText(R.string.lockscreen_sim_locked_message);
// layout
mScreenLocked.setVisibility(View.INVISIBLE);
mRotary.setVisibility(View.VISIBLE);
mEmergencyCallButton.setVisibility(View.GONE);
break;
case SimPukLocked:
// text
mCarrier.setText(R.string.lockscreen_sim_puk_locked_message);
mScreenLocked.setText(R.string.lockscreen_sim_puk_locked_instructions);
// layout
mScreenLocked.setVisibility(View.VISIBLE);
mRotary.setVisibility(View.GONE);
mEmergencyCallButton.setVisibility(View.VISIBLE);
putEmergencyBelow(R.id.screenLocked);
break;
}
}
static CharSequence getCarrierString(CharSequence telephonyPlmn, CharSequence telephonySpn) {
if (telephonyPlmn != null && telephonySpn == null) {
return telephonyPlmn;
} else if (telephonyPlmn != null && telephonySpn != null) {
return telephonyPlmn + "\n" + telephonySpn;
} else if (telephonyPlmn == null && telephonySpn != null) {
return telephonySpn;
} else {
return "";
}
}
public void onSimStateChanged(IccCard.State simState) {
if (DBG) Log.d(TAG, "onSimStateChanged(" + simState + ")");
mStatus = getCurrentStatus(simState);
updateLayout(mStatus);
updateStatusLines();
}
public void onOrientationChange(boolean inPortrait) {
if (inPortrait != mCreatedInPortrait) {
mCallback.recreateMe();
}
}
public void onKeyboardChange(boolean isKeyboardOpen) {
if (mUpdateMonitor.isKeyguardBypassEnabled() && isKeyboardOpen) {
mCallback.goToUnlockScreen();
}
}
/** {@inheritDoc} */
public boolean needsInput() {
return false;
}
/** {@inheritDoc} */
public void onPause() {
}
/** {@inheritDoc} */
public void onResume() {
}
/** {@inheritDoc} */
public void cleanUp() {
mUpdateMonitor.removeCallback(this);
}
}