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