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