M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions
+14
View File
@@ -0,0 +1,14 @@
LOCAL_PATH:= $(call my-dir)
# the library
# ============================================================
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_MODULE := android.policy
include $(BUILD_JAVA_LIBRARY)
# additionally, build unit tests in a separate .apk
include $(call all-makefiles-under,$(LOCAL_PATH))
@@ -0,0 +1,350 @@
/*
* 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 android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.OperationCanceledException;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.AccountManagerCallback;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.text.Editable;
import android.text.InputFilter;
import android.text.LoginFilter;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.os.Bundle;
import java.io.IOException;
/**
* When the user forgets their password a bunch of times, we fall back on their
* account's login/password to unlock the phone (and reset their lock pattern).
*/
public class AccountUnlockScreen extends RelativeLayout implements KeyguardScreen,
KeyguardUpdateMonitor.InfoCallback,View.OnClickListener, TextWatcher {
private static final String LOCK_PATTERN_PACKAGE = "com.android.settings";
private static final String LOCK_PATTERN_CLASS =
"com.android.settings.ChooseLockPattern";
/**
* The amount of millis to stay awake once this screen detects activity
*/
private static final int AWAKE_POKE_MILLIS = 30000;
private KeyguardScreenCallback mCallback;
private LockPatternUtils mLockPatternUtils;
private KeyguardUpdateMonitor mUpdateMonitor;
private TextView mTopHeader;
private TextView mInstructions;
private EditText mLogin;
private EditText mPassword;
private Button mOk;
private Button mEmergencyCall;
/**
* Shown while making asynchronous check of password.
*/
private ProgressDialog mCheckingDialog;
/**
* AccountUnlockScreen constructor.
* @param configuration
* @param updateMonitor
*/
public AccountUnlockScreen(Context context,Configuration configuration,
KeyguardUpdateMonitor updateMonitor, KeyguardScreenCallback callback,
LockPatternUtils lockPatternUtils) {
super(context);
mCallback = callback;
mLockPatternUtils = lockPatternUtils;
LayoutInflater.from(context).inflate(
R.layout.keyguard_screen_glogin_unlock, this, true);
mTopHeader = (TextView) findViewById(R.id.topHeader);
mTopHeader.setText(mLockPatternUtils.isPermanentlyLocked() ?
R.string.lockscreen_glogin_too_many_attempts :
R.string.lockscreen_glogin_forgot_pattern);
mInstructions = (TextView) findViewById(R.id.instructions);
mLogin = (EditText) findViewById(R.id.login);
mLogin.setFilters(new InputFilter[] { new LoginFilter.UsernameFilterGeneric() } );
mLogin.addTextChangedListener(this);
mPassword = (EditText) findViewById(R.id.password);
mPassword.addTextChangedListener(this);
mOk = (Button) findViewById(R.id.ok);
mOk.setOnClickListener(this);
mEmergencyCall = (Button) findViewById(R.id.emergencyCall);
mEmergencyCall.setOnClickListener(this);
mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCall);
mUpdateMonitor = updateMonitor;
mUpdateMonitor.registerInfoCallback(this);
}
public void afterTextChanged(Editable s) {
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
mCallback.pokeWakelock(AWAKE_POKE_MILLIS);
}
@Override
protected boolean onRequestFocusInDescendants(int direction,
Rect previouslyFocusedRect) {
// send focus to the login field
return mLogin.requestFocus(direction, previouslyFocusedRect);
}
/** {@inheritDoc} */
public boolean needsInput() {
return true;
}
/** {@inheritDoc} */
public void onPause() {
}
/** {@inheritDoc} */
public void onResume() {
// start fresh
mLogin.setText("");
mPassword.setText("");
mLogin.requestFocus();
mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCall);
}
/** {@inheritDoc} */
public void cleanUp() {
if (mCheckingDialog != null) {
mCheckingDialog.hide();
}
mUpdateMonitor.removeCallback(this); // this must be first
mCallback = null;
mLockPatternUtils = null;
mUpdateMonitor = null;
}
/** {@inheritDoc} */
public void onClick(View v) {
mCallback.pokeWakelock();
if (v == mOk) {
asyncCheckPassword();
}
if (v == mEmergencyCall) {
mCallback.takeEmergencyCallAction();
}
}
private void postOnCheckPasswordResult(final boolean success) {
// ensure this runs on UI thread
mLogin.post(new Runnable() {
public void run() {
if (success) {
// clear out forgotten password
mLockPatternUtils.setPermanentlyLocked(false);
mLockPatternUtils.setLockPatternEnabled(false);
mLockPatternUtils.saveLockPattern(null);
// launch the 'choose lock pattern' activity so
// the user can pick a new one if they want to
Intent intent = new Intent();
intent.setClassName(LOCK_PATTERN_PACKAGE, LOCK_PATTERN_CLASS);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
mCallback.reportSuccessfulUnlockAttempt();
// close the keyguard
mCallback.keyguardDone(true);
} else {
mInstructions.setText(R.string.lockscreen_glogin_invalid_input);
mPassword.setText("");
mCallback.reportFailedUnlockAttempt();
}
}
});
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN
&& event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
if (mLockPatternUtils.isPermanentlyLocked()) {
mCallback.goToLockScreen();
} else {
mCallback.forgotPattern(false);
}
return true;
}
return super.dispatchKeyEvent(event);
}
/**
* Given the string the user entered in the 'username' field, find
* the stored account that they probably intended. Prefer, in order:
*
* - an exact match for what was typed, or
* - a case-insensitive match for what was typed, or
* - if they didn't include a domain, an exact match of the username, or
* - if they didn't include a domain, a case-insensitive
* match of the username.
*
* If there is a tie for the best match, choose neither --
* the user needs to be more specific.
*
* @return an account name from the database, or null if we can't
* find a single best match.
*/
private Account findIntendedAccount(String username) {
Account[] accounts = AccountManager.get(mContext).getAccountsByType("com.google");
// Try to figure out which account they meant if they
// typed only the username (and not the domain), or got
// the case wrong.
Account bestAccount = null;
int bestScore = 0;
for (Account a: accounts) {
int score = 0;
if (username.equals(a.name)) {
score = 4;
} else if (username.equalsIgnoreCase(a.name)) {
score = 3;
} else if (username.indexOf('@') < 0) {
int i = a.name.indexOf('@');
if (i >= 0) {
String aUsername = a.name.substring(0, i);
if (username.equals(aUsername)) {
score = 2;
} else if (username.equalsIgnoreCase(aUsername)) {
score = 1;
}
}
}
if (score > bestScore) {
bestAccount = a;
bestScore = score;
} else if (score == bestScore) {
bestAccount = null;
}
}
return bestAccount;
}
private void asyncCheckPassword() {
mCallback.pokeWakelock(AWAKE_POKE_MILLIS);
final String login = mLogin.getText().toString();
final String password = mPassword.getText().toString();
Account account = findIntendedAccount(login);
if (account == null) {
postOnCheckPasswordResult(false);
return;
}
getProgressDialog().show();
Bundle options = new Bundle();
options.putString(AccountManager.KEY_PASSWORD, password);
AccountManager.get(mContext).confirmCredentials(account, options, null /* activity */,
new AccountManagerCallback<Bundle>() {
public void run(AccountManagerFuture<Bundle> future) {
try {
mCallback.pokeWakelock(AWAKE_POKE_MILLIS);
final Bundle result = future.getResult();
final boolean verified = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
postOnCheckPasswordResult(verified);
} catch (OperationCanceledException e) {
postOnCheckPasswordResult(false);
} catch (IOException e) {
postOnCheckPasswordResult(false);
} catch (AuthenticatorException e) {
postOnCheckPasswordResult(false);
} finally {
mLogin.post(new Runnable() {
public void run() {
getProgressDialog().hide();
}
});
}
}
}, null /* handler */);
}
private Dialog getProgressDialog() {
if (mCheckingDialog == null) {
mCheckingDialog = new ProgressDialog(mContext);
mCheckingDialog.setMessage(
mContext.getString(R.string.lockscreen_glogin_checking_password));
mCheckingDialog.setIndeterminate(true);
mCheckingDialog.setCancelable(false);
mCheckingDialog.getWindow().setType(
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
if (!mContext.getResources().getBoolean(
com.android.internal.R.bool.config_sf_slowBlur)) {
mCheckingDialog.getWindow().setFlags(
WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
}
}
return mCheckingDialog;
}
public void onPhoneStateChanged(String newState) {
mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCall);
}
public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) {
}
public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn, int subscription) {
}
public void onRingerModeChanged(int state) {
}
public void onTimeChanged() {
}
}
@@ -0,0 +1,605 @@
/*
* 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 android.app.Activity;
import android.app.AlertDialog;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.os.Handler;
import android.os.Message;
import android.os.SystemProperties;
import android.provider.Settings;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.internal.R;
import com.android.internal.app.ShutdownThread;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyProperties;
import com.google.android.collect.Lists;
import java.util.ArrayList;
/**
* Helper to show the global actions dialog. Each item is an {@link Action} that
* may show depending on whether the keyguard is showing, and whether the device
* is provisioned.
*/
class GlobalActions implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener {
private static final String TAG = "GlobalActions";
private StatusBarManager mStatusBar;
private final Context mContext;
private final AudioManager mAudioManager;
private ArrayList<Action> mItems;
private AlertDialog mDialog;
private ToggleAction mSilentModeToggle;
private ToggleAction mAirplaneModeOn;
private MyAdapter mAdapter;
private boolean mKeyguardShowing = false;
private boolean mDeviceProvisioned = false;
private ToggleAction.State mAirplaneState = ToggleAction.State.Off;
private boolean mIsWaitingForEcmExit = false;
/**
* @param context everything needs a context :(
*/
public GlobalActions(Context context) {
mContext = context;
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
// receive broadcasts
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
context.registerReceiver(mBroadcastReceiver, filter);
// get notified of phone state changes
TelephonyManager telephonyManager =
(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
}
/**
* Show the global actions dialog (creating if necessary)
* @param keyguardShowing True if keyguard is showing
*/
public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
mKeyguardShowing = keyguardShowing;
mDeviceProvisioned = isDeviceProvisioned;
if (mDialog == null) {
mStatusBar = (StatusBarManager)mContext.getSystemService(Context.STATUS_BAR_SERVICE);
mDialog = createDialog();
}
prepareDialog();
mStatusBar.disable(StatusBarManager.DISABLE_EXPAND);
mDialog.show();
}
/**
* Create the global actions dialog.
* @return A new dialog.
*/
private AlertDialog createDialog() {
mSilentModeToggle = new ToggleAction(
R.drawable.ic_lock_silent_mode,
R.drawable.ic_lock_silent_mode_off,
R.string.global_action_toggle_silent_mode,
R.string.global_action_silent_mode_on_status,
R.string.global_action_silent_mode_off_status) {
void willCreate() {
// XXX: FIXME: switch to ic_lock_vibrate_mode when available
mEnabledIconResId = (Settings.System.getInt(mContext.getContentResolver(),
Settings.System.VIBRATE_IN_SILENT, 1) == 1)
? R.drawable.ic_lock_silent_mode_vibrate
: R.drawable.ic_lock_silent_mode;
}
void onToggle(boolean on) {
if (on) {
mAudioManager.setRingerMode((Settings.System.getInt(mContext.getContentResolver(),
Settings.System.VIBRATE_IN_SILENT, 1) == 1)
? AudioManager.RINGER_MODE_VIBRATE
: AudioManager.RINGER_MODE_SILENT);
} else {
mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
}
}
public boolean showDuringKeyguard() {
return true;
}
public boolean showBeforeProvisioning() {
return false;
}
};
mAirplaneModeOn = new ToggleAction(
R.drawable.ic_lock_airplane_mode,
R.drawable.ic_lock_airplane_mode_off,
R.string.global_actions_toggle_airplane_mode,
R.string.global_actions_airplane_mode_on_status,
R.string.global_actions_airplane_mode_off_status) {
void onToggle(boolean on) {
if (Boolean.parseBoolean(
SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {
mIsWaitingForEcmExit = true;
// Launch ECM exit dialog
Intent ecmDialogIntent =
new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);
ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(ecmDialogIntent);
} else {
changeAirplaneModeSystemSetting(on);
}
}
@Override
protected void changeStateFromPress(boolean buttonOn) {
// In ECM mode airplane state cannot be changed
if (!(Boolean.parseBoolean(
SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) {
mState = buttonOn ? State.TurningOn : State.TurningOff;
mAirplaneState = mState;
}
}
public boolean showDuringKeyguard() {
return true;
}
public boolean showBeforeProvisioning() {
return false;
}
};
mItems = Lists.newArrayList(
// silent mode
mSilentModeToggle,
// next: airplane mode
mAirplaneModeOn,
// last: power off
new SinglePressAction(
com.android.internal.R.drawable.ic_lock_power_off,
R.string.global_action_power_off) {
public void onPress() {
// shutdown by making sure radio and power are handled accordingly.
ShutdownThread.shutdown(mContext, true);
}
public boolean showDuringKeyguard() {
return true;
}
public boolean showBeforeProvisioning() {
return true;
}
});
boolean warmBootCapable = SystemProperties.getBoolean("ro.warmboot.capability", false);
Log.d(TAG, "Device WarmBoot Capability = " + warmBootCapable );
if ( warmBootCapable ) {
SinglePressAction suspendAction;
suspendAction = new SinglePressAction(
com.android.internal.R.drawable.ic_lock_power_off,
R.string.global_action_suspend) {
public void onPress() {
// Starts the SuspendActivity which enables Device to enter deep sleep "suspend2ram" state
Intent intent = new Intent(Intent.ACTION_REQUEST_SUSPEND);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
}
public boolean showDuringKeyguard() {
return true;
}
public boolean showBeforeProvisioning() {
return true;
}
};
mItems.add( suspendAction );
}
mAdapter = new MyAdapter();
final AlertDialog.Builder ab = new AlertDialog.Builder(mContext);
ab.setAdapter(mAdapter, this)
.setInverseBackgroundForced(true);
final AlertDialog dialog = ab.create();
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
if (!mContext.getResources().getBoolean(
com.android.internal.R.bool.config_sf_slowBlur)) {
dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
}
dialog.setOnDismissListener(this);
return dialog;
}
private void prepareDialog() {
final boolean silentModeOn =
mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
mSilentModeToggle.updateState(
silentModeOn ? ToggleAction.State.On : ToggleAction.State.Off);
mAirplaneModeOn.updateState(mAirplaneState);
mAdapter.notifyDataSetChanged();
if (mKeyguardShowing) {
mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
} else {
mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
}
mDialog.setTitle(R.string.global_actions);
}
/** {@inheritDoc} */
public void onDismiss(DialogInterface dialog) {
mStatusBar.disable(StatusBarManager.DISABLE_NONE);
}
/** {@inheritDoc} */
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
mAdapter.getItem(which).onPress();
}
/**
* The adapter used for the list within the global actions dialog, taking
* into account whether the keyguard is showing via
* {@link GlobalActions#mKeyguardShowing} and whether the device is provisioned
* via {@link GlobalActions#mDeviceProvisioned}.
*/
private class MyAdapter extends BaseAdapter {
public int getCount() {
int count = 0;
for (int i = 0; i < mItems.size(); i++) {
final Action action = mItems.get(i);
if (mKeyguardShowing && !action.showDuringKeyguard()) {
continue;
}
if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
continue;
}
count++;
}
return count;
}
@Override
public boolean isEnabled(int position) {
return getItem(position).isEnabled();
}
@Override
public boolean areAllItemsEnabled() {
return false;
}
public Action getItem(int position) {
int filteredPos = 0;
for (int i = 0; i < mItems.size(); i++) {
final Action action = mItems.get(i);
if (mKeyguardShowing && !action.showDuringKeyguard()) {
continue;
}
if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
continue;
}
if (filteredPos == position) {
return action;
}
filteredPos++;
}
throw new IllegalArgumentException("position " + position + " out of "
+ "range of showable actions, filtered count = "
+ "= " + getCount() + ", keyguardshowing=" + mKeyguardShowing
+ ", provisioned=" + mDeviceProvisioned);
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
Action action = getItem(position);
return action.create(mContext, convertView, parent, LayoutInflater.from(mContext));
}
}
// note: the scheme below made more sense when we were planning on having
// 8 different things in the global actions dialog. seems overkill with
// only 3 items now, but may as well keep this flexible approach so it will
// be easy should someone decide at the last minute to include something
// else, such as 'enable wifi', or 'enable bluetooth'
/**
* What each item in the global actions dialog must be able to support.
*/
private interface Action {
View create(Context context, View convertView, ViewGroup parent, LayoutInflater inflater);
void onPress();
/**
* @return whether this action should appear in the dialog when the keygaurd
* is showing.
*/
boolean showDuringKeyguard();
/**
* @return whether this action should appear in the dialog before the
* device is provisioned.
*/
boolean showBeforeProvisioning();
boolean isEnabled();
}
/**
* A single press action maintains no state, just responds to a press
* and takes an action.
*/
private static abstract class SinglePressAction implements Action {
private final int mIconResId;
private final int mMessageResId;
protected SinglePressAction(int iconResId, int messageResId) {
mIconResId = iconResId;
mMessageResId = messageResId;
}
public boolean isEnabled() {
return true;
}
abstract public void onPress();
public View create(
Context context, View convertView, ViewGroup parent, LayoutInflater inflater) {
View v = (convertView != null) ?
convertView :
inflater.inflate(R.layout.global_actions_item, parent, false);
ImageView icon = (ImageView) v.findViewById(R.id.icon);
TextView messageView = (TextView) v.findViewById(R.id.message);
v.findViewById(R.id.status).setVisibility(View.GONE);
icon.setImageDrawable(context.getResources().getDrawable(mIconResId));
messageView.setText(mMessageResId);
return v;
}
}
/**
* A toggle action knows whether it is on or off, and displays an icon
* and status message accordingly.
*/
private static abstract class ToggleAction implements Action {
enum State {
Off(false),
TurningOn(true),
TurningOff(true),
On(false);
private final boolean inTransition;
State(boolean intermediate) {
inTransition = intermediate;
}
public boolean inTransition() {
return inTransition;
}
}
protected State mState = State.Off;
// prefs
protected int mEnabledIconResId;
protected int mDisabledIconResid;
protected int mMessageResId;
protected int mEnabledStatusMessageResId;
protected int mDisabledStatusMessageResId;
/**
* @param enabledIconResId The icon for when this action is on.
* @param disabledIconResid The icon for when this action is off.
* @param essage The general information message, e.g 'Silent Mode'
* @param enabledStatusMessageResId The on status message, e.g 'sound disabled'
* @param disabledStatusMessageResId The off status message, e.g. 'sound enabled'
*/
public ToggleAction(int enabledIconResId,
int disabledIconResid,
int essage,
int enabledStatusMessageResId,
int disabledStatusMessageResId) {
mEnabledIconResId = enabledIconResId;
mDisabledIconResid = disabledIconResid;
mMessageResId = essage;
mEnabledStatusMessageResId = enabledStatusMessageResId;
mDisabledStatusMessageResId = disabledStatusMessageResId;
}
/**
* Override to make changes to resource IDs just before creating the
* View.
*/
void willCreate() {
}
public View create(Context context, View convertView, ViewGroup parent,
LayoutInflater inflater) {
willCreate();
View v = (convertView != null) ?
convertView :
inflater.inflate(R
.layout.global_actions_item, parent, false);
ImageView icon = (ImageView) v.findViewById(R.id.icon);
TextView messageView = (TextView) v.findViewById(R.id.message);
TextView statusView = (TextView) v.findViewById(R.id.status);
messageView.setText(mMessageResId);
boolean on = ((mState == State.On) || (mState == State.TurningOn));
icon.setImageDrawable(context.getResources().getDrawable(
(on ? mEnabledIconResId : mDisabledIconResid)));
statusView.setText(on ? mEnabledStatusMessageResId : mDisabledStatusMessageResId);
statusView.setVisibility(View.VISIBLE);
final boolean enabled = isEnabled();
messageView.setEnabled(enabled);
statusView.setEnabled(enabled);
icon.setEnabled(enabled);
v.setEnabled(enabled);
return v;
}
public final void onPress() {
if (mState.inTransition()) {
Log.w(TAG, "shouldn't be able to toggle when in transition");
return;
}
final boolean nowOn = !(mState == State.On);
onToggle(nowOn);
changeStateFromPress(nowOn);
}
public boolean isEnabled() {
return !mState.inTransition();
}
/**
* Implementations may override this if their state can be in on of the intermediate
* states until some notification is received (e.g airplane mode is 'turning off' until
* we know the wireless connections are back online
* @param buttonOn Whether the button was turned on or off
*/
protected void changeStateFromPress(boolean buttonOn) {
mState = buttonOn ? State.On : State.Off;
}
abstract void onToggle(boolean on);
public void updateState(State state) {
mState = state;
}
}
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
|| Intent.ACTION_SCREEN_OFF.equals(action)) {
String reason = intent.getStringExtra(PhoneWindowManager.SYSTEM_DIALOG_REASON_KEY);
if (!PhoneWindowManager.SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS.equals(reason)) {
mHandler.sendEmptyMessage(MESSAGE_DISMISS);
}
} else if (TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED.equals(action)) {
// Airplane mode can be changed after ECM exits if airplane toggle button
// is pressed during ECM mode
if (!(intent.getBooleanExtra("PHONE_IN_ECM_STATE", false)) &&
mIsWaitingForEcmExit) {
mIsWaitingForEcmExit = false;
changeAirplaneModeSystemSetting(true);
}
}
}
};
PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
@Override
public void onServiceStateChanged(ServiceState serviceState) {
final boolean inAirplaneMode = serviceState.getState() == ServiceState.STATE_POWER_OFF;
mAirplaneState = inAirplaneMode ? ToggleAction.State.On : ToggleAction.State.Off;
mAirplaneModeOn.updateState(mAirplaneState);
mAdapter.notifyDataSetChanged();
}
};
private static final int MESSAGE_DISMISS = 0;
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
if (msg.what == MESSAGE_DISMISS) {
if (mDialog != null) {
mDialog.dismiss();
}
}
}
};
/**
* Change the airplane mode system setting
*/
private void changeAirplaneModeSystemSetting(boolean on) {
Settings.System.putInt(
mContext.getContentResolver(),
Settings.System.AIRPLANE_MODE_ON,
on ? 1 : 0);
Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.putExtra("state", on);
mContext.sendBroadcast(intent);
}
}
@@ -0,0 +1,192 @@
/*
* 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 android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.PaintDrawable;
import android.graphics.drawable.StateListDrawable;
import android.graphics.Bitmap;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.TableMaskFilter;
import android.graphics.Typeface;
import android.text.Layout.Alignment;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.DisplayMetrics;
import android.util.Log;
import android.content.res.Resources;
import android.content.Context;
/**
* Various utilities shared amongst the Launcher's classes.
*/
final class IconUtilities {
private static final String TAG = "IconUtilities";
private static final int sColors[] = { 0xffff0000, 0xff00ff00, 0xff0000ff };
private int mIconWidth = -1;
private int mIconHeight = -1;
private int mIconTextureWidth = -1;
private int mIconTextureHeight = -1;
private final Paint mPaint = new Paint();
private final Paint mBlurPaint = new Paint();
private final Paint mGlowColorPressedPaint = new Paint();
private final Paint mGlowColorFocusedPaint = new Paint();
private final Rect mOldBounds = new Rect();
private final Canvas mCanvas = new Canvas();
private final DisplayMetrics mDisplayMetrics;
private int mColorIndex = 0;
public IconUtilities(Context context) {
final Resources resources = context.getResources();
DisplayMetrics metrics = mDisplayMetrics = resources.getDisplayMetrics();
final float density = metrics.density;
final float blurPx = 5 * density;
mIconWidth = mIconHeight = (int) resources.getDimension(android.R.dimen.app_icon_size);
mIconTextureWidth = mIconTextureHeight = mIconWidth + (int)(blurPx*2);
mBlurPaint.setMaskFilter(new BlurMaskFilter(blurPx, BlurMaskFilter.Blur.NORMAL));
mGlowColorPressedPaint.setColor(0xffffc300);
mGlowColorPressedPaint.setMaskFilter(TableMaskFilter.CreateClipTable(0, 30));
mGlowColorFocusedPaint.setColor(0xffff8e00);
mGlowColorFocusedPaint.setMaskFilter(TableMaskFilter.CreateClipTable(0, 30));
ColorMatrix cm = new ColorMatrix();
cm.setSaturation(0.2f);
mCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG,
Paint.FILTER_BITMAP_FLAG));
}
public Drawable createIconDrawable(Drawable src) {
Bitmap scaled = createIconBitmap(src);
StateListDrawable result = new StateListDrawable();
result.addState(new int[] { android.R.attr.state_focused },
new BitmapDrawable(createSelectedBitmap(scaled, false)));
result.addState(new int[] { android.R.attr.state_pressed },
new BitmapDrawable(createSelectedBitmap(scaled, true)));
result.addState(new int[0], new BitmapDrawable(scaled));
result.setBounds(0, 0, mIconTextureWidth, mIconTextureHeight);
return result;
}
/**
* Returns a bitmap suitable for the all apps view. The bitmap will be a power
* of two sized ARGB_8888 bitmap that can be used as a gl texture.
*/
private Bitmap createIconBitmap(Drawable icon) {
int width = mIconWidth;
int height = mIconHeight;
if (icon instanceof PaintDrawable) {
PaintDrawable painter = (PaintDrawable) icon;
painter.setIntrinsicWidth(width);
painter.setIntrinsicHeight(height);
} else if (icon instanceof BitmapDrawable) {
// Ensure the bitmap has a density.
BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
Bitmap bitmap = bitmapDrawable.getBitmap();
if (bitmap.getDensity() == Bitmap.DENSITY_NONE) {
bitmapDrawable.setTargetDensity(mDisplayMetrics);
}
}
int sourceWidth = icon.getIntrinsicWidth();
int sourceHeight = icon.getIntrinsicHeight();
if (sourceWidth > 0 && sourceWidth > 0) {
// There are intrinsic sizes.
if (width < sourceWidth || height < sourceHeight) {
// It's too big, scale it down.
final float ratio = (float) sourceWidth / sourceHeight;
if (sourceWidth > sourceHeight) {
height = (int) (width / ratio);
} else if (sourceHeight > sourceWidth) {
width = (int) (height * ratio);
}
} else if (sourceWidth < width && sourceHeight < height) {
// It's small, use the size they gave us.
width = sourceWidth;
height = sourceHeight;
}
}
// no intrinsic size --> use default size
int textureWidth = mIconTextureWidth;
int textureHeight = mIconTextureHeight;
final Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight,
Bitmap.Config.ARGB_8888);
final Canvas canvas = mCanvas;
canvas.setBitmap(bitmap);
final int left = (textureWidth-width) / 2;
final int top = (textureHeight-height) / 2;
if (false) {
// draw a big box for the icon for debugging
canvas.drawColor(sColors[mColorIndex]);
if (++mColorIndex >= sColors.length) mColorIndex = 0;
Paint debugPaint = new Paint();
debugPaint.setColor(0xffcccc00);
canvas.drawRect(left, top, left+width, top+height, debugPaint);
}
mOldBounds.set(icon.getBounds());
icon.setBounds(left, top, left+width, top+height);
icon.draw(canvas);
icon.setBounds(mOldBounds);
return bitmap;
}
private Bitmap createSelectedBitmap(Bitmap src, boolean pressed) {
final Bitmap result = Bitmap.createBitmap(mIconTextureWidth, mIconTextureHeight,
Bitmap.Config.ARGB_8888);
final Canvas dest = new Canvas(result);
dest.drawColor(0, PorterDuff.Mode.CLEAR);
int[] xy = new int[2];
Bitmap mask = src.extractAlpha(mBlurPaint, xy);
dest.drawBitmap(mask, xy[0], xy[1],
pressed ? mGlowColorPressedPaint : mGlowColorFocusedPaint);
mask.recycle();
dest.drawBitmap(src, 0, 0, mPaint);
return result;
}
}
@@ -0,0 +1,45 @@
/*
* 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;
/**
* Common interface of each {@link android.view.View} that is a screen of
* {@link LockPatternKeyguardView}.
*/
public interface KeyguardScreen {
/**
* Return true if your view needs input, so should allow the soft
* keyboard to be displayed.
*/
boolean needsInput();
/**
* This screen is no longer in front of the user.
*/
void onPause();
/**
* This screen is going to be in front of the user.
*/
void onResume();
/**
* This view is going away; a hook to do cleanup.
*/
void cleanUp();
}
@@ -0,0 +1,89 @@
/*
* 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 android.content.res.Configuration;
/**
* Within a keyguard, there may be several screens that need a callback
* to the host keyguard view.
*/
public interface KeyguardScreenCallback extends KeyguardViewCallback {
/**
* Transition to the lock screen.
*/
void goToLockScreen();
/**
* Transition to the unlock screen.
*/
void goToUnlockScreen();
/**
* User dismissed the PIN dialog by clicking cancel.Update
* the subscription for which the dialog was dismissed.The
* dialog will not be prompted again for that subscription.
*/
void updatePinUnlockCancel(int subscription);
/**
* The user reported that they forgot their pattern (or not, when they want to back out of the
* forgot pattern screen).
*
* @param isForgotten True if the user hit the forgot pattern, false if they want to back out
* of the account screen.
*/
void forgotPattern(boolean isForgotten);
/**
* @return Whether the keyguard requires some sort of PIN.
*/
boolean isSecure();
/**
* @return Whether we are in a mode where we only want to verify the
* user can get past the keyguard.
*/
boolean isVerifyUnlockOnly();
/**
* Stay on me, but recreate me (so I can use a different layout).
*/
void recreateMe(Configuration config);
/**
* Take action to send an emergency call.
*/
void takeEmergencyCallAction();
/**
* Report that the user had a failed attempt to unlock with password or pattern.
*/
void reportFailedUnlockAttempt();
/**
* Report that the user successfully entered their password or pattern.
*/
void reportSuccessfulUnlockAttempt();
/**
* Report whether we there's another way to unlock the device.
* @return true
*/
boolean doesFallbackUnlockScreenExist();
}
@@ -0,0 +1,611 @@
/*
* Copyright (C) 2008 The Android Open Source Project
* Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
*
* 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 android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.database.ContentObserver;
import static android.os.BatteryManager.BATTERY_STATUS_CHARGING;
import static android.os.BatteryManager.BATTERY_STATUS_FULL;
import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
import android.media.AudioManager;
import android.os.BatteryManager;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.provider.Settings;
import android.provider.Telephony;
import static android.provider.Telephony.Intents.EXTRA_PLMN;
import static android.provider.Telephony.Intents.EXTRA_SHOW_PLMN;
import static android.provider.Telephony.Intents.EXTRA_SHOW_SPN;
import static android.provider.Telephony.Intents.EXTRA_SPN;
import static android.provider.Telephony.Intents.EXTRA_SUBSCRIPTION;
import static android.provider.Telephony.Intents.SPN_STRINGS_UPDATED_ACTION;
import com.android.internal.telephony.IccCard;
import static com.android.internal.telephony.IccCard.INTENT_KEY_SUBSCRIPTION;
import com.android.internal.telephony.TelephonyIntents;
import android.telephony.TelephonyManager;
import android.util.Log;
import com.android.internal.R;
import com.google.android.collect.Lists;
import java.util.ArrayList;
/**
* Watches for updates that may be interesting to the keyguard, and provides
* the up to date information as well as a registration for callbacks that care
* to be updated.
*
* Note: under time crunch, this has been extended to include some stuff that
* doesn't really belong here. see {@link #handleBatteryUpdate} where it shutdowns
* the device, and {@link #getFailedAttempts()}, {@link #reportFailedAttempt()}
* and {@link #clearFailedAttempts()}. Maybe we should rename this 'KeyguardContext'...
*/
public class KeyguardUpdateMonitor {
static private final String TAG = "KeyguardUpdateMonitor";
static private final boolean DEBUG = false;
private static final int LOW_BATTERY_THRESHOLD = 20;
private final Context mContext;
private IccCard.State[] mSimState;
private boolean mKeyguardBypassEnabled;
private boolean mDeviceProvisioned;
private int mBatteryLevel;
private CharSequence[] mTelephonyPlmn;
private CharSequence[] mTelephonySpn;
private int mBatteryStatus;
private int mFailedAttempts = 0;
private Handler mHandler;
private ArrayList<InfoCallback> mInfoCallbacks = Lists.newArrayList();
private ArrayList<SimStateCallback> mSimStateCallbacks = Lists.newArrayList();
private ContentObserver mContentObserver;
// messages for the handler
private static final int MSG_TIME_UPDATE = 301;
private static final int MSG_BATTERY_UPDATE = 302;
private static final int MSG_CARRIER_INFO_UPDATE = 303;
private static final int MSG_SIM_STATE_CHANGE = 304;
private static final int MSG_RINGER_MODE_CHANGED = 305;
private static final int MSG_PHONE_STATE_CHANGED = 306;
/**
* When we receive a
* {@link com.android.internal.telephony.TelephonyIntents#ACTION_SIM_STATE_CHANGED} broadcast,
* and then pass a result via our handler to {@link KeyguardUpdateMonitor#handleSimStateChange},
* we need a single object to pass to the handler. This class helps decode
* the intent and provide a {@link SimCard.State} result.
*/
private static class SimArgs {
public final IccCard.State simState;
public final int subscription;
private SimArgs(Intent intent) {
if (!TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(intent.getAction())) {
throw new IllegalArgumentException("only handles intent ACTION_SIM_STATE_CHANGED");
}
this.subscription = intent.getIntExtra(IccCard.INTENT_KEY_SUBSCRIPTION, 0);
Log.d(TAG,"ACTION_SIM_STATE_CHANGED intent received on sub = " + this.subscription);
String stateExtra = intent.getStringExtra(IccCard.INTENT_KEY_ICC_STATE);
if (IccCard.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
this.simState = IccCard.State.ABSENT;
} else if (IccCard.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
this.simState = IccCard.State.READY;
} else if (IccCard.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
final String lockedReason = intent
.getStringExtra(IccCard.INTENT_KEY_LOCKED_REASON);
if (IccCard.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
this.simState = IccCard.State.PIN_REQUIRED;
} else if (IccCard.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
this.simState = IccCard.State.PUK_REQUIRED;
} else if(IccCard.INTENT_VALUE_LOCKED_NETWORK.equals(lockedReason)) {
this.simState = IccCard.State.NETWORK_LOCKED;
} else if(IccCard.INTENT_VALUE_LOCKED_NETWORK_SUBSET.equals(lockedReason)) {
this.simState = IccCard.State.SIM_NETWORK_SUBSET_LOCKED;
} else if(IccCard.INTENT_VALUE_LOCKED_CORPORATE.equals(lockedReason)) {
this.simState = IccCard.State.SIM_CORPORATE_LOCKED;
} else if(IccCard.INTENT_VALUE_LOCKED_SERVICE_PROVIDER.equals(lockedReason)) {
this.simState = IccCard.State.SIM_SERVICE_PROVIDER_LOCKED;
} else if(IccCard.INTENT_VALUE_LOCKED_SIM.equals(lockedReason)) {
this.simState = IccCard.State.SIM_SIM_LOCKED;
} else if(IccCard.INTENT_VALUE_LOCKED_RUIM_NETWORK1.equals(lockedReason)) {
this.simState = IccCard.State.RUIM_NETWORK1_LOCKED;
} else if(IccCard.INTENT_VALUE_LOCKED_RUIM_NETWORK2.equals(lockedReason)) {
this.simState = IccCard.State.RUIM_NETWORK2_LOCKED;
} else if(IccCard.INTENT_VALUE_LOCKED_RUIM_HRPD.equals(lockedReason)) {
this.simState = IccCard.State.RUIM_HRPD_LOCKED;
} else if(IccCard.INTENT_VALUE_LOCKED_RUIM_CORPORATE.equals(lockedReason)) {
this.simState = IccCard.State.RUIM_CORPORATE_LOCKED;
} else if(IccCard.INTENT_VALUE_LOCKED_RUIM_SERVICE_PROVIDER.equals(lockedReason)) {
this.simState = IccCard.State.RUIM_SERVICE_PROVIDER_LOCKED;
} else if(IccCard.INTENT_VALUE_LOCKED_RUIM_RUIM.equals(lockedReason)) {
this.simState = IccCard.State.RUIM_RUIM_LOCKED;
} else {
this.simState = IccCard.State.UNKNOWN;
}
} else if (IccCard.INTENT_VALUE_ICC_CARD_IO_ERROR.equals(stateExtra)) {
this.simState = IccCard.State.CARD_IO_ERROR;
} else {
this.simState = IccCard.State.UNKNOWN;
}
}
public String toString() {
return simState.toString();
}
}
public KeyguardUpdateMonitor(Context context) {
mContext = context;
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_TIME_UPDATE:
handleTimeUpdate();
break;
case MSG_BATTERY_UPDATE:
handleBatteryUpdate(msg.arg1, msg.arg2);
break;
case MSG_CARRIER_INFO_UPDATE:
handleCarrierInfoUpdate(msg.arg1);
break;
case MSG_SIM_STATE_CHANGE:
handleSimStateChange((SimArgs)msg.obj);
break;
case MSG_RINGER_MODE_CHANGED:
handleRingerModeChange(msg.arg1);
break;
case MSG_PHONE_STATE_CHANGED:
handlePhoneStateChanged((String)msg.obj);
break;
}
}
};
mKeyguardBypassEnabled = context.getResources().getBoolean(
com.android.internal.R.bool.config_bypass_keyguard_if_slider_open);
mDeviceProvisioned = Settings.Secure.getInt(
mContext.getContentResolver(), Settings.Secure.DEVICE_PROVISIONED, 0) != 0;
// Since device can't be un-provisioned, we only need to register a content observer
// to update mDeviceProvisioned when we are...
if (!mDeviceProvisioned) {
mContentObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
mDeviceProvisioned = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.DEVICE_PROVISIONED, 0) != 0;
if (mDeviceProvisioned && mContentObserver != null) {
// We don't need the observer anymore...
mContext.getContentResolver().unregisterContentObserver(mContentObserver);
mContentObserver = null;
}
if (DEBUG) Log.d(TAG, "DEVICE_PROVISIONED state = " + mDeviceProvisioned);
}
};
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.DEVICE_PROVISIONED),
false, mContentObserver);
// prevent a race condition between where we check the flag and where we register the
// observer by grabbing the value once again...
mDeviceProvisioned = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.DEVICE_PROVISIONED, 0) != 0;
}
// take a guess to start
mBatteryStatus = BATTERY_STATUS_FULL;
mBatteryLevel = 100;
// TelephonyManager.getPhoneCount() returns '1' for single SIM mode
// and '2' for dual SIM mode.
int numPhones = TelephonyManager.getPhoneCount();
// Initialize PLMN, SPN strings and SIM states for the subscriptions.
mTelephonyPlmn = new CharSequence[numPhones];
mTelephonySpn = new CharSequence[numPhones];
mSimState = new IccCard.State[numPhones];
for (int i = 0; i < numPhones; i++) {
mTelephonyPlmn[i] = getDefaultPlmn();
mTelephonySpn[i] = null;
mSimState[i] = IccCard.State.READY;
}
// setup receiver
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_TIME_TICK);
filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
filter.addAction(SPN_STRINGS_UPDATED_ACTION);
filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
context.registerReceiver(new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (DEBUG) Log.d(TAG, "received broadcast " + action);
if (Intent.ACTION_TIME_TICK.equals(action)
|| Intent.ACTION_TIME_CHANGED.equals(action)
|| Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_TIME_UPDATE));
} else if (SPN_STRINGS_UPDATED_ACTION.equals(action)) {
// Get the subscription from the intent.
final int subscription = intent.getIntExtra(EXTRA_SUBSCRIPTION, 0);
Log.d(TAG, "Received SPN update on sub :" + subscription);
// Update PLMN and SPN for corresponding subscriptions.
mTelephonyPlmn[subscription] = getTelephonyPlmnFrom(intent);
mTelephonySpn[subscription] = getTelephonySpnFrom(intent);
final Message msg = mHandler.obtainMessage(
MSG_CARRIER_INFO_UPDATE);
msg.arg1 = subscription;
mHandler.sendMessage(msg);
} else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
final int pluggedInStatus = intent
.getIntExtra("status", BATTERY_STATUS_UNKNOWN);
int batteryLevel = intent.getIntExtra("level", 0);
final Message msg = mHandler.obtainMessage(
MSG_BATTERY_UPDATE,
pluggedInStatus,
batteryLevel);
mHandler.sendMessage(msg);
} else if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)) {
mHandler.sendMessage(mHandler.obtainMessage(
MSG_SIM_STATE_CHANGE,
new SimArgs(intent)));
} else if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_RINGER_MODE_CHANGED,
intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1), 0));
} else if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)) {
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
mHandler.sendMessage(mHandler.obtainMessage(MSG_PHONE_STATE_CHANGED, state));
}
}
}, filter);
}
protected void handlePhoneStateChanged(String newState) {
if (DEBUG) Log.d(TAG, "handlePhoneStateChanged(" + newState + ")");
for (int i = 0; i < mInfoCallbacks.size(); i++) {
mInfoCallbacks.get(i).onPhoneStateChanged(newState);
}
}
protected void handleRingerModeChange(int mode) {
if (DEBUG) Log.d(TAG, "handleRingerModeChange(" + mode + ")");
for (int i = 0; i < mInfoCallbacks.size(); i++) {
mInfoCallbacks.get(i).onRingerModeChanged(mode);
}
}
/**
* Handle {@link #MSG_TIME_UPDATE}
*/
private void handleTimeUpdate() {
if (DEBUG) Log.d(TAG, "handleTimeUpdate");
for (int i = 0; i < mInfoCallbacks.size(); i++) {
mInfoCallbacks.get(i).onTimeChanged();
}
}
/**
* Handle {@link #MSG_BATTERY_UPDATE}
*/
private void handleBatteryUpdate(int batteryStatus, int batteryLevel) {
if (DEBUG) Log.d(TAG, "handleBatteryUpdate");
if (isBatteryUpdateInteresting(batteryStatus, batteryLevel)) {
mBatteryStatus = batteryStatus;
mBatteryLevel = batteryLevel;
final boolean pluggedIn = isPluggedIn(batteryStatus);;
for (int i = 0; i < mInfoCallbacks.size(); i++) {
mInfoCallbacks.get(i).onRefreshBatteryInfo(
shouldShowBatteryInfo(), pluggedIn, batteryLevel);
}
}
}
/**
* Handle {@link #MSG_CARRIER_INFO_UPDATE}
*/
private void handleCarrierInfoUpdate(int subscription) {
if (DEBUG) Log.d(TAG, "handleCarrierInfoUpdate: plmn = " + mTelephonyPlmn[subscription]
+ ", spn = " + mTelephonySpn[subscription] + ", subscription = " + subscription);
for (int i = 0; i < mInfoCallbacks.size(); i++) {
mInfoCallbacks.get(i).onRefreshCarrierInfo(mTelephonyPlmn[subscription],
mTelephonySpn[subscription], subscription);
}
}
/**
* Handle {@link #MSG_SIM_STATE_CHANGE}
*/
private void handleSimStateChange(SimArgs simArgs) {
final IccCard.State state = simArgs.simState;
final int subscription = simArgs.subscription;
if (DEBUG) {
Log.d(TAG, "handleSimStateChange: intentValue = " + simArgs + " "
+ "state resolved to " + state.toString() + " "
+ "subscription =" + subscription);
}
if (state != IccCard.State.UNKNOWN && state != mSimState[subscription]) {
mSimState[subscription] = state;
for (int i = 0; i < mSimStateCallbacks.size(); i++) {
// Pass the subscription on which SIM_STATE_CHANGE indication was received.
mSimStateCallbacks.get(i).onSimStateChanged(state, subscription);
}
}
}
/**
* @param status One of the statuses of {@link android.os.BatteryManager}
* @return Whether the status maps to a status for being plugged in.
*/
private boolean isPluggedIn(int status) {
return status == BATTERY_STATUS_CHARGING || status == BATTERY_STATUS_FULL;
}
private boolean isBatteryUpdateInteresting(int batteryStatus, int batteryLevel) {
// change in plug is always interesting
final boolean isPluggedIn = isPluggedIn(batteryStatus);
final boolean wasPluggedIn = isPluggedIn(mBatteryStatus);
final boolean stateChangedWhilePluggedIn =
wasPluggedIn == true && isPluggedIn == true && (mBatteryStatus != batteryStatus);
if (wasPluggedIn != isPluggedIn || stateChangedWhilePluggedIn) {
return true;
}
// change in battery level while plugged in
if (isPluggedIn && mBatteryLevel != batteryLevel) {
return true;
}
if (!isPluggedIn) {
// not plugged in and below threshold
if (isBatteryLow(batteryLevel) && batteryLevel != mBatteryLevel) {
return true;
}
}
return false;
}
private boolean isBatteryLow(int batteryLevel) {
return batteryLevel < LOW_BATTERY_THRESHOLD;
}
/**
* @param intent The intent with action {@link Telephony.Intents#SPN_STRINGS_UPDATED_ACTION}
* @return The string to use for the plmn, or null if it should not be shown.
*/
private CharSequence getTelephonyPlmnFrom(Intent intent) {
if (intent.getBooleanExtra(EXTRA_SHOW_PLMN, false)) {
final String plmn = intent.getStringExtra(EXTRA_PLMN);
if (plmn != null) {
return plmn;
} else {
return getDefaultPlmn();
}
}
return null;
}
/**
* @return The default plmn (no service)
*/
private CharSequence getDefaultPlmn() {
return mContext.getResources().getText(
R.string.lockscreen_carrier_default);
}
/**
* @param intent The intent with action {@link Telephony.Intents#SPN_STRINGS_UPDATED_ACTION}
* @return The string to use for the plmn, or null if it should not be shown.
*/
private CharSequence getTelephonySpnFrom(Intent intent) {
if (intent.getBooleanExtra(EXTRA_SHOW_SPN, false)) {
final String spn = intent.getStringExtra(EXTRA_SPN);
if (spn != null) {
return spn;
}
}
return null;
}
/**
* Remove the given observer from being registered from any of the kinds
* of callbacks.
* @param observer The observer to remove (an instance of {@link ConfigurationChangeCallback},
* {@link InfoCallback} or {@link SimStateCallback}
*/
public void removeCallback(Object observer) {
mInfoCallbacks.remove(observer);
mSimStateCallbacks.remove(observer);
}
/**
* Callback for general information relevant to lock screen.
*/
interface InfoCallback {
void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel);
void onTimeChanged();
/**
* @param plmn The operator name of the registered network. May be null if it shouldn't
* be displayed.
* @param spn The service provider name. May be null if it shouldn't be displayed.
*/
void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn, int subscription);
/**
* Called when the ringer mode changes.
* @param state the current ringer state, as defined in
* {@link AudioManager#RINGER_MODE_CHANGED_ACTION}
*/
void onRingerModeChanged(int state);
/**
* Called when the phone state changes. String will be one of:
* {@link TelephonyManager#EXTRA_STATE_IDLE}
* {@link TelephonyManager@EXTRA_STATE_RINGING}
* {@link TelephonyManager#EXTRA_STATE_OFFHOOK
*/
void onPhoneStateChanged(String newState);
}
/**
* Callback to notify of sim state change.
*/
interface SimStateCallback {
void onSimStateChanged(IccCard.State simState, int subscription);
}
/**
* Register to receive notifications about general keyguard information
* (see {@link InfoCallback}.
* @param callback The callback.
*/
public void registerInfoCallback(InfoCallback callback) {
if (!mInfoCallbacks.contains(callback)) {
mInfoCallbacks.add(callback);
} else {
Log.e(TAG, "Object tried to add another INFO callback", new Exception("Whoops"));
}
}
/**
* Register to be notified of sim state changes.
* @param callback The callback.
*/
public void registerSimStateCallback(SimStateCallback callback) {
if (!mSimStateCallbacks.contains(callback)) {
mSimStateCallbacks.add(callback);
} else {
Log.e(TAG, "Object tried to add another SIM callback", new Exception("Whoops"));
}
}
public IccCard.State getSimState() {
return getSimState(TelephonyManager.getDefaultSubscription());
}
/**
* Get the simstate for the subscription.
* @param subscription the subscription for which sim state is requested.
*/
public IccCard.State getSimState(int subscription) {
return mSimState[subscription];
}
/**
* Report that the user succesfully entered the sim pin for the
* subscription so we have the information earlier than waiting
* for the intent broadcast from the telephony code.
* @param subscription the subscription for which sim was unlocked.
*/
public void reportSimPinUnlocked(int subscription) {
mSimState[subscription] = IccCard.State.READY;
}
public boolean isKeyguardBypassEnabled() {
return mKeyguardBypassEnabled;
}
public boolean isDevicePluggedIn() {
return isPluggedIn(mBatteryStatus);
}
public boolean isDeviceCharged() {
return mBatteryStatus == BatteryManager.BATTERY_STATUS_FULL
|| mBatteryLevel >= 100; // in case a particular device doesn't flag it
}
public int getBatteryLevel() {
return mBatteryLevel;
}
public boolean shouldShowBatteryInfo() {
return isPluggedIn(mBatteryStatus) || isBatteryLow(mBatteryLevel);
}
public CharSequence getTelephonyPlmn() {
return getTelephonyPlmn(TelephonyManager.getDefaultSubscription());
}
/**
* Get the PLMN for the subscription.
* @param subscription the subscription for which PLMN is requested.
*/
public CharSequence getTelephonyPlmn(int subscription) {
return mTelephonyPlmn[subscription];
}
public CharSequence getTelephonySpn() {
return getTelephonySpn(TelephonyManager.getDefaultSubscription());
}
/**
* Get the SPN for the subscription.
* @param subscription the subscription for which SPN is requested.
*/
public CharSequence getTelephonySpn(int subscription) {
return mTelephonySpn[subscription];
}
/**
* @return Whether the device is provisioned (whether they have gone through
* the setup wizard)
*/
public boolean isDeviceProvisioned() {
return mDeviceProvisioned;
}
public int getFailedAttempts() {
return mFailedAttempts;
}
public void clearFailedAttempts() {
mFailedAttempts = 0;
}
public void reportFailedAttempt() {
mFailedAttempts++;
}
}
@@ -0,0 +1,217 @@
/*
* Copyright (C) 2007 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 android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.telephony.TelephonyManager;
import android.view.KeyEvent;
import android.view.View;
import android.view.Gravity;
import android.widget.FrameLayout;
import android.util.AttributeSet;
/**
* Base class for keyguard views. {@link #reset} is where you should
* reset the state of your view. Use the {@link KeyguardViewCallback} via
* {@link #getCallback()} to send information back (such as poking the wake lock,
* or finishing the keyguard).
*
* Handles intercepting of media keys that still work when the keyguard is
* showing.
*/
public abstract class KeyguardViewBase extends FrameLayout {
private KeyguardViewCallback mCallback;
private AudioManager mAudioManager;
private TelephonyManager mTelephonyManager = null;
public KeyguardViewBase(Context context) {
super(context);
// drop shadow below status bar in keyguard too
mForegroundInPadding = false;
setForegroundGravity(Gravity.FILL_HORIZONTAL | Gravity.TOP);
setForeground(
context.getResources().getDrawable(
com.android.internal.R.drawable.title_bar_shadow));
}
// used to inject callback
void setCallback(KeyguardViewCallback callback) {
mCallback = callback;
}
public KeyguardViewCallback getCallback() {
return mCallback;
}
/**
* Called when you need to reset the state of your view.
*/
abstract public void reset();
/**
* Called when the screen turned off.
*/
abstract public void onScreenTurnedOff();
/**
* Called when the screen turned on.
*/
abstract public void onScreenTurnedOn();
/**
* Called when a key has woken the device to give us a chance to adjust our
* state according the the key. We are responsible for waking the device
* (by poking the wake lock) once we are ready.
*
* The 'Tq' suffix is per the documentation in {@link android.view.WindowManagerPolicy}.
* Be sure not to take any action that takes a long time; any significant
* action should be posted to a handler.
*
* @param keyCode The wake key, which may be relevant for configuring the
* keyguard.
*/
abstract public void wakeWhenReadyTq(int keyCode);
/**
* Verify that the user can get past the keyguard securely. This is called,
* for example, when the phone disables the keyguard but then wants to launch
* something else that requires secure access.
*
* The result will be propogated back via {@link KeyguardViewCallback#keyguardDone(boolean)}
*/
abstract public void verifyUnlock();
/**
* Called before this view is being removed.
*/
abstract public void cleanUp();
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (shouldEventKeepScreenOnWhileKeyguardShowing(event)) {
mCallback.pokeWakelock();
}
if (interceptMediaKey(event)) {
return true;
}
return super.dispatchKeyEvent(event);
}
private boolean shouldEventKeepScreenOnWhileKeyguardShowing(KeyEvent event) {
if (event.getAction() != KeyEvent.ACTION_DOWN) {
return false;
}
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_DPAD_DOWN:
case KeyEvent.KEYCODE_DPAD_LEFT:
case KeyEvent.KEYCODE_DPAD_RIGHT:
case KeyEvent.KEYCODE_DPAD_UP:
return false;
default:
return true;
}
}
/**
* Allows the media keys to work when the keyguard is showing.
* The media keys should be of no interest to the actual keyguard view(s),
* so intercepting them here should not be of any harm.
* @param event The key event
* @return whether the event was consumed as a media key.
*/
private boolean interceptMediaKey(KeyEvent event) {
final int keyCode = event.getKeyCode();
if (event.getAction() == KeyEvent.ACTION_DOWN) {
switch (keyCode) {
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
/* Suppress PLAYPAUSE toggle when phone is ringing or
* in-call to avoid music playback */
if (mTelephonyManager == null) {
mTelephonyManager = (TelephonyManager) getContext().getSystemService(
Context.TELEPHONY_SERVICE);
}
if (mTelephonyManager != null &&
mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) {
return true; // suppress key event
}
case KeyEvent.KEYCODE_HEADSETHOOK:
case KeyEvent.KEYCODE_MEDIA_STOP:
case KeyEvent.KEYCODE_MEDIA_NEXT:
case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
case KeyEvent.KEYCODE_MEDIA_REWIND:
case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
intent.putExtra(Intent.EXTRA_KEY_EVENT, event);
getContext().sendOrderedBroadcast(intent, null);
return true;
}
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_DOWN: {
synchronized (this) {
if (mAudioManager == null) {
mAudioManager = (AudioManager) getContext().getSystemService(
Context.AUDIO_SERVICE);
}
}
// Volume buttons should only function for music.
if (mAudioManager.isMusicActive()) {
mAudioManager.adjustStreamVolume(
AudioManager.STREAM_MUSIC,
keyCode == KeyEvent.KEYCODE_VOLUME_UP
? AudioManager.ADJUST_RAISE
: AudioManager.ADJUST_LOWER,
0);
}
else if (mAudioManager.isFMActive()) {
mAudioManager.adjustStreamVolume(
AudioManager.STREAM_FM,
keyCode == KeyEvent.KEYCODE_VOLUME_UP
? AudioManager.ADJUST_RAISE
: AudioManager.ADJUST_LOWER,
0);
}
// Don't execute default volume behavior
return true;
}
}
} else if (event.getAction() == KeyEvent.ACTION_UP) {
switch (keyCode) {
case KeyEvent.KEYCODE_MUTE:
case KeyEvent.KEYCODE_HEADSETHOOK:
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
case KeyEvent.KEYCODE_MEDIA_STOP:
case KeyEvent.KEYCODE_MEDIA_NEXT:
case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
case KeyEvent.KEYCODE_MEDIA_REWIND:
case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
intent.putExtra(Intent.EXTRA_KEY_EVENT, event);
getContext().sendOrderedBroadcast(intent, null);
return true;
}
}
}
return false;
}
}
@@ -0,0 +1,49 @@
/*
* Copyright (C) 2007 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;
/**
* The callback used by the keyguard view to tell the {@link KeyguardViewMediator}
* various things.
*/
public interface KeyguardViewCallback {
/**
* Request the wakelock to be poked for the default amount of time.
*/
void pokeWakelock();
/**
* Request the wakelock to be poked for a specific amount of time.
* @param millis The amount of time in millis.
*/
void pokeWakelock(int millis);
/**
* Report that the keyguard is done.
* @param authenticated Whether the user securely got past the keyguard.
* the only reason for this to be false is if the keyguard was instructed
* to appear temporarily to verify the user is supposed to get past the
* keyguard, and the user fails to do so.
*/
void keyguardDone(boolean authenticated);
/**
* Report that the keyguard is done drawing.
*/
void keyguardDoneDrawing();
}
@@ -0,0 +1,241 @@
/*
* Copyright (C) 2007 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 android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.PixelFormat;
import android.graphics.Canvas;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewManager;
import android.view.WindowManager;
import android.widget.FrameLayout;
/**
* Manages creating, showing, hiding and resetting the keyguard. Calls back
* via {@link com.android.internal.policy.impl.KeyguardViewCallback} to poke
* the wake lock and report that the keyguard is done, which is in turn,
* reported to this class by the current {@link KeyguardViewBase}.
*/
public class KeyguardViewManager implements KeyguardWindowController {
private final static boolean DEBUG = false;
private static String TAG = "KeyguardViewManager";
private final Context mContext;
private final ViewManager mViewManager;
private final KeyguardViewCallback mCallback;
private final KeyguardViewProperties mKeyguardViewProperties;
private final KeyguardUpdateMonitor mUpdateMonitor;
private WindowManager.LayoutParams mWindowLayoutParams;
private boolean mNeedsInput = false;
private FrameLayout mKeyguardHost;
private KeyguardViewBase mKeyguardView;
private boolean mScreenOn = false;
/**
* @param context Used to create views.
* @param viewManager Keyguard will be attached to this.
* @param callback Used to notify of changes.
*/
public KeyguardViewManager(Context context, ViewManager viewManager,
KeyguardViewCallback callback, KeyguardViewProperties keyguardViewProperties, KeyguardUpdateMonitor updateMonitor) {
mContext = context;
mViewManager = viewManager;
mCallback = callback;
mKeyguardViewProperties = keyguardViewProperties;
mUpdateMonitor = updateMonitor;
}
/**
* Helper class to host the keyguard view.
*/
private static class KeyguardViewHost extends FrameLayout {
private final KeyguardViewCallback mCallback;
private KeyguardViewHost(Context context, KeyguardViewCallback callback) {
super(context);
mCallback = callback;
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
mCallback.keyguardDoneDrawing();
}
}
/**
* Show the keyguard. Will handle creating and attaching to the view manager
* lazily.
*/
public synchronized void show() {
if (DEBUG) Log.d(TAG, "show(); mKeyguardView==" + mKeyguardView);
if (mKeyguardHost == null) {
if (DEBUG) Log.d(TAG, "keyguard host is null, creating it...");
mKeyguardHost = new KeyguardViewHost(mContext, mCallback);
final int stretch = ViewGroup.LayoutParams.MATCH_PARENT;
int flags = WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN
| WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER
| WindowManager.LayoutParams.FLAG_KEEP_SURFACE_WHILE_ANIMATING
/*| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR*/ ;
if (!mNeedsInput) {
flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
}
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
stretch, stretch, WindowManager.LayoutParams.TYPE_KEYGUARD,
flags, PixelFormat.TRANSLUCENT);
lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
lp.windowAnimations = com.android.internal.R.style.Animation_LockScreen;
lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
lp.setTitle("Keyguard");
mWindowLayoutParams = lp;
mViewManager.addView(mKeyguardHost, lp);
}
if (mKeyguardView == null) {
if (DEBUG) Log.d(TAG, "keyguard view is null, creating it...");
mKeyguardView = mKeyguardViewProperties.createKeyguardView(mContext, mUpdateMonitor, this);
mKeyguardView.setId(R.id.lock_screen);
mKeyguardView.setCallback(mCallback);
final ViewGroup.LayoutParams lp = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
mKeyguardHost.addView(mKeyguardView, lp);
if (mScreenOn) {
mKeyguardView.onScreenTurnedOn();
}
}
mKeyguardHost.setVisibility(View.VISIBLE);
mKeyguardView.requestFocus();
}
public void setNeedsInput(boolean needsInput) {
mNeedsInput = needsInput;
if (mWindowLayoutParams != null) {
if (needsInput) {
mWindowLayoutParams.flags &=
~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
} else {
mWindowLayoutParams.flags |=
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
}
mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
}
}
/**
* Reset the state of the view.
*/
public synchronized void reset() {
if (DEBUG) Log.d(TAG, "reset()");
if (mKeyguardView != null) {
mKeyguardView.reset();
}
}
public synchronized void onScreenTurnedOff() {
if (DEBUG) Log.d(TAG, "onScreenTurnedOff()");
mScreenOn = false;
if (mKeyguardView != null) {
mKeyguardView.onScreenTurnedOff();
}
}
public synchronized void onScreenTurnedOn() {
if (DEBUG) Log.d(TAG, "onScreenTurnedOn()");
mScreenOn = true;
if (mKeyguardView != null) {
mKeyguardView.onScreenTurnedOn();
}
}
public synchronized void verifyUnlock() {
if (DEBUG) Log.d(TAG, "verifyUnlock()");
show();
mKeyguardView.verifyUnlock();
}
/**
* A key has woken the device. We use this to potentially adjust the state
* of the lock screen based on the key.
*
* The 'Tq' suffix is per the documentation in {@link android.view.WindowManagerPolicy}.
* Be sure not to take any action that takes a long time; any significant
* action should be posted to a handler.
*
* @param keyCode The wake key.
*/
public boolean wakeWhenReadyTq(int keyCode) {
if (DEBUG) Log.d(TAG, "wakeWhenReady(" + keyCode + ")");
if (mKeyguardView != null) {
mKeyguardView.wakeWhenReadyTq(keyCode);
return true;
} else {
Log.w(TAG, "mKeyguardView is null in wakeWhenReadyTq");
return false;
}
}
/**
* Hides the keyguard view
*/
public synchronized void hide() {
if (DEBUG) Log.d(TAG, "hide()");
if (mKeyguardHost != null) {
mKeyguardHost.setVisibility(View.GONE);
// Don't do this right away, so we can let the view continue to animate
// as it goes away.
if (mKeyguardView != null) {
final KeyguardViewBase lastView = mKeyguardView;
mKeyguardView = null;
mKeyguardHost.postDelayed(new Runnable() {
public void run() {
synchronized (KeyguardViewManager.this) {
lastView.cleanUp();
mKeyguardHost.removeView(lastView);
}
}
}, 500);
}
}
}
/**
* @return Whether the keyguard is showing
*/
public synchronized boolean isShowing() {
return (mKeyguardHost != null && mKeyguardHost.getVisibility() == View.VISIBLE);
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,46 @@
/*
* 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 android.content.Context;
/**
* Defines operations necessary for showing a keyguard, including how to create
* it, and various properties that are useful to be able to query independant
* of whether the keyguard instance is around or not.
*/
public interface KeyguardViewProperties {
/**
* Create a keyguard view.
* @param context the context to use when creating the view.
* @param updateMonitor configuration may be based on this.
* @param controller for talking back with the containing window.
* @return the view.
*/
KeyguardViewBase createKeyguardView(Context context,
KeyguardUpdateMonitor updateMonitor,
KeyguardWindowController controller);
/**
* Would the keyguard be secure right now?
* @return Whether the keyguard is currently secure, meaning it will block
* the user from getting past it until the user enters some sort of PIN.
*/
boolean isSecure();
}
@@ -0,0 +1,29 @@
/*
* Copyright (C) 2009 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;
/**
* Interface passed to the keyguard view, for it to call up to control
* its containing window.
*/
public interface KeyguardWindowController {
/**
* Control whether the window needs input -- that is if it has
* text fields and thus should allow input method interaction.
*/
void setNeedsInput(boolean needsInput);
}
@@ -0,0 +1,896 @@
/*
* Copyright (C) 2007 The Android Open Source Project
* Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
*
* 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.widget.LockPatternUtils;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.app.AlertDialog;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
import java.io.IOException;
/**
* The host view for all of the screens of the pattern unlock screen. There are
* two {@link Mode}s of operation, lock and unlock. This will show the appropriate
* screen, and listen for callbacks via
* {@link com.android.internal.policy.impl.KeyguardScreenCallback}
* from the current screen.
*
* This view, in turn, communicates back to
* {@link com.android.internal.policy.impl.KeyguardViewManager}
* via its {@link com.android.internal.policy.impl.KeyguardViewCallback}, as appropriate.
*/
public class LockPatternKeyguardView extends KeyguardViewBase {
static final boolean DEBUG_CONFIGURATION = false;
// time after launching EmergencyDialer before the screen goes blank.
private static final int EMERGENCY_CALL_TIMEOUT = 10000;
private static final int SUB1 = 0;
private static final int SUB2 = 1;
// intent action for launching emergency dialer activity.
static final String ACTION_EMERGENCY_DIAL = "com.android.phone.EmergencyDialer.DIAL";
private static final boolean DEBUG = false;
private static final String TAG = "LockPatternKeyguardView";
private final KeyguardUpdateMonitor mUpdateMonitor;
private final KeyguardWindowController mWindowController;
private View mLockScreen;
private View mUnlockScreen;
private boolean mScreenOn = false;
private boolean mEnableFallback = false; // assume no fallback UI until we know better
/**
* The current {@link KeyguardScreen} will use this to communicate back to us.
*/
KeyguardScreenCallback mKeyguardScreenCallback;
private boolean mRequiresSim;
/**
* Either a lock screen (an informational keyguard screen), or an unlock
* screen (a means for unlocking the device) is shown at any given time.
*/
enum Mode {
LockScreen,
UnlockScreen
}
/**
* The different types screens available for {@link Mode#UnlockScreen}.
* @see com.android.internal.policy.impl.LockPatternKeyguardView#getUnlockMode()
*/
enum UnlockMode {
/**
* Unlock by drawing a pattern.
*/
Pattern,
/**
* Unlock by entering a sim pin.
*/
SimPin,
/**
* Unlock by entering an account's login and password.
*/
Account,
/**
* Unlock by entering a password or PIN
*/
Password,
/**
* Unknown (uninitialized) value
*/
Unknown
}
/**
* The current mode.
*/
private Mode mMode = Mode.LockScreen;
/**
* Keeps track of what mode the current unlock screen is (cached from most recent computation in
* {@link #getUnlockMode}).
*/
private UnlockMode mUnlockScreenMode;
private boolean mForgotPattern;
/**
* If true, it means we are in the process of verifying that the user
* can get past the lock screen per {@link #verifyUnlock()}
*/
private boolean mIsVerifyUnlockOnly = false;
/**
* Used to lookup the state of the lock pattern
*/
private final LockPatternUtils mLockPatternUtils;
private UnlockMode mCurrentUnlockMode = UnlockMode.Unknown;
/**
* The current configuration.
*/
private Configuration mConfiguration;
private boolean[] isPinUnlockCancelled = {false, false};
/**
* @return Whether we are stuck on the lock screen because the sim is
* missing.
*/
private boolean stuckOnLockScreenBecauseSimMissing() {
boolean result = mRequiresSim && !mUpdateMonitor.isDeviceProvisioned();
for (int i = 0; i < TelephonyManager.getPhoneCount(); i++) {
//In case of dual subscription, stuck on lock screen
//only when SIM is absent on both subscriptions.
result = result && (getSimState(i) == IccCard.State.ABSENT);
if (!result) break;
}
return result;
}
/**
* @param context Used to inflate, and create views.
* @param updateMonitor Knows the state of the world, and passed along to each
* screen so they can use the knowledge, and also register for callbacks
* on dynamic information.
* @param lockPatternUtils Used to look up state of lock pattern.
*/
public LockPatternKeyguardView(
Context context,
KeyguardUpdateMonitor updateMonitor,
LockPatternUtils lockPatternUtils,
KeyguardWindowController controller) {
super(context);
mConfiguration = context.getResources().getConfiguration();
mEnableFallback = false;
mRequiresSim =
TextUtils.isEmpty(SystemProperties.get("keyguard.no_require_sim"));
mUpdateMonitor = updateMonitor;
mLockPatternUtils = lockPatternUtils;
mWindowController = controller;
mMode = getInitialMode();
mKeyguardScreenCallback = new KeyguardScreenCallback() {
public void goToLockScreen() {
mForgotPattern = false;
if (mIsVerifyUnlockOnly) {
// navigating away from unlock screen during verify mode means
// we are done and the user failed to authenticate.
mIsVerifyUnlockOnly = false;
getCallback().keyguardDone(false);
} else {
updateScreen(Mode.LockScreen);
}
}
public void goToUnlockScreen() {
boolean isPukRequired = true;
for (int i = 0; i < TelephonyManager.getPhoneCount(); i++) {
isPukRequired = isPukRequired && isSimPukLocked(i);
if (!isPukRequired) break;
}
if (stuckOnLockScreenBecauseSimMissing()
|| isPukRequired) {
// stuck on lock screen when sim missing or puk'd
return;
}
if (!isSecure()) {
getCallback().keyguardDone(true);
} else {
updateScreen(Mode.UnlockScreen);
}
}
/**
* SimUnlockScreen invokes this method when user dismisses the
* PIN dialog for any of the subscription.PIN Dialog will not be
* prompted again for that subscription if the other subscription
* is in ready state.
* In case, the other subscription is PUK-Locked, user is not
* allowed to dismiss the PIN dialog.
*/
public void updatePinUnlockCancel(int subscription) {
Log.d(TAG, "updatePinUnlockCancel sub :" + subscription);
boolean canCancelUnlock = false;
if ((subscription == SUB1) &&
(!isPinUnlockCancelled[SUB2]) && (!isSimPukLocked(SUB2))) {
canCancelUnlock = true;
} else if ((subscription == SUB2) && (!isSimPukLocked(SUB1))) {
canCancelUnlock = true;
// Cannot cancel both subs, so enable PIN dialog for SUB1.
isPinUnlockCancelled[SUB1] = false;
} else {
Log.d(TAG, "Cannot cancel PIN Dialog");
}
if (canCancelUnlock) {
isPinUnlockCancelled[subscription] = true;
}
}
public void forgotPattern(boolean isForgotten) {
if (mEnableFallback) {
mForgotPattern = isForgotten;
updateScreen(Mode.UnlockScreen);
}
}
public boolean isSecure() {
return LockPatternKeyguardView.this.isSecure();
}
public boolean isVerifyUnlockOnly() {
return mIsVerifyUnlockOnly;
}
public void recreateMe(Configuration config) {
mConfiguration = config;
recreateScreens();
}
public void takeEmergencyCallAction() {
pokeWakelock(EMERGENCY_CALL_TIMEOUT);
if (TelephonyManager.getDefault().getCallState()
== TelephonyManager.CALL_STATE_OFFHOOK) {
mLockPatternUtils.resumeCall();
} else {
Intent intent = new Intent(ACTION_EMERGENCY_DIAL);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
getContext().startActivity(intent);
}
}
public void pokeWakelock() {
getCallback().pokeWakelock();
}
public void pokeWakelock(int millis) {
getCallback().pokeWakelock(millis);
}
public void keyguardDone(boolean authenticated) {
getCallback().keyguardDone(authenticated);
}
public void keyguardDoneDrawing() {
// irrelevant to keyguard screen, they shouldn't be calling this
}
public void reportFailedUnlockAttempt() {
mUpdateMonitor.reportFailedAttempt();
final int failedAttempts = mUpdateMonitor.getFailedAttempts();
if (DEBUG) Log.d(TAG,
"reportFailedPatternAttempt: #" + failedAttempts +
" (enableFallback=" + mEnableFallback + ")");
final boolean usingLockPattern = mLockPatternUtils.getKeyguardStoredPasswordQuality()
== DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
if (usingLockPattern && mEnableFallback && failedAttempts ==
(LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
- LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
showAlmostAtAccountLoginDialog();
} else if (usingLockPattern && mEnableFallback
&& failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) {
mLockPatternUtils.setPermanentlyLocked(true);
updateScreen(mMode);
} else if ((failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)
== 0) {
showTimeoutDialog();
}
mLockPatternUtils.reportFailedPasswordAttempt();
}
public boolean doesFallbackUnlockScreenExist() {
return mEnableFallback;
}
public void reportSuccessfulUnlockAttempt() {
mLockPatternUtils.reportSuccessfulPasswordAttempt();
}
};
/**
* We'll get key events the current screen doesn't use. see
* {@link KeyguardViewBase#onKeyDown(int, android.view.KeyEvent)}
*/
setFocusableInTouchMode(true);
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
// create both the lock and unlock screen so they are quickly available
// when the screen turns on
mLockScreen = createLockScreen();
addView(mLockScreen);
final UnlockMode unlockMode = getUnlockMode();
if (DEBUG) Log.d(TAG,
"LockPatternKeyguardView ctor: about to createUnlockScreenFor; mEnableFallback="
+ mEnableFallback);
mUnlockScreen = createUnlockScreenFor(unlockMode);
mUnlockScreenMode = unlockMode;
maybeEnableFallback(context);
addView(mUnlockScreen);
updateScreen(mMode);
}
private class AccountAnalyzer implements AccountManagerCallback<Bundle> {
private final AccountManager mAccountManager;
private final Account[] mAccounts;
private int mAccountIndex;
private AccountAnalyzer(AccountManager accountManager) {
mAccountManager = accountManager;
mAccounts = accountManager.getAccountsByType("com.google");
}
private void next() {
// if we are ready to enable the fallback or if we depleted the list of accounts
// then finish and get out
if (mEnableFallback || mAccountIndex >= mAccounts.length) {
if (mUnlockScreen == null) {
Log.w(TAG, "no unlock screen when trying to enable fallback");
} else if (mUnlockScreen instanceof PatternUnlockScreen) {
((PatternUnlockScreen)mUnlockScreen).setEnableFallback(mEnableFallback);
}
return;
}
// lookup the confirmCredentials intent for the current account
mAccountManager.confirmCredentials(mAccounts[mAccountIndex], null, null, this, null);
}
public void start() {
mEnableFallback = false;
mAccountIndex = 0;
next();
}
public void run(AccountManagerFuture<Bundle> future) {
try {
Bundle result = future.getResult();
if (result.getParcelable(AccountManager.KEY_INTENT) != null) {
mEnableFallback = true;
}
} catch (OperationCanceledException e) {
// just skip the account if we are unable to query it
} catch (IOException e) {
// just skip the account if we are unable to query it
} catch (AuthenticatorException e) {
// just skip the account if we are unable to query it
} finally {
mAccountIndex++;
next();
}
}
}
private void maybeEnableFallback(Context context) {
// Ask the account manager if we have an account that can be used as a
// fallback in case the user forgets his pattern.
AccountAnalyzer accountAnalyzer = new AccountAnalyzer(AccountManager.get(context));
accountAnalyzer.start();
}
// TODO:
// This overloaded method was added to workaround a race condition in the framework between
// notification for orientation changed, layout() and switching resources. This code attempts
// to avoid drawing the incorrect layout while things are in transition. The method can just
// be removed once the race condition is fixed. See bugs 2262578 and 2292713.
@Override
protected void dispatchDraw(Canvas canvas) {
if (DEBUG) Log.v(TAG, "*** dispatchDraw() time: " + SystemClock.elapsedRealtime());
super.dispatchDraw(canvas);
}
@Override
public void reset() {
mIsVerifyUnlockOnly = false;
mForgotPattern = false;
updateScreen(getInitialMode());
}
@Override
public void onScreenTurnedOff() {
mScreenOn = false;
mForgotPattern = false;
if (mMode == Mode.LockScreen) {
((KeyguardScreen) mLockScreen).onPause();
} else {
((KeyguardScreen) mUnlockScreen).onPause();
}
}
@Override
public void onScreenTurnedOn() {
mScreenOn = true;
if (mMode == Mode.LockScreen) {
((KeyguardScreen) mLockScreen).onResume();
} else {
((KeyguardScreen) mUnlockScreen).onResume();
}
}
private void recreateLockScreen() {
if (mLockScreen.getVisibility() == View.VISIBLE) {
((KeyguardScreen) mLockScreen).onPause();
}
((KeyguardScreen) mLockScreen).cleanUp();
removeView(mLockScreen);
mLockScreen = createLockScreen();
mLockScreen.setVisibility(View.INVISIBLE);
addView(mLockScreen);
}
private void recreateUnlockScreen() {
if (mUnlockScreen.getVisibility() == View.VISIBLE) {
((KeyguardScreen) mUnlockScreen).onPause();
}
((KeyguardScreen) mUnlockScreen).cleanUp();
removeView(mUnlockScreen);
final UnlockMode unlockMode = getUnlockMode();
mUnlockScreen = createUnlockScreenFor(unlockMode);
mUnlockScreen.setVisibility(View.INVISIBLE);
mUnlockScreenMode = unlockMode;
addView(mUnlockScreen);
}
private void recreateScreens() {
recreateLockScreen();
recreateUnlockScreen();
updateScreen(mMode);
}
/**
* Get the subscription that is PIN-Locked.
* Return '0' if SUB1 is PIN-Locked and,
* PIN dialog for SUB1 was not dismissed by user.
* Return '1' if SUB2 is PIN-Locked and,
* PIN dialog for SUB2 was not dismissed by user.
*/
private int getPinLockedSubscription() {
int subscription = SUB1;
for (int i = 0; i < TelephonyManager.getPhoneCount(); i++) {
if (isSimPinLocked(i) && !isPinUnlockCancelled[i]) {
subscription = i;
break;
}
}
return subscription;
}
private IccCard.State getSimState(int subscription) {
return mUpdateMonitor.getSimState(subscription);
}
private boolean isSimPinLocked(int subscription) {
return (getSimState(subscription) == IccCard.State.PIN_REQUIRED);
}
private boolean isSimPukLocked(int subscription) {
return (getSimState(subscription) == IccCard.State.PUK_REQUIRED);
}
@Override
public void wakeWhenReadyTq(int keyCode) {
if (DEBUG) Log.d(TAG, "onWakeKey");
boolean isPukRequired = true;
for (int i = 0; i < TelephonyManager.getPhoneCount(); i++) {
isPukRequired = isPukRequired && isSimPukLocked(i);
if (!isPukRequired) break;
}
if (keyCode == KeyEvent.KEYCODE_MENU && isSecure() && (mMode == Mode.LockScreen)
&& !isPukRequired) {
if (DEBUG) Log.d(TAG, "switching screens to unlock screen because wake key was MENU");
updateScreen(Mode.UnlockScreen);
getCallback().pokeWakelock();
} else {
if (DEBUG) Log.d(TAG, "poking wake lock immediately");
getCallback().pokeWakelock();
}
}
@Override
public void verifyUnlock() {
if (!isSecure()) {
// non-secure keyguard screens are successfull by default
getCallback().keyguardDone(true);
} else if (mUnlockScreenMode != UnlockMode.Pattern) {
// can only verify unlock when in pattern mode
getCallback().keyguardDone(false);
} else {
// otherwise, go to the unlock screen, see if they can verify it
mIsVerifyUnlockOnly = true;
updateScreen(Mode.UnlockScreen);
}
}
@Override
public void cleanUp() {
((KeyguardScreen) mLockScreen).onPause();
((KeyguardScreen) mLockScreen).cleanUp();
this.removeView(mLockScreen);
((KeyguardScreen) mUnlockScreen).onPause();
((KeyguardScreen) mUnlockScreen).cleanUp();
this.removeView(mUnlockScreen);
}
private boolean isSecure() {
UnlockMode unlockMode = getUnlockMode();
boolean secure = false;
switch (unlockMode) {
case Pattern:
secure = mLockPatternUtils.isLockPatternEnabled();
break;
case SimPin:
for (int i = 0; i < TelephonyManager.getPhoneCount(); i++) {
// Check if subscription is PIN/PUK locked.
// isPinLocked returns true if the state is PIN_REQUIRED/PUK_REQUIRED.
secure = secure || getSimState(i).isPinLocked();
if (secure) break;
}
break;
case Account:
secure = true;
break;
case Password:
secure = mLockPatternUtils.isLockPasswordEnabled();
break;
default:
throw new IllegalStateException("unknown unlock mode " + unlockMode);
}
return secure;
}
private void updateScreen(final Mode mode) {
if (DEBUG_CONFIGURATION) Log.v(TAG, "**** UPDATE SCREEN: mode=" + mode
+ " last mode=" + mMode, new RuntimeException());
mMode = mode;
// Re-create the unlock screen if necessary. This is primarily required to properly handle
// SIM state changes. This typically happens when this method is called by reset()
if ((mode == Mode.UnlockScreen && mCurrentUnlockMode != getUnlockMode()) ||
(getUnlockMode() == UnlockMode.SimPin)) {
recreateUnlockScreen();
}
final View goneScreen = (mode == Mode.LockScreen) ? mUnlockScreen : mLockScreen;
final View visibleScreen = (mode == Mode.LockScreen) ? mLockScreen : mUnlockScreen;
// do this before changing visibility so focus isn't requested before the input
// flag is set
mWindowController.setNeedsInput(((KeyguardScreen)visibleScreen).needsInput());
if (DEBUG_CONFIGURATION) {
Log.v(TAG, "Gone=" + goneScreen);
Log.v(TAG, "Visible=" + visibleScreen);
}
if (mScreenOn) {
if (goneScreen.getVisibility() == View.VISIBLE) {
((KeyguardScreen) goneScreen).onPause();
}
if (visibleScreen.getVisibility() != View.VISIBLE) {
((KeyguardScreen) visibleScreen).onResume();
}
}
goneScreen.setVisibility(View.GONE);
visibleScreen.setVisibility(View.VISIBLE);
requestLayout();
if (!visibleScreen.requestFocus()) {
throw new IllegalStateException("keyguard screen must be able to take "
+ "focus when shown " + visibleScreen.getClass().getCanonicalName());
}
}
View createLockScreen() {
return new LockScreen(
mContext,
mConfiguration,
mLockPatternUtils,
mUpdateMonitor,
mKeyguardScreenCallback);
}
View createUnlockScreenFor(UnlockMode unlockMode) {
View unlockView = null;
if (unlockMode == UnlockMode.Pattern) {
PatternUnlockScreen view = new PatternUnlockScreen(
mContext,
mConfiguration,
mLockPatternUtils,
mUpdateMonitor,
mKeyguardScreenCallback,
mUpdateMonitor.getFailedAttempts());
if (DEBUG) Log.d(TAG,
"createUnlockScreenFor(" + unlockMode + "): mEnableFallback=" + mEnableFallback);
view.setEnableFallback(mEnableFallback);
unlockView = view;
} else if (unlockMode == UnlockMode.SimPin) {
int subscription = getPinLockedSubscription();
Log.d(TAG, "Display SimUnlockScreen for sub :" + subscription);
unlockView = new SimUnlockScreen(
mContext,
mConfiguration,
mUpdateMonitor,
mKeyguardScreenCallback,
mLockPatternUtils, subscription);
} else if (unlockMode == UnlockMode.Account) {
try {
unlockView = new AccountUnlockScreen(
mContext,
mConfiguration,
mUpdateMonitor,
mKeyguardScreenCallback,
mLockPatternUtils);
} catch (IllegalStateException e) {
Log.i(TAG, "Couldn't instantiate AccountUnlockScreen"
+ " (IAccountsService isn't available)");
// TODO: Need a more general way to provide a
// platform-specific fallback UI here.
// For now, if we can't display the account login
// unlock UI, just bring back the regular "Pattern" unlock mode.
// (We do this by simply returning a regular UnlockScreen
// here. This means that the user will still see the
// regular pattern unlock UI, regardless of the value of
// mUnlockScreenMode or whether or not we're in the
// "permanently locked" state.)
unlockView = createUnlockScreenFor(UnlockMode.Pattern);
}
} else if (unlockMode == UnlockMode.Password) {
unlockView = new PasswordUnlockScreen(
mContext,
mConfiguration,
mLockPatternUtils,
mUpdateMonitor,
mKeyguardScreenCallback);
} else {
throw new IllegalArgumentException("unknown unlock mode " + unlockMode);
}
mCurrentUnlockMode = unlockMode;
return unlockView;
}
/**
* Given the current state of things, what should be the initial mode of
* the lock screen (lock or unlock).
*/
private Mode getInitialMode() {
boolean isPukRequired = false;
for (int i = 0; i < TelephonyManager.getPhoneCount(); i++) {
isPukRequired = isPukRequired || isSimPukLocked(i);
if (isPukRequired) break;
}
if (stuckOnLockScreenBecauseSimMissing() || isPukRequired) {
return Mode.LockScreen;
} else {
// Show LockScreen first for any screen other than Pattern unlock.
final boolean usingLockPattern = mLockPatternUtils.getKeyguardStoredPasswordQuality()
== DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
if (isSecure() && usingLockPattern) {
return Mode.UnlockScreen;
} else {
return Mode.LockScreen;
}
}
}
/**
* Given the current state of things, what should the unlock screen be?
*/
private UnlockMode getUnlockMode() {
boolean isPinLocked = false, isPukLocked = false, isPinRequired = false;
boolean allSubsPukLocked = true, simPinMode;
// In case of multi SIM mode,
// Set the unlock mode to "SimPin" in any of these cases.
// 1. SUB1 is PIN-Locked and user has not cancelled PIN entry
// 2. SUB2 is PIN-Locked and user has not cancelled PIN entry
// 3. One of the SUB is PUK-Locked and the other SUB is PIN-Locked.
// 4. Both SUBs are PUK-Locked.
for (int i = 0; i < TelephonyManager.getPhoneCount(); i++) {
isPinRequired = isPinRequired || (isSimPinLocked(i) && !isPinUnlockCancelled[i]);
isPinLocked = isPinLocked || isSimPinLocked(i);
isPukLocked = isPukLocked || isSimPukLocked(i);
allSubsPukLocked = allSubsPukLocked && isSimPukLocked(i);
}
simPinMode = isPinRequired || (isPukLocked && isPinLocked) || allSubsPukLocked;
UnlockMode currentMode;
if (simPinMode) {
// SimUnlockScreen is displayed when the unlock mode is set to SimPin.
currentMode = UnlockMode.SimPin;
} else {
final int mode = mLockPatternUtils.getKeyguardStoredPasswordQuality();
switch (mode) {
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
currentMode = UnlockMode.Password;
break;
case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED:
// "forgot pattern" button is only available in the pattern mode...
if (mForgotPattern || mLockPatternUtils.isPermanentlyLocked()) {
currentMode = UnlockMode.Account;
} else {
currentMode = UnlockMode.Pattern;
}
break;
default:
throw new IllegalStateException("Unknown unlock mode:" + mode);
}
}
return currentMode;
}
private void showTimeoutDialog() {
int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
String message = mContext.getString(
R.string.lockscreen_too_many_failed_attempts_dialog_message,
mUpdateMonitor.getFailedAttempts(),
timeoutInSeconds);
final AlertDialog dialog = new AlertDialog.Builder(mContext)
.setTitle(null)
.setMessage(message)
.setNeutralButton(R.string.ok, null)
.create();
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
if (!mContext.getResources().getBoolean(
com.android.internal.R.bool.config_sf_slowBlur)) {
dialog.getWindow().setFlags(
WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
}
dialog.show();
}
private void showAlmostAtAccountLoginDialog() {
int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
String message = mContext.getString(
R.string.lockscreen_failed_attempts_almost_glogin,
LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
- LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT,
LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT,
timeoutInSeconds);
final AlertDialog dialog = new AlertDialog.Builder(mContext)
.setTitle(null)
.setMessage(message)
.setNeutralButton(R.string.ok, null)
.create();
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
if (!mContext.getResources().getBoolean(
com.android.internal.R.bool.config_sf_slowBlur)) {
dialog.getWindow().setFlags(
WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
}
dialog.show();
}
/**
* Used to put wallpaper on the background of the lock screen. Centers it
* Horizontally and pins the bottom (assuming that the lock screen is aligned
* with the bottom, so the wallpaper should extend above the top into the
* status bar).
*/
static private class FastBitmapDrawable extends Drawable {
private Bitmap mBitmap;
private int mOpacity;
private FastBitmapDrawable(Bitmap bitmap) {
mBitmap = bitmap;
mOpacity = mBitmap.hasAlpha() ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
}
@Override
public void draw(Canvas canvas) {
canvas.drawBitmap(
mBitmap,
(getBounds().width() - mBitmap.getWidth()) / 2,
(getBounds().height() - mBitmap.getHeight()),
null);
}
@Override
public int getOpacity() {
return mOpacity;
}
@Override
public void setAlpha(int alpha) {
}
@Override
public void setColorFilter(ColorFilter cf) {
}
@Override
public int getIntrinsicWidth() {
return mBitmap.getWidth();
}
@Override
public int getIntrinsicHeight() {
return mBitmap.getHeight();
}
@Override
public int getMinimumWidth() {
return mBitmap.getWidth();
}
@Override
public int getMinimumHeight() {
return mBitmap.getHeight();
}
}
}
@@ -0,0 +1,73 @@
/*
* Copyright (C) 2008 The Android Open Source Project
* Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
*
* 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.widget.LockPatternUtils;
import android.content.Context;
import com.android.internal.telephony.IccCard;
import android.telephony.TelephonyManager;
/**
* Knows how to create a lock pattern keyguard view, and answer questions about
* it (even if it hasn't been created, per the interface specs).
*/
public class LockPatternKeyguardViewProperties implements KeyguardViewProperties {
private final LockPatternUtils mLockPatternUtils;
private final KeyguardUpdateMonitor mUpdateMonitor;
/**
* @param lockPatternUtils Used to know whether the pattern enabled, and passed
* onto the keygaurd view when it is created.
* @param updateMonitor Used to know whether the sim pin is enabled, and passed
* onto the keyguard view when it is created.
*/
public LockPatternKeyguardViewProperties(LockPatternUtils lockPatternUtils,
KeyguardUpdateMonitor updateMonitor) {
mLockPatternUtils = lockPatternUtils;
mUpdateMonitor = updateMonitor;
}
public KeyguardViewBase createKeyguardView(Context context,
KeyguardUpdateMonitor updateMonitor,
KeyguardWindowController controller) {
return new LockPatternKeyguardView(context, updateMonitor,
mLockPatternUtils, controller);
}
public boolean isSecure() {
return mLockPatternUtils.isSecure() || isSimPinSecure();
}
private boolean isSimPinSecure() {
final IccCard.State[] simState;
boolean isSimPinSecure = false;
int numPhones = TelephonyManager.getPhoneCount();
simState = new IccCard.State[numPhones];
for (int i = 0; i < numPhones; i++) {
simState[i] = mUpdateMonitor.getSimState(i);
// isPinLocked returns true if SIM is PIN/PUK Locked.
isSimPinSecure = isSimPinSecure || (simState[i].isPinLocked()
|| simState[i] == IccCard.State.ABSENT);
if (isSimPinSecure) break;
}
return isSimPinSecure;
}
}
@@ -0,0 +1,867 @@
/*
* Copyright (C) 2008 The Android Open Source Project
* Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
*
* 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.widget.LockPatternUtils;
import com.android.internal.widget.SlidingTab;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.ColorStateList;
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 android.os.BatteryManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import java.util.ArrayList;
import java.util.Date;
import java.io.File;
/**
* 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, SlidingTab.OnTriggerListener {
private static final boolean DBG = false;
private static final String TAG = "LockScreen";
private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key";
private Status[] mStatus = {Status.Normal};
private LockPatternUtils mLockPatternUtils;
private KeyguardUpdateMonitor mUpdateMonitor;
private KeyguardScreenCallback mCallback;
private TextView[] mCarrier;
private SlidingTab mSelector;
private TextView mTime;
private TextView mDate;
private TextView mStatus1;
private TextView mStatus2;
private TextView mScreenLocked;
private TextView[] mEmergencyCallText;
private Button mEmergencyCallButton;
// current configuration state of keyboard and display
private int mKeyboardHidden;
private int mCreationOrientation;
// 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 String mDateFormatString;
private java.text.DateFormat mTimeFormat;
private boolean mEnableMenuKeyInLockScreen;
private int[] mResId = {R.id.carrier, R.id.carrier_sub2};
private int[] mEmergencyId = {R.id.emergencyCallText, R.id.emergencyCallTextSub2};
/**
* 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),
/**
* The sim card is faulty.
*/
SimIOError(true),
/**
* The ICC card is 'SIM network subset locked'.
*/
NetworkSubsetLocked(true),
/**
* The ICC card is 'SIM corporate locked'.
*/
CorporateLocked(true),
/**
* The ICC card is 'SIM service provider locked'.
*/
ServiceProviderLocked(true),
/**
* The ICC card is 'SIM SIM locked'.
*/
SimSimLocked(true),
/**
* The ICC card is 'RUIM network1 locked'.
*/
RuimNetwork1Locked(true),
/**
* The ICC card is 'RUIM network2 locked'.
*/
RuimNetwork2Locked(true),
/**
* The ICC card is 'RUIM hrpd locked'.
*/
RuimHrpdLocked(true),
/**
* The ICC card is 'RUIM corporate locked'.
*/
RuimCorporateLocked(true),
/**
* The ICC card is 'RUIM service provider locked'.
*/
RuimServiceProviderLocked(true),
/**
* The ICC card is 'RUIM RUIM locked'.
*/
RuimRuimLocked(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;
}
}
/**
* In general, we enable unlocking the insecure key guard with the menu key. However, there are
* some cases where we wish to disable it, notably when the menu button placement or technology
* is prone to false positives.
*
* @return true if the menu key should be enabled
*/
private boolean shouldEnableMenuKey() {
final Resources res = getResources();
final boolean configDisabled = res.getBoolean(R.bool.config_disableMenuKeyInLockScreen);
final boolean isMonkey = SystemProperties.getBoolean("ro.monkey", false);
final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists();
return !configDisabled || isMonkey || fileOverride;
}
/**
* @param context Used to setup the view.
* @param configuration The current configuration. Used to use when selecting layout, etc.
* @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, Configuration configuration, LockPatternUtils lockPatternUtils,
KeyguardUpdateMonitor updateMonitor,
KeyguardScreenCallback callback) {
super(context);
mLockPatternUtils = lockPatternUtils;
mUpdateMonitor = updateMonitor;
mCallback = callback;
mEnableMenuKeyInLockScreen = shouldEnableMenuKey();
mCreationOrientation = configuration.orientation;
mKeyboardHidden = configuration.hardKeyboardHidden;
if (LockPatternKeyguardView.DEBUG_CONFIGURATION) {
Log.v(TAG, "***** CREATING LOCK SCREEN", new RuntimeException());
Log.v(TAG, "Cur orient=" + mCreationOrientation
+ " res orient=" + context.getResources().getConfiguration().orientation);
}
final LayoutInflater inflater = LayoutInflater.from(context);
Log.d(TAG, "Keyguard screen = " + mCreationOrientation);
if (mCreationOrientation != Configuration.ORIENTATION_LANDSCAPE) {
inflater.inflate(R.layout.keyguard_screen_tab_unlock, this, true);
} else {
inflater.inflate(R.layout.keyguard_screen_tab_unlock_land, this, true);
}
int numPhones = TelephonyManager.getPhoneCount();
// Sim States for the subscription
mStatus = new Status[numPhones];
mCarrier = new TextView[numPhones];
for (int i = 0; i < numPhones; i++) {
mStatus[i] = Status.Normal;
mCarrier[i] = (TextView) findViewById(mResId[i]);
// Required for Marquee to work
mCarrier[i].setSelected(true);
mCarrier[i].setTextColor(0xffffffff);
}
mEmergencyCallText = new TextView[mEmergencyId.length];
for (int i=0; i < mEmergencyId.length; i++) {
mEmergencyCallText[i] = (TextView) findViewById(mEmergencyId[i]);
mEmergencyCallText[i].setVisibility(View.GONE);
}
mDate = (TextView) findViewById(R.id.date);
mStatus1 = (TextView) findViewById(R.id.status1);
mStatus2 = (TextView) findViewById(R.id.status2);
mScreenLocked = (TextView) findViewById(R.id.screenLocked);
mSelector = (SlidingTab) findViewById(R.id.tab_selector);
mSelector.setHoldAfterTrigger(true, false);
mSelector.setLeftHintText(R.string.lockscreen_unlock_label);
mEmergencyCallButton = (Button) findViewById(R.id.emergencyCallButton);
mEmergencyCallButton.setText(R.string.lockscreen_emergency_call);
mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
mEmergencyCallButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mCallback.takeEmergencyCallAction();
}
});
setFocusable(true);
setFocusableInTouchMode(true);
setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
mUpdateMonitor.registerInfoCallback(this);
mUpdateMonitor.registerSimStateCallback(this);
mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
mSilentMode = isSilentMode();
mSelector.setLeftTabResources(
R.drawable.ic_jog_dial_unlock,
R.drawable.jog_tab_target_green,
R.drawable.jog_tab_bar_left_unlock,
R.drawable.jog_tab_left_unlock);
updateRightTabResources();
mSelector.setOnTriggerListener(this);
resetStatusInfo(updateMonitor);
}
private boolean isSilentMode() {
return mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
}
private void updateRightTabResources() {
boolean vibe = mSilentMode
&& (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE);
mSelector.setRightTabResources(
mSilentMode ? ( vibe ? R.drawable.ic_jog_dial_vibrate_on
: R.drawable.ic_jog_dial_sound_off )
: R.drawable.ic_jog_dial_sound_on,
mSilentMode ? R.drawable.jog_tab_target_yellow
: R.drawable.jog_tab_target_gray,
mSilentMode ? R.drawable.jog_tab_bar_right_sound_on
: R.drawable.jog_tab_bar_right_sound_off,
mSilentMode ? R.drawable.jog_tab_right_sound_on
: R.drawable.jog_tab_right_sound_off);
}
private void resetStatusInfo(KeyguardUpdateMonitor updateMonitor) {
mShowingBatteryInfo = updateMonitor.shouldShowBatteryInfo();
mPluggedIn = updateMonitor.isDevicePluggedIn();
mBatteryLevel = updateMonitor.getBatteryLevel();
for (int i = 0; i < TelephonyManager.getPhoneCount(); i++) {
mStatus[i] = getCurrentStatus(updateMonitor.getSimState(i));
updateLayout(mStatus[i], i);
}
refreshBatteryStringAndIcon();
refreshAlarmDisplay();
mTimeFormat = DateFormat.getTimeFormat(getContext());
mDateFormatString = getContext().getString(R.string.full_wday_month_day_no_year);
refreshTimeAndDateDisplay();
updateStatusLines();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_MENU && mEnableMenuKeyInLockScreen) {
mCallback.goToUnlockScreen();
}
return false;
}
/** {@inheritDoc} */
public void onTrigger(View v, int whichHandle) {
if (whichHandle == SlidingTab.OnTriggerListener.LEFT_HANDLE) {
mCallback.goToUnlockScreen();
} else if (whichHandle == SlidingTab.OnTriggerListener.RIGHT_HANDLE) {
// toggle silent mode
mSilentMode = !mSilentMode;
if (mSilentMode) {
final boolean vibe = (Settings.System.getInt(
getContext().getContentResolver(),
Settings.System.VIBRATE_IN_SILENT, 1) == 1);
mAudioManager.setRingerMode(vibe
? AudioManager.RINGER_MODE_VIBRATE
: AudioManager.RINGER_MODE_SILENT);
} else {
mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
}
updateRightTabResources();
String message = mSilentMode ?
getContext().getString(R.string.global_action_silent_mode_on_status) :
getContext().getString(R.string.global_action_silent_mode_off_status);
final int toastIcon = mSilentMode
? R.drawable.ic_lock_ringer_off
: R.drawable.ic_lock_ringer_on;
final int toastColor = mSilentMode
? getContext().getResources().getColor(R.color.keyguard_text_color_soundoff)
: getContext().getResources().getColor(R.color.keyguard_text_color_soundon);
toastMessage(mScreenLocked, message, toastColor, toastIcon);
mCallback.pokeWakelock();
}
}
/** {@inheritDoc} */
public void onGrabbedStateChange(View v, int grabbedState) {
if (grabbedState == SlidingTab.OnTriggerListener.RIGHT_HANDLE) {
mSilentMode = isSilentMode();
mSelector.setRightHintText(mSilentMode ? R.string.lockscreen_sound_on_label
: R.string.lockscreen_sound_off_label);
}
// Don't poke the wake lock when returning to a state where the handle is
// not grabbed since that can happen when the system (instead of the user)
// cancels the grab.
if (grabbedState != SlidingTab.OnTriggerListener.NO_HANDLE) {
mCallback.pokeWakelock();
}
}
/**
* Displays a message in a text view and then restores the previous text.
* @param textView The text view.
* @param text The text.
* @param color The color to apply to the text, or 0 if the existing color should be used.
* @param iconResourceId The left hand icon.
*/
private void toastMessage(final TextView textView, final String text, final int color, final int iconResourceId) {
if (mPendingR1 != null) {
textView.removeCallbacks(mPendingR1);
mPendingR1 = null;
}
if (mPendingR2 != null) {
mPendingR2.run(); // fire immediately, restoring non-toasted appearance
textView.removeCallbacks(mPendingR2);
mPendingR2 = null;
}
final String oldText = textView.getText().toString();
final ColorStateList oldColors = textView.getTextColors();
mPendingR1 = new Runnable() {
public void run() {
textView.setText(text);
if (color != 0) {
textView.setTextColor(color);
}
textView.setCompoundDrawablesWithIntrinsicBounds(iconResourceId, 0, 0, 0);
}
};
textView.postDelayed(mPendingR1, 0);
mPendingR2 = new Runnable() {
public void run() {
textView.setText(oldText);
textView.setTextColor(oldColors);
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 (mUpdateMonitor.isDeviceCharged()) {
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() {
mDate.setText(DateFormat.format(mDateFormatString, new Date()));
}
private void updateStatusLines() {
// Use default subscription status for alarm and charging text display.
int sub = TelephonyManager.getDefaultSubscription();
if (!mStatus[sub].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, int subscription) {
if (DBG) Log.d(TAG, "onRefreshCarrierInfo(" + plmn + ", " + spn + ")");
// Update operator name display.
updateLayout(mStatus[subscription], subscription);
}
/**
* 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.NetworkLocked;
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;
case CARD_IO_ERROR:
return Status.SimIOError;
case SIM_NETWORK_SUBSET_LOCKED:
return Status.NetworkSubsetLocked;
case SIM_CORPORATE_LOCKED:
return Status.CorporateLocked;
case SIM_SERVICE_PROVIDER_LOCKED:
return Status.ServiceProviderLocked;
case SIM_SIM_LOCKED:
return Status.SimSimLocked;
case RUIM_NETWORK1_LOCKED:
return Status.RuimNetwork1Locked;
case RUIM_NETWORK2_LOCKED:
return Status.RuimNetwork2Locked;
case RUIM_HRPD_LOCKED:
return Status.RuimHrpdLocked;
case RUIM_CORPORATE_LOCKED:
return Status.RuimCorporateLocked;
case RUIM_SERVICE_PROVIDER_LOCKED:
return Status.RuimServiceProviderLocked;
case RUIM_RUIM_LOCKED:
return Status.RuimRuimLocked;
}
return Status.SimMissing;
}
/**
* Update the layout to match the current status.
*/
private void updateLayout(Status status, int subscription) {
// The emergency call button no longer appears on this screen.
Log.d(TAG, "updateLayout: status=" + status + "subscription=" + subscription);
mEmergencyCallButton.setVisibility(View.GONE); // in almost all cases
switch (status) {
case Normal:
// text
mCarrier[subscription].setText(
getCarrierString(
mUpdateMonitor.getTelephonyPlmn(subscription),
mUpdateMonitor.getTelephonySpn(subscription)));
// Empty now, but used for sliding tab feedback
mScreenLocked.setText("");
// layout
mScreenLocked.setVisibility(View.VISIBLE);
mSelector.setVisibility(View.VISIBLE);
mEmergencyCallText[subscription].setVisibility(View.GONE);
break;
case NetworkLocked:
// The carrier string shows both sim card status (i.e. No Sim Card) and
// carrier's name and/or "Emergency Calls Only" status
mCarrier[subscription].setText(
getCarrierString(
mUpdateMonitor.getTelephonyPlmn(subscription),
getContext().getText(R.string.lockscreen_sim_network_locked_message)));
mScreenLocked.setText(R.string.lockscreen_instructions_when_pattern_disabled);
// layout
mScreenLocked.setVisibility(View.VISIBLE);
mSelector.setVisibility(View.VISIBLE);
mEmergencyCallText[subscription].setVisibility(View.GONE);
break;
case SimMissing:
// text
mCarrier[subscription].setText(R.string.lockscreen_missing_sim_message_short);
mScreenLocked.setText(R.string.lockscreen_missing_sim_instructions);
// layout
mScreenLocked.setVisibility(View.VISIBLE);
mSelector.setVisibility(View.VISIBLE);
mEmergencyCallText[subscription].setVisibility(View.VISIBLE);
// do not need to show the e-call button; user may unlock
break;
case SimMissingLocked:
// text
mCarrier[subscription].setText(
getCarrierString(
mUpdateMonitor.getTelephonyPlmn(subscription),
getContext().getText(R.string.lockscreen_missing_sim_message_short)));
disableUnlockScreen(R.string.lockscreen_missing_sim_instructions, subscription);
break;
case SimLocked:
// text
mCarrier[subscription].setText(
getCarrierString(
mUpdateMonitor.getTelephonyPlmn(subscription),
getContext().getText(R.string.lockscreen_sim_sim_locked_message)));
// layout
mScreenLocked.setVisibility(View.INVISIBLE);
mSelector.setVisibility(View.VISIBLE);
mEmergencyCallText[subscription].setVisibility(View.GONE);
break;
case SimPukLocked:
// text
mCarrier[subscription].setText(
getCarrierString(
mUpdateMonitor.getTelephonyPlmn(subscription),
getContext().getText(R.string.lockscreen_sim_puk_locked_message)));
disableUnlockScreen(R.string.lockscreen_sim_puk_locked_instructions, subscription);
break;
case SimIOError:
// text
mCarrier[subscription].setText(R.string.lockscreen_sim_error_message_short);
mScreenLocked.setText(R.string.lockscreen_instructions_when_pattern_disabled);
// layout
mScreenLocked.setVisibility(View.INVISIBLE);
mSelector.setVisibility(View.VISIBLE);
mEmergencyCallButton.setVisibility(View.VISIBLE);
mEmergencyCallText[subscription].setVisibility(View.VISIBLE);
break;
case NetworkSubsetLocked:
// text
mCarrier[subscription].setText(R.string.lockscreen_sim_network_subset_locked_message);
updateLayoutForPersoText();
break;
case CorporateLocked:
// text
mCarrier[subscription].setText(R.string.lockscreen_sim_corporate_locked_message);
updateLayoutForPersoText();
break;
case ServiceProviderLocked:
// text
mCarrier[subscription].setText(R.string.lockscreen_sim_service_provider_locked_message);
updateLayoutForPersoText();
break;
case SimSimLocked:
// text
mCarrier[subscription].setText(R.string.lockscreen_sim_sim_locked_message);
updateLayoutForPersoText();
break;
case RuimNetwork1Locked:
// text
mCarrier[subscription].setText(R.string.lockscreen_ruim_network1_locked_message);
updateLayoutForPersoText();
break;
case RuimNetwork2Locked:
// text
mCarrier[subscription].setText(R.string.lockscreen_ruim_network2_locked_message);
updateLayoutForPersoText();
break;
case RuimHrpdLocked:
// text
mCarrier[subscription].setText(R.string.lockscreen_ruim_hrpd_locked_message);
updateLayoutForPersoText();
break;
case RuimCorporateLocked:
// text
mCarrier[subscription].setText(R.string.lockscreen_ruim_corporate_locked_message);
updateLayoutForPersoText();
break;
case RuimServiceProviderLocked:
// text
mCarrier[subscription].setText(R.string.lockscreen_ruim_service_provider_locked_message);
updateLayoutForPersoText();
break;
case RuimRuimLocked:
// text
mCarrier[subscription].setText(R.string.lockscreen_ruim_ruim_locked_message);
updateLayoutForPersoText();
break;
}
}
private void disableUnlockScreen(int resId, int subscription) {
// layout
boolean disableUnlockScreen = true;
for (int i = 0; i < TelephonyManager.getPhoneCount(); i++) {
disableUnlockScreen = disableUnlockScreen
&& (mStatus[i] == Status.SimMissingLocked ||
mStatus[i] == Status.SimPukLocked);
if (!disableUnlockScreen) break;
}
if (disableUnlockScreen) {
mScreenLocked.setText(resId);
mSelector.setVisibility(View.GONE); // cannot unlock
mEmergencyCallText[subscription].setVisibility(View.VISIBLE);
mEmergencyCallButton.setVisibility(View.VISIBLE);
} else {
mSelector.setVisibility(View.VISIBLE);
}
}
private void updateLayoutForPersoText() {
mScreenLocked.setText(R.string.lockscreen_instructions_when_pattern_disabled);
// layout
mScreenLocked.setVisibility(View.VISIBLE);
mSelector.setVisibility(View.VISIBLE);
mEmergencyCallButton.setVisibility(View.GONE);
}
static CharSequence getCarrierString(CharSequence telephonyPlmn, CharSequence telephonySpn) {
if (telephonyPlmn != null && telephonySpn == null) {
return telephonyPlmn;
} else if (telephonyPlmn != null && telephonySpn != null) {
return telephonyPlmn + "|" + telephonySpn;
} else if (telephonyPlmn == null && telephonySpn != null) {
return telephonySpn;
} else {
return "";
}
}
public void onSimStateChanged(IccCard.State simState, int subscription) {
Log.d(TAG, "onSimStateChanged : simState = " + simState + ", subscription = " + subscription);
mStatus[subscription] = getCurrentStatus(simState);
updateLayout(mStatus[subscription], subscription);
updateStatusLines();
}
void updateConfiguration() {
Configuration newConfig = getResources().getConfiguration();
if (newConfig.orientation != mCreationOrientation) {
mCallback.recreateMe(newConfig);
} else if (newConfig.hardKeyboardHidden != mKeyboardHidden) {
mKeyboardHidden = newConfig.hardKeyboardHidden;
final boolean isKeyboardOpen = mKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO;
if (mUpdateMonitor.isKeyguardBypassEnabled() && isKeyboardOpen) {
mCallback.goToUnlockScreen();
}
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (LockPatternKeyguardView.DEBUG_CONFIGURATION) {
Log.v(TAG, "***** LOCK ATTACHED TO WINDOW");
Log.v(TAG, "Cur orient=" + mCreationOrientation
+ ", new config=" + getResources().getConfiguration());
}
updateConfiguration();
}
/** {@inheritDoc} */
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (LockPatternKeyguardView.DEBUG_CONFIGURATION) {
Log.w(TAG, "***** LOCK CONFIG CHANGING", new RuntimeException());
Log.v(TAG, "Cur orient=" + mCreationOrientation
+ ", new config=" + newConfig);
}
updateConfiguration();
}
/** {@inheritDoc} */
public boolean needsInput() {
return false;
}
/** {@inheritDoc} */
public void onPause() {
}
/** {@inheritDoc} */
public void onResume() {
resetStatusInfo(mUpdateMonitor);
mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
}
/** {@inheritDoc} */
public void cleanUp() {
mUpdateMonitor.removeCallback(this); // this must be first
mLockPatternUtils = null;
mUpdateMonitor = null;
mCallback = null;
}
/** {@inheritDoc} */
public void onRingerModeChanged(int state) {
boolean silent = AudioManager.RINGER_MODE_NORMAL != state;
if (silent != mSilentMode) {
mSilentMode = silent;
updateRightTabResources();
}
}
public void onPhoneStateChanged(String newState) {
mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
}
}
@@ -0,0 +1,267 @@
/*
* Copyright (C) 2010 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 android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
import com.android.internal.policy.impl.PatternUnlockScreen.FooterMode;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.PasswordEntryKeyboardView;
import android.os.CountDownTimer;
import android.os.SystemClock;
import android.telephony.TelephonyManager;
import android.text.method.DigitsKeyListener;
import android.text.method.TextKeyListener;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import com.android.internal.R;
import com.android.internal.widget.PasswordEntryKeyboardHelper;
/**
* Displays a dialer-like interface or alphanumeric (latin-1) key entry for the user to enter
* an unlock password
*/
public class PasswordUnlockScreen extends LinearLayout implements KeyguardScreen,
View.OnClickListener, KeyguardUpdateMonitor.InfoCallback, OnEditorActionListener {
private final KeyguardUpdateMonitor mUpdateMonitor;
private final KeyguardScreenCallback mCallback;
private EditText mPasswordEntry;
private Button mEmergencyCallButton;
private LockPatternUtils mLockPatternUtils;
private PasswordEntryKeyboardView mKeyboardView;
private PasswordEntryKeyboardHelper mKeyboardHelper;
private int mCreationOrientation;
private int mCreationHardKeyboardHidden;
private CountDownTimer mCountdownTimer;
private TextView mTitle;
// To avoid accidental lockout due to events while the device in in the pocket, ignore
// any passwords with length less than or equal to this length.
private static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3;
public PasswordUnlockScreen(Context context, Configuration configuration,
LockPatternUtils lockPatternUtils, KeyguardUpdateMonitor updateMonitor,
KeyguardScreenCallback callback) {
super(context);
mCreationHardKeyboardHidden = configuration.hardKeyboardHidden;
mCreationOrientation = configuration.orientation;
mUpdateMonitor = updateMonitor;
mCallback = callback;
mLockPatternUtils = lockPatternUtils;
LayoutInflater layoutInflater = LayoutInflater.from(context);
if (mCreationOrientation != Configuration.ORIENTATION_LANDSCAPE) {
layoutInflater.inflate(R.layout.keyguard_screen_password_portrait, this, true);
} else {
layoutInflater.inflate(R.layout.keyguard_screen_password_landscape, this, true);
}
final int quality = lockPatternUtils.getKeyguardStoredPasswordQuality();
final boolean isAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == quality
|| DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == quality;
mKeyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard);
mPasswordEntry = (EditText) findViewById(R.id.passwordEntry);
mPasswordEntry.setOnEditorActionListener(this);
mEmergencyCallButton = (Button) findViewById(R.id.emergencyCall);
mEmergencyCallButton.setOnClickListener(this);
mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
mTitle = (TextView) findViewById(R.id.enter_password_label);
mKeyboardHelper = new PasswordEntryKeyboardHelper(context, mKeyboardView, this);
mKeyboardHelper.setKeyboardMode(isAlpha ? PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA
: PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
mKeyboardView.setVisibility(mCreationHardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO
? View.INVISIBLE : View.VISIBLE);
mPasswordEntry.requestFocus();
// This allows keyboards with overlapping qwerty/numeric keys to choose just the
// numeric keys.
if (isAlpha) {
mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
} else {
mPasswordEntry.setKeyListener(DigitsKeyListener.getInstance());
}
mKeyboardHelper.setVibratePattern(mLockPatternUtils.isTactileFeedbackEnabled() ?
com.android.internal.R.array.config_virtualKeyVibePattern : 0);
}
@Override
protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
// send focus to the password field
return mPasswordEntry.requestFocus(direction, previouslyFocusedRect);
}
/** {@inheritDoc} */
public boolean needsInput() {
return false;
}
/** {@inheritDoc} */
public void onPause() {
}
/** {@inheritDoc} */
public void onResume() {
// start fresh
mPasswordEntry.setText("");
mPasswordEntry.requestFocus();
mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
// if the user is currently locked out, enforce it.
long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
if (deadline != 0) {
handleAttemptLockout(deadline);
}
}
/** {@inheritDoc} */
public void cleanUp() {
mUpdateMonitor.removeCallback(this);
}
public void onClick(View v) {
if (v == mEmergencyCallButton) {
mCallback.takeEmergencyCallAction();
}
mCallback.pokeWakelock();
}
private void verifyPasswordAndUnlock() {
String entry = mPasswordEntry.getText().toString();
if (mLockPatternUtils.checkPassword(entry)) {
mCallback.keyguardDone(true);
mCallback.reportSuccessfulUnlockAttempt();
} else if (entry.length() > MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT ) {
// to avoid accidental lockout, only count attempts that are long enough to be a
// real password. This may require some tweaking.
mCallback.reportFailedUnlockAttempt();
if (0 == (mUpdateMonitor.getFailedAttempts()
% LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
handleAttemptLockout(deadline);
}
}
mPasswordEntry.setText("");
}
// Prevent user from using the PIN/Password entry until scheduled deadline.
private void handleAttemptLockout(long elapsedRealtimeDeadline) {
mPasswordEntry.setEnabled(false);
mKeyboardView.setEnabled(false);
long elapsedRealtime = SystemClock.elapsedRealtime();
mCountdownTimer = new CountDownTimer(elapsedRealtimeDeadline - elapsedRealtime, 1000) {
@Override
public void onTick(long millisUntilFinished) {
int secondsRemaining = (int) (millisUntilFinished / 1000);
String instructions = getContext().getString(
R.string.lockscreen_too_many_failed_attempts_countdown,
secondsRemaining);
mTitle.setText(instructions);
}
@Override
public void onFinish() {
mPasswordEntry.setEnabled(true);
mTitle.setText(R.string.keyguard_password_enter_password_code);
mKeyboardView.setEnabled(true);
}
}.start();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
mCallback.pokeWakelock();
return false;
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
Configuration config = getResources().getConfiguration();
if (config.orientation != mCreationOrientation
|| config.hardKeyboardHidden != mCreationHardKeyboardHidden) {
mCallback.recreateMe(config);
}
}
/** {@inheritDoc} */
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (newConfig.orientation != mCreationOrientation
|| newConfig.hardKeyboardHidden != mCreationHardKeyboardHidden) {
mCallback.recreateMe(newConfig);
}
}
public void onKeyboardChange(boolean isKeyboardOpen) {
// Don't show the soft keyboard when the real keyboard is open
mKeyboardView.setVisibility(isKeyboardOpen ? View.INVISIBLE : View.VISIBLE);
}
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
// Check if this was the result of hitting the enter key
if (actionId == EditorInfo.IME_NULL) {
verifyPasswordAndUnlock();
return true;
}
return false;
}
public void onPhoneStateChanged(String newState) {
mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
}
public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) {
}
public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn, int subscription) {
}
public void onRingerModeChanged(int state) {
}
public void onTimeChanged() {
}
}
@@ -0,0 +1,591 @@
/*
* 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 android.content.Context;
import android.content.res.Configuration;
import android.os.CountDownTimer;
import android.os.SystemClock;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.MotionEvent;
import android.widget.Button;
import android.widget.TextView;
import android.text.format.DateFormat;
import android.text.TextUtils;
import android.util.Log;
import com.android.internal.R;
import com.android.internal.telephony.IccCard;
import com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternView;
import com.android.internal.widget.LockPatternView.Cell;
import android.telephony.TelephonyManager;
import java.util.List;
import java.util.Date;
/**
* This is the screen that shows the 9 circle unlock widget and instructs
* the user how to unlock their device, or make an emergency call.
*/
class PatternUnlockScreen extends LinearLayoutWithDefaultTouchRecepient
implements KeyguardScreen, KeyguardUpdateMonitor.InfoCallback,
KeyguardUpdateMonitor.SimStateCallback {
private static final boolean DEBUG = false;
private static final String TAG = "UnlockScreen";
// how long before we clear the wrong pattern
private static final int PATTERN_CLEAR_TIMEOUT_MS = 2000;
// how long we stay awake after each key beyond MIN_PATTERN_BEFORE_POKE_WAKELOCK
private static final int UNLOCK_PATTERN_WAKE_INTERVAL_MS = 7000;
// how long we stay awake after the user hits the first dot.
private static final int UNLOCK_PATTERN_WAKE_INTERVAL_FIRST_DOTS_MS = 2000;
// how many cells the user has to cross before we poke the wakelock
private static final int MIN_PATTERN_BEFORE_POKE_WAKELOCK = 2;
private int mFailedPatternAttemptsSinceLastTimeout = 0;
private int mTotalFailedPatternAttempts = 0;
private CountDownTimer mCountdownTimer = null;
private LockPatternUtils mLockPatternUtils;
private KeyguardUpdateMonitor mUpdateMonitor;
private KeyguardScreenCallback mCallback;
/**
* whether there is a fallback option available when the pattern is forgotten.
*/
private boolean mEnableFallback;
private String mDateFormatString;
private TextView[] mCarrier;
private TextView mDate;
// 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 String mInstructions = null;
private TextView mStatus1;
private TextView mStatusSep;
private TextView mStatus2;
private LockPatternView mLockPatternView;
private ViewGroup mFooterNormal;
private ViewGroup mFooterForgotPattern;
private int[] mResId = {R.id.carrier, R.id.carrier_sub2};
/**
* Keeps track of the last time we poked the wake lock during dispatching
* of the touch event, initalized to something gauranteed to make us
* poke it when the user starts drawing the pattern.
* @see #dispatchTouchEvent(android.view.MotionEvent)
*/
private long mLastPokeTime = -UNLOCK_PATTERN_WAKE_INTERVAL_MS;
/**
* Useful for clearing out the wrong pattern after a delay
*/
private Runnable mCancelPatternRunnable = new Runnable() {
public void run() {
mLockPatternView.clearPattern();
}
};
private Button mForgotPatternButton;
private Button mEmergencyAlone;
private Button mEmergencyTogether;
private int mCreationOrientation;
enum FooterMode {
Normal,
ForgotLockPattern,
VerifyUnlocked
}
private void updateFooter(FooterMode mode) {
switch (mode) {
case Normal:
mFooterNormal.setVisibility(View.VISIBLE);
mFooterForgotPattern.setVisibility(View.GONE);
break;
case ForgotLockPattern:
mFooterNormal.setVisibility(View.GONE);
mFooterForgotPattern.setVisibility(View.VISIBLE);
mForgotPatternButton.setVisibility(View.VISIBLE);
break;
case VerifyUnlocked:
mFooterNormal.setVisibility(View.GONE);
mFooterForgotPattern.setVisibility(View.GONE);
}
}
/**
* @param context The context.
* @param configuration
* @param lockPatternUtils Used to lookup lock pattern settings.
* @param updateMonitor Used to lookup state affecting keyguard.
* @param callback Used to notify the manager when we're done, etc.
* @param totalFailedAttempts The current number of failed attempts.
* @param enableFallback True if a backup unlock option is available when the user has forgotten
* their pattern (e.g they have a google account so we can show them the account based
* backup option).
*/
PatternUnlockScreen(Context context,
Configuration configuration, LockPatternUtils lockPatternUtils,
KeyguardUpdateMonitor updateMonitor,
KeyguardScreenCallback callback,
int totalFailedAttempts) {
super(context);
mLockPatternUtils = lockPatternUtils;
mUpdateMonitor = updateMonitor;
mCallback = callback;
mTotalFailedPatternAttempts = totalFailedAttempts;
mFailedPatternAttemptsSinceLastTimeout =
totalFailedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
if (DEBUG) Log.d(TAG,
"UnlockScreen() ctor: totalFailedAttempts="
+ totalFailedAttempts + ", mFailedPat...="
+ mFailedPatternAttemptsSinceLastTimeout
);
mCreationOrientation = configuration.orientation;
LayoutInflater inflater = LayoutInflater.from(context);
if (mCreationOrientation != Configuration.ORIENTATION_LANDSCAPE) {
inflater.inflate(R.layout.keyguard_screen_unlock_portrait, this, true);
} else {
inflater.inflate(R.layout.keyguard_screen_unlock_landscape, this, true);
}
int numPhones = TelephonyManager.getPhoneCount();
mCarrier = new TextView[numPhones];
for (int i = 0; i < TelephonyManager.getPhoneCount(); i++) {
mCarrier[i] = (TextView) findViewById(mResId[i]);
}
mDate = (TextView) findViewById(R.id.date);
mDateFormatString = getContext().getString(R.string.full_wday_month_day_no_year);
refreshTimeAndDateDisplay();
mStatus1 = (TextView) findViewById(R.id.status1);
mStatusSep = (TextView) findViewById(R.id.statusSep);
mStatus2 = (TextView) findViewById(R.id.status2);
resetStatusInfo();
mLockPatternView = (LockPatternView) findViewById(R.id.lockPattern);
mFooterNormal = (ViewGroup) findViewById(R.id.footerNormal);
mFooterForgotPattern = (ViewGroup) findViewById(R.id.footerForgotPattern);
// emergency call buttons
final OnClickListener emergencyClick = new OnClickListener() {
public void onClick(View v) {
mCallback.takeEmergencyCallAction();
}
};
mEmergencyAlone = (Button) findViewById(R.id.emergencyCallAlone);
mEmergencyAlone.setFocusable(false); // touch only!
mEmergencyAlone.setOnClickListener(emergencyClick);
mEmergencyTogether = (Button) findViewById(R.id.emergencyCallTogether);
mEmergencyTogether.setFocusable(false);
mEmergencyTogether.setOnClickListener(emergencyClick);
refreshEmergencyButtonText();
mForgotPatternButton = (Button) findViewById(R.id.forgotPattern);
mForgotPatternButton.setText(R.string.lockscreen_forgot_pattern_button_text);
mForgotPatternButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
mCallback.forgotPattern(true);
}
});
// make it so unhandled touch events within the unlock screen go to the
// lock pattern view.
setDefaultTouchRecepient(mLockPatternView);
mLockPatternView.setSaveEnabled(false);
mLockPatternView.setFocusable(false);
mLockPatternView.setOnPatternListener(new UnlockPatternListener());
// stealth mode will be the same for the life of this screen
mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled());
// vibrate mode will be the same for the life of this screen
mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
// assume normal footer mode for now
updateFooter(FooterMode.Normal);
updateMonitor.registerInfoCallback(this);
updateMonitor.registerSimStateCallback(this);
setFocusableInTouchMode(true);
// Required to get Marquee to work.
for (int i = 0; i < TelephonyManager.getPhoneCount(); i++) {
mCarrier[i].setSelected(true);
mCarrier[i].setTextColor(0xffffffff);
// until we get an update...
mCarrier[i].setText(
LockScreen.getCarrierString(
mUpdateMonitor.getTelephonyPlmn(i),
mUpdateMonitor.getTelephonySpn(i)));
}
}
private void refreshEmergencyButtonText() {
mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyAlone);
mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyTogether);
}
public void setEnableFallback(boolean state) {
if (DEBUG) Log.d(TAG, "setEnableFallback(" + state + ")");
mEnableFallback = state;
}
private void resetStatusInfo() {
mInstructions = null;
mShowingBatteryInfo = mUpdateMonitor.shouldShowBatteryInfo();
mPluggedIn = mUpdateMonitor.isDevicePluggedIn();
mBatteryLevel = mUpdateMonitor.getBatteryLevel();
mNextAlarm = mLockPatternUtils.getNextAlarm();
updateStatusLines();
}
private void updateStatusLines() {
if (mInstructions != null) {
// instructions only
mStatus1.setText(mInstructions);
if (TextUtils.isEmpty(mInstructions)) {
mStatus1.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
} else {
mStatus1.setCompoundDrawablesWithIntrinsicBounds(
R.drawable.ic_lock_idle_lock, 0, 0, 0);
}
mStatus1.setVisibility(View.VISIBLE);
mStatusSep.setVisibility(View.GONE);
mStatus2.setVisibility(View.GONE);
} else if (mShowingBatteryInfo && mNextAlarm == null) {
// battery only
if (mPluggedIn) {
if (mUpdateMonitor.isDeviceCharged()) {
mStatus1.setText(getContext().getString(R.string.lockscreen_charged));
} else {
mStatus1.setText(getContext().getString(R.string.lockscreen_plugged_in, mBatteryLevel));
}
} else {
mStatus1.setText(getContext().getString(R.string.lockscreen_low_battery));
}
mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_charging, 0, 0, 0);
mStatus1.setVisibility(View.VISIBLE);
mStatusSep.setVisibility(View.GONE);
mStatus2.setVisibility(View.GONE);
} else if (mNextAlarm != null && !mShowingBatteryInfo) {
// alarm only
mStatus1.setText(mNextAlarm);
mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_alarm, 0, 0, 0);
mStatus1.setVisibility(View.VISIBLE);
mStatusSep.setVisibility(View.GONE);
mStatus2.setVisibility(View.GONE);
} else if (mNextAlarm != null && mShowingBatteryInfo) {
// both battery and next alarm
mStatus1.setText(mNextAlarm);
mStatusSep.setText("|");
mStatus2.setText(getContext().getString(
R.string.lockscreen_battery_short,
Math.min(100, mBatteryLevel)));
mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_alarm, 0, 0, 0);
if (mPluggedIn) {
mStatus2.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_charging, 0, 0, 0);
} else {
mStatus2.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
}
mStatus1.setVisibility(View.VISIBLE);
mStatusSep.setVisibility(View.VISIBLE);
mStatus2.setVisibility(View.VISIBLE);
} else {
// nothing specific to show; show general instructions
mStatus1.setText(R.string.lockscreen_pattern_instructions);
mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_lock, 0, 0, 0);
mStatus1.setVisibility(View.VISIBLE);
mStatusSep.setVisibility(View.GONE);
mStatus2.setVisibility(View.GONE);
}
}
private void refreshTimeAndDateDisplay() {
mDate.setText(DateFormat.format(mDateFormatString, new Date()));
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// as long as the user is entering a pattern (i.e sending a touch
// event that was handled by this screen), keep poking the
// wake lock so that the screen will stay on.
final boolean result = super.dispatchTouchEvent(ev);
if (result &&
((SystemClock.elapsedRealtime() - mLastPokeTime)
> (UNLOCK_PATTERN_WAKE_INTERVAL_MS - 100))) {
mLastPokeTime = SystemClock.elapsedRealtime();
}
return result;
}
// ---------- InfoCallback
/** {@inheritDoc} */
public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) {
mShowingBatteryInfo = showBatteryInfo;
mPluggedIn = pluggedIn;
mBatteryLevel = batteryLevel;
updateStatusLines();
}
/** {@inheritDoc} */
public void onTimeChanged() {
refreshTimeAndDateDisplay();
}
/** {@inheritDoc} */
public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn, int subscription) {
mCarrier[subscription].setText(LockScreen.getCarrierString(plmn, spn));
}
/** {@inheritDoc} */
public void onRingerModeChanged(int state) {
// not currently used
}
// ---------- SimStateCallback
/** {@inheritDoc} */
public void onSimStateChanged(IccCard.State simState, int subscription) {
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (LockPatternKeyguardView.DEBUG_CONFIGURATION) {
Log.v(TAG, "***** PATTERN ATTACHED TO WINDOW");
Log.v(TAG, "Cur orient=" + mCreationOrientation
+ ", new config=" + getResources().getConfiguration());
}
if (getResources().getConfiguration().orientation != mCreationOrientation) {
mCallback.recreateMe(getResources().getConfiguration());
}
}
/** {@inheritDoc} */
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (LockPatternKeyguardView.DEBUG_CONFIGURATION) {
Log.v(TAG, "***** PATTERN CONFIGURATION CHANGED");
Log.v(TAG, "Cur orient=" + mCreationOrientation
+ ", new config=" + getResources().getConfiguration());
}
if (newConfig.orientation != mCreationOrientation) {
mCallback.recreateMe(newConfig);
}
}
/** {@inheritDoc} */
public void onKeyboardChange(boolean isKeyboardOpen) {}
/** {@inheritDoc} */
public boolean needsInput() {
return false;
}
/** {@inheritDoc} */
public void onPause() {
if (mCountdownTimer != null) {
mCountdownTimer.cancel();
mCountdownTimer = null;
}
}
/** {@inheritDoc} */
public void onResume() {
// reset header
resetStatusInfo();
// reset lock pattern
mLockPatternView.enableInput();
mLockPatternView.setEnabled(true);
mLockPatternView.clearPattern();
// show "forgot pattern?" button if we have an alternate authentication method
mForgotPatternButton.setVisibility(mCallback.doesFallbackUnlockScreenExist()
? View.VISIBLE : View.INVISIBLE);
// if the user is currently locked out, enforce it.
long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
if (deadline != 0) {
handleAttemptLockout(deadline);
}
// the footer depends on how many total attempts the user has failed
if (mCallback.isVerifyUnlockOnly()) {
updateFooter(FooterMode.VerifyUnlocked);
} else if (mEnableFallback &&
(mTotalFailedPatternAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
updateFooter(FooterMode.ForgotLockPattern);
} else {
updateFooter(FooterMode.Normal);
}
refreshEmergencyButtonText();
}
/** {@inheritDoc} */
public void cleanUp() {
mUpdateMonitor.removeCallback(this);
mLockPatternUtils = null;
mUpdateMonitor = null;
mCallback = null;
}
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
if (hasWindowFocus) {
// when timeout dialog closes we want to update our state
onResume();
}
}
private class UnlockPatternListener
implements LockPatternView.OnPatternListener {
public void onPatternStart() {
mLockPatternView.removeCallbacks(mCancelPatternRunnable);
}
public void onPatternCleared() {
}
public void onPatternCellAdded(List<Cell> pattern) {
// To guard against accidental poking of the wakelock, look for
// the user actually trying to draw a pattern of some minimal length.
if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
mCallback.pokeWakelock(UNLOCK_PATTERN_WAKE_INTERVAL_MS);
} else {
// Give just a little extra time if they hit one of the first few dots
mCallback.pokeWakelock(UNLOCK_PATTERN_WAKE_INTERVAL_FIRST_DOTS_MS);
}
}
public void onPatternDetected(List<LockPatternView.Cell> pattern) {
if (mLockPatternUtils.checkPattern(pattern)) {
mLockPatternView
.setDisplayMode(LockPatternView.DisplayMode.Correct);
mInstructions = "";
updateStatusLines();
mCallback.keyguardDone(true);
mCallback.reportSuccessfulUnlockAttempt();
} else {
if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
mCallback.pokeWakelock(UNLOCK_PATTERN_WAKE_INTERVAL_MS);
}
mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
if (pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
mTotalFailedPatternAttempts++;
mFailedPatternAttemptsSinceLastTimeout++;
mCallback.reportFailedUnlockAttempt();
}
if (mFailedPatternAttemptsSinceLastTimeout >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) {
long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
handleAttemptLockout(deadline);
} else {
// TODO mUnlockIcon.setVisibility(View.VISIBLE);
mInstructions = getContext().getString(R.string.lockscreen_pattern_wrong);
updateStatusLines();
mLockPatternView.postDelayed(
mCancelPatternRunnable,
PATTERN_CLEAR_TIMEOUT_MS);
}
}
}
}
private void handleAttemptLockout(long elapsedRealtimeDeadline) {
mLockPatternView.clearPattern();
mLockPatternView.setEnabled(false);
long elapsedRealtime = SystemClock.elapsedRealtime();
mCountdownTimer = new CountDownTimer(elapsedRealtimeDeadline - elapsedRealtime, 1000) {
@Override
public void onTick(long millisUntilFinished) {
int secondsRemaining = (int) (millisUntilFinished / 1000);
mInstructions = getContext().getString(
R.string.lockscreen_too_many_failed_attempts_countdown,
secondsRemaining);
updateStatusLines();
}
@Override
public void onFinish() {
mLockPatternView.setEnabled(true);
mInstructions = getContext().getString(R.string.lockscreen_pattern_instructions);
updateStatusLines();
// TODO mUnlockIcon.setVisibility(View.VISIBLE);
mFailedPatternAttemptsSinceLastTimeout = 0;
if (mEnableFallback) {
updateFooter(FooterMode.ForgotLockPattern);
} else {
updateFooter(FooterMode.Normal);
}
}
}.start();
}
public void onPhoneStateChanged(String newState) {
refreshEmergencyButtonText();
}
}
@@ -0,0 +1,73 @@
/*
* Copyright (C) 2006 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 java.util.Map;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.LayoutInflater;
public class PhoneLayoutInflater extends LayoutInflater {
private static final String[] sClassPrefixList = {
"android.widget.",
"android.webkit."
};
/**
* Instead of instantiating directly, you should retrieve an instance
* through {@link Context#getSystemService}
*
* @param context The Context in which in which to find resources and other
* application-specific things.
*
* @see Context#getSystemService
*/
public PhoneLayoutInflater(Context context) {
super(context);
}
protected PhoneLayoutInflater(LayoutInflater original, Context newContext) {
super(original, newContext);
}
/** Override onCreateView to instantiate names that correspond to the
widgets known to the Widget factory. If we don't find a match,
call through to our super class.
*/
@Override protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
for (String prefix : sClassPrefixList) {
try {
View view = createView(name, prefix, attrs);
if (view != null) {
return view;
}
} catch (ClassNotFoundException e) {
// In this case we want to let the base class take a crack
// at it.
}
}
return super.onCreateView(name, attrs);
}
public LayoutInflater cloneInContext(Context newContext) {
return new PhoneLayoutInflater(this, newContext);
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,69 @@
/*
* 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 android.content.Context;
import android.util.Log;
import com.android.internal.policy.IPolicy;
import com.android.internal.policy.impl.PhoneLayoutInflater;
import com.android.internal.policy.impl.PhoneWindow;
import com.android.internal.policy.impl.PhoneWindowManager;
/**
* {@hide}
*/
// Simple implementation of the policy interface that spawns the right
// set of objects
public class Policy implements IPolicy {
private static final String TAG = "PhonePolicy";
private static final String[] preload_classes = {
"com.android.internal.policy.impl.PhoneLayoutInflater",
"com.android.internal.policy.impl.PhoneWindow",
"com.android.internal.policy.impl.PhoneWindow$1",
"com.android.internal.policy.impl.PhoneWindow$ContextMenuCallback",
"com.android.internal.policy.impl.PhoneWindow$DecorView",
"com.android.internal.policy.impl.PhoneWindow$PanelFeatureState",
"com.android.internal.policy.impl.PhoneWindow$PanelFeatureState$SavedState",
};
static {
// For performance reasons, preload some policy specific classes when
// the policy gets loaded.
for (String s : preload_classes) {
try {
Class.forName(s);
} catch (ClassNotFoundException ex) {
Log.e(TAG, "Could not preload class for phone policy: " + s);
}
}
}
public PhoneWindow makeNewWindow(Context context) {
return new PhoneWindow(context);
}
public PhoneLayoutInflater makeNewLayoutInflater(Context context) {
return new PhoneLayoutInflater(context);
}
public PhoneWindowManager makeNewWindowManager() {
return new PhoneWindowManager();
}
}
@@ -0,0 +1,182 @@
/*
* Copyright (C) 2007 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 android.app.Dialog;
import android.app.StatusBarManager;
import android.content.Context;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.LocalPowerManager;
import android.os.ServiceManager;
import android.os.SystemClock;
import com.android.internal.app.ShutdownThread;
import com.android.internal.telephony.ITelephony;
import android.view.KeyEvent;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.view.View.OnClickListener;
import android.view.View.OnKeyListener;
import android.widget.Button;
/**
* @deprecated use {@link GlobalActions} instead.
*/
public class PowerDialog extends Dialog implements OnClickListener,
OnKeyListener {
private static final String TAG = "PowerDialog";
static private StatusBarManager sStatusBar;
private Button mKeyguard;
private Button mPower;
private Button mRadioPower;
private Button mSilent;
private LocalPowerManager mPowerManager;
public PowerDialog(Context context, LocalPowerManager powerManager) {
super(context);
mPowerManager = powerManager;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Context context = getContext();
if (sStatusBar == null) {
sStatusBar = (StatusBarManager)context.getSystemService(Context.STATUS_BAR_SERVICE);
}
setContentView(com.android.internal.R.layout.power_dialog);
getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
if (!getContext().getResources().getBoolean(
com.android.internal.R.bool.config_sf_slowBlur)) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
}
getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
setTitle(context.getText(R.string.power_dialog));
mKeyguard = (Button) findViewById(R.id.keyguard);
mPower = (Button) findViewById(R.id.off);
mRadioPower = (Button) findViewById(R.id.radio_power);
mSilent = (Button) findViewById(R.id.silent);
if (mKeyguard != null) {
mKeyguard.setOnKeyListener(this);
mKeyguard.setOnClickListener(this);
}
if (mPower != null) {
mPower.setOnClickListener(this);
}
if (mRadioPower != null) {
mRadioPower.setOnClickListener(this);
}
if (mSilent != null) {
mSilent.setOnClickListener(this);
// XXX: HACK for now hide the silent until we get mute support
mSilent.setVisibility(View.GONE);
}
CharSequence text;
// set the keyguard button's text
text = context.getText(R.string.screen_lock);
mKeyguard.setText(text);
mKeyguard.requestFocus();
try {
ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
if (phone != null) {
text = phone.isRadioOn() ? context
.getText(R.string.turn_off_radio) : context
.getText(R.string.turn_on_radio);
}
} catch (RemoteException ex) {
// ignore it
}
mRadioPower.setText(text);
}
public void onClick(View v) {
this.dismiss();
if (v == mPower) {
// shutdown by making sure radio and power are handled accordingly.
ShutdownThread.shutdown(getContext(), true);
} else if (v == mRadioPower) {
try {
ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
if (phone != null) {
phone.toggleRadioOnOff();
}
} catch (RemoteException ex) {
// ignore it
}
} else if (v == mSilent) {
// do something
} else if (v == mKeyguard) {
if (v.isInTouchMode()) {
// only in touch mode for the reasons explained in onKey.
this.dismiss();
mPowerManager.goToSleep(SystemClock.uptimeMillis() + 1);
}
}
}
public boolean onKey(View v, int keyCode, KeyEvent event) {
// The activate keyguard button needs to put the device to sleep on the
// key up event. If we try to put it to sleep on the click or down
// action
// the the up action will cause the device to wake back up.
// Log.i(TAG, "keyCode: " + keyCode + " action: " + event.getAction());
if (keyCode != KeyEvent.KEYCODE_DPAD_CENTER
|| event.getAction() != KeyEvent.ACTION_UP) {
// Log.i(TAG, "getting out of dodge...");
return false;
}
// Log.i(TAG, "Clicked mKeyguard! dimissing dialog");
this.dismiss();
// Log.i(TAG, "onKey: turning off the screen...");
// XXX: This is a hack for now
mPowerManager.goToSleep(event.getEventTime() + 1);
return true;
}
public void show() {
super.show();
Log.d(TAG, "show... disabling expand");
sStatusBar.disable(StatusBarManager.DISABLE_EXPAND);
}
public void dismiss() {
super.dismiss();
Log.d(TAG, "dismiss... reenabling expand");
sStatusBar.disable(StatusBarManager.DISABLE_NONE);
}
}
@@ -0,0 +1,152 @@
/*
* Copyright (C) 2010 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 android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.widget.LinearLayout;
/**
* A vertical linear layout. However, instead of drawing the background
* behnd the items, it draws the background outside the items based on the
* padding. If there isn't enough room to draw both, it clips the background
* instead of the contents.
*/
public class RecentApplicationsBackground extends LinearLayout {
private static final String TAG = "RecentApplicationsBackground";
private boolean mBackgroundSizeChanged;
private Drawable mBackground;
private Rect mTmp0 = new Rect();
private Rect mTmp1 = new Rect();
public RecentApplicationsBackground(Context context) {
this(context, null);
init();
}
public RecentApplicationsBackground(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
mBackground = getBackground();
setBackgroundDrawable(null);
setPadding(0, 0, 0, 0);
setGravity(Gravity.CENTER);
}
@Override
protected boolean setFrame(int left, int top, int right, int bottom) {
setWillNotDraw(false);
if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
mBackgroundSizeChanged = true;
}
return super.setFrame(left, top, right, bottom);
}
@Override
protected boolean verifyDrawable(Drawable who) {
return who == mBackground || super.verifyDrawable(who);
}
@Override
protected void drawableStateChanged() {
Drawable d = mBackground;
if (d != null && d.isStateful()) {
d.setState(getDrawableState());
}
super.drawableStateChanged();
}
@Override
public void draw(Canvas canvas) {
final Drawable background = mBackground;
if (background != null) {
if (mBackgroundSizeChanged) {
mBackgroundSizeChanged = false;
Rect chld = mTmp0;
Rect bkg = mTmp1;
mBackground.getPadding(bkg);
getChildBounds(chld);
// This doesn't clamp to this view's bounds, which is what we want,
// so that the drawing is clipped.
final int top = chld.top - bkg.top;
final int bottom = chld.bottom + bkg.bottom;
// The background here is a gradient that wants to
// extend the full width of the screen (whatever that
// may be).
int left, right;
if (false) {
// This limits the width of the drawable.
left = chld.left - bkg.left;
right = chld.right + bkg.right;
} else {
// This expands it to full width.
left = 0;
right = getRight();
}
background.setBounds(left, top, right, bottom);
}
}
mBackground.draw(canvas);
if (false) {
android.graphics.Paint p = new android.graphics.Paint();
p.setColor(0x88ffff00);
canvas.drawRect(background.getBounds(), p);
}
canvas.drawARGB((int)(0.75*0xff), 0, 0, 0);
super.draw(canvas);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mBackground.setCallback(this);
setWillNotDraw(false);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mBackground.setCallback(null);
}
private void getChildBounds(Rect r) {
r.left = r.top = Integer.MAX_VALUE;
r.bottom = r.right = Integer.MIN_VALUE;
final int N = getChildCount();
for (int i=0; i<N; i++) {
View v = getChildAt(i);
if (v.getVisibility() == View.VISIBLE) {
r.left = Math.min(r.left, v.getLeft());
r.top = Math.min(r.top, v.getTop());
r.right = Math.max(r.right, v.getRight());
r.bottom = Math.max(r.bottom, v.getBottom());
}
}
}
}
@@ -0,0 +1,272 @@
/*
* 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 android.app.ActivityManager;
import android.app.Dialog;
import android.app.StatusBarManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.View.OnClickListener;
import android.widget.TextView;
import java.util.List;
public class RecentApplicationsDialog extends Dialog implements OnClickListener {
// Elements for debugging support
// private static final String LOG_TAG = "RecentApplicationsDialog";
private static final boolean DBG_FORCE_EMPTY_LIST = false;
static private StatusBarManager sStatusBar;
private static final int NUM_BUTTONS = 8;
private static final int MAX_RECENT_TASKS = NUM_BUTTONS * 2; // allow for some discards
final TextView[] mIcons = new TextView[NUM_BUTTONS];
View mNoAppsText;
IntentFilter mBroadcastIntentFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
Handler mHandler = new Handler();
Runnable mCleanup = new Runnable() {
public void run() {
// dump extra memory we're hanging on to
for (TextView icon: mIcons) {
icon.setCompoundDrawables(null, null, null, null);
icon.setTag(null);
}
}
};
private int mIconSize;
public RecentApplicationsDialog(Context context) {
super(context, com.android.internal.R.style.Theme_Dialog_RecentApplications);
final Resources resources = context.getResources();
mIconSize = (int) resources.getDimension(android.R.dimen.app_icon_size);
}
/**
* We create the recent applications dialog just once, and it stays around (hidden)
* until activated by the user.
*
* @see PhoneWindowManager#showRecentAppsDialog
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Context context = getContext();
if (sStatusBar == null) {
sStatusBar = (StatusBarManager)context.getSystemService(Context.STATUS_BAR_SERVICE);
}
Window window = getWindow();
window.requestFeature(Window.FEATURE_NO_TITLE);
window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
window.setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
window.setTitle("Recents");
setContentView(com.android.internal.R.layout.recent_apps_dialog);
final WindowManager.LayoutParams params = window.getAttributes();
params.width = WindowManager.LayoutParams.MATCH_PARENT;
params.height = WindowManager.LayoutParams.MATCH_PARENT;
window.setAttributes(params);
window.setFlags(0, WindowManager.LayoutParams.FLAG_DIM_BEHIND);
mIcons[0] = (TextView)findViewById(com.android.internal.R.id.button0);
mIcons[1] = (TextView)findViewById(com.android.internal.R.id.button1);
mIcons[2] = (TextView)findViewById(com.android.internal.R.id.button2);
mIcons[3] = (TextView)findViewById(com.android.internal.R.id.button3);
mIcons[4] = (TextView)findViewById(com.android.internal.R.id.button4);
mIcons[5] = (TextView)findViewById(com.android.internal.R.id.button5);
mIcons[6] = (TextView)findViewById(com.android.internal.R.id.button6);
mIcons[7] = (TextView)findViewById(com.android.internal.R.id.button7);
mNoAppsText = findViewById(com.android.internal.R.id.no_applications_message);
for (TextView b: mIcons) {
b.setOnClickListener(this);
}
}
/**
* Handler for user clicks. If a button was clicked, launch the corresponding activity.
*/
public void onClick(View v) {
for (TextView b: mIcons) {
if (b == v) {
// prepare a launch intent and send it
Intent intent = (Intent)b.getTag();
if (intent != null) {
intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
try {
getContext().startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.w("Recent", "Unable to launch recent task", e);
}
}
break;
}
}
dismiss();
}
/**
* Set up and show the recent activities dialog.
*/
@Override
public void onStart() {
super.onStart();
reloadButtons();
if (sStatusBar != null) {
sStatusBar.disable(StatusBarManager.DISABLE_EXPAND);
}
// receive broadcasts
getContext().registerReceiver(mBroadcastReceiver, mBroadcastIntentFilter);
mHandler.removeCallbacks(mCleanup);
}
/**
* Dismiss the recent activities dialog.
*/
@Override
public void onStop() {
super.onStop();
if (sStatusBar != null) {
sStatusBar.disable(StatusBarManager.DISABLE_NONE);
}
// stop receiving broadcasts
getContext().unregisterReceiver(mBroadcastReceiver);
mHandler.postDelayed(mCleanup, 100);
}
/**
* Reload the 6 buttons with recent activities
*/
private void reloadButtons() {
final Context context = getContext();
final PackageManager pm = context.getPackageManager();
final ActivityManager am = (ActivityManager)
context.getSystemService(Context.ACTIVITY_SERVICE);
final List<ActivityManager.RecentTaskInfo> recentTasks =
am.getRecentTasks(MAX_RECENT_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
ActivityInfo homeInfo =
new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
.resolveActivityInfo(pm, 0);
IconUtilities iconUtilities = new IconUtilities(getContext());
// Performance note: Our android performance guide says to prefer Iterator when
// using a List class, but because we know that getRecentTasks() always returns
// an ArrayList<>, we'll use a simple index instead.
int index = 0;
int numTasks = recentTasks.size();
for (int i = 0; i < numTasks && (index < NUM_BUTTONS); ++i) {
final ActivityManager.RecentTaskInfo info = recentTasks.get(i);
// for debug purposes only, disallow first result to create empty lists
if (DBG_FORCE_EMPTY_LIST && (i == 0)) continue;
Intent intent = new Intent(info.baseIntent);
if (info.origActivity != null) {
intent.setComponent(info.origActivity);
}
// Skip the current home activity.
if (homeInfo != null) {
if (homeInfo.packageName.equals(
intent.getComponent().getPackageName())
&& homeInfo.name.equals(
intent.getComponent().getClassName())) {
continue;
}
}
intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
| Intent.FLAG_ACTIVITY_NEW_TASK);
final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
if (resolveInfo != null) {
final ActivityInfo activityInfo = resolveInfo.activityInfo;
final String title = activityInfo.loadLabel(pm).toString();
Drawable icon = activityInfo.loadIcon(pm);
if (title != null && title.length() > 0 && icon != null) {
final TextView tv = mIcons[index];
tv.setText(title);
icon = iconUtilities.createIconDrawable(icon);
tv.setCompoundDrawables(null, icon, null, null);
tv.setTag(intent);
tv.setVisibility(View.VISIBLE);
tv.setPressed(false);
tv.clearFocus();
++index;
}
}
}
// handle the case of "no icons to show"
mNoAppsText.setVisibility((index == 0) ? View.VISIBLE : View.GONE);
// hide the rest
for (; index < NUM_BUTTONS; ++index) {
mIcons[index].setVisibility(View.GONE);
}
}
/**
* This is the listener for the ACTION_CLOSE_SYSTEM_DIALOGS intent. It's an indication that
* we should close ourselves immediately, in order to allow a higher-priority UI to take over
* (e.g. phone call received).
*/
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
String reason = intent.getStringExtra(PhoneWindowManager.SYSTEM_DIALOG_REASON_KEY);
if (! PhoneWindowManager.SYSTEM_DIALOG_REASON_RECENT_APPS.equals(reason)) {
dismiss();
}
}
}
};
}
@@ -0,0 +1,120 @@
/*
* Copyright (C) 2007 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 android.content.Context;
import android.content.Intent;
import android.database.ContentObserver;
import android.database.Cursor;
import android.os.Handler;
import android.provider.Settings;
import android.util.Log;
import android.util.SparseArray;
import android.view.KeyCharacterMap;
import java.net.URISyntaxException;
/**
* Manages quick launch shortcuts by:
* <li> Keeping the local copy in sync with the database (this is an observer)
* <li> Returning a shortcut-matching intent to clients
*/
class ShortcutManager extends ContentObserver {
private static final String TAG = "ShortcutManager";
private static final int COLUMN_SHORTCUT = 0;
private static final int COLUMN_INTENT = 1;
private static final String[] sProjection = new String[] {
Settings.Bookmarks.SHORTCUT, Settings.Bookmarks.INTENT
};
private Context mContext;
private Cursor mCursor;
/** Map of a shortcut to its intent. */
private SparseArray<Intent> mShortcutIntents;
public ShortcutManager(Context context, Handler handler) {
super(handler);
mContext = context;
mShortcutIntents = new SparseArray<Intent>();
}
/** Observes the provider of shortcut+intents */
public void observe() {
mCursor = mContext.getContentResolver().query(
Settings.Bookmarks.CONTENT_URI, sProjection, null, null, null);
mCursor.registerContentObserver(this);
updateShortcuts();
}
@Override
public void onChange(boolean selfChange) {
updateShortcuts();
}
private void updateShortcuts() {
Cursor c = mCursor;
if (!c.requery()) {
Log.e(TAG, "ShortcutObserver could not re-query shortcuts.");
return;
}
mShortcutIntents.clear();
while (c.moveToNext()) {
int shortcut = c.getInt(COLUMN_SHORTCUT);
if (shortcut == 0) continue;
String intentURI = c.getString(COLUMN_INTENT);
Intent intent = null;
try {
intent = Intent.getIntent(intentURI);
} catch (URISyntaxException e) {
Log.w(TAG, "Intent URI for shortcut invalid.", e);
}
if (intent == null) continue;
mShortcutIntents.put(shortcut, intent);
}
}
/**
* Gets the shortcut intent for a given keycode+modifier. Make sure you
* strip whatever modifier is used for invoking shortcuts (for example,
* if 'Sym+A' should invoke a shortcut on 'A', you should strip the
* 'Sym' bit from the modifiers before calling this method.
* <p>
* This will first try an exact match (with modifiers), and then try a
* match without modifiers (primary character on a key).
*
* @param keyCode The keycode of the key pushed.
* @param modifiers The modifiers without any that are used for chording
* to invoke a shortcut.
* @return The intent that matches the shortcut, or null if not found.
*/
public Intent getIntent(int keyCode, int modifiers) {
KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);
// First try the exact keycode (with modifiers)
int shortcut = kcm.get(keyCode, modifiers);
Intent intent = shortcut != 0 ? mShortcutIntents.get(shortcut) : null;
if (intent != null) return intent;
// Next try the keycode without modifiers (the primary character on that key)
shortcut = Character.toLowerCase(kcm.get(keyCode, 0));
return shortcut != 0 ? mShortcutIntents.get(shortcut) : null;
}
}
@@ -0,0 +1,476 @@
/*
* Copyright (C) 2008 The Android Open Source Project
* Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
*
* 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 android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.res.Configuration;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.telephony.TelephonyManager;
import com.android.internal.telephony.ITelephony;
import com.android.internal.widget.LockPatternUtils;
import android.text.Editable;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.view.ViewGroup;
import android.view.Gravity;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.android.internal.R;
/**
* Displays a dialer like interface to unlock the SIM PIN.
*/
public class SimUnlockScreen extends LinearLayout implements KeyguardScreen, View.OnClickListener,
KeyguardUpdateMonitor.InfoCallback {
private static final int DIGIT_PRESS_WAKE_MILLIS = 5000;
private final KeyguardUpdateMonitor mUpdateMonitor;
private final KeyguardScreenCallback mCallback;
private TextView mHeaderText;
private TextView mPinText;
private TextView mOkButton;
private Button mEmergencyCallButton;
private View mBackSpaceButton;
private Context mContext;
private final int[] mEnteredPin = {0, 0, 0, 0, 0, 0, 0, 0};
private int mEnteredDigits = 0;
private ProgressDialog mSimUnlockProgressDialog = null;
private LockPatternUtils mLockPatternUtils;
private int mCreationOrientation;
private int mKeyboardHidden;
private int mSubscription = 0;
private static final char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
public SimUnlockScreen(Context context, Configuration configuration,
KeyguardUpdateMonitor updateMonitor, KeyguardScreenCallback callback,
LockPatternUtils lockpatternutils, int subscription) {
super(context);
mUpdateMonitor = updateMonitor;
mCallback = callback;
mContext = context;
mCreationOrientation = configuration.orientation;
mKeyboardHidden = configuration.hardKeyboardHidden;
mLockPatternUtils = lockpatternutils;
LayoutInflater inflater = LayoutInflater.from(context);
if (mKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {
inflater.inflate(R.layout.keyguard_screen_sim_pin_landscape, this, true);
} else {
inflater.inflate(R.layout.keyguard_screen_sim_pin_portrait, this, true);
new TouchInput();
}
mHeaderText = (TextView) findViewById(R.id.headerText);
mPinText = (TextView) findViewById(R.id.pinDisplay);
mBackSpaceButton = findViewById(R.id.backspace);
mBackSpaceButton.setOnClickListener(this);
mEmergencyCallButton = (Button) findViewById(R.id.emergencyCall);
mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
mOkButton = (TextView) findViewById(R.id.ok);
mSubscription = subscription;
if (TelephonyManager.isMultiSimEnabled()) {
String displayText = getContext().getString(R.string.keyguard_password_enter_pin_code_for_subscription)
+ (mSubscription + 1);
mHeaderText.setText(displayText);
} else {
mHeaderText.setText(R.string.keyguard_password_enter_pin_code);
}
mPinText.setFocusable(false);
mEmergencyCallButton.setOnClickListener(this);
mOkButton.setOnClickListener(this);
setFocusableInTouchMode(true);
}
/** {@inheritDoc} */
public boolean needsInput() {
return true;
}
/** {@inheritDoc} */
public void onPause() {
}
/** {@inheritDoc} */
public void onResume() {
// start fresh
if (TelephonyManager.isMultiSimEnabled()) {
String displayText = getContext().getString(R.string.keyguard_password_enter_pin_code_for_subscription)
+ (mSubscription + 1);
mHeaderText.setText(displayText);
} else {
mHeaderText.setText(R.string.keyguard_password_enter_pin_code);
}
// make sure that the number of entered digits is consistent when we
// erase the SIM unlock code, including orientation changes.
mPinText.setText("");
mEnteredDigits = 0;
mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
}
/** {@inheritDoc} */
public void cleanUp() {
// hide the dialog.
if (mSimUnlockProgressDialog != null) {
mSimUnlockProgressDialog.hide();
}
mUpdateMonitor.removeCallback(this);
}
/**
* Since the IPC can block, we want to run the request in a separate thread
* with a callback.
*/
private abstract class CheckSimPin extends Thread {
private final String mPin;
protected CheckSimPin(String pin) {
mPin = pin;
}
abstract void onSimLockChangedResponse(boolean success);
@Override
public void run() {
try {
final boolean result = ITelephony.Stub.asInterface(ServiceManager
.checkService("phone")).supplyPinOnSubscription(mPin, mSubscription);
post(new Runnable() {
public void run() {
onSimLockChangedResponse(result);
}
});
} catch (RemoteException e) {
post(new Runnable() {
public void run() {
onSimLockChangedResponse(false);
}
});
}
}
}
public void onClick(View v) {
if (v == mBackSpaceButton) {
final Editable digits = mPinText.getEditableText();
final int len = digits.length();
if (len > 0) {
digits.delete(len-1, len);
mEnteredDigits--;
}
mCallback.pokeWakelock();
} else if (v == mEmergencyCallButton) {
mCallback.takeEmergencyCallAction();
} else if (v == mOkButton) {
checkPin();
}
}
private Dialog getSimUnlockProgressDialog() {
if (mSimUnlockProgressDialog == null) {
mSimUnlockProgressDialog = new ProgressDialog(mContext);
mSimUnlockProgressDialog.setMessage(
mContext.getString(R.string.lockscreen_sim_unlock_progress_dialog_message));
mSimUnlockProgressDialog.setIndeterminate(true);
mSimUnlockProgressDialog.setCancelable(false);
mSimUnlockProgressDialog.getWindow().setType(
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
if (!mContext.getResources().getBoolean(
com.android.internal.R.bool.config_sf_slowBlur)) {
mSimUnlockProgressDialog.getWindow().setFlags(
WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
}
}
return mSimUnlockProgressDialog;
}
private void checkPin() {
// make sure that the pin is at least 4 digits long.
if (mEnteredDigits < 4) {
// otherwise, display a message to the user, and don't submit.
mHeaderText.setText(R.string.invalidPin);
mPinText.setText("");
mEnteredDigits = 0;
mCallback.pokeWakelock();
return;
}
getSimUnlockProgressDialog().show();
new CheckSimPin(mPinText.getText().toString()) {
void onSimLockChangedResponse(boolean success) {
if (mSimUnlockProgressDialog != null) {
mSimUnlockProgressDialog.hide();
}
if (success) {
//Display message to user that the PIN1 entered is accepted.
LayoutInflater inflater = LayoutInflater.from(mContext);
View layout = inflater.inflate(R.layout.transient_notification,
(ViewGroup) findViewById(R.id.toast_layout_root));
TextView text = (TextView) layout.findViewById(R.id.message);
text.setText(R.string.keyguard_pin_accepted);
Toast toast = new Toast(mContext);
toast.setDuration(Toast.LENGTH_LONG);
toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
toast.setView(layout);
toast.show();
// before closing the keyguard, report back that
// the sim is unlocked so it knows right away
mUpdateMonitor.reportSimPinUnlocked(mSubscription);
mCallback.goToUnlockScreen();
} else {
try {
//Displays No. of attempts remaining to unlock PIN1 in case of wrong entry.
int attemptsRemaining = ITelephony.Stub.asInterface(ServiceManager
.checkService("phone")).getIccPin1RetryCountOnSubscription(mSubscription);
if (attemptsRemaining >= 0) {
String displayMessage = getContext().getString(R.string.keyguard_password_wrong_pin_code)
+ getContext().getString(R.string.pinpuk_attempts) + attemptsRemaining;
mHeaderText.setText(displayMessage);
} else {
mHeaderText.setText(R.string.keyguard_password_wrong_pin_code);
}
} catch (RemoteException ex) {
mHeaderText.setText(R.string.keyguard_password_wrong_pin_code);
}
mPinText.setText("");
mEnteredDigits = 0;
}
mCallback.pokeWakelock();
}
}.start();
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
mCallback.goToLockScreen();
return true;
}
final char match = event.getMatch(DIGITS);
if (match != 0) {
reportDigit(match - '0');
return true;
}
if (keyCode == KeyEvent.KEYCODE_DEL) {
if (mEnteredDigits > 0) {
mPinText.onKeyDown(keyCode, event);
mEnteredDigits--;
}
return true;
}
if (keyCode == KeyEvent.KEYCODE_ENTER) {
checkPin();
return true;
}
return false;
}
private void reportDigit(int digit) {
if (mEnteredDigits == 0) {
mPinText.setText("");
}
if (mEnteredDigits == 8) {
return;
}
mPinText.append(Integer.toString(digit));
mEnteredPin[mEnteredDigits++] = digit;
}
void updateConfiguration() {
Configuration newConfig = getResources().getConfiguration();
if (newConfig.orientation != mCreationOrientation) {
mCallback.recreateMe(newConfig);
} else if (newConfig.hardKeyboardHidden != mKeyboardHidden) {
mKeyboardHidden = newConfig.hardKeyboardHidden;
final boolean isKeyboardOpen = mKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO;
if (mUpdateMonitor.isKeyguardBypassEnabled() && isKeyboardOpen) {
mCallback.goToUnlockScreen();
}
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
updateConfiguration();
}
/** {@inheritDoc} */
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
updateConfiguration();
}
/**
* Helper class to handle input from touch dialer. Only relevant when
* the keyboard is shut.
*/
private class TouchInput implements View.OnClickListener {
private TextView mZero;
private TextView mOne;
private TextView mTwo;
private TextView mThree;
private TextView mFour;
private TextView mFive;
private TextView mSix;
private TextView mSeven;
private TextView mEight;
private TextView mNine;
private TextView mCancelButton;
private TouchInput() {
mZero = (TextView) findViewById(R.id.zero);
mOne = (TextView) findViewById(R.id.one);
mTwo = (TextView) findViewById(R.id.two);
mThree = (TextView) findViewById(R.id.three);
mFour = (TextView) findViewById(R.id.four);
mFive = (TextView) findViewById(R.id.five);
mSix = (TextView) findViewById(R.id.six);
mSeven = (TextView) findViewById(R.id.seven);
mEight = (TextView) findViewById(R.id.eight);
mNine = (TextView) findViewById(R.id.nine);
mCancelButton = (TextView) findViewById(R.id.cancel);
mZero.setText("0");
mOne.setText("1");
mTwo.setText("2");
mThree.setText("3");
mFour.setText("4");
mFive.setText("5");
mSix.setText("6");
mSeven.setText("7");
mEight.setText("8");
mNine.setText("9");
mZero.setOnClickListener(this);
mOne.setOnClickListener(this);
mTwo.setOnClickListener(this);
mThree.setOnClickListener(this);
mFour.setOnClickListener(this);
mFive.setOnClickListener(this);
mSix.setOnClickListener(this);
mSeven.setOnClickListener(this);
mEight.setOnClickListener(this);
mNine.setOnClickListener(this);
mCancelButton.setOnClickListener(this);
}
public void onClick(View v) {
if (v == mCancelButton) {
if (TelephonyManager.isMultiSimEnabled()) {
mCallback.updatePinUnlockCancel(mSubscription);
}
mCallback.goToLockScreen();
return;
}
final int digit = checkDigit(v);
if (digit >= 0) {
mCallback.pokeWakelock(DIGIT_PRESS_WAKE_MILLIS);
reportDigit(digit);
}
}
private int checkDigit(View v) {
int digit = -1;
if (v == mZero) {
digit = 0;
} else if (v == mOne) {
digit = 1;
} else if (v == mTwo) {
digit = 2;
} else if (v == mThree) {
digit = 3;
} else if (v == mFour) {
digit = 4;
} else if (v == mFive) {
digit = 5;
} else if (v == mSix) {
digit = 6;
} else if (v == mSeven) {
digit = 7;
} else if (v == mEight) {
digit = 8;
} else if (v == mNine) {
digit = 9;
}
return digit;
}
}
public void onPhoneStateChanged(String newState) {
mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
}
public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) {
}
public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn, int subscription) {
}
public void onRingerModeChanged(int state) {
}
public void onTimeChanged() {
}
}
@@ -0,0 +1,5 @@
<body>
{@hide}
</body>
+29
View File
@@ -0,0 +1,29 @@
# Copyright 2010, 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.
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
# We only want this apk build for tests.
LOCAL_MODULE_TAGS := tests
LOCAL_JAVA_LIBRARIES := android.policy android.test.runner
# Include all test java files.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := FrameworkPolicyTests
include $(BUILD_PACKAGE)
+31
View File
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.frameworks.policy.tests">
<application>
<uses-library android:name="android.test.runner" />
</application>
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<instrumentation
android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.android.frameworks.policy.tests"
android:label="Framework policy tests" />
</manifest>
@@ -0,0 +1,350 @@
/*
* 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 android.content.Context;
import com.android.internal.telephony.IccCard;
import android.content.res.Configuration;
import android.test.AndroidTestCase;
import android.view.View;
import android.view.KeyEvent;
import com.android.internal.widget.LockPatternUtils;
import com.google.android.collect.Lists;
import java.util.List;
/**
* Tests for {@link com.android.internal.policy.impl.LockPatternKeyguardView},
* which handles the management of screens while the keyguard is showing.
*/
public class LockPatternKeyguardViewTest extends AndroidTestCase {
private MockUpdateMonitor mUpdateMonitor;
private LockPatternUtils mLockPatternUtils;
private TestableLockPatternKeyguardView mLPKV;
private MockKeyguardCallback mKeyguardViewCallback;
private static class MockUpdateMonitor extends KeyguardUpdateMonitor {
public IccCard.State simState = IccCard.State.READY;
private MockUpdateMonitor(Context context) {
super(context);
}
@Override
public IccCard.State getSimState() {
return simState;
}
}
private static class MockLockPatternUtils extends LockPatternUtils {
boolean isLockPatternEnabled = true;
public boolean isPermanentlyLocked = false;
public MockLockPatternUtils(Context context) {
super(context);
}
@Override
public boolean isLockPatternEnabled() {
return isLockPatternEnabled;
}
@Override
public void setLockPatternEnabled(boolean lockPatternEnabled) {
isLockPatternEnabled = lockPatternEnabled;
}
@Override
public boolean isPermanentlyLocked() {
return isPermanentlyLocked;
}
public void setPermanentlyLocked(boolean permanentlyLocked) {
isPermanentlyLocked = permanentlyLocked;
}
}
private static class MockKeyguardScreen extends View implements KeyguardScreen {
private int mOnPauseCount = 0;
private int mOnResumeCount = 0;
private int mCleanupCount = 0;
private MockKeyguardScreen(Context context) {
super(context);
setFocusable(true);
}
/** {@inheritDoc} */
public boolean needsInput() {
return false;
}
/** {@inheritDoc} */
public void onPause() {
mOnPauseCount++;
}
/** {@inheritDoc} */
public void onResume() {
mOnResumeCount++;
}
/** {@inheritDoc} */
public void cleanUp() {
mCleanupCount++;
}
public int getOnPauseCount() {
return mOnPauseCount;
}
public int getOnResumeCount() {
return mOnResumeCount;
}
public int getCleanupCount() {
return mCleanupCount;
}
}
/**
* Allows us to inject the lock and unlock views to simulate their behavior
* and detect their creation.
*/
private static class TestableLockPatternKeyguardView extends LockPatternKeyguardView {
private List<MockKeyguardScreen> mInjectedLockScreens;
private List<MockKeyguardScreen> mInjectedUnlockScreens;
private TestableLockPatternKeyguardView(Context context, KeyguardUpdateMonitor updateMonitor,
LockPatternUtils lockPatternUtils, KeyguardWindowController controller) {
super(context, updateMonitor, lockPatternUtils, controller);
}
@Override
View createLockScreen() {
final MockKeyguardScreen newView = new MockKeyguardScreen(getContext());
if (mInjectedLockScreens == null) mInjectedLockScreens = Lists.newArrayList();
mInjectedLockScreens.add(newView);
return newView;
}
@Override
View createUnlockScreenFor(UnlockMode unlockMode) {
final MockKeyguardScreen newView = new MockKeyguardScreen(getContext());
if (mInjectedUnlockScreens == null) mInjectedUnlockScreens = Lists.newArrayList();
mInjectedUnlockScreens.add(newView);
return newView;
}
public List<MockKeyguardScreen> getInjectedLockScreens() {
return mInjectedLockScreens;
}
public List<MockKeyguardScreen> getInjectedUnlockScreens() {
return mInjectedUnlockScreens;
}
}
private static class MockKeyguardCallback implements KeyguardViewCallback {
private int mPokeWakelockCount = 0;
private int mKeyguardDoneCount = 0;
public void pokeWakelock() {
mPokeWakelockCount++;
}
public void pokeWakelock(int millis) {
mPokeWakelockCount++;
}
public void keyguardDone(boolean authenticated) {
mKeyguardDoneCount++;
}
public void keyguardDoneDrawing() {
}
public int getPokeWakelockCount() {
return mPokeWakelockCount;
}
public int getKeyguardDoneCount() {
return mKeyguardDoneCount;
}
}
@Override
protected void setUp() throws Exception {
super.setUp();
mUpdateMonitor = new MockUpdateMonitor(getContext());
mLockPatternUtils = new MockLockPatternUtils(getContext());
mLPKV = new TestableLockPatternKeyguardView(getContext(), mUpdateMonitor,
mLockPatternUtils, new KeyguardWindowController() {
public void setNeedsInput(boolean needsInput) {
}
});
mKeyguardViewCallback = new MockKeyguardCallback();
mLPKV.setCallback(mKeyguardViewCallback);
}
public void testStateAfterCreatedWhileScreenOff() {
assertEquals(1, mLPKV.getInjectedLockScreens().size());
assertEquals(1, mLPKV.getInjectedUnlockScreens().size());
MockKeyguardScreen lockScreen = mLPKV.getInjectedLockScreens().get(0);
MockKeyguardScreen unlockScreen = mLPKV.getInjectedUnlockScreens().get(0);
assertEquals(0, lockScreen.getOnPauseCount());
assertEquals(0, lockScreen.getOnResumeCount());
assertEquals(0, lockScreen.getCleanupCount());
assertEquals(0, unlockScreen.getOnPauseCount());
assertEquals(0, unlockScreen.getOnResumeCount());
assertEquals(0, unlockScreen.getCleanupCount());
assertEquals(0, mKeyguardViewCallback.getPokeWakelockCount());
assertEquals(0, mKeyguardViewCallback.getKeyguardDoneCount());
}
public void testWokenByNonMenuKey() {
mLPKV.wakeWhenReadyTq(0);
// should have poked the wakelock to turn on the screen
assertEquals(1, mKeyguardViewCallback.getPokeWakelockCount());
// shouldn't be any additional views created
assertEquals(1, mLPKV.getInjectedLockScreens().size());
assertEquals(1, mLPKV.getInjectedUnlockScreens().size());
MockKeyguardScreen lockScreen = mLPKV.getInjectedLockScreens().get(0);
MockKeyguardScreen unlockScreen = mLPKV.getInjectedUnlockScreens().get(0);
// lock screen should be only visible one
assertEquals(View.VISIBLE, lockScreen.getVisibility());
assertEquals(View.GONE, unlockScreen.getVisibility());
// on resume not called until screen turns on
assertEquals(0, lockScreen.getOnPauseCount());
assertEquals(0, lockScreen.getOnResumeCount());
assertEquals(0, lockScreen.getCleanupCount());
assertEquals(0, unlockScreen.getOnPauseCount());
assertEquals(0, unlockScreen.getOnResumeCount());
assertEquals(0, unlockScreen.getCleanupCount());
// simulate screen turning on
mLPKV.onScreenTurnedOn();
assertEquals(0, lockScreen.getOnPauseCount());
assertEquals(1, lockScreen.getOnResumeCount());
assertEquals(0, lockScreen.getCleanupCount());
assertEquals(0, unlockScreen.getOnPauseCount());
assertEquals(0, unlockScreen.getOnResumeCount());
assertEquals(0, unlockScreen.getCleanupCount());
}
public void testWokenByMenuKeyWhenPatternSet() {
assertEquals(true, mLockPatternUtils.isLockPatternEnabled());
mLPKV.wakeWhenReadyTq(KeyEvent.KEYCODE_MENU);
// should have poked the wakelock to turn on the screen
assertEquals(1, mKeyguardViewCallback.getPokeWakelockCount());
// shouldn't be any additional views created
assertEquals(1, mLPKV.getInjectedLockScreens().size());
assertEquals(1, mLPKV.getInjectedUnlockScreens().size());
MockKeyguardScreen lockScreen = mLPKV.getInjectedLockScreens().get(0);
MockKeyguardScreen unlockScreen = mLPKV.getInjectedUnlockScreens().get(0);
// unlock screen should be only visible one
assertEquals(View.GONE, lockScreen.getVisibility());
assertEquals(View.VISIBLE, unlockScreen.getVisibility());
}
public void testScreenRequestsRecreation() {
mLPKV.wakeWhenReadyTq(0);
mLPKV.onScreenTurnedOn();
assertEquals(1, mLPKV.getInjectedLockScreens().size());
assertEquals(1, mLPKV.getInjectedUnlockScreens().size());
MockKeyguardScreen lockScreen = mLPKV.getInjectedLockScreens().get(0);
assertEquals(0, lockScreen.getOnPauseCount());
assertEquals(1, lockScreen.getOnResumeCount());
// simulate screen asking to be recreated
mLPKV.mKeyguardScreenCallback.recreateMe(new Configuration());
// should have been recreated
assertEquals(2, mLPKV.getInjectedLockScreens().size());
assertEquals(2, mLPKV.getInjectedUnlockScreens().size());
// both old screens should have been cleaned up
assertEquals(1, mLPKV.getInjectedLockScreens().get(0).getCleanupCount());
assertEquals(1, mLPKV.getInjectedUnlockScreens().get(0).getCleanupCount());
// old lock screen should have been paused
assertEquals(1, mLPKV.getInjectedLockScreens().get(0).getOnPauseCount());
assertEquals(0, mLPKV.getInjectedUnlockScreens().get(0).getOnPauseCount());
// new lock screen should have been resumed
assertEquals(1, mLPKV.getInjectedLockScreens().get(1).getOnResumeCount());
assertEquals(0, mLPKV.getInjectedUnlockScreens().get(1).getOnResumeCount());
}
public void testMenuDoesntGoToUnlockScreenOnWakeWhenPukLocked() {
// PUK locked
mUpdateMonitor.simState = IccCard.State.PUK_REQUIRED;
// wake by menu
mLPKV.wakeWhenReadyTq(KeyEvent.KEYCODE_MENU);
assertEquals(1, mLPKV.getInjectedLockScreens().size());
assertEquals(1, mLPKV.getInjectedUnlockScreens().size());
MockKeyguardScreen lockScreen = mLPKV.getInjectedLockScreens().get(0);
MockKeyguardScreen unlockScreen = mLPKV.getInjectedUnlockScreens().get(0);
// lock screen should be only visible one
assertEquals(View.VISIBLE, lockScreen.getVisibility());
assertEquals(View.GONE, unlockScreen.getVisibility());
}
public void testMenuGoesToLockScreenWhenDeviceNotSecure() {
mLockPatternUtils.setLockPatternEnabled(false);
// wake by menu
mLPKV.wakeWhenReadyTq(KeyEvent.KEYCODE_MENU);
assertEquals(1, mLPKV.getInjectedLockScreens().size());
assertEquals(1, mLPKV.getInjectedUnlockScreens().size());
MockKeyguardScreen lockScreen = mLPKV.getInjectedLockScreens().get(0);
MockKeyguardScreen unlockScreen = mLPKV.getInjectedUnlockScreens().get(0);
// lock screen should be only visible one
assertEquals(View.VISIBLE, lockScreen.getVisibility());
assertEquals(View.GONE, unlockScreen.getVisibility());
}
}