| /* |
| * 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); |
| } |
| } |
| |