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