blob: 01435b61ad081fe823e8bd8a71e11a368abcb297 [file] [log] [blame]
John Reck0f602f32011-07-07 15:38:43 -07001/*
2 * Copyright (C) 2011 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 */
Bijan Amirzada41242f22014-03-21 12:12:18 -070016package com.android.browser;
John Reck0f602f32011-07-07 15:38:43 -070017
Enrico Ros1f5a0952014-11-18 20:15:48 -080018import android.app.Activity;
John Reck0f602f32011-07-07 15:38:43 -070019import android.app.SearchManager;
kaiyizc4ada322013-07-30 09:58:07 +080020import android.content.ActivityNotFoundException;
John Reck0f602f32011-07-07 15:38:43 -070021import android.content.Context;
22import android.content.Intent;
23import android.graphics.Bitmap;
24import android.graphics.drawable.Drawable;
kaiyizc4ada322013-07-30 09:58:07 +080025import android.net.Uri;
John Reck0f602f32011-07-07 15:38:43 -070026import android.os.Bundle;
Narayan Kamathf3174a52011-11-17 14:43:32 +000027import android.text.Editable;
28import android.text.TextWatcher;
John Reck0f602f32011-07-07 15:38:43 -070029import android.util.AttributeSet;
kaiyizc4ada322013-07-30 09:58:07 +080030import android.util.Log;
John Reck0f602f32011-07-07 15:38:43 -070031import android.view.KeyEvent;
Enrico Ros1f5a0952014-11-18 20:15:48 -080032import android.view.Menu;
33import android.view.MenuItem;
John Reck0f602f32011-07-07 15:38:43 -070034import android.view.View;
35import android.view.View.OnClickListener;
36import android.view.View.OnFocusChangeListener;
Enrico Ros1f5a0952014-11-18 20:15:48 -080037import android.view.ViewConfiguration;
John Reck0f602f32011-07-07 15:38:43 -070038import android.widget.ImageView;
39import android.widget.LinearLayout;
Enrico Ros1f5a0952014-11-18 20:15:48 -080040import android.widget.PopupMenu;
kaiyizc4ada322013-07-30 09:58:07 +080041import android.widget.Toast;
John Reck0f602f32011-07-07 15:38:43 -070042
Bijan Amirzada41242f22014-03-21 12:12:18 -070043import com.android.browser.R;
44import com.android.browser.UrlInputView.UrlInputListener;
John Reck0f602f32011-07-07 15:38:43 -070045
kaiyizc4ada322013-07-30 09:58:07 +080046import java.io.UnsupportedEncodingException;
kaiyiz950eca22013-08-08 11:01:28 +080047import java.net.URISyntaxException;
kaiyizc4ada322013-07-30 09:58:07 +080048
Bijan Amirzada9b1e9882014-02-26 17:15:46 -080049import org.codeaurora.swe.WebView;
50
John Reck42229bc2011-08-19 13:26:43 -070051public class NavigationBarBase extends LinearLayout implements
52 OnClickListener, UrlInputListener, OnFocusChangeListener,
Enrico Ros1f5a0952014-11-18 20:15:48 -080053 TextWatcher, UrlInputView.StateListener,
54 PopupMenu.OnMenuItemClickListener, PopupMenu.OnDismissListener {
John Reck0f602f32011-07-07 15:38:43 -070055
kaiyizc4ada322013-07-30 09:58:07 +080056 private final static String TAG = "NavigationBarBase";
57
John Reck0f602f32011-07-07 15:38:43 -070058 protected BaseUi mBaseUi;
59 protected TitleBar mTitleBar;
60 protected UiController mUiController;
61 protected UrlInputView mUrlInput;
Axesh R. Ajmera2e241242014-05-19 15:53:38 -070062 protected LocationButton mLocationButton;
John Reck0f602f32011-07-07 15:38:43 -070063
64 private ImageView mFavicon;
65 private ImageView mLockIcon;
John Reck0f602f32011-07-07 15:38:43 -070066
Enrico Ros1f5a0952014-11-18 20:15:48 -080067 private View mMore;
68 private PopupMenu mPopupMenu;
69 private boolean mOverflowMenuShowing;
70 private boolean mNeedsMenu;
Axesh R. Ajmera2e241242014-05-19 15:53:38 -070071
John Reck0f602f32011-07-07 15:38:43 -070072 public NavigationBarBase(Context context) {
73 super(context);
74 }
75
76 public NavigationBarBase(Context context, AttributeSet attrs) {
77 super(context, attrs);
78 }
79
80 public NavigationBarBase(Context context, AttributeSet attrs, int defStyle) {
81 super(context, attrs, defStyle);
82 }
83
84 @Override
85 protected void onFinishInflate() {
86 super.onFinishInflate();
87 mLockIcon = (ImageView) findViewById(R.id.lock);
Tarun Nainani8eb00912014-07-17 12:28:32 -070088 mLocationButton = (LocationButton) findViewById(R.id.location_button);
John Reck0f602f32011-07-07 15:38:43 -070089 mFavicon = (ImageView) findViewById(R.id.favicon);
90 mUrlInput = (UrlInputView) findViewById(R.id.url);
91 mUrlInput.setUrlInputListener(this);
John Reck0f602f32011-07-07 15:38:43 -070092 mUrlInput.setOnFocusChangeListener(this);
93 mUrlInput.setSelectAllOnFocus(true);
Narayan Kamathf3174a52011-11-17 14:43:32 +000094 mUrlInput.addTextChangedListener(this);
Enrico Ros1f5a0952014-11-18 20:15:48 -080095 mMore = findViewById(R.id.more_browser_settings);
96 mMore.setOnClickListener(this);
97 mNeedsMenu = !ViewConfiguration.get(getContext()).hasPermanentMenuKey();
John Reck0f602f32011-07-07 15:38:43 -070098 }
99
100 public void setTitleBar(TitleBar titleBar) {
101 mTitleBar = titleBar;
102 mBaseUi = mTitleBar.getUi();
103 mUiController = mTitleBar.getUiController();
Michael Kolbb2e91fd2011-07-14 13:47:29 -0700104 mUrlInput.setController(mUiController);
John Reck0f602f32011-07-07 15:38:43 -0700105 }
106
107 public void setLock(Drawable d) {
108 if (mLockIcon == null) return;
109 if (d == null) {
110 mLockIcon.setVisibility(View.GONE);
111 } else {
112 mLockIcon.setImageDrawable(d);
113 mLockIcon.setVisibility(View.VISIBLE);
114 }
115 }
116
117 public void setFavicon(Bitmap icon) {
118 if (mFavicon == null) return;
119 mFavicon.setImageDrawable(mBaseUi.getFaviconDrawable(icon));
120 }
121
John Reck0f602f32011-07-07 15:38:43 -0700122 @Override
123 public void onClick(View v) {
Enrico Ros1f5a0952014-11-18 20:15:48 -0800124 if (mMore == v) {
125 showMenu(mMore);
126 }
127 }
128
129 void showMenu(View anchor) {
130 Activity activity = mUiController.getActivity();
131 if (mPopupMenu == null) {
132 mPopupMenu = new PopupMenu(getContext(), anchor);
133 mPopupMenu.setOnMenuItemClickListener(this);
134 mPopupMenu.setOnDismissListener(this);
135 if (!activity.onCreateOptionsMenu(mPopupMenu.getMenu())) {
136 mPopupMenu = null;
137 return;
138 }
139 }
140 Menu menu = mPopupMenu.getMenu();
141
142 if (mUiController instanceof Controller) {
143 Controller controller = (Controller) mUiController;
144 if (controller.onPrepareOptionsMenu(menu)) {
145 mOverflowMenuShowing = true;
146 }
147 }
John Reck0f602f32011-07-07 15:38:43 -0700148 }
149
150 @Override
151 public void onFocusChange(View view, boolean hasFocus) {
152 // if losing focus and not in touch mode, leave as is
153 if (hasFocus || view.isInTouchMode() || mUrlInput.needsUpdate()) {
154 setFocusState(hasFocus);
155 }
156 if (hasFocus) {
157 mBaseUi.showTitleBar();
John Reck0f602f32011-07-07 15:38:43 -0700158 } else if (!mUrlInput.needsUpdate()) {
159 mUrlInput.dismissDropDown();
160 mUrlInput.hideIME();
161 if (mUrlInput.getText().length() == 0) {
162 Tab currentTab = mUiController.getTabControl().getCurrentTab();
163 if (currentTab != null) {
John Reck434e9f82011-08-10 18:16:52 -0700164 setDisplayTitle(currentTab.getUrl());
John Reck0f602f32011-07-07 15:38:43 -0700165 }
166 }
167 mBaseUi.suggestHideTitleBar();
168 }
169 mUrlInput.clearNeedsUpdate();
170 }
171
172 protected void setFocusState(boolean focus) {
173 }
174
John Reck0f602f32011-07-07 15:38:43 -0700175 public boolean isEditingUrl() {
176 return mUrlInput.hasFocus();
177 }
178
179 void stopEditingUrl() {
Michael Kolb0b129122012-06-04 16:31:58 -0700180 WebView currentTopWebView = mUiController.getCurrentTopWebView();
181 if (currentTopWebView != null) {
182 currentTopWebView.requestFocus();
183 }
John Reck0f602f32011-07-07 15:38:43 -0700184 }
185
186 void setDisplayTitle(String title) {
187 if (!isEditingUrl()) {
Michael Kolb29aab402012-05-29 17:22:35 -0700188 if (!title.equals(mUrlInput.getText().toString())) {
189 mUrlInput.setText(title, false);
190 }
John Reck0f602f32011-07-07 15:38:43 -0700191 }
192 }
193
John Reck0f602f32011-07-07 15:38:43 -0700194 void setIncognitoMode(boolean incognito) {
195 mUrlInput.setIncognitoMode(incognito);
196 }
197
198 void clearCompletions() {
Narayan Kamathf3174a52011-11-17 14:43:32 +0000199 mUrlInput.dismissDropDown();
John Reck0f602f32011-07-07 15:38:43 -0700200 }
201
202 // UrlInputListener implementation
203
204 /**
205 * callback from suggestion dropdown
206 * user selected a suggestion
207 */
208 @Override
209 public void onAction(String text, String extra, String source) {
Michael Kolb0b129122012-06-04 16:31:58 -0700210 stopEditingUrl();
John Reck0f602f32011-07-07 15:38:43 -0700211 if (UrlInputView.TYPED.equals(source)) {
kaiyizc4ada322013-07-30 09:58:07 +0800212 String url = null;
Bijan Amirzadae75909d2014-05-06 14:18:54 -0700213 boolean wap2estore = getContext().getResources().getBoolean(R.bool.wap2estore);
Vivek Sekhar5e631472014-03-31 23:59:10 -0700214 if ((wap2estore && isEstoreTypeUrl(text)) || isRtspTypeUrl(text)
215 || isMakeCallTypeUrl(text)) {
kaiyizc4ada322013-07-30 09:58:07 +0800216 url = text;
217 } else {
218 url = UrlUtils.smartUrlFilter(text, false);
219 }
220
John Reck0f602f32011-07-07 15:38:43 -0700221 Tab t = mBaseUi.getActiveTab();
222 // Only shortcut javascript URIs for now, as there is special
223 // logic in UrlHandler for other schemas
224 if (url != null && t != null && url.startsWith("javascript:")) {
225 mUiController.loadUrl(t, url);
226 setDisplayTitle(text);
227 return;
228 }
kaiyizc4ada322013-07-30 09:58:07 +0800229
230 // add for carrier wap2estore feature
231 if (url != null && t != null && wap2estore && isEstoreTypeUrl(url)) {
Vivek Sekharb54614f2014-05-01 19:03:37 -0700232 if (handleEstoreTypeUrl(url)) {
233 setDisplayTitle(text);
234 return;
235 }
kaiyizc4ada322013-07-30 09:58:07 +0800236 }
kaiyiz950eca22013-08-08 11:01:28 +0800237 // add for rtsp scheme feature
238 if (url != null && t != null && isRtspTypeUrl(url)) {
239 if (handleRtspTypeUrl(url)) {
Vivek Sekharb54614f2014-05-01 19:03:37 -0700240 setDisplayTitle(text);
kaiyiz950eca22013-08-08 11:01:28 +0800241 return;
242 }
243 }
Vivek Sekhar5e631472014-03-31 23:59:10 -0700244 // add for "wtai://wp/mc;" scheme feature
245 if (url != null && t != null && isMakeCallTypeUrl(url)) {
246 if (handleMakeCallTypeUrl(url)) {
247 return;
248 }
249 }
John Reck0f602f32011-07-07 15:38:43 -0700250 }
251 Intent i = new Intent();
Michael Kolb5ff5c8b2012-05-03 11:37:58 -0700252 String action = Intent.ACTION_SEARCH;
John Reck0f602f32011-07-07 15:38:43 -0700253 i.setAction(action);
254 i.putExtra(SearchManager.QUERY, text);
255 if (extra != null) {
256 i.putExtra(SearchManager.EXTRA_DATA_KEY, extra);
257 }
258 if (source != null) {
259 Bundle appData = new Bundle();
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800260 appData.putString("source", source);
261 i.putExtra("source", appData);
John Reck0f602f32011-07-07 15:38:43 -0700262 }
263 mUiController.handleNewIntent(i);
264 setDisplayTitle(text);
265 }
266
Vivek Sekhar5e631472014-03-31 23:59:10 -0700267 private boolean isMakeCallTypeUrl(String url) {
268 String utf8Url = null;
269 try {
270 utf8Url = new String(url.getBytes("UTF-8"), "UTF-8");
271 } catch (UnsupportedEncodingException e) {
272 Log.e(TAG, "err " + e);
273 }
274 if (utf8Url != null && utf8Url.startsWith(UrlHandler.SCHEME_WTAI_MC)) {
275 return true;
276 }
277 return false;
278 }
279
280 private boolean handleMakeCallTypeUrl(String url) {
281 // wtai://wp/mc;number
282 // number=string(phone-number)
283 if (url.startsWith(UrlHandler.SCHEME_WTAI_MC)) {
284 Intent intent = new Intent(Intent.ACTION_VIEW,
285 Uri.parse(WebView.SCHEME_TEL +
286 url.substring(UrlHandler.SCHEME_WTAI_MC.length())));
287 getContext().startActivity(intent);
288 // before leaving BrowserActivity, close the empty child tab.
289 // If a new tab is created through JavaScript open to load this
290 // url, we would like to close it as we will load this url in a
291 // different Activity.
292 Tab current = mUiController.getCurrentTab();
293 if (current != null
294 && current.getWebView().copyBackForwardList().getSize() == 0) {
295 mUiController.closeCurrentTab();
296 }
297 return true;
298 }
299 return false;
300 }
301
kaiyizc4ada322013-07-30 09:58:07 +0800302 private boolean isEstoreTypeUrl(String url) {
Vivek Sekharb54614f2014-05-01 19:03:37 -0700303 if (url != null && url.startsWith("estore:")) {
kaiyizc4ada322013-07-30 09:58:07 +0800304 return true;
305 }
306 return false;
307 }
308
Vivek Sekharb54614f2014-05-01 19:03:37 -0700309 private boolean handleEstoreTypeUrl(String url) {
310 if (url.getBytes().length > 256) {
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800311 Toast.makeText(getContext(), R.string.estore_url_warning, Toast.LENGTH_LONG).show();
Vivek Sekharb54614f2014-05-01 19:03:37 -0700312 return false;
kaiyizc4ada322013-07-30 09:58:07 +0800313 }
Vivek Sekharb54614f2014-05-01 19:03:37 -0700314
315 Intent intent;
316 // perform generic parsing of the URI to turn it into an Intent.
317 try {
318 intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
319 } catch (URISyntaxException ex) {
320 Log.w("Browser", "Bad URI " + url + ": " + ex.getMessage());
321 return false;
322 }
323
kaiyizc4ada322013-07-30 09:58:07 +0800324 try {
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800325 getContext().startActivity(intent);
kaiyizc4ada322013-07-30 09:58:07 +0800326 } catch (ActivityNotFoundException ex) {
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800327 String downloadUrl = getContext().getResources().getString(R.string.estore_homepage);
kaiyizc4ada322013-07-30 09:58:07 +0800328 mUiController.loadUrl(mBaseUi.getActiveTab(), downloadUrl);
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800329 Toast.makeText(getContext(), R.string.download_estore_app, Toast.LENGTH_LONG).show();
kaiyizc4ada322013-07-30 09:58:07 +0800330 }
Vivek Sekharb54614f2014-05-01 19:03:37 -0700331
332 return true;
kaiyizc4ada322013-07-30 09:58:07 +0800333 }
334
kaiyiz950eca22013-08-08 11:01:28 +0800335 private boolean isRtspTypeUrl(String url) {
336 String utf8Url = null;
337 try {
338 utf8Url = new String(url.getBytes("UTF-8"), "UTF-8");
339 } catch (UnsupportedEncodingException e) {
340 Log.e(TAG, "err " + e);
341 }
342 if (utf8Url != null && utf8Url.startsWith("rtsp://")) {
343 return true;
344 }
345 return false;
346 }
347
348 private boolean handleRtspTypeUrl(String url) {
349 Intent intent;
350 // perform generic parsing of the URI to turn it into an Intent.
351 try {
352 intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
353 } catch (URISyntaxException ex) {
354 Log.w("Browser", "Bad URI " + url + ": " + ex.getMessage());
355 return false;
356 }
357
358 try {
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800359 getContext().startActivity(intent);
kaiyiz950eca22013-08-08 11:01:28 +0800360 } catch (ActivityNotFoundException ex) {
361 Log.w("Browser", "No resolveActivity " + url);
362 return false;
363 }
364 return true;
365 }
366
John Reck0f602f32011-07-07 15:38:43 -0700367 @Override
368 public void onDismiss() {
369 final Tab currentTab = mBaseUi.getActiveTab();
370 mBaseUi.hideTitleBar();
371 post(new Runnable() {
372 public void run() {
373 clearFocus();
Michael Kolb5ff5c8b2012-05-03 11:37:58 -0700374 if (currentTab != null) {
John Reck0f602f32011-07-07 15:38:43 -0700375 setDisplayTitle(currentTab.getUrl());
376 }
377 }
378 });
379 }
380
381 /**
382 * callback from the suggestion dropdown
383 * copy text to input field and stay in edit mode
384 */
385 @Override
386 public void onCopySuggestion(String text) {
387 mUrlInput.setText(text, true);
388 if (text != null) {
389 mUrlInput.setSelection(text.length());
390 }
391 }
392
393 public void setCurrentUrlIsBookmark(boolean isBookmark) {
394 }
395
396 @Override
397 public boolean dispatchKeyEventPreIme(KeyEvent evt) {
398 if (evt.getKeyCode() == KeyEvent.KEYCODE_BACK) {
399 // catch back key in order to do slightly more cleanup than usual
Michael Kolb0b129122012-06-04 16:31:58 -0700400 stopEditingUrl();
John Reck0f602f32011-07-07 15:38:43 -0700401 }
402 return super.dispatchKeyEventPreIme(evt);
403 }
404
John Reck0f602f32011-07-07 15:38:43 -0700405 /**
406 * called from the Ui when the user wants to edit
407 * @param clearInput clear the input field
408 */
Michael Kolb1f9b3562012-04-24 14:38:34 -0700409 void startEditingUrl(boolean clearInput, boolean forceIME) {
John Reck0f602f32011-07-07 15:38:43 -0700410 // editing takes preference of progress
411 setVisibility(View.VISIBLE);
John Reck0f602f32011-07-07 15:38:43 -0700412 if (!mUrlInput.hasFocus()) {
413 mUrlInput.requestFocus();
414 }
415 if (clearInput) {
416 mUrlInput.setText("");
John Reck0f602f32011-07-07 15:38:43 -0700417 }
Michael Kolb1f9b3562012-04-24 14:38:34 -0700418 if (forceIME) {
Michael Kolb4bb6fcb2012-04-13 14:25:27 -0700419 mUrlInput.showIME();
420 }
John Reck0f602f32011-07-07 15:38:43 -0700421 }
422
423 public void onProgressStarted() {
424 }
425
426 public void onProgressStopped() {
427 }
428
John Reck419f6b42011-08-16 16:10:51 -0700429 public void onTabDataChanged(Tab tab) {
Tarun Nainani8eb00912014-07-17 12:28:32 -0700430 mLocationButton.onTabDataChanged(tab);
John Reck419f6b42011-08-16 16:10:51 -0700431 }
432
Michael Kolb0b129122012-06-04 16:31:58 -0700433 public void onVoiceResult(String s) {
434 startEditingUrl(true, true);
435 onCopySuggestion(s);
436 }
437
Narayan Kamathf3174a52011-11-17 14:43:32 +0000438 @Override
439 public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
440
441 @Override
Michael Kolb5ff5c8b2012-05-03 11:37:58 -0700442 public void onTextChanged(CharSequence s, int start, int before, int count) { }
Narayan Kamathf3174a52011-11-17 14:43:32 +0000443
444 @Override
445 public void afterTextChanged(Editable s) { }
Michael Kolb0b129122012-06-04 16:31:58 -0700446
Enrico Ros1f5a0952014-11-18 20:15:48 -0800447 @Override
448 public void onStateChanged(int state) {
449 switch(state) {
450 case STATE_NORMAL:
451 mMore.setVisibility(mNeedsMenu ? View.VISIBLE : View.GONE);
452 break;
453 case STATE_HIGHLIGHTED:
454 mMore.setVisibility(View.GONE);
455 break;
456 case STATE_EDITED:
457 mMore.setVisibility(View.GONE);
458 break;
459 }
460 }
461
462 public boolean isMenuShowing() {
463 return mOverflowMenuShowing;
464 }
465
466
467 @Override
468 public void onDismiss(PopupMenu popupMenu) {
469 if (popupMenu == mPopupMenu) {
470 onMenuHidden();
471 }
472 }
473
474 private void onMenuHidden() {
475 mOverflowMenuShowing = false;
476 mBaseUi.showTitleBarForDuration();
477 }
478
479
480 @Override
481 public boolean onMenuItemClick(MenuItem item) {
482 return mUiController.onOptionsItemSelected(item);
483 }
John Reck0f602f32011-07-07 15:38:43 -0700484}