/* * 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.browser; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Matrix; import android.os.Message; import android.util.Log; import android.util.TypedValue; import android.view.ActionMode; import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.webkit.WebView; import android.widget.FrameLayout; import android.widget.ImageView; import com.android.browser.UrlInputView.StateListener; /** * Ui for regular phone screen sizes */ public class PhoneUi extends BaseUi { private static final String LOGTAG = "PhoneUi"; private static final int MSG_INIT_NAVSCREEN = 100; private PieControlPhone mPieControl; private NavScreen mNavScreen; private AnimScreen mAnimScreen; private NavigationBarPhone mNavigationBar; private int mActionBarHeight; boolean mExtendedMenuOpen; boolean mOptionsMenuOpen; boolean mAnimating; /** * @param browser * @param controller */ public PhoneUi(Activity browser, UiController controller) { super(browser, controller); setUseQuickControls(BrowserSettings.getInstance().useQuickControls()); mNavigationBar = (NavigationBarPhone) mTitleBar.getNavigationBar(); TypedValue heightValue = new TypedValue(); browser.getTheme().resolveAttribute( com.android.internal.R.attr.actionBarSize, heightValue, true); mActionBarHeight = TypedValue.complexToDimensionPixelSize(heightValue.data, browser.getResources().getDisplayMetrics()); } @Override public void onDestroy() { hideTitleBar(); } @Override public void editUrl(boolean clearInput) { if (mUseQuickControls) { mTitleBar.setShowProgressOnly(false); } super.editUrl(clearInput); } @Override public boolean onBackKey() { if (showingNavScreen()) { mNavScreen.close(mUiController.getTabControl().getCurrentPosition()); return true; } return super.onBackKey(); } private boolean showingNavScreen() { return mNavScreen != null && mNavScreen.getVisibility() == View.VISIBLE; } @Override public boolean dispatchKey(int code, KeyEvent event) { return false; } @Override public void onProgressChanged(Tab tab) { if (tab.inForeground()) { int progress = tab.getLoadProgress(); mTitleBar.setProgress(progress); if (progress == 100) { if (!mOptionsMenuOpen || !mExtendedMenuOpen) { suggestHideTitleBar(); if (mUseQuickControls) { mTitleBar.setShowProgressOnly(false); } } } else { if (!mOptionsMenuOpen || mExtendedMenuOpen) { if (mUseQuickControls && !mTitleBar.isEditingUrl()) { mTitleBar.setShowProgressOnly(true); setTitleGravity(Gravity.TOP); } showTitleBar(); } } } if (mNavScreen == null && getTitleBar().getHeight() > 0) { mHandler.sendEmptyMessage(MSG_INIT_NAVSCREEN); } } @Override protected void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == MSG_INIT_NAVSCREEN) { if (mNavScreen == null) { mNavScreen = new NavScreen(mActivity, mUiController, this); mCustomViewContainer.addView(mNavScreen, COVER_SCREEN_PARAMS); mNavScreen.setVisibility(View.GONE); } if (mAnimScreen == null) { mAnimScreen = new AnimScreen(mActivity); // initialize bitmaps mAnimScreen.set(getTitleBar(), getWebView()); } } } @Override public void setActiveTab(final Tab tab) { mTitleBar.cancelTitleBarAnimation(true); mTitleBar.setSkipTitleBarAnimations(true); super.setActiveTab(tab); BrowserWebView view = (BrowserWebView) tab.getWebView(); // TabControl.setCurrentTab has been called before this, // so the tab is guaranteed to have a webview if (view == null) { Log.e(LOGTAG, "active tab with no webview detected"); return; } // Request focus on the top window. if (mUseQuickControls) { mPieControl.forceToTop(mContentView); } else { // check if title bar is already attached by animation if (mTitleBar.getParent() == null) { view.setEmbeddedTitleBar(mTitleBar); } } if (tab.isInVoiceSearchMode()) { showVoiceTitleBar(tab.getVoiceDisplayTitle(), tab.getVoiceSearchResults()); } else { revertVoiceTitleBar(tab); } // update nav bar state mNavigationBar.onStateChanged(StateListener.STATE_NORMAL); updateLockIconToLatest(tab); tab.getTopWindow().requestFocus(); mTitleBar.setSkipTitleBarAnimations(false); } // menu handling callbacks @Override public boolean onPrepareOptionsMenu(Menu menu) { updateMenuState(mActiveTab, menu); return true; } @Override public void updateMenuState(Tab tab, Menu menu) { MenuItem abm = menu.findItem(R.id.add_bookmark_menu_id); if (abm != null) { abm.setVisible((tab != null) && !tab.isSnapshot() && !showingNavScreen()); } MenuItem info = menu.findItem(R.id.page_info_menu_id); if (info != null) { info.setVisible(false); } MenuItem newtab = menu.findItem(R.id.new_tab_menu_id); if (newtab != null && !mUseQuickControls) { newtab.setVisible(false); } MenuItem incognito = menu.findItem(R.id.incognito_menu_id); if (incognito != null) { incognito.setVisible(showingNavScreen() || mUseQuickControls); } if (showingNavScreen()) { menu.setGroupVisible(R.id.LIVE_MENU, false); menu.setGroupVisible(R.id.SNAPSHOT_MENU, false); menu.setGroupVisible(R.id.NAV_MENU, false); menu.setGroupVisible(R.id.COMBO_MENU, true); } } @Override public boolean onOptionsItemSelected(MenuItem item) { if (showingNavScreen() && (item.getItemId() != R.id.history_menu_id) && (item.getItemId() != R.id.snapshots_menu_id)) { hideNavScreen(mUiController.getTabControl().getCurrentPosition(), false); } return false; } @Override public void onContextMenuCreated(Menu menu) { hideTitleBar(); } @Override public void onContextMenuClosed(Menu menu, boolean inLoad) { if (inLoad) { showTitleBar(); } } // action mode callbacks @Override public void onActionModeStarted(ActionMode mode) { if (!isEditingUrl()) { hideTitleBar(); } else { mTitleBar.animate().translationY(mActionBarHeight); } } @Override public void onActionModeFinished(boolean inLoad) { mTitleBar.animate().translationY(0); if (inLoad) { if (mUseQuickControls) { mTitleBar.setShowProgressOnly(true); } showTitleBar(); } } @Override protected void setTitleGravity(int gravity) { if (mUseQuickControls) { FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mTitleBar.getLayoutParams(); lp.gravity = gravity; mTitleBar.setLayoutParams(lp); } else { super.setTitleGravity(gravity); } } @Override public void setUseQuickControls(boolean useQuickControls) { mUseQuickControls = useQuickControls; mTitleBar.setUseQuickControls(mUseQuickControls); if (useQuickControls) { mPieControl = new PieControlPhone(mActivity, mUiController, this); mPieControl.attachToContainer(mContentView); WebView web = getWebView(); if (web != null) { web.setEmbeddedTitleBar(null); } } else { if (mPieControl != null) { mPieControl.removeFromContainer(mContentView); } WebView web = getWebView(); if (web != null) { // make sure we can re-parent titlebar if ((mTitleBar != null) && (mTitleBar.getParent() != null)) { ((ViewGroup) mTitleBar.getParent()).removeView(mTitleBar); } web.setEmbeddedTitleBar(mTitleBar); } setTitleGravity(Gravity.NO_GRAVITY); } updateUrlBarAutoShowManagerTarget(); } @Override public boolean isWebShowing() { return super.isWebShowing() && !showingNavScreen(); } @Override public void showWeb(boolean animate) { super.showWeb(animate); hideNavScreen(mUiController.getTabControl().getCurrentPosition(), animate); } void showNavScreen() { mUiController.setBlockEvents(true); if (mNavScreen == null) { mNavScreen = new NavScreen(mActivity, mUiController, this); mCustomViewContainer.addView(mNavScreen, COVER_SCREEN_PARAMS); } else { mNavScreen.setVisibility(View.VISIBLE); mNavScreen.setAlpha(1f); mNavScreen.refreshAdapter(); } mActiveTab.capture(); if (mAnimScreen == null) { mAnimScreen = new AnimScreen(mActivity); } else { mAnimScreen.mMain.setAlpha(1f); mAnimScreen.mTitle.setAlpha(1f); mAnimScreen.setScaleFactor(1f); } mAnimScreen.set(getTitleBar(), getWebView()); if (mAnimScreen.mMain.getParent() == null) { mCustomViewContainer.addView(mAnimScreen.mMain, COVER_SCREEN_PARAMS); } mCustomViewContainer.setVisibility(View.VISIBLE); mCustomViewContainer.bringToFront(); mAnimScreen.mMain.layout(0, 0, mContentView.getWidth(), mContentView.getHeight()); int fromLeft = 0; int fromTop = getTitleBar().getHeight(); int fromRight = mContentView.getWidth(); int fromBottom = mContentView.getHeight(); int width = mActivity.getResources().getDimensionPixelSize(R.dimen.nav_tab_width); int height = mActivity.getResources().getDimensionPixelSize(R.dimen.nav_tab_height); int ntth = mActivity.getResources().getDimensionPixelSize(R.dimen.nav_tab_titleheight); int toLeft = (mContentView.getWidth() - width) / 2; int toTop = ((fromBottom - (ntth + height)) / 2 + ntth); int toRight = toLeft + width; int toBottom = toTop + height; float scaleFactor = width / (float) mContentView.getWidth(); detachTab(mActiveTab); mContentView.setVisibility(View.GONE); AnimatorSet set1 = new AnimatorSet(); AnimatorSet inanim = new AnimatorSet(); ObjectAnimator tx = ObjectAnimator.ofInt(mAnimScreen.mContent, "left", fromLeft, toLeft); ObjectAnimator ty = ObjectAnimator.ofInt(mAnimScreen.mContent, "top", fromTop, toTop); ObjectAnimator tr = ObjectAnimator.ofInt(mAnimScreen.mContent, "right", fromRight, toRight); ObjectAnimator tb = ObjectAnimator.ofInt(mAnimScreen.mContent, "bottom", fromBottom, toBottom); ObjectAnimator title = ObjectAnimator.ofFloat(mAnimScreen.mTitle, "alpha", 1f, 0f); ObjectAnimator sx = ObjectAnimator.ofFloat(mAnimScreen, "scaleFactor", 1f, scaleFactor); ObjectAnimator blend1 = ObjectAnimator.ofFloat(mAnimScreen.mMain, "alpha", 1f, 0f); blend1.setDuration(100); inanim.playTogether(tx, ty, tr, tb, sx, title); inanim.setDuration(200); set1.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator anim) { mCustomViewContainer.removeView(mAnimScreen.mMain); finishAnimationIn(); mUiController.setBlockEvents(false); } }); set1.playSequentially(inanim, blend1); set1.start(); } private void finishAnimationIn() { if (showingNavScreen()) { // notify accessibility manager about the screen change mNavScreen.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); mTabControl.setOnThumbnailUpdatedListener(mNavScreen); } } void hideNavScreen(int position, boolean animate) { if (!showingNavScreen()) return; final Tab tab = mUiController.getTabControl().getTab(position); if ((tab == null) || !animate) { if (tab != null) { setActiveTab(tab); } else if (mTabControl.getTabCount() > 0) { // use a fallback tab setActiveTab(mTabControl.getCurrentTab()); } mContentView.setVisibility(View.VISIBLE); finishAnimateOut(); return; } NavTabView tabview = (NavTabView) mNavScreen.getTabView(position); if (tabview == null) { if (mTabControl.getTabCount() > 0) { // use a fallback tab setActiveTab(mTabControl.getCurrentTab()); } mContentView.setVisibility(View.VISIBLE); finishAnimateOut(); return; } mUiController.setBlockEvents(true); mUiController.setActiveTab(tab); mContentView.setVisibility(View.VISIBLE); if (mAnimScreen == null) { mAnimScreen = new AnimScreen(mActivity); } mAnimScreen.set(tab.getScreenshot()); mCustomViewContainer.addView(mAnimScreen.mMain, COVER_SCREEN_PARAMS); mAnimScreen.mMain.layout(0, 0, mContentView.getWidth(), mContentView.getHeight()); mNavScreen.mScroller.finishScroller(); ImageView target = tabview.mImage; int toLeft = 0; int toTop = getTitleBar().getHeight(); int toRight = mContentView.getWidth(); int width = target.getDrawable().getIntrinsicWidth(); int height = target.getDrawable().getIntrinsicHeight(); int fromLeft = tabview.getLeft() + target.getLeft() - mNavScreen.mScroller.getScrollX(); int fromTop = tabview.getTop() + target.getTop() - mNavScreen.mScroller.getScrollY(); int fromRight = fromLeft + width; int fromBottom = fromTop + height; float scaleFactor = mContentView.getWidth() / (float) width; int toBottom = toTop + (int) (height * scaleFactor); mAnimScreen.mContent.setLeft(fromLeft); mAnimScreen.mContent.setTop(fromTop); mAnimScreen.mContent.setRight(fromRight); mAnimScreen.mContent.setBottom(fromBottom); mAnimScreen.setScaleFactor(1f); AnimatorSet set1 = new AnimatorSet(); ObjectAnimator fade2 = ObjectAnimator.ofFloat(mAnimScreen.mMain, "alpha", 0f, 1f); ObjectAnimator fade1 = ObjectAnimator.ofFloat(mNavScreen, "alpha", 1f, 0f); set1.playTogether(fade1, fade2); set1.setDuration(100); AnimatorSet set2 = new AnimatorSet(); ObjectAnimator l = ObjectAnimator.ofInt(mAnimScreen.mContent, "left", fromLeft, toLeft); ObjectAnimator t = ObjectAnimator.ofInt(mAnimScreen.mContent, "top", fromTop, toTop); ObjectAnimator r = ObjectAnimator.ofInt(mAnimScreen.mContent, "right", fromRight, toRight); ObjectAnimator b = ObjectAnimator.ofInt(mAnimScreen.mContent, "bottom", fromBottom, toBottom); ObjectAnimator scale = ObjectAnimator.ofFloat(mAnimScreen, "scaleFactor", 1f, scaleFactor); ObjectAnimator otheralpha = ObjectAnimator.ofFloat(mCustomViewContainer, "alpha", 1f, 0f); otheralpha.setDuration(100); set2.playTogether(l, t, r, b, scale); set2.setDuration(200); AnimatorSet combo = new AnimatorSet(); combo.playSequentially(set1, set2, otheralpha); combo.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator anim) { mCustomViewContainer.removeView(mAnimScreen.mMain); finishAnimateOut(); mUiController.setBlockEvents(false); } }); combo.start(); } private void finishAnimateOut() { mTabControl.setOnThumbnailUpdatedListener(null); mNavScreen.setVisibility(View.GONE); mCustomViewContainer.setAlpha(1f); mCustomViewContainer.setVisibility(View.GONE); } @Override public boolean needsRestoreAllTabs() { return false; } public void toggleNavScreen() { if (!showingNavScreen()) { showNavScreen(); } else { hideNavScreen(mUiController.getTabControl().getCurrentPosition(), false); } } @Override public boolean shouldCaptureThumbnails() { return true; } static class AnimScreen { private View mMain; private ImageView mTitle; private ImageView mContent; private float mScale; private Bitmap mTitleBarBitmap; private Bitmap mContentBitmap; public AnimScreen(Context ctx) { mMain = LayoutInflater.from(ctx).inflate(R.layout.anim_screen, null); mTitle = (ImageView) mMain.findViewById(R.id.title); mContent = (ImageView) mMain.findViewById(R.id.content); mContent.setScaleType(ImageView.ScaleType.MATRIX); mContent.setImageMatrix(new Matrix()); mScale = 1.0f; setScaleFactor(getScaleFactor()); } public void set(TitleBar tbar, WebView web) { if (tbar == null || web == null) { return; } if (tbar.getWidth() > 0 && tbar.getEmbeddedHeight() > 0) { if (mTitleBarBitmap == null || mTitleBarBitmap.getWidth() != tbar.getWidth() || mTitleBarBitmap.getHeight() != tbar.getEmbeddedHeight()) { mTitleBarBitmap = safeCreateBitmap(tbar.getWidth(), tbar.getEmbeddedHeight()); } if (mTitleBarBitmap != null) { Canvas c = new Canvas(mTitleBarBitmap); tbar.draw(c); c.setBitmap(null); } } else { mTitleBarBitmap = null; } mTitle.setImageBitmap(mTitleBarBitmap); mTitle.setVisibility(View.VISIBLE); int h = web.getHeight() - tbar.getEmbeddedHeight(); if (mContentBitmap == null || mContentBitmap.getWidth() != web.getWidth() || mContentBitmap.getHeight() != h) { mContentBitmap = safeCreateBitmap(web.getWidth(), h); } if (mContentBitmap != null) { Canvas c = new Canvas(mContentBitmap); int tx = web.getScrollX(); int ty = web.getScrollY(); c.translate(-tx, -ty - tbar.getEmbeddedHeight()); web.draw(c); c.setBitmap(null); } mContent.setImageBitmap(mContentBitmap); } private Bitmap safeCreateBitmap(int width, int height) { if (width <= 0 || height <= 0) { Log.w(LOGTAG, "safeCreateBitmap failed! width: " + width + ", height: " + height); return null; } return Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); } public void set(Bitmap image) { mTitle.setVisibility(View.GONE); mContent.setImageBitmap(image); } private void setScaleFactor(float sf) { mScale = sf; Matrix m = new Matrix(); m.postScale(sf,sf); mContent.setImageMatrix(m); } private float getScaleFactor() { return mScale; } } }