diff options
author | Jim Miller <jaggies@google.com> | 2012-08-30 16:50:10 -0700 |
---|---|---|
committer | Jim Miller <jaggies@google.com> | 2012-08-30 21:40:44 -0700 |
commit | 258341c377b6aa9f1bd29a9b507a97967e432dfe (patch) | |
tree | 13e5c7d833147082acc60450e24cf8afc375bfb1 /policy | |
parent | e217ee4d7a8223289a1af7363627c69956c46d41 (diff) | |
download | frameworks_base-258341c377b6aa9f1bd29a9b507a97967e432dfe.zip frameworks_base-258341c377b6aa9f1bd29a9b507a97967e432dfe.tar.gz frameworks_base-258341c377b6aa9f1bd29a9b507a97967e432dfe.tar.bz2 |
Lots of keyguard improvements
- Fix "too many attempts" dialogs
- Fix account unlock mechanism so the user can use email account as backup for pattern unlock
- Add mechanism to support future account recovery from non-pattern screen
- Tune animation timing for flipping security view.
- Move password field to the top of the security view
- Add padding and visual feedback to navigation area button
Fixes bugs 7088482, 7088631
Change-Id: I23099feae3b7446ec291d8f860601bfc12f9edd8
Diffstat (limited to 'policy')
8 files changed, 243 insertions, 56 deletions
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java index d74a5e7..2551c04 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java @@ -16,8 +16,11 @@ package com.android.internal.policy.impl.keyguard; +import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityOptions; +import android.app.AlertDialog; +import android.app.admin.DevicePolicyManager; import android.appwidget.AppWidgetHost; import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetManager; @@ -31,9 +34,10 @@ import android.graphics.Canvas; import android.telephony.TelephonyManager; import android.util.AttributeSet; import android.util.Log; +import android.util.Slog; import android.view.KeyEvent; import android.view.View; -import android.view.ViewGroup; +import android.view.WindowManager; import android.view.animation.AnimationUtils; import android.widget.Button; import android.widget.ViewFlipper; @@ -74,8 +78,8 @@ public class KeyguardHostView extends KeyguardViewBase { private ViewFlipper mViewFlipper; private Button mEmergencyDialerButton; private boolean mEnableMenuKey; - private boolean mScreenOn; private boolean mIsVerifyUnlockOnly; + private boolean mEnableFallback; // TODO: This should get the value from KeyguardPatternView private int mCurrentSecurityId = SECURITY_SELECTOR_ID; // KeyguardSecurityViews @@ -116,7 +120,7 @@ public class KeyguardHostView extends KeyguardViewBase { @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); - mCallback.keyguardDoneDrawing(); + mViewMediatorCallback.keyguardDoneDrawing(); } @Override @@ -196,29 +200,25 @@ public class KeyguardHostView extends KeyguardViewBase { } public boolean isVerifyUnlockOnly() { - // TODO - return false; + return mIsVerifyUnlockOnly; } public void reportSuccessfulUnlockAttempt() { - KeyguardUpdateMonitor.getInstance(mContext).clearFailedAttempts(); + KeyguardUpdateMonitor.getInstance(mContext).clearFailedUnlockAttempts(); } public void reportFailedUnlockAttempt() { // TODO: handle biometric attempt differently. - KeyguardUpdateMonitor.getInstance(mContext).reportFailedAttempt(); + KeyguardHostView.this.reportFailedUnlockAttempt(); } public int getFailedAttempts() { - return KeyguardUpdateMonitor.getInstance(mContext).getFailedAttempts(); + return KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(); } - public void showBackupUnlock() { - // TODO - } - - public void keyguardDoneDrawing() { - mViewMediatorCallback.keyguardDoneDrawing(); + @Override + public void showBackupSecurity() { + KeyguardHostView.this.showBackupSecurity(); } @Override @@ -228,6 +228,9 @@ public class KeyguardHostView extends KeyguardViewBase { }; + /** + * Shows the emergency dialer or returns the user to the existing call. + */ public void takeEmergencyCallAction() { mCallback.userActivity(EMERGENCY_CALL_TIMEOUT); if (TelephonyManager.getDefault().getCallState() @@ -241,19 +244,147 @@ public class KeyguardHostView extends KeyguardViewBase { } } - protected void showNextSecurityScreenOrFinish(boolean authenticated) { + private void showDialog(String title, String message) { + final AlertDialog dialog = new AlertDialog.Builder(mContext) + .setTitle(title) + .setMessage(message) + .setNeutralButton(com.android.internal.R.string.ok, null) + .create(); + if (!(mContext instanceof Activity)) { + dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); + } + dialog.show(); + } + + private void showTimeoutDialog() { + int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; + int messageId = 0; + + switch (mSecurityModel.getSecurityMode()) { + case Pattern: + messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message; + break; + + case Password: { + final boolean isPin = mLockPatternUtils.getKeyguardStoredPasswordQuality() == + DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; + messageId = isPin ? R.string.kg_too_many_failed_pin_attempts_dialog_message + : R.string.kg_too_many_failed_password_attempts_dialog_message; + } + break; + } + + if (messageId != 0) { + final String message = mContext.getString(messageId, + KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(), + timeoutInSeconds); + showDialog(null, message); + } + } + + private void showAlmostAtWipeDialog(int attempts, int remaining) { + int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; + String message = mContext.getString(R.string.kg_failed_attempts_almost_at_wipe, + attempts, remaining); + showDialog(null, message); + } + + private void showWipeDialog(int attempts) { + String message = mContext.getString(R.string.kg_failed_attempts_now_wiping, attempts); + showDialog(null, message); + } + + private void showAlmostAtAccountLoginDialog() { + final int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; + final int count = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET + - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT; + String message = mContext.getString(R.string.kg_failed_attempts_almost_at_login, + count, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, timeoutInSeconds); + showDialog(null, message); + } + + private void reportFailedUnlockAttempt() { + final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext); + final int failedAttempts = monitor.getFailedUnlockAttempts() + 1; // +1 for this time + + if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts); + + SecurityMode mode = mSecurityModel.getSecurityMode(); + final boolean usingPattern = mode == KeyguardSecurityModel.SecurityMode.Pattern; + + final int failedAttemptsBeforeWipe = mLockPatternUtils.getDevicePolicyManager() + .getMaximumFailedPasswordsForWipe(null); + + final int failedAttemptWarning = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET + - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT; + + final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ? + (failedAttemptsBeforeWipe - failedAttempts) + : Integer.MAX_VALUE; // because DPM returns 0 if no restriction + + if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) { + // If we reach this code, it means the user has installed a DevicePolicyManager + // that requests device wipe after N attempts. Once we get below the grace + // period, we'll post this dialog every time as a clear warning until the + // bombshell hits and the device is wiped. + if (remainingBeforeWipe > 0) { + showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe); + } else { + // Too many attempts. The device will be wiped shortly. + Slog.i(TAG, "Too many unlock attempts; device will be wiped!"); + showWipeDialog(failedAttempts); + } + } else { + boolean showTimeout = + (failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0; + if (usingPattern && mEnableFallback) { + if (failedAttempts == failedAttemptWarning) { + showAlmostAtAccountLoginDialog(); + showTimeout = false; // don't show both dialogs + } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) { + mLockPatternUtils.setPermanentlyLocked(true); + showSecurityScreen(SECURITY_ACCOUNT_ID); + // don't show timeout dialog because we show account unlock screen next + showTimeout = false; + } + } + if (showTimeout) { + showTimeoutDialog(); + } + } + monitor.reportFailedUnlockAttempt(); + mLockPatternUtils.reportFailedPasswordAttempt(); + } + + /** + * Shows the backup security screen for the current security mode. This could be used for + * password recovery screens but is currently only used for pattern unlock to show the + * account unlock screen and biometric unlock to show the user's normal unlock. + */ + private void showBackupSecurity() { + SecurityMode currentMode = mSecurityModel.getSecurityMode(); + SecurityMode backup = mSecurityModel.getBackupFor(currentMode); + showSecurityScreen(getSecurityViewIdForMode(backup)); + } + + private void showNextSecurityScreenOrFinish(boolean authenticated) { boolean finish = false; if (SECURITY_SELECTOR_ID == mCurrentSecurityId) { - int realSecurityId = getSecurityViewIdForMode(mSecurityModel.getSecurityMode()); - if (realSecurityId == mCurrentSecurityId) { + SecurityMode securityMode = mSecurityModel.getSecurityMode(); + // Allow an alternate, such as biometric unlock + // TODO: un-comment when face unlock is working again: + // securityMode = mSecurityModel.getAlternateFor(securityMode); + int realSecurityId = getSecurityViewIdForMode(securityMode); + if (SECURITY_SELECTOR_ID == realSecurityId) { finish = true; // no security required } else { showSecurityScreen(realSecurityId); // switch to the "real" security view } } else if (authenticated) { - if ((mCurrentSecurityId == SECURITY_PATTERN_ID + if (mCurrentSecurityId == SECURITY_PATTERN_ID || mCurrentSecurityId == SECURITY_PASSWORD_ID - || mCurrentSecurityId == SECURITY_ACCOUNT_ID)) { + || mCurrentSecurityId == SECURITY_ACCOUNT_ID + || mCurrentSecurityId == SECURITY_BIOMETRIC_ID) { finish = true; } } else { @@ -309,6 +440,7 @@ public class KeyguardHostView extends KeyguardViewBase { @Override public void reset() { + mIsVerifyUnlockOnly = false; requestFocus(); } @@ -330,6 +462,12 @@ public class KeyguardHostView extends KeyguardViewBase { return null; } + /** + * Switches to the given security view unless it's already being shown, in which case + * this is a no-op. + * + * @param securityViewId + */ private void showSecurityScreen(int securityViewId) { if (securityViewId == mCurrentSecurityId) return; @@ -352,19 +490,22 @@ public class KeyguardHostView extends KeyguardViewBase { break; } } + + // Discard current runnable if we're switching back to the selector view + if (securityViewId == SECURITY_SELECTOR_ID) { + setOnDismissRunnable(null); + } } @Override public void onScreenTurnedOn() { if (DEBUG) Log.d(TAG, "screen on"); - mScreenOn = true; showSecurityScreen(mCurrentSecurityId); } @Override public void onScreenTurnedOff() { if (DEBUG) Log.d(TAG, "screen off"); - mScreenOn = false; showSecurityScreen(SECURITY_SELECTOR_ID); } @@ -469,7 +610,8 @@ public class KeyguardHostView extends KeyguardViewBase { private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key"; private boolean shouldEnableMenuKey() { final Resources res = getResources(); - final boolean configDisabled = res.getBoolean(R.bool.config_disableMenuKeyInLockScreen); + final boolean configDisabled = res.getBoolean( + com.android.internal.R.bool.config_disableMenuKeyInLockScreen); final boolean isTestHarness = ActivityManager.isRunningInTestHarness(); final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists(); return !configDisabled || isTestHarness || fileOverride; diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardNavigationManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardNavigationManager.java index d3feced..4f29825 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardNavigationManager.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardNavigationManager.java @@ -25,12 +25,15 @@ public class KeyguardNavigationManager { private TextView mMessageArea; private KeyguardSecurityView mKeyguardSecurityView; + private View mClickArea; public KeyguardNavigationManager(KeyguardSecurityView view) { mKeyguardSecurityView = view; - mMessageArea = (TextView) ((View) view).findViewById(R.id.message_area); + mMessageArea = (TextView) ((View) view).findViewById(R.id.keyguard_message_area); mMessageArea.setSelected(true); // Make marquee work - mMessageArea.setOnClickListener(new View.OnClickListener() { + + mClickArea = ((View) view).findViewById(R.id.keyguard_click_area); + mClickArea.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { mKeyguardSecurityView.getCallback().dismiss(false); } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPatternView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPatternView.java index a95cfcb..3a32b5b 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPatternView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPatternView.java @@ -129,7 +129,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit mForgotPatternButton.setText(R.string.kg_forgot_pattern_button_text); mForgotPatternButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { - mCallback.showBackupUnlock(); + mCallback.showBackupSecurity(); } }); @@ -233,6 +233,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct); mCallback.dismiss(true); // keyguardDone(true) KeyStore.getInstance().password(LockPatternUtils.patternToString(pattern)); + mTotalFailedPatternAttempts = 0; } else { if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) { mCallback.userActivity(UNLOCK_PATTERN_WAKE_INTERVAL_MS); @@ -313,13 +314,15 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit mLockPatternView.clearPattern(); mLockPatternView.setEnabled(false); final long elapsedRealtime = SystemClock.elapsedRealtime(); + updateFooter(FooterMode.ForgotLockPattern); + mCountdownTimer = new CountDownTimer(elapsedRealtimeDeadline - elapsedRealtime, 1000) { @Override public void onTick(long millisUntilFinished) { final int secondsRemaining = (int) (millisUntilFinished / 1000); mNavigationManager.setMessage( - R.string.kg_too_many_failed_attempts_countdown,secondsRemaining); + R.string.kg_too_many_failed_attempts_countdown, secondsRemaining); } @Override diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityCallback.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityCallback.java index 36342a5..3b8df5d 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityCallback.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityCallback.java @@ -53,17 +53,12 @@ public interface KeyguardSecurityCallback { int getFailedAttempts(); /** - * Shows the backup unlock for the current method. If none available, this call is a NOP. + * Shows the backup security for the current method. If none available, this call is a no-op. */ - void showBackupUnlock(); + void showBackupSecurity(); /** - * Used to inform keyguard when the current view is done drawing. - */ - void keyguardDoneDrawing(); - - /** - * Sets a runnable to launch after the user enters their + * Sets a runnable to launch after the user successfully enters their credentials. * @param runnable */ void setOnDismissRunnable(Runnable runnable); diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java index d041dd3..a19b35d 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java @@ -48,38 +48,85 @@ public class KeyguardSecurityModel { mLockPatternUtils = utils; } + /** + * This returns false if there is any condition that indicates that the biometric unlock should + * not be used before the next time the unlock screen is recreated. In other words, if this + * returns false there is no need to even construct the biometric unlock. + */ + private boolean isBiometricUnlockEnabled() { + KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext); + final boolean backupIsTimedOut = + monitor.getFailedUnlockAttempts() >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT; + return mLockPatternUtils.usingBiometricWeak() + && mLockPatternUtils.isBiometricWeakInstalled() + && !monitor.getMaxBiometricUnlockAttemptsReached() + && !backupIsTimedOut; + } + SecurityMode getSecurityMode() { KeyguardUpdateMonitor mUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext); final IccCardConstants.State simState = mUpdateMonitor.getSimState(); + SecurityMode mode = SecurityMode.None; if (simState == IccCardConstants.State.PIN_REQUIRED) { - return SecurityMode.SimPin; + mode = SecurityMode.SimPin; } else if (simState == IccCardConstants.State.PUK_REQUIRED) { - return SecurityMode.SimPuk; + mode = SecurityMode.SimPuk; } else { - final int mode = mLockPatternUtils.getKeyguardStoredPasswordQuality(); - switch (mode) { + final int security = mLockPatternUtils.getKeyguardStoredPasswordQuality(); + switch (security) { case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX: - return mLockPatternUtils.isLockPasswordEnabled() ? + mode = mLockPatternUtils.isLockPasswordEnabled() ? SecurityMode.Password : SecurityMode.None; + break; case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED: if (mLockPatternUtils.isLockPatternEnabled()) { - return mLockPatternUtils.isPermanentlyLocked() ? + mode = mLockPatternUtils.isPermanentlyLocked() ? SecurityMode.Account : SecurityMode.Pattern; - } else { - return SecurityMode.None; } + break; + default: - throw new IllegalStateException("Unknown unlock mode:" + mode); + throw new IllegalStateException("Unknown unlock mode:" + mode); } } + return mode; } + /** + * Some unlock methods can have an alternate, such as biometric unlocks (e.g. face unlock). + * This function decides if an alternate unlock is available and returns it. Otherwise, + * returns @param mode. + * + * @param mode the mode we want the alternate for + * @return alternate or the given mode + */ + SecurityMode getAlternateFor(SecurityMode mode) { + if (isBiometricUnlockEnabled() + && (mode == SecurityMode.Password || mode == SecurityMode.Pattern)) { + return SecurityMode.Biometric; + } + return mode; // no alternate, return what was given + } + + /** + * Some unlock methods can have a backup which gives the user another way to get into + * the device. This is currently only supported for Biometric and Pattern unlock. + * + * @param mode the mode we want the backup for + * @return backup method or given mode + */ SecurityMode getBackupFor(SecurityMode mode) { - return SecurityMode.None; // TODO: handle biometric unlock, etc. + switch(mode) { + case Biometric: + return getSecurityMode(); + case Pattern: + return SecurityMode.Account; + } + return mode; // no backup, return what was given } } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java index dad0dff..39df5a2 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java @@ -53,8 +53,8 @@ import java.util.ArrayList; * * 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'... + * the device, and {@link #getFailedUnlockAttempts()}, {@link #reportFailedAttempt()} + * and {@link #clearFailedUnlockAttempts()}. Maybe we should rename this 'KeyguardContext'... */ public class KeyguardUpdateMonitor { @@ -658,16 +658,16 @@ public class KeyguardUpdateMonitor { return mDeviceProvisioned; } - public int getFailedAttempts() { + public int getFailedUnlockAttempts() { return mFailedAttempts; } - public void clearFailedAttempts() { + public void clearFailedUnlockAttempts() { mFailedAttempts = 0; mFailedBiometricUnlockAttempts = 0; } - public void reportFailedAttempt() { + public void reportFailedUnlockAttempt() { mFailedAttempts++; } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java index d6733ea..3ee82f7 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java @@ -986,7 +986,7 @@ public class KeyguardViewMediator { mHandler.sendMessage(msg); if (authenticated) { - mUpdateMonitor.clearFailedAttempts(); + mUpdateMonitor.clearFailedUnlockAttempts(); } if (mExitSecureCallback != null) { diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java index d778129..e8078fd 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java @@ -53,12 +53,9 @@ public class KeyguardWidgetFrame extends FrameLayout { super(context, attrs, defStyle); if (sLeftOverscrollDrawable == null) { Resources res = context.getResources(); - sLeftOverscrollDrawable = res.getDrawable( - com.android.internal.R.drawable.kg_widget_overscroll_layer_left); - sRightOverscrollDrawable = res.getDrawable( - com.android.internal.R.drawable.kg_widget_overscroll_layer_right); - sWidgetPagePadding = - res.getDimensionPixelSize(com.android.internal.R.dimen.kg_widget_page_padding); + sLeftOverscrollDrawable = res.getDrawable(R.drawable.kg_widget_overscroll_layer_left); + sRightOverscrollDrawable = res.getDrawable(R.drawable.kg_widget_overscroll_layer_right); + sWidgetPagePadding = res.getDimensionPixelSize(R.dimen.kg_widget_page_padding); } setPadding(sWidgetPagePadding, sWidgetPagePadding, sWidgetPagePadding, sWidgetPagePadding); } |