1 /* 2 * Copyright (C) 2010 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.browser; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorListenerAdapter; 21 import android.animation.AnimatorSet; 22 import android.animation.ObjectAnimator; 23 import android.app.Activity; 24 import android.content.Context; 25 import android.graphics.Bitmap; 26 import android.graphics.Canvas; 27 import android.graphics.Matrix; 28 import android.os.Message; 29 import android.util.Log; 30 import android.util.TypedValue; 31 import android.view.ActionMode; 32 import android.view.KeyEvent; 33 import android.view.LayoutInflater; 34 import android.view.Menu; 35 import android.view.MenuItem; 36 import android.view.View; 37 import android.view.accessibility.AccessibilityEvent; 38 import android.webkit.WebView; 39 import android.widget.ImageView; 40 41 import com.android.browser.UrlInputView.StateListener; 42 43 /** 44 * Ui for regular phone screen sizes 45 */ 46 public class PhoneUi extends BaseUi { 47 48 private static final String LOGTAG = "PhoneUi"; 49 private static final int MSG_INIT_NAVSCREEN = 100; 50 51 private NavScreen mNavScreen; 52 private AnimScreen mAnimScreen; 53 private NavigationBarPhone mNavigationBar; 54 private int mActionBarHeight; 55 56 boolean mAnimating; 57 boolean mShowNav = false; 58 59 /** 60 * @param browser 61 * @param controller 62 */ 63 public PhoneUi(Activity browser, UiController controller) { 64 super(browser, controller); 65 setUseQuickControls(BrowserSettings.getInstance().useQuickControls()); 66 mNavigationBar = (NavigationBarPhone) mTitleBar.getNavigationBar(); 67 TypedValue heightValue = new TypedValue(); 68 browser.getTheme().resolveAttribute( 69 com.android.internal.R.attr.actionBarSize, heightValue, true); 70 mActionBarHeight = TypedValue.complexToDimensionPixelSize(heightValue.data, 71 browser.getResources().getDisplayMetrics()); 72 } 73 74 @Override 75 public void onDestroy() { 76 hideTitleBar(); 77 } 78 79 @Override 80 public void editUrl(boolean clearInput, boolean forceIME) { 81 if (mUseQuickControls) { 82 mTitleBar.setShowProgressOnly(false); 83 } 84 //Do nothing while at Nav show screen. 85 if (mShowNav) return; 86 super.editUrl(clearInput, forceIME); 87 } 88 89 @Override 90 public boolean onBackKey() { 91 if (showingNavScreen()) { 92 mNavScreen.close(mUiController.getTabControl().getCurrentPosition()); 93 return true; 94 } 95 return super.onBackKey(); 96 } 97 98 private boolean showingNavScreen() { 99 return mNavScreen != null && mNavScreen.getVisibility() == View.VISIBLE; 100 } 101 102 @Override 103 public boolean dispatchKey(int code, KeyEvent event) { 104 return false; 105 } 106 107 @Override 108 public void onProgressChanged(Tab tab) { 109 super.onProgressChanged(tab); 110 if (mNavScreen == null && getTitleBar().getHeight() > 0) { 111 mHandler.sendEmptyMessage(MSG_INIT_NAVSCREEN); 112 } 113 } 114 115 @Override 116 protected void handleMessage(Message msg) { 117 super.handleMessage(msg); 118 if (msg.what == MSG_INIT_NAVSCREEN) { 119 if (mNavScreen == null) { 120 mNavScreen = new NavScreen(mActivity, mUiController, this); 121 mCustomViewContainer.addView(mNavScreen, COVER_SCREEN_PARAMS); 122 mNavScreen.setVisibility(View.GONE); 123 } 124 if (mAnimScreen == null) { 125 mAnimScreen = new AnimScreen(mActivity); 126 // initialize bitmaps 127 mAnimScreen.set(getTitleBar(), getWebView()); 128 } 129 } 130 } 131 132 @Override 133 public void setActiveTab(final Tab tab) { 134 mTitleBar.cancelTitleBarAnimation(true); 135 mTitleBar.setSkipTitleBarAnimations(true); 136 super.setActiveTab(tab); 137 138 //if at Nav screen show, detach tab like what showNavScreen() do. 139 if (mShowNav) { 140 detachTab(mActiveTab); 141 } 142 143 BrowserWebView view = (BrowserWebView) tab.getWebView(); 144 // TabControl.setCurrentTab has been called before this, 145 // so the tab is guaranteed to have a webview 146 if (view == null) { 147 Log.e(LOGTAG, "active tab with no webview detected"); 148 return; 149 } 150 // Request focus on the top window. 151 if (mUseQuickControls) { 152 mPieControl.forceToTop(mContentView); 153 view.setTitleBar(null); 154 mTitleBar.setShowProgressOnly(true); 155 } else { 156 view.setTitleBar(mTitleBar); 157 } 158 // update nav bar state 159 mNavigationBar.onStateChanged(StateListener.STATE_NORMAL); 160 updateLockIconToLatest(tab); 161 mTitleBar.setSkipTitleBarAnimations(false); 162 } 163 164 // menu handling callbacks 165 166 @Override 167 public boolean onPrepareOptionsMenu(Menu menu) { 168 updateMenuState(mActiveTab, menu); 169 return true; 170 } 171 172 @Override 173 public void updateMenuState(Tab tab, Menu menu) { 174 MenuItem bm = menu.findItem(R.id.bookmarks_menu_id); 175 if (bm != null) { 176 bm.setVisible(!showingNavScreen()); 177 } 178 MenuItem abm = menu.findItem(R.id.add_bookmark_menu_id); 179 if (abm != null) { 180 abm.setVisible((tab != null) && !tab.isSnapshot() && !showingNavScreen()); 181 } 182 MenuItem info = menu.findItem(R.id.page_info_menu_id); 183 if (info != null) { 184 info.setVisible(false); 185 } 186 MenuItem newtab = menu.findItem(R.id.new_tab_menu_id); 187 if (newtab != null && !mUseQuickControls) { 188 newtab.setVisible(false); 189 } 190 MenuItem incognito = menu.findItem(R.id.incognito_menu_id); 191 if (incognito != null) { 192 incognito.setVisible(showingNavScreen() || mUseQuickControls); 193 } 194 MenuItem closeOthers = menu.findItem(R.id.close_other_tabs_id); 195 if (closeOthers != null) { 196 boolean isLastTab = true; 197 if (tab != null) { 198 isLastTab = (mTabControl.getTabCount() <= 1); 199 } 200 closeOthers.setEnabled(!isLastTab); 201 } 202 if (showingNavScreen()) { 203 menu.setGroupVisible(R.id.LIVE_MENU, false); 204 menu.setGroupVisible(R.id.SNAPSHOT_MENU, false); 205 menu.setGroupVisible(R.id.NAV_MENU, false); 206 menu.setGroupVisible(R.id.COMBO_MENU, true); 207 } 208 } 209 210 @Override 211 public boolean onOptionsItemSelected(MenuItem item) { 212 if (showingNavScreen() 213 && (item.getItemId() != R.id.history_menu_id) 214 && (item.getItemId() != R.id.snapshots_menu_id)) { 215 hideNavScreen(mUiController.getTabControl().getCurrentPosition(), false); 216 } 217 return false; 218 } 219 220 @Override 221 public void onContextMenuCreated(Menu menu) { 222 hideTitleBar(); 223 } 224 225 @Override 226 public void onContextMenuClosed(Menu menu, boolean inLoad) { 227 if (inLoad) { 228 showTitleBar(); 229 } 230 } 231 232 // action mode callbacks 233 234 @Override 235 public void onActionModeStarted(ActionMode mode) { 236 if (!isEditingUrl()) { 237 hideTitleBar(); 238 } else { 239 mTitleBar.animate().translationY(mActionBarHeight); 240 } 241 } 242 243 @Override 244 public void onActionModeFinished(boolean inLoad) { 245 mTitleBar.animate().translationY(0); 246 if (inLoad) { 247 if (mUseQuickControls) { 248 mTitleBar.setShowProgressOnly(true); 249 } 250 showTitleBar(); 251 } 252 } 253 254 @Override 255 public boolean isWebShowing() { 256 return super.isWebShowing() && !showingNavScreen(); 257 } 258 259 @Override 260 public void showWeb(boolean animate) { 261 super.showWeb(animate); 262 hideNavScreen(mUiController.getTabControl().getCurrentPosition(), animate); 263 } 264 265 void showNavScreen() { 266 mShowNav = true; 267 mUiController.setBlockEvents(true); 268 if (mNavScreen == null) { 269 mNavScreen = new NavScreen(mActivity, mUiController, this); 270 mCustomViewContainer.addView(mNavScreen, COVER_SCREEN_PARAMS); 271 } else { 272 mNavScreen.setVisibility(View.VISIBLE); 273 mNavScreen.setAlpha(1f); 274 mNavScreen.refreshAdapter(); 275 } 276 mActiveTab.capture(); 277 if (mAnimScreen == null) { 278 mAnimScreen = new AnimScreen(mActivity); 279 } else { 280 mAnimScreen.mMain.setAlpha(1f); 281 mAnimScreen.mTitle.setAlpha(1f); 282 mAnimScreen.setScaleFactor(1f); 283 } 284 mAnimScreen.set(getTitleBar(), getWebView()); 285 if (mAnimScreen.mMain.getParent() == null) { 286 mCustomViewContainer.addView(mAnimScreen.mMain, COVER_SCREEN_PARAMS); 287 } 288 mCustomViewContainer.setVisibility(View.VISIBLE); 289 mCustomViewContainer.bringToFront(); 290 mAnimScreen.mMain.layout(0, 0, mContentView.getWidth(), 291 mContentView.getHeight()); 292 int fromLeft = 0; 293 int fromTop = getTitleBar().getHeight(); 294 int fromRight = mContentView.getWidth(); 295 int fromBottom = mContentView.getHeight(); 296 int width = mActivity.getResources().getDimensionPixelSize(R.dimen.nav_tab_width); 297 int height = mActivity.getResources().getDimensionPixelSize(R.dimen.nav_tab_height); 298 int ntth = mActivity.getResources().getDimensionPixelSize(R.dimen.nav_tab_titleheight); 299 int toLeft = (mContentView.getWidth() - width) / 2; 300 int toTop = ((fromBottom - (ntth + height)) / 2 + ntth); 301 int toRight = toLeft + width; 302 int toBottom = toTop + height; 303 float scaleFactor = width / (float) mContentView.getWidth(); 304 detachTab(mActiveTab); 305 mContentView.setVisibility(View.GONE); 306 AnimatorSet set1 = new AnimatorSet(); 307 AnimatorSet inanim = new AnimatorSet(); 308 ObjectAnimator tx = ObjectAnimator.ofInt(mAnimScreen.mContent, "left", 309 fromLeft, toLeft); 310 ObjectAnimator ty = ObjectAnimator.ofInt(mAnimScreen.mContent, "top", 311 fromTop, toTop); 312 ObjectAnimator tr = ObjectAnimator.ofInt(mAnimScreen.mContent, "right", 313 fromRight, toRight); 314 ObjectAnimator tb = ObjectAnimator.ofInt(mAnimScreen.mContent, "bottom", 315 fromBottom, toBottom); 316 ObjectAnimator title = ObjectAnimator.ofFloat(mAnimScreen.mTitle, "alpha", 317 1f, 0f); 318 ObjectAnimator sx = ObjectAnimator.ofFloat(mAnimScreen, "scaleFactor", 319 1f, scaleFactor); 320 ObjectAnimator blend1 = ObjectAnimator.ofFloat(mAnimScreen.mMain, 321 "alpha", 1f, 0f); 322 blend1.setDuration(100); 323 324 inanim.playTogether(tx, ty, tr, tb, sx, title); 325 inanim.setDuration(200); 326 set1.addListener(new AnimatorListenerAdapter() { 327 @Override 328 public void onAnimationEnd(Animator anim) { 329 mCustomViewContainer.removeView(mAnimScreen.mMain); 330 finishAnimationIn(); 331 mUiController.setBlockEvents(false); 332 } 333 }); 334 set1.playSequentially(inanim, blend1); 335 set1.start(); 336 } 337 338 private void finishAnimationIn() { 339 if (showingNavScreen()) { 340 // notify accessibility manager about the screen change 341 mNavScreen.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); 342 mTabControl.setOnThumbnailUpdatedListener(mNavScreen); 343 } 344 } 345 346 void hideNavScreen(int position, boolean animate) { 347 mShowNav = false; 348 if (!showingNavScreen()) return; 349 final Tab tab = mUiController.getTabControl().getTab(position); 350 if ((tab == null) || !animate) { 351 if (tab != null) { 352 setActiveTab(tab); 353 } else if (mTabControl.getTabCount() > 0) { 354 // use a fallback tab 355 setActiveTab(mTabControl.getCurrentTab()); 356 } 357 mContentView.setVisibility(View.VISIBLE); 358 finishAnimateOut(); 359 return; 360 } 361 NavTabView tabview = (NavTabView) mNavScreen.getTabView(position); 362 if (tabview == null) { 363 if (mTabControl.getTabCount() > 0) { 364 // use a fallback tab 365 setActiveTab(mTabControl.getCurrentTab()); 366 } 367 mContentView.setVisibility(View.VISIBLE); 368 finishAnimateOut(); 369 return; 370 } 371 mUiController.setBlockEvents(true); 372 mUiController.setActiveTab(tab); 373 mContentView.setVisibility(View.VISIBLE); 374 if (mAnimScreen == null) { 375 mAnimScreen = new AnimScreen(mActivity); 376 } 377 mAnimScreen.set(tab.getScreenshot()); 378 if (mAnimScreen.mMain.getParent() == null) { 379 mCustomViewContainer.addView(mAnimScreen.mMain, COVER_SCREEN_PARAMS); 380 } 381 mAnimScreen.mMain.layout(0, 0, mContentView.getWidth(), 382 mContentView.getHeight()); 383 mNavScreen.mScroller.finishScroller(); 384 ImageView target = tabview.mImage; 385 int toLeft = 0; 386 int toTop = (tab.getWebView() != null) ? tab.getWebView().getVisibleTitleHeight() : 0; 387 int toRight = mContentView.getWidth(); 388 int width = target.getDrawable().getIntrinsicWidth(); 389 int height = target.getDrawable().getIntrinsicHeight(); 390 int fromLeft = tabview.getLeft() + target.getLeft() - mNavScreen.mScroller.getScrollX(); 391 int fromTop = tabview.getTop() + target.getTop() - mNavScreen.mScroller.getScrollY(); 392 int fromRight = fromLeft + width; 393 int fromBottom = fromTop + height; 394 float scaleFactor = mContentView.getWidth() / (float) width; 395 int toBottom = toTop + (int) (height * scaleFactor); 396 mAnimScreen.mContent.setLeft(fromLeft); 397 mAnimScreen.mContent.setTop(fromTop); 398 mAnimScreen.mContent.setRight(fromRight); 399 mAnimScreen.mContent.setBottom(fromBottom); 400 mAnimScreen.setScaleFactor(1f); 401 AnimatorSet set1 = new AnimatorSet(); 402 ObjectAnimator fade2 = ObjectAnimator.ofFloat(mAnimScreen.mMain, "alpha", 0f, 1f); 403 ObjectAnimator fade1 = ObjectAnimator.ofFloat(mNavScreen, "alpha", 1f, 0f); 404 set1.playTogether(fade1, fade2); 405 set1.setDuration(100); 406 AnimatorSet set2 = new AnimatorSet(); 407 ObjectAnimator l = ObjectAnimator.ofInt(mAnimScreen.mContent, "left", 408 fromLeft, toLeft); 409 ObjectAnimator t = ObjectAnimator.ofInt(mAnimScreen.mContent, "top", 410 fromTop, toTop); 411 ObjectAnimator r = ObjectAnimator.ofInt(mAnimScreen.mContent, "right", 412 fromRight, toRight); 413 ObjectAnimator b = ObjectAnimator.ofInt(mAnimScreen.mContent, "bottom", 414 fromBottom, toBottom); 415 ObjectAnimator scale = ObjectAnimator.ofFloat(mAnimScreen, "scaleFactor", 416 1f, scaleFactor); 417 ObjectAnimator otheralpha = ObjectAnimator.ofFloat(mCustomViewContainer, "alpha", 1f, 0f); 418 otheralpha.setDuration(100); 419 set2.playTogether(l, t, r, b, scale); 420 set2.setDuration(200); 421 AnimatorSet combo = new AnimatorSet(); 422 combo.playSequentially(set1, set2, otheralpha); 423 combo.addListener(new AnimatorListenerAdapter() { 424 @Override 425 public void onAnimationEnd(Animator anim) { 426 mCustomViewContainer.removeView(mAnimScreen.mMain); 427 finishAnimateOut(); 428 mUiController.setBlockEvents(false); 429 } 430 }); 431 combo.start(); 432 } 433 434 private void finishAnimateOut() { 435 mTabControl.setOnThumbnailUpdatedListener(null); 436 mNavScreen.setVisibility(View.GONE); 437 mCustomViewContainer.setAlpha(1f); 438 mCustomViewContainer.setVisibility(View.GONE); 439 } 440 441 @Override 442 public boolean needsRestoreAllTabs() { 443 return false; 444 } 445 446 public void toggleNavScreen() { 447 if (!showingNavScreen()) { 448 showNavScreen(); 449 } else { 450 hideNavScreen(mUiController.getTabControl().getCurrentPosition(), false); 451 } 452 } 453 454 @Override 455 public boolean shouldCaptureThumbnails() { 456 return true; 457 } 458 459 static class AnimScreen { 460 461 private View mMain; 462 private ImageView mTitle; 463 private ImageView mContent; 464 private float mScale; 465 private Bitmap mTitleBarBitmap; 466 private Bitmap mContentBitmap; 467 468 public AnimScreen(Context ctx) { 469 mMain = LayoutInflater.from(ctx).inflate(R.layout.anim_screen, 470 null); 471 mTitle = (ImageView) mMain.findViewById(R.id.title); 472 mContent = (ImageView) mMain.findViewById(R.id.content); 473 mContent.setScaleType(ImageView.ScaleType.MATRIX); 474 mContent.setImageMatrix(new Matrix()); 475 mScale = 1.0f; 476 setScaleFactor(getScaleFactor()); 477 } 478 479 public void set(TitleBar tbar, WebView web) { 480 if (tbar == null || web == null) { 481 return; 482 } 483 if (tbar.getWidth() > 0 && tbar.getEmbeddedHeight() > 0) { 484 if (mTitleBarBitmap == null 485 || mTitleBarBitmap.getWidth() != tbar.getWidth() 486 || mTitleBarBitmap.getHeight() != tbar.getEmbeddedHeight()) { 487 mTitleBarBitmap = safeCreateBitmap(tbar.getWidth(), 488 tbar.getEmbeddedHeight()); 489 } 490 if (mTitleBarBitmap != null) { 491 Canvas c = new Canvas(mTitleBarBitmap); 492 tbar.draw(c); 493 c.setBitmap(null); 494 } 495 } else { 496 mTitleBarBitmap = null; 497 } 498 mTitle.setImageBitmap(mTitleBarBitmap); 499 mTitle.setVisibility(View.VISIBLE); 500 int h = web.getHeight() - tbar.getEmbeddedHeight(); 501 if (mContentBitmap == null 502 || mContentBitmap.getWidth() != web.getWidth() 503 || mContentBitmap.getHeight() != h) { 504 mContentBitmap = safeCreateBitmap(web.getWidth(), h); 505 } 506 if (mContentBitmap != null) { 507 Canvas c = new Canvas(mContentBitmap); 508 int tx = web.getScrollX(); 509 int ty = web.getScrollY(); 510 c.translate(-tx, -ty - tbar.getEmbeddedHeight()); 511 web.draw(c); 512 c.setBitmap(null); 513 } 514 mContent.setImageBitmap(mContentBitmap); 515 } 516 517 private Bitmap safeCreateBitmap(int width, int height) { 518 if (width <= 0 || height <= 0) { 519 Log.w(LOGTAG, "safeCreateBitmap failed! width: " + width 520 + ", height: " + height); 521 return null; 522 } 523 return Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); 524 } 525 526 public void set(Bitmap image) { 527 mTitle.setVisibility(View.GONE); 528 mContent.setImageBitmap(image); 529 } 530 531 private void setScaleFactor(float sf) { 532 mScale = sf; 533 Matrix m = new Matrix(); 534 m.postScale(sf,sf); 535 mContent.setImageMatrix(m); 536 } 537 538 private float getScaleFactor() { 539 return mScale; 540 } 541 542 } 543 544 } 545