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.KeyEvent;
33import android.view.LayoutInflater;
34import android.view.Menu;
35import android.view.MenuItem;
36import android.view.View;
37import android.view.accessibility.AccessibilityEvent;
38import android.webkit.WebView;
39import android.widget.ImageView;
40
41import com.android.browser.UrlInputView.StateListener;
42
43/**
44 * Ui for regular phone screen sizes
45 */
46public 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