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