TitleBar.java revision 11e71b1d376c5c41baf24f737b56b82d0e814988
1/*
2 * Copyright (C) 2009 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.content.Context;
20import android.content.Intent;
21import android.content.pm.PackageManager;
22import android.content.pm.ResolveInfo;
23import android.content.res.Resources;
24import android.graphics.Bitmap;
25import android.graphics.Color;
26import android.graphics.Rect;
27import android.graphics.drawable.Animatable;
28import android.graphics.drawable.BitmapDrawable;
29import android.graphics.drawable.Drawable;
30import android.graphics.drawable.LayerDrawable;
31import android.graphics.drawable.PaintDrawable;
32import android.os.Handler;
33import android.os.Message;
34import android.speech.RecognizerIntent;
35import android.util.TypedValue;
36import android.view.ContextMenu;
37import android.view.LayoutInflater;
38import android.view.MenuInflater;
39import android.view.MotionEvent;
40import android.view.View;
41import android.view.ViewConfiguration;
42import android.widget.ImageView;
43import android.widget.LinearLayout;
44import android.widget.ProgressBar;
45import android.widget.TextView;
46
47/**
48 * This class represents a title bar for a particular "tab" or "window" in the
49 * browser.
50 */
51public class TitleBar extends LinearLayout {
52    private TextView        mTitle;
53    private Drawable        mCloseDrawable;
54    private ImageView       mRtButton;
55    private Drawable        mCircularProgress;
56    private ProgressBar     mHorizontalProgress;
57    private ImageView       mFavicon;
58    private ImageView       mLockIcon;
59    private Drawable        mStopDrawable;
60    private Drawable        mBookmarkDrawable;
61    private Drawable        mVoiceDrawable;
62    private boolean         mInLoad;
63    private BrowserActivity mBrowserActivity;
64    private Drawable        mGenericFavicon;
65    private int             mIconDimension;
66    private View            mTitleBg;
67    private MyHandler       mHandler;
68    private Intent          mVoiceSearchIntent;
69    private boolean         mInVoiceMode;
70
71    private static int LONG_PRESS = 1;
72
73    public TitleBar(BrowserActivity context) {
74        super(context, null);
75        mHandler = new MyHandler();
76        LayoutInflater factory = LayoutInflater.from(context);
77        factory.inflate(R.layout.title_bar, this);
78        mBrowserActivity = context;
79
80        mTitle = (TextView) findViewById(R.id.title);
81        mTitle.setCompoundDrawablePadding(5);
82
83        mTitleBg = findViewById(R.id.title_bg);
84        mLockIcon = (ImageView) findViewById(R.id.lock);
85        mFavicon = (ImageView) findViewById(R.id.favicon);
86
87        mRtButton = (ImageView) findViewById(R.id.rt_btn);
88        Resources resources = context.getResources();
89        mCircularProgress = (Drawable) resources.getDrawable(
90                com.android.internal.R.drawable.search_spinner);
91        mIconDimension = (int) TypedValue.applyDimension(
92                TypedValue.COMPLEX_UNIT_DIP, 20f,
93                resources.getDisplayMetrics());
94        mCircularProgress.setBounds(0, 0, mIconDimension, mIconDimension);
95        mHorizontalProgress = (ProgressBar) findViewById(
96                R.id.progress_horizontal);
97        mGenericFavicon = context.getResources().getDrawable(
98                R.drawable.app_web_browser_sm);
99        mVoiceSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
100        mVoiceSearchIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
101                RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
102        PackageManager pm = context.getPackageManager();
103        ResolveInfo ri = pm.resolveActivity(mVoiceSearchIntent,
104                PackageManager.MATCH_DEFAULT_ONLY);
105        if (ri == null) {
106            mVoiceSearchIntent = null;
107        } else {
108            mVoiceDrawable = resources.getDrawable(
109                    android.R.drawable.ic_btn_speak_now);
110        }
111        mStopDrawable = resources.getDrawable(R.drawable.ic_btn_stop_v2);
112        mBookmarkDrawable = mRtButton.getDrawable();
113    }
114
115    private class MyHandler extends Handler {
116        public void handleMessage(Message msg) {
117            if (msg.what == LONG_PRESS) {
118                // Prevent the normal action from happening by setting the title
119                // bar's state to false.
120                mTitleBg.setPressed(false);
121                // Need to call a special method on BrowserActivity for when the
122                // fake title bar is up, because its ViewGroup does not show a
123                // context menu.
124                mBrowserActivity.showTitleBarContextMenu();
125            }
126        }
127    };
128
129    @Override
130    protected void onCreateContextMenu(ContextMenu menu) {
131        MenuInflater inflater = mBrowserActivity.getMenuInflater();
132        inflater.inflate(R.menu.title_context, menu);
133    }
134
135    @Override
136    public boolean onTouchEvent(MotionEvent event) {
137        switch (event.getAction()) {
138            case MotionEvent.ACTION_DOWN:
139                // Make all touches hit either the textfield or the button,
140                // depending on which side of the right edge of the textfield
141                // they hit.
142                if ((int) event.getX() > mTitleBg.getRight()) {
143                    mRtButton.setPressed(true);
144                } else {
145                    mTitleBg.setPressed(true);
146                    mHandler.sendMessageDelayed(mHandler.obtainMessage(
147                            LONG_PRESS),
148                            ViewConfiguration.getLongPressTimeout());
149                }
150                break;
151            case MotionEvent.ACTION_MOVE:
152                int slop = ViewConfiguration.get(mBrowserActivity)
153                        .getScaledTouchSlop();
154                if ((int) event.getY() > getHeight() + slop) {
155                    // We only trigger the actions in ACTION_UP if one or the
156                    // other is pressed.  Since the user moved off the title
157                    // bar, mark both as not pressed.
158                    mTitleBg.setPressed(false);
159                    mRtButton.setPressed(false);
160                    mHandler.removeMessages(LONG_PRESS);
161                    break;
162                }
163                int x = (int) event.getX();
164                int titleRight = mTitleBg.getRight();
165                if (mTitleBg.isPressed() && x > titleRight + slop) {
166                    mTitleBg.setPressed(false);
167                    mHandler.removeMessages(LONG_PRESS);
168                } else if (mRtButton.isPressed() && x < titleRight - slop) {
169                    mRtButton.setPressed(false);
170                }
171                break;
172            case MotionEvent.ACTION_CANCEL:
173                mRtButton.setPressed(false);
174                mTitleBg.setPressed(false);
175                mHandler.removeMessages(LONG_PRESS);
176                break;
177            case MotionEvent.ACTION_UP:
178                if (mRtButton.isPressed()) {
179                    if (mInVoiceMode) {
180                        mBrowserActivity.startActivity(mVoiceSearchIntent);
181                    } else if (mInLoad) {
182                        mBrowserActivity.stopLoading();
183                    } else {
184                        mBrowserActivity.bookmarksOrHistoryPicker(false);
185                    }
186                    mRtButton.setPressed(false);
187                } else if (mTitleBg.isPressed()) {
188                    mHandler.removeMessages(LONG_PRESS);
189                    mBrowserActivity.onSearchRequested();
190                    mTitleBg.setPressed(false);
191                }
192                break;
193            default:
194                break;
195        }
196        return true;
197    }
198
199    /**
200     * Set a new Bitmap for the Favicon.
201     */
202    /* package */ void setFavicon(Bitmap icon) {
203        Drawable[] array = new Drawable[3];
204        array[0] = new PaintDrawable(Color.BLACK);
205        PaintDrawable p = new PaintDrawable(Color.WHITE);
206        array[1] = p;
207        if (icon == null) {
208            array[2] = mGenericFavicon;
209        } else {
210            array[2] = new BitmapDrawable(icon);
211        }
212        LayerDrawable d = new LayerDrawable(array);
213        d.setLayerInset(1, 1, 1, 1, 1);
214        d.setLayerInset(2, 2, 2, 2, 2);
215        mFavicon.setImageDrawable(d);
216    }
217
218    /**
219     * Change the TitleBar to or from voice mode.  If there is no package to
220     * handle voice search, the TitleBar cannot be set to voice mode.
221     */
222    /* package */ void setInVoiceMode(boolean inVoiceMode) {
223        if (mInVoiceMode == inVoiceMode) return;
224        mInVoiceMode = inVoiceMode && mVoiceSearchIntent != null;
225        if (mInVoiceMode) {
226            mRtButton.setImageDrawable(mVoiceDrawable);
227        } else if (mInLoad) {
228            mRtButton.setImageDrawable(mStopDrawable);
229        } else {
230            mRtButton.setImageDrawable(mBookmarkDrawable);
231        }
232    }
233
234    /**
235     * Set the Drawable for the lock icon, or null to hide it.
236     */
237    /* package */ void setLock(Drawable d) {
238        if (null == d) {
239            mLockIcon.setVisibility(View.GONE);
240        } else {
241            mLockIcon.setImageDrawable(d);
242            mLockIcon.setVisibility(View.VISIBLE);
243        }
244    }
245
246    /**
247     * Update the progress, from 0 to 100.
248     */
249    /* package */ void setProgress(int newProgress) {
250        if (newProgress >= mHorizontalProgress.getMax()) {
251            mTitle.setCompoundDrawables(null, null, null, null);
252            ((Animatable) mCircularProgress).stop();
253            mHorizontalProgress.setVisibility(View.INVISIBLE);
254            if (!mInVoiceMode) {
255                mRtButton.setImageDrawable(mBookmarkDrawable);
256            }
257            mInLoad = false;
258        } else {
259            mHorizontalProgress.setProgress(newProgress);
260            if (!mInLoad && getWindowToken() != null) {
261                // checking the window token lets us be sure that we
262                // are attached to a window before starting the animation,
263                // preventing a potential race condition
264                // (fix for bug http://b/2115736)
265                mTitle.setCompoundDrawables(null, null, mCircularProgress,
266                        null);
267                ((Animatable) mCircularProgress).start();
268                mHorizontalProgress.setVisibility(View.VISIBLE);
269                if (!mInVoiceMode) {
270                    mRtButton.setImageDrawable(mStopDrawable);
271                }
272                mInLoad = true;
273            }
274        }
275    }
276
277    /**
278     * Update the title and url.
279     */
280    /* package */ void setTitleAndUrl(CharSequence title, CharSequence url) {
281        if (url == null) {
282            mTitle.setText(R.string.title_bar_loading);
283        } else {
284            mTitle.setText(url.toString());
285        }
286    }
287
288    /* package */ void setToTabPicker() {
289        mTitle.setText(R.string.tab_picker_title);
290        setFavicon(null);
291        setLock(null);
292        mHorizontalProgress.setVisibility(View.GONE);
293    }
294}
295