1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.policy.impl; 18 19 import com.android.internal.R; 20 import com.android.internal.telephony.IccCard; 21 import com.android.internal.widget.LockPatternUtils; 22 23 import android.accounts.Account; 24 import android.accounts.AccountManager; 25 import android.accounts.AccountManagerCallback; 26 import android.accounts.AccountManagerFuture; 27 import android.accounts.AuthenticatorException; 28 import android.accounts.OperationCanceledException; 29 import android.app.AlertDialog; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.graphics.Bitmap; 33 import android.graphics.Canvas; 34 import android.graphics.ColorFilter; 35 import android.graphics.PixelFormat; 36 import android.graphics.drawable.BitmapDrawable; 37 import android.graphics.drawable.Drawable; 38 import android.os.SystemProperties; 39 import android.text.TextUtils; 40 import android.util.Log; 41 import android.view.KeyEvent; 42 import android.view.View; 43 import android.view.WindowManager; 44 45 import java.io.IOException; 46 47 /** 48 * The host view for all of the screens of the pattern unlock screen. There are 49 * two {@link Mode}s of operation, lock and unlock. This will show the appropriate 50 * screen, and listen for callbacks via 51 * {@link com.android.internal.policy.impl.KeyguardScreenCallback} 52 * from the current screen. 53 * 54 * This view, in turn, communicates back to 55 * {@link com.android.internal.policy.impl.KeyguardViewManager} 56 * via its {@link com.android.internal.policy.impl.KeyguardViewCallback}, as appropriate. 57 */ 58 public class LockPatternKeyguardView extends KeyguardViewBase 59 implements AccountManagerCallback<Account[]> { 60 61 // intent action for launching emergency dialer activity. 62 static final String ACTION_EMERGENCY_DIAL = "com.android.phone.EmergencyDialer.DIAL"; 63 64 private static final boolean DEBUG = false; 65 private static final String TAG = "LockPatternKeyguardView"; 66 67 private final KeyguardUpdateMonitor mUpdateMonitor; 68 private final KeyguardWindowController mWindowController; 69 70 private View mLockScreen; 71 private View mUnlockScreen; 72 73 private boolean mScreenOn = false; 74 private boolean mEnableFallback = false; // assume no fallback UI until we know better 75 76 77 /** 78 * The current {@link KeyguardScreen} will use this to communicate back to us. 79 */ 80 KeyguardScreenCallback mKeyguardScreenCallback; 81 82 83 private boolean mRequiresSim; 84 85 86 /** 87 * Either a lock screen (an informational keyguard screen), or an unlock 88 * screen (a means for unlocking the device) is shown at any given time. 89 */ 90 enum Mode { 91 LockScreen, 92 UnlockScreen 93 } 94 95 /** 96 * The different types screens available for {@link Mode#UnlockScreen}. 97 * @see com.android.internal.policy.impl.LockPatternKeyguardView#getUnlockMode() 98 */ 99 enum UnlockMode { 100 101 /** 102 * Unlock by drawing a pattern. 103 */ 104 Pattern, 105 106 /** 107 * Unlock by entering a sim pin. 108 */ 109 SimPin, 110 111 /** 112 * Unlock by entering an account's login and password. 113 */ 114 Account 115 } 116 117 /** 118 * The current mode. 119 */ 120 private Mode mMode = Mode.LockScreen; 121 122 /** 123 * Keeps track of what mode the current unlock screen is (cached from most recent computation in 124 * {@link #getUnlockMode}). 125 */ 126 private UnlockMode mUnlockScreenMode; 127 128 private boolean mForgotPattern; 129 130 /** 131 * If true, it means we are in the process of verifying that the user 132 * can get past the lock screen per {@link #verifyUnlock()} 133 */ 134 private boolean mIsVerifyUnlockOnly = false; 135 136 137 /** 138 * Used to lookup the state of the lock pattern 139 */ 140 private final LockPatternUtils mLockPatternUtils; 141 142 /** 143 * @return Whether we are stuck on the lock screen because the sim is 144 * missing. 145 */ stuckOnLockScreenBecauseSimMissing()146 private boolean stuckOnLockScreenBecauseSimMissing() { 147 return mRequiresSim 148 && (!mUpdateMonitor.isDeviceProvisioned()) 149 && (mUpdateMonitor.getSimState() == IccCard.State.ABSENT); 150 } 151 run(AccountManagerFuture<Account[]> future)152 public void run(AccountManagerFuture<Account[]> future) { 153 // We err on the side of caution. 154 // In case of error we assume we have a SAML account. 155 boolean hasSAMLAccount = true; 156 try { 157 hasSAMLAccount = future.getResult().length > 0; 158 } catch (OperationCanceledException e) { 159 } catch (IOException e) { 160 } catch (AuthenticatorException e) { 161 } 162 mEnableFallback = !hasSAMLAccount; 163 164 if (mUnlockScreen == null) { 165 Log.w(TAG, "no unlock screen when receiving AccountManager information"); 166 } else if (mUnlockScreen instanceof UnlockScreen) { 167 ((UnlockScreen)mUnlockScreen).setEnableFallback(true); 168 } 169 } 170 171 /** 172 * @param context Used to inflate, and create views. 173 * @param updateMonitor Knows the state of the world, and passed along to each 174 * screen so they can use the knowledge, and also register for callbacks 175 * on dynamic information. 176 * @param lockPatternUtils Used to look up state of lock pattern. 177 */ LockPatternKeyguardView( Context context, KeyguardUpdateMonitor updateMonitor, LockPatternUtils lockPatternUtils, KeyguardWindowController controller)178 public LockPatternKeyguardView( 179 Context context, 180 KeyguardUpdateMonitor updateMonitor, 181 LockPatternUtils lockPatternUtils, 182 KeyguardWindowController controller) { 183 super(context); 184 185 mEnableFallback = false; 186 187 mRequiresSim = 188 TextUtils.isEmpty(SystemProperties.get("keyguard.no_require_sim")); 189 190 mUpdateMonitor = updateMonitor; 191 mLockPatternUtils = lockPatternUtils; 192 mWindowController = controller; 193 194 mMode = getInitialMode(); 195 196 mKeyguardScreenCallback = new KeyguardScreenCallback() { 197 198 public void goToLockScreen() { 199 mForgotPattern = false; 200 if (mIsVerifyUnlockOnly) { 201 // navigating away from unlock screen during verify mode means 202 // we are done and the user failed to authenticate. 203 mIsVerifyUnlockOnly = false; 204 getCallback().keyguardDone(false); 205 } else { 206 updateScreen(Mode.LockScreen); 207 } 208 } 209 210 public void goToUnlockScreen() { 211 final IccCard.State simState = mUpdateMonitor.getSimState(); 212 if (stuckOnLockScreenBecauseSimMissing() 213 || (simState == IccCard.State.PUK_REQUIRED)){ 214 // stuck on lock screen when sim missing or puk'd 215 return; 216 } 217 if (!isSecure()) { 218 getCallback().keyguardDone(true); 219 } else { 220 updateScreen(Mode.UnlockScreen); 221 } 222 } 223 224 public void forgotPattern(boolean isForgotten) { 225 if (mEnableFallback) { 226 mForgotPattern = isForgotten; 227 updateScreen(Mode.UnlockScreen); 228 } 229 } 230 231 public boolean isSecure() { 232 return LockPatternKeyguardView.this.isSecure(); 233 } 234 235 public boolean isVerifyUnlockOnly() { 236 return mIsVerifyUnlockOnly; 237 } 238 239 public void recreateMe() { 240 recreateScreens(); 241 } 242 243 public void takeEmergencyCallAction() { 244 Intent intent = new Intent(ACTION_EMERGENCY_DIAL); 245 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 246 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 247 getContext().startActivity(intent); 248 } 249 250 public void pokeWakelock() { 251 getCallback().pokeWakelock(); 252 } 253 254 public void pokeWakelock(int millis) { 255 getCallback().pokeWakelock(millis); 256 } 257 258 public void keyguardDone(boolean authenticated) { 259 getCallback().keyguardDone(authenticated); 260 } 261 262 public void keyguardDoneDrawing() { 263 // irrelevant to keyguard screen, they shouldn't be calling this 264 } 265 266 public void reportFailedPatternAttempt() { 267 mUpdateMonitor.reportFailedAttempt(); 268 final int failedAttempts = mUpdateMonitor.getFailedAttempts(); 269 if (DEBUG) Log.d(TAG, 270 "reportFailedPatternAttempt: #" + failedAttempts + 271 " (enableFallback=" + mEnableFallback + ")"); 272 if (mEnableFallback && failedAttempts == 273 (LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET 274 - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) { 275 showAlmostAtAccountLoginDialog(); 276 } else if (mEnableFallback 277 && failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) { 278 mLockPatternUtils.setPermanentlyLocked(true); 279 updateScreen(mMode); 280 } else if ((failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) 281 == 0) { 282 showTimeoutDialog(); 283 } 284 } 285 286 public boolean doesFallbackUnlockScreenExist() { 287 return mEnableFallback; 288 } 289 }; 290 291 /** 292 * We'll get key events the current screen doesn't use. see 293 * {@link KeyguardViewBase#onKeyDown(int, android.view.KeyEvent)} 294 */ 295 setFocusableInTouchMode(true); 296 setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); 297 298 // wall paper background 299 if (false) { 300 final BitmapDrawable drawable = (BitmapDrawable) context.getWallpaper(); 301 setBackgroundDrawable( 302 new FastBitmapDrawable(drawable.getBitmap())); 303 } 304 305 // create both the lock and unlock screen so they are quickly available 306 // when the screen turns on 307 mLockScreen = createLockScreen(); 308 addView(mLockScreen); 309 final UnlockMode unlockMode = getUnlockMode(); 310 if (DEBUG) Log.d(TAG, 311 "LockPatternKeyguardView ctor: about to createUnlockScreenFor; mEnableFallback=" 312 + mEnableFallback); 313 mUnlockScreen = createUnlockScreenFor(unlockMode); 314 mUnlockScreenMode = unlockMode; 315 316 // Ask the account manager if we have an account that can be used as a 317 // fallback in case the user forgets his pattern. The response comes 318 // back in run() below; don't bother asking until you've called 319 // createUnlockScreenFor(), else the information will go unused. 320 final boolean hasAccount = AccountManager.get(context).getAccounts().length > 0; 321 if (hasAccount) { 322 /* If we have a SAML account which requires web login we can not use the 323 fallback screen UI to ask the user for credentials. 324 For now we will disable fallback screen in this case. 325 Ultimately we could consider bringing up a web login from GLS 326 but need to make sure that it will work in the "locked screen" mode. */ 327 String[] features = new String[] {"saml"}; 328 AccountManager.get(context).getAccountsByTypeAndFeatures( 329 "com.google", features, this, null); 330 } 331 332 addView(mUnlockScreen); 333 updateScreen(mMode); 334 } 335 336 337 @Override reset()338 public void reset() { 339 mIsVerifyUnlockOnly = false; 340 mForgotPattern = false; 341 updateScreen(getInitialMode()); 342 } 343 344 @Override onScreenTurnedOff()345 public void onScreenTurnedOff() { 346 mScreenOn = false; 347 mForgotPattern = false; 348 if (mMode == Mode.LockScreen) { 349 ((KeyguardScreen) mLockScreen).onPause(); 350 } else { 351 ((KeyguardScreen) mUnlockScreen).onPause(); 352 } 353 } 354 355 @Override onScreenTurnedOn()356 public void onScreenTurnedOn() { 357 mScreenOn = true; 358 if (mMode == Mode.LockScreen) { 359 ((KeyguardScreen) mLockScreen).onResume(); 360 } else { 361 ((KeyguardScreen) mUnlockScreen).onResume(); 362 } 363 } 364 365 recreateScreens()366 private void recreateScreens() { 367 if (mLockScreen.getVisibility() == View.VISIBLE) { 368 ((KeyguardScreen) mLockScreen).onPause(); 369 } 370 ((KeyguardScreen) mLockScreen).cleanUp(); 371 removeViewInLayout(mLockScreen); 372 373 mLockScreen = createLockScreen(); 374 mLockScreen.setVisibility(View.INVISIBLE); 375 addView(mLockScreen); 376 377 if (mUnlockScreen.getVisibility() == View.VISIBLE) { 378 ((KeyguardScreen) mUnlockScreen).onPause(); 379 } 380 ((KeyguardScreen) mUnlockScreen).cleanUp(); 381 removeViewInLayout(mUnlockScreen); 382 383 final UnlockMode unlockMode = getUnlockMode(); 384 mUnlockScreen = createUnlockScreenFor(unlockMode); 385 mUnlockScreen.setVisibility(View.INVISIBLE); 386 mUnlockScreenMode = unlockMode; 387 addView(mUnlockScreen); 388 389 updateScreen(mMode); 390 } 391 392 393 @Override wakeWhenReadyTq(int keyCode)394 public void wakeWhenReadyTq(int keyCode) { 395 if (DEBUG) Log.d(TAG, "onWakeKey"); 396 if (keyCode == KeyEvent.KEYCODE_MENU && isSecure() && (mMode == Mode.LockScreen) 397 && (mUpdateMonitor.getSimState() != IccCard.State.PUK_REQUIRED)) { 398 if (DEBUG) Log.d(TAG, "switching screens to unlock screen because wake key was MENU"); 399 updateScreen(Mode.UnlockScreen); 400 getCallback().pokeWakelock(); 401 } else { 402 if (DEBUG) Log.d(TAG, "poking wake lock immediately"); 403 getCallback().pokeWakelock(); 404 } 405 } 406 407 @Override verifyUnlock()408 public void verifyUnlock() { 409 if (!isSecure()) { 410 // non-secure keyguard screens are successfull by default 411 getCallback().keyguardDone(true); 412 } else if (mUnlockScreenMode != UnlockMode.Pattern) { 413 // can only verify unlock when in pattern mode 414 getCallback().keyguardDone(false); 415 } else { 416 // otherwise, go to the unlock screen, see if they can verify it 417 mIsVerifyUnlockOnly = true; 418 updateScreen(Mode.UnlockScreen); 419 } 420 } 421 422 @Override cleanUp()423 public void cleanUp() { 424 ((KeyguardScreen) mLockScreen).onPause(); 425 ((KeyguardScreen) mLockScreen).cleanUp(); 426 ((KeyguardScreen) mUnlockScreen).onPause(); 427 ((KeyguardScreen) mUnlockScreen).cleanUp(); 428 } 429 isSecure()430 private boolean isSecure() { 431 UnlockMode unlockMode = getUnlockMode(); 432 if (unlockMode == UnlockMode.Pattern) { 433 return mLockPatternUtils.isLockPatternEnabled(); 434 } else if (unlockMode == UnlockMode.SimPin) { 435 return mUpdateMonitor.getSimState() == IccCard.State.PIN_REQUIRED 436 || mUpdateMonitor.getSimState() == IccCard.State.PUK_REQUIRED; 437 } else if (unlockMode == UnlockMode.Account) { 438 return true; 439 } else { 440 throw new IllegalStateException("unknown unlock mode " + unlockMode); 441 } 442 } 443 updateScreen(final Mode mode)444 private void updateScreen(final Mode mode) { 445 446 mMode = mode; 447 448 final View goneScreen = (mode == Mode.LockScreen) ? mUnlockScreen : mLockScreen; 449 final View visibleScreen = (mode == Mode.LockScreen) 450 ? mLockScreen : getUnlockScreenForCurrentUnlockMode(); 451 452 // do this before changing visibility so focus isn't requested before the input 453 // flag is set 454 mWindowController.setNeedsInput(((KeyguardScreen)visibleScreen).needsInput()); 455 456 457 if (mScreenOn) { 458 if (goneScreen.getVisibility() == View.VISIBLE) { 459 ((KeyguardScreen) goneScreen).onPause(); 460 } 461 if (visibleScreen.getVisibility() != View.VISIBLE) { 462 ((KeyguardScreen) visibleScreen).onResume(); 463 } 464 } 465 466 goneScreen.setVisibility(View.GONE); 467 visibleScreen.setVisibility(View.VISIBLE); 468 469 470 if (!visibleScreen.requestFocus()) { 471 throw new IllegalStateException("keyguard screen must be able to take " 472 + "focus when shown " + visibleScreen.getClass().getCanonicalName()); 473 } 474 } 475 createLockScreen()476 View createLockScreen() { 477 return new LockScreen( 478 mContext, 479 mLockPatternUtils, 480 mUpdateMonitor, 481 mKeyguardScreenCallback); 482 } 483 createUnlockScreenFor(UnlockMode unlockMode)484 View createUnlockScreenFor(UnlockMode unlockMode) { 485 if (unlockMode == UnlockMode.Pattern) { 486 UnlockScreen view = new UnlockScreen( 487 mContext, 488 mLockPatternUtils, 489 mUpdateMonitor, 490 mKeyguardScreenCallback, 491 mUpdateMonitor.getFailedAttempts()); 492 if (DEBUG) Log.d(TAG, 493 "createUnlockScreenFor(" + unlockMode + "): mEnableFallback=" + mEnableFallback); 494 view.setEnableFallback(mEnableFallback); 495 return view; 496 } else if (unlockMode == UnlockMode.SimPin) { 497 return new SimUnlockScreen( 498 mContext, 499 mUpdateMonitor, 500 mKeyguardScreenCallback); 501 } else if (unlockMode == UnlockMode.Account) { 502 try { 503 return new AccountUnlockScreen( 504 mContext, 505 mKeyguardScreenCallback, 506 mLockPatternUtils); 507 } catch (IllegalStateException e) { 508 Log.i(TAG, "Couldn't instantiate AccountUnlockScreen" 509 + " (IAccountsService isn't available)"); 510 // TODO: Need a more general way to provide a 511 // platform-specific fallback UI here. 512 // For now, if we can't display the account login 513 // unlock UI, just bring back the regular "Pattern" unlock mode. 514 515 // (We do this by simply returning a regular UnlockScreen 516 // here. This means that the user will still see the 517 // regular pattern unlock UI, regardless of the value of 518 // mUnlockScreenMode or whether or not we're in the 519 // "permanently locked" state.) 520 return createUnlockScreenFor(UnlockMode.Pattern); 521 } 522 } else { 523 throw new IllegalArgumentException("unknown unlock mode " + unlockMode); 524 } 525 } 526 getUnlockScreenForCurrentUnlockMode()527 private View getUnlockScreenForCurrentUnlockMode() { 528 final UnlockMode unlockMode = getUnlockMode(); 529 530 // if a screen exists for the correct mode, we're done 531 if (unlockMode == mUnlockScreenMode) { 532 return mUnlockScreen; 533 } 534 535 // remember the mode 536 mUnlockScreenMode = unlockMode; 537 538 // unlock mode has changed and we have an existing old unlock screen 539 // to clean up 540 if (mScreenOn && (mUnlockScreen.getVisibility() == View.VISIBLE)) { 541 ((KeyguardScreen) mUnlockScreen).onPause(); 542 } 543 ((KeyguardScreen) mUnlockScreen).cleanUp(); 544 removeViewInLayout(mUnlockScreen); 545 546 // create the new one 547 mUnlockScreen = createUnlockScreenFor(unlockMode); 548 mUnlockScreen.setVisibility(View.INVISIBLE); 549 addView(mUnlockScreen); 550 return mUnlockScreen; 551 } 552 553 /** 554 * Given the current state of things, what should be the initial mode of 555 * the lock screen (lock or unlock). 556 */ getInitialMode()557 private Mode getInitialMode() { 558 final IccCard.State simState = mUpdateMonitor.getSimState(); 559 if (stuckOnLockScreenBecauseSimMissing() || (simState == IccCard.State.PUK_REQUIRED)) { 560 return Mode.LockScreen; 561 } else if (isSecure()) { 562 return Mode.UnlockScreen; 563 } else { 564 return Mode.LockScreen; 565 } 566 } 567 568 /** 569 * Given the current state of things, what should the unlock screen be? 570 */ getUnlockMode()571 private UnlockMode getUnlockMode() { 572 final IccCard.State simState = mUpdateMonitor.getSimState(); 573 if (simState == IccCard.State.PIN_REQUIRED || simState == IccCard.State.PUK_REQUIRED) { 574 return UnlockMode.SimPin; 575 } else { 576 return (mForgotPattern || mLockPatternUtils.isPermanentlyLocked()) ? 577 UnlockMode.Account: 578 UnlockMode.Pattern; 579 } 580 } 581 showTimeoutDialog()582 private void showTimeoutDialog() { 583 int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; 584 String message = mContext.getString( 585 R.string.lockscreen_too_many_failed_attempts_dialog_message, 586 mUpdateMonitor.getFailedAttempts(), 587 timeoutInSeconds); 588 final AlertDialog dialog = new AlertDialog.Builder(mContext) 589 .setTitle(null) 590 .setMessage(message) 591 .setNeutralButton(R.string.ok, null) 592 .create(); 593 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 594 if (!mContext.getResources().getBoolean( 595 com.android.internal.R.bool.config_sf_slowBlur)) { 596 dialog.getWindow().setFlags( 597 WindowManager.LayoutParams.FLAG_BLUR_BEHIND, 598 WindowManager.LayoutParams.FLAG_BLUR_BEHIND); 599 } 600 dialog.show(); 601 } 602 showAlmostAtAccountLoginDialog()603 private void showAlmostAtAccountLoginDialog() { 604 int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; 605 String message = mContext.getString( 606 R.string.lockscreen_failed_attempts_almost_glogin, 607 LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET 608 - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, 609 LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, 610 timeoutInSeconds); 611 final AlertDialog dialog = new AlertDialog.Builder(mContext) 612 .setTitle(null) 613 .setMessage(message) 614 .setNeutralButton(R.string.ok, null) 615 .create(); 616 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 617 if (!mContext.getResources().getBoolean( 618 com.android.internal.R.bool.config_sf_slowBlur)) { 619 dialog.getWindow().setFlags( 620 WindowManager.LayoutParams.FLAG_BLUR_BEHIND, 621 WindowManager.LayoutParams.FLAG_BLUR_BEHIND); 622 } 623 dialog.show(); 624 } 625 626 /** 627 * Used to put wallpaper on the background of the lock screen. Centers it 628 * Horizontally and pins the bottom (assuming that the lock screen is aligned 629 * with the bottom, so the wallpaper should extend above the top into the 630 * status bar). 631 */ 632 static private class FastBitmapDrawable extends Drawable { 633 private Bitmap mBitmap; 634 private int mOpacity; 635 FastBitmapDrawable(Bitmap bitmap)636 private FastBitmapDrawable(Bitmap bitmap) { 637 mBitmap = bitmap; 638 mOpacity = mBitmap.hasAlpha() ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE; 639 } 640 641 @Override draw(Canvas canvas)642 public void draw(Canvas canvas) { 643 canvas.drawBitmap( 644 mBitmap, 645 (getBounds().width() - mBitmap.getWidth()) / 2, 646 (getBounds().height() - mBitmap.getHeight()), 647 null); 648 } 649 650 @Override getOpacity()651 public int getOpacity() { 652 return mOpacity; 653 } 654 655 @Override setAlpha(int alpha)656 public void setAlpha(int alpha) { 657 } 658 659 @Override setColorFilter(ColorFilter cf)660 public void setColorFilter(ColorFilter cf) { 661 } 662 663 @Override getIntrinsicWidth()664 public int getIntrinsicWidth() { 665 return mBitmap.getWidth(); 666 } 667 668 @Override getIntrinsicHeight()669 public int getIntrinsicHeight() { 670 return mBitmap.getHeight(); 671 } 672 673 @Override getMinimumWidth()674 public int getMinimumWidth() { 675 return mBitmap.getWidth(); 676 } 677 678 @Override getMinimumHeight()679 public int getMinimumHeight() { 680 return mBitmap.getHeight(); 681 } 682 } 683 } 684 685