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 
17 package com.android.browser;
18 
19 import android.animation.Animator;
20 import android.animation.AnimatorListenerAdapter;
21 import android.animation.AnimatorSet;
22 import android.animation.ObjectAnimator;
23 import android.app.Activity;
24 import android.content.Context;
25 import android.graphics.Bitmap;
26 import android.graphics.Canvas;
27 import android.graphics.Matrix;
28 import android.os.Build;
29 import android.os.Message;
30 import android.util.Log;
31 import android.util.TypedValue;
32 import android.view.ActionMode;
33 import android.view.KeyEvent;
34 import android.view.LayoutInflater;
35 import android.view.Menu;
36 import android.view.MenuItem;
37 import android.view.View;
38 import android.view.accessibility.AccessibilityEvent;
39 import android.webkit.WebView;
40 import android.widget.ImageView;
41 
42 import com.android.browser.UrlInputView.StateListener;
43 
44 /**
45  * Ui for regular phone screen sizes
46  */
47 public 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      */
PhoneUi(Activity browser, UiController controller)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
onDestroy()76     public void onDestroy() {
77         hideTitleBar();
78     }
79 
80     @Override
editUrl(boolean clearInput, boolean forceIME)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
onBackKey()91     public boolean onBackKey() {
92         if (showingNavScreen()) {
93             mNavScreen.close(mUiController.getTabControl().getCurrentPosition());
94             return true;
95         }
96         return super.onBackKey();
97     }
98 
showingNavScreen()99     private boolean showingNavScreen() {
100         return mNavScreen != null && mNavScreen.getVisibility() == View.VISIBLE;
101     }
102 
103     @Override
dispatchKey(int code, KeyEvent event)104     public boolean dispatchKey(int code, KeyEvent event) {
105         return false;
106     }
107 
108     @Override
onProgressChanged(Tab tab)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
handleMessage(Message msg)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
setActiveTab(final Tab tab)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
onPrepareOptionsMenu(Menu menu)168     public boolean onPrepareOptionsMenu(Menu menu) {
169         updateMenuState(mActiveTab, menu);
170         return true;
171     }
172 
173     @Override
updateMenuState(Tab tab, Menu menu)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
onOptionsItemSelected(MenuItem item)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
onContextMenuCreated(Menu menu)218     public void onContextMenuCreated(Menu menu) {
219         hideTitleBar();
220     }
221 
222     @Override
onContextMenuClosed(Menu menu, boolean inLoad)223     public void onContextMenuClosed(Menu menu, boolean inLoad) {
224         if (inLoad) {
225             showTitleBar();
226         }
227     }
228 
229     // action mode callbacks
230 
231     @Override
onActionModeStarted(ActionMode mode)232     public void onActionModeStarted(ActionMode mode) {
233         if (!isEditingUrl()) {
234             hideTitleBar();
235         } else {
236             mTitleBar.animate().translationY(mActionBarHeight);
237         }
238     }
239 
240     @Override
onActionModeFinished(boolean inLoad)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
isWebShowing()252     public boolean isWebShowing() {
253         return super.isWebShowing() && !showingNavScreen();
254     }
255 
256     @Override
showWeb(boolean animate)257     public void showWeb(boolean animate) {
258         super.showWeb(animate);
259         hideNavScreen(mUiController.getTabControl().getCurrentPosition(), animate);
260     }
261 
showNavScreen()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 
finishAnimationIn()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 
hideNavScreen(int position, boolean animate)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 
finishAnimateOut()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
needsRestoreAllTabs()439     public boolean needsRestoreAllTabs() {
440         return false;
441     }
442 
toggleNavScreen()443     public void toggleNavScreen() {
444         if (!showingNavScreen()) {
445             showNavScreen();
446         } else {
447             hideNavScreen(mUiController.getTabControl().getCurrentPosition(), false);
448         }
449     }
450 
451     @Override
shouldCaptureThumbnails()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 
AnimScreen(Context ctx)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 
set(TitleBar tbar, WebView web)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 
safeCreateBitmap(int width, int height)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 
set(Bitmap image)523         public void set(Bitmap image) {
524             mTitle.setVisibility(View.GONE);
525             mContent.setImageBitmap(image);
526         }
527 
setScaleFactor(float sf)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 
getScaleFactor()535         private float getScaleFactor() {
536             return mScale;
537         }
538 
539     }
540 
541 }
542