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