blob: 819608d0fbe09e59f25f64db90c347aa3e618b7e [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;
Panos Thomas4bdb5252014-11-13 16:20:11 -0800213 boolean wap2estore = BrowserConfig.getInstance(getContext())
214 .hasFeature(BrowserConfig.Feature.WAP2ESTORE);
Vivek Sekhar5e631472014-03-31 23:59:10 -0700215 if ((wap2estore && isEstoreTypeUrl(text)) || isRtspTypeUrl(text)
216 || isMakeCallTypeUrl(text)) {
kaiyizc4ada322013-07-30 09:58:07 +0800217 url = text;
218 } else {
219 url = UrlUtils.smartUrlFilter(text, false);
220 }
221
John Reck0f602f32011-07-07 15:38:43 -0700222 Tab t = mBaseUi.getActiveTab();
223 // Only shortcut javascript URIs for now, as there is special
224 // logic in UrlHandler for other schemas
225 if (url != null && t != null && url.startsWith("javascript:")) {
226 mUiController.loadUrl(t, url);
227 setDisplayTitle(text);
228 return;
229 }
kaiyizc4ada322013-07-30 09:58:07 +0800230
231 // add for carrier wap2estore feature
232 if (url != null && t != null && wap2estore && isEstoreTypeUrl(url)) {
Vivek Sekharb54614f2014-05-01 19:03:37 -0700233 if (handleEstoreTypeUrl(url)) {
234 setDisplayTitle(text);
235 return;
236 }
kaiyizc4ada322013-07-30 09:58:07 +0800237 }
kaiyiz950eca22013-08-08 11:01:28 +0800238 // add for rtsp scheme feature
239 if (url != null && t != null && isRtspTypeUrl(url)) {
240 if (handleRtspTypeUrl(url)) {
Vivek Sekharb54614f2014-05-01 19:03:37 -0700241 setDisplayTitle(text);
kaiyiz950eca22013-08-08 11:01:28 +0800242 return;
243 }
244 }
Vivek Sekhar5e631472014-03-31 23:59:10 -0700245 // add for "wtai://wp/mc;" scheme feature
246 if (url != null && t != null && isMakeCallTypeUrl(url)) {
247 if (handleMakeCallTypeUrl(url)) {
248 return;
249 }
250 }
John Reck0f602f32011-07-07 15:38:43 -0700251 }
252 Intent i = new Intent();
Michael Kolb5ff5c8b2012-05-03 11:37:58 -0700253 String action = Intent.ACTION_SEARCH;
John Reck0f602f32011-07-07 15:38:43 -0700254 i.setAction(action);
255 i.putExtra(SearchManager.QUERY, text);
256 if (extra != null) {
257 i.putExtra(SearchManager.EXTRA_DATA_KEY, extra);
258 }
259 if (source != null) {
260 Bundle appData = new Bundle();
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800261 appData.putString("source", source);
262 i.putExtra("source", appData);
John Reck0f602f32011-07-07 15:38:43 -0700263 }
264 mUiController.handleNewIntent(i);
265 setDisplayTitle(text);
266 }
267
Vivek Sekhar5e631472014-03-31 23:59:10 -0700268 private boolean isMakeCallTypeUrl(String url) {
269 String utf8Url = null;
270 try {
271 utf8Url = new String(url.getBytes("UTF-8"), "UTF-8");
272 } catch (UnsupportedEncodingException e) {
273 Log.e(TAG, "err " + e);
274 }
275 if (utf8Url != null && utf8Url.startsWith(UrlHandler.SCHEME_WTAI_MC)) {
276 return true;
277 }
278 return false;
279 }
280
281 private boolean handleMakeCallTypeUrl(String url) {
282 // wtai://wp/mc;number
283 // number=string(phone-number)
284 if (url.startsWith(UrlHandler.SCHEME_WTAI_MC)) {
285 Intent intent = new Intent(Intent.ACTION_VIEW,
286 Uri.parse(WebView.SCHEME_TEL +
287 url.substring(UrlHandler.SCHEME_WTAI_MC.length())));
288 getContext().startActivity(intent);
289 // before leaving BrowserActivity, close the empty child tab.
290 // If a new tab is created through JavaScript open to load this
291 // url, we would like to close it as we will load this url in a
292 // different Activity.
293 Tab current = mUiController.getCurrentTab();
294 if (current != null
295 && current.getWebView().copyBackForwardList().getSize() == 0) {
296 mUiController.closeCurrentTab();
297 }
298 return true;
299 }
300 return false;
301 }
302
kaiyizc4ada322013-07-30 09:58:07 +0800303 private boolean isEstoreTypeUrl(String url) {
Vivek Sekharb54614f2014-05-01 19:03:37 -0700304 if (url != null && url.startsWith("estore:")) {
kaiyizc4ada322013-07-30 09:58:07 +0800305 return true;
306 }
307 return false;
308 }
309
Vivek Sekharb54614f2014-05-01 19:03:37 -0700310 private boolean handleEstoreTypeUrl(String url) {
311 if (url.getBytes().length > 256) {
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800312 Toast.makeText(getContext(), R.string.estore_url_warning, Toast.LENGTH_LONG).show();
Vivek Sekharb54614f2014-05-01 19:03:37 -0700313 return false;
kaiyizc4ada322013-07-30 09:58:07 +0800314 }
Vivek Sekharb54614f2014-05-01 19:03:37 -0700315
316 Intent intent;
317 // perform generic parsing of the URI to turn it into an Intent.
318 try {
319 intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
320 } catch (URISyntaxException ex) {
321 Log.w("Browser", "Bad URI " + url + ": " + ex.getMessage());
322 return false;
323 }
324
kaiyizc4ada322013-07-30 09:58:07 +0800325 try {
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800326 getContext().startActivity(intent);
kaiyizc4ada322013-07-30 09:58:07 +0800327 } catch (ActivityNotFoundException ex) {
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800328 String downloadUrl = getContext().getResources().getString(R.string.estore_homepage);
kaiyizc4ada322013-07-30 09:58:07 +0800329 mUiController.loadUrl(mBaseUi.getActiveTab(), downloadUrl);
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800330 Toast.makeText(getContext(), R.string.download_estore_app, Toast.LENGTH_LONG).show();
kaiyizc4ada322013-07-30 09:58:07 +0800331 }
Vivek Sekharb54614f2014-05-01 19:03:37 -0700332
333 return true;
kaiyizc4ada322013-07-30 09:58:07 +0800334 }
335
kaiyiz950eca22013-08-08 11:01:28 +0800336 private boolean isRtspTypeUrl(String url) {
337 String utf8Url = null;
338 try {
339 utf8Url = new String(url.getBytes("UTF-8"), "UTF-8");
340 } catch (UnsupportedEncodingException e) {
341 Log.e(TAG, "err " + e);
342 }
343 if (utf8Url != null && utf8Url.startsWith("rtsp://")) {
344 return true;
345 }
346 return false;
347 }
348
349 private boolean handleRtspTypeUrl(String url) {
350 Intent intent;
351 // perform generic parsing of the URI to turn it into an Intent.
352 try {
353 intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
354 } catch (URISyntaxException ex) {
355 Log.w("Browser", "Bad URI " + url + ": " + ex.getMessage());
356 return false;
357 }
358
359 try {
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800360 getContext().startActivity(intent);
kaiyiz950eca22013-08-08 11:01:28 +0800361 } catch (ActivityNotFoundException ex) {
362 Log.w("Browser", "No resolveActivity " + url);
363 return false;
364 }
365 return true;
366 }
367
John Reck0f602f32011-07-07 15:38:43 -0700368 @Override
369 public void onDismiss() {
370 final Tab currentTab = mBaseUi.getActiveTab();
371 mBaseUi.hideTitleBar();
372 post(new Runnable() {
373 public void run() {
374 clearFocus();
Michael Kolb5ff5c8b2012-05-03 11:37:58 -0700375 if (currentTab != null) {
John Reck0f602f32011-07-07 15:38:43 -0700376 setDisplayTitle(currentTab.getUrl());
377 }
378 }
379 });
380 }
381
382 /**
383 * callback from the suggestion dropdown
384 * copy text to input field and stay in edit mode
385 */
386 @Override
387 public void onCopySuggestion(String text) {
388 mUrlInput.setText(text, true);
389 if (text != null) {
390 mUrlInput.setSelection(text.length());
391 }
392 }
393
394 public void setCurrentUrlIsBookmark(boolean isBookmark) {
395 }
396
397 @Override
398 public boolean dispatchKeyEventPreIme(KeyEvent evt) {
399 if (evt.getKeyCode() == KeyEvent.KEYCODE_BACK) {
400 // catch back key in order to do slightly more cleanup than usual
Michael Kolb0b129122012-06-04 16:31:58 -0700401 stopEditingUrl();
John Reck0f602f32011-07-07 15:38:43 -0700402 }
403 return super.dispatchKeyEventPreIme(evt);
404 }
405
John Reck0f602f32011-07-07 15:38:43 -0700406 /**
407 * called from the Ui when the user wants to edit
408 * @param clearInput clear the input field
409 */
Michael Kolb1f9b3562012-04-24 14:38:34 -0700410 void startEditingUrl(boolean clearInput, boolean forceIME) {
John Reck0f602f32011-07-07 15:38:43 -0700411 // editing takes preference of progress
412 setVisibility(View.VISIBLE);
John Reck0f602f32011-07-07 15:38:43 -0700413 if (!mUrlInput.hasFocus()) {
414 mUrlInput.requestFocus();
415 }
416 if (clearInput) {
417 mUrlInput.setText("");
John Reck0f602f32011-07-07 15:38:43 -0700418 }
Michael Kolb1f9b3562012-04-24 14:38:34 -0700419 if (forceIME) {
Michael Kolb4bb6fcb2012-04-13 14:25:27 -0700420 mUrlInput.showIME();
421 }
John Reck0f602f32011-07-07 15:38:43 -0700422 }
423
424 public void onProgressStarted() {
425 }
426
427 public void onProgressStopped() {
428 }
429
John Reck419f6b42011-08-16 16:10:51 -0700430 public void onTabDataChanged(Tab tab) {
Tarun Nainani8eb00912014-07-17 12:28:32 -0700431 mLocationButton.onTabDataChanged(tab);
John Reck419f6b42011-08-16 16:10:51 -0700432 }
433
Michael Kolb0b129122012-06-04 16:31:58 -0700434 public void onVoiceResult(String s) {
435 startEditingUrl(true, true);
436 onCopySuggestion(s);
437 }
438
Narayan Kamathf3174a52011-11-17 14:43:32 +0000439 @Override
440 public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
441
442 @Override
Michael Kolb5ff5c8b2012-05-03 11:37:58 -0700443 public void onTextChanged(CharSequence s, int start, int before, int count) { }
Narayan Kamathf3174a52011-11-17 14:43:32 +0000444
445 @Override
446 public void afterTextChanged(Editable s) { }
Michael Kolb0b129122012-06-04 16:31:58 -0700447
Enrico Ros1f5a0952014-11-18 20:15:48 -0800448 @Override
449 public void onStateChanged(int state) {
450 switch(state) {
451 case STATE_NORMAL:
452 mMore.setVisibility(mNeedsMenu ? View.VISIBLE : View.GONE);
453 break;
454 case STATE_HIGHLIGHTED:
455 mMore.setVisibility(View.GONE);
456 break;
457 case STATE_EDITED:
458 mMore.setVisibility(View.GONE);
459 break;
460 }
461 }
462
463 public boolean isMenuShowing() {
464 return mOverflowMenuShowing;
465 }
466
467
468 @Override
469 public void onDismiss(PopupMenu popupMenu) {
470 if (popupMenu == mPopupMenu) {
471 onMenuHidden();
472 }
473 }
474
475 private void onMenuHidden() {
476 mOverflowMenuShowing = false;
477 mBaseUi.showTitleBarForDuration();
478 }
479
480
481 @Override
482 public boolean onMenuItemClick(MenuItem item) {
483 return mUiController.onOptionsItemSelected(item);
484 }
John Reck0f602f32011-07-07 15:38:43 -0700485}