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