2011-11-18 10:28:17 -08:00
|
|
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
2012-05-21 04:12:37 -07:00
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2011-11-18 10:28:17 -08:00
|
|
|
|
|
|
|
package org.mozilla.gecko;
|
|
|
|
|
|
|
|
import android.app.Activity;
|
2012-03-12 13:01:09 -07:00
|
|
|
import android.app.AlertDialog;
|
|
|
|
import android.content.DialogInterface;
|
2011-11-18 10:28:17 -08:00
|
|
|
import android.content.Intent;
|
2011-12-20 07:28:12 -08:00
|
|
|
import android.content.ContentResolver;
|
2011-11-18 10:28:17 -08:00
|
|
|
import android.content.Context;
|
2012-06-05 14:07:14 -07:00
|
|
|
import android.content.SharedPreferences;
|
2011-11-18 10:28:17 -08:00
|
|
|
import android.content.res.Configuration;
|
2011-12-20 07:28:12 -08:00
|
|
|
import android.database.Cursor;
|
|
|
|
import android.graphics.Bitmap;
|
|
|
|
import android.graphics.BitmapFactory;
|
2012-06-02 11:23:45 -07:00
|
|
|
import android.os.AsyncTask;
|
2011-11-18 10:28:17 -08:00
|
|
|
import android.os.Bundle;
|
|
|
|
import android.text.Editable;
|
2012-03-11 19:30:35 -07:00
|
|
|
import android.text.Spanned;
|
2012-04-20 11:49:30 -07:00
|
|
|
import android.text.TextUtils;
|
2011-11-18 10:28:17 -08:00
|
|
|
import android.text.TextWatcher;
|
|
|
|
import android.util.AttributeSet;
|
|
|
|
import android.util.Log;
|
2011-12-20 07:28:12 -08:00
|
|
|
import android.view.ContextMenu;
|
|
|
|
import android.view.ContextMenu.ContextMenuInfo;
|
2011-11-18 10:28:17 -08:00
|
|
|
import android.view.KeyEvent;
|
2012-03-23 12:00:17 -07:00
|
|
|
import android.view.LayoutInflater;
|
2011-12-20 07:28:12 -08:00
|
|
|
import android.view.MenuInflater;
|
|
|
|
import android.view.MenuItem;
|
2011-11-18 10:28:17 -08:00
|
|
|
import android.view.View;
|
|
|
|
import android.view.inputmethod.InputMethodManager;
|
2011-12-14 23:34:54 -08:00
|
|
|
import android.view.inputmethod.EditorInfo;
|
2011-12-20 07:28:12 -08:00
|
|
|
import android.widget.AdapterView;
|
2011-11-24 08:54:39 -08:00
|
|
|
import android.widget.Button;
|
2011-11-18 10:28:17 -08:00
|
|
|
import android.widget.EditText;
|
2011-12-26 22:07:46 -08:00
|
|
|
import android.widget.ExpandableListView;
|
2011-11-24 08:54:39 -08:00
|
|
|
import android.widget.ImageButton;
|
2011-12-20 07:28:12 -08:00
|
|
|
import android.widget.ListView;
|
2012-01-27 11:58:18 -08:00
|
|
|
import android.widget.TabWidget;
|
2012-01-15 13:51:23 -08:00
|
|
|
import android.widget.Toast;
|
2011-12-20 07:28:12 -08:00
|
|
|
|
2012-02-07 18:15:20 -08:00
|
|
|
import java.net.URLEncoder;
|
2012-06-05 14:07:14 -07:00
|
|
|
import java.util.ArrayList;
|
2011-12-26 22:07:46 -08:00
|
|
|
import java.util.Map;
|
|
|
|
|
2012-02-09 10:01:57 -08:00
|
|
|
import org.mozilla.gecko.db.BrowserContract.Bookmarks;
|
2012-05-14 11:44:34 -07:00
|
|
|
import org.mozilla.gecko.db.BrowserContract.Combined;
|
2011-12-20 07:28:12 -08:00
|
|
|
import org.mozilla.gecko.db.BrowserDB.URLColumns;
|
|
|
|
import org.mozilla.gecko.db.BrowserDB;
|
2011-11-18 10:28:17 -08:00
|
|
|
|
2011-12-07 17:52:07 -08:00
|
|
|
import org.json.JSONObject;
|
|
|
|
|
2012-04-03 11:58:01 -07:00
|
|
|
public class AwesomeBar extends GeckoActivity implements GeckoEventListener {
|
2011-11-18 10:28:17 -08:00
|
|
|
private static final String LOGTAG = "GeckoAwesomeBar";
|
|
|
|
|
2012-06-05 14:07:14 -07:00
|
|
|
private static final int SUGGESTION_TIMEOUT = 2000;
|
|
|
|
private static final int SUGGESTION_MAX = 3;
|
|
|
|
|
2011-11-18 10:28:17 -08:00
|
|
|
static final String URL_KEY = "url";
|
|
|
|
static final String CURRENT_URL_KEY = "currenturl";
|
2012-06-07 10:13:38 -07:00
|
|
|
static final String TARGET_KEY = "target";
|
2011-12-07 17:52:07 -08:00
|
|
|
static final String SEARCH_KEY = "search";
|
2012-02-22 18:26:04 -08:00
|
|
|
static final String USER_ENTERED_KEY = "user_entered";
|
2012-06-07 10:13:38 -07:00
|
|
|
static enum Target { NEW_TAB, CURRENT_TAB };
|
2011-11-18 10:28:17 -08:00
|
|
|
|
2012-06-07 10:13:38 -07:00
|
|
|
private String mTarget;
|
2011-11-18 10:28:17 -08:00
|
|
|
private AwesomeBarTabs mAwesomeTabs;
|
|
|
|
private AwesomeBarEditText mText;
|
2011-11-24 08:54:39 -08:00
|
|
|
private ImageButton mGoButton;
|
2012-02-07 18:15:20 -08:00
|
|
|
private ContentResolver mResolver;
|
2012-03-05 13:31:55 -08:00
|
|
|
private ContextMenuSubject mContextMenuSubject;
|
2012-06-05 14:07:14 -07:00
|
|
|
private SuggestClient mSuggestClient;
|
|
|
|
private AsyncTask<String, Void, ArrayList<String>> mSuggestTask;
|
|
|
|
|
2011-11-18 10:28:17 -08:00
|
|
|
@Override
|
|
|
|
public void onCreate(Bundle savedInstanceState) {
|
|
|
|
super.onCreate(savedInstanceState);
|
|
|
|
|
|
|
|
Log.d(LOGTAG, "creating awesomebar");
|
|
|
|
|
2012-02-07 18:15:20 -08:00
|
|
|
mResolver = Tabs.getInstance().getContentResolver();
|
2012-03-23 12:00:17 -07:00
|
|
|
LayoutInflater.from(this).setFactory(GeckoViewsFactory.getInstance());
|
2012-02-07 18:15:20 -08:00
|
|
|
|
2011-12-21 00:51:56 -08:00
|
|
|
setContentView(R.layout.awesomebar);
|
2011-11-18 10:28:17 -08:00
|
|
|
|
2012-03-12 12:15:57 -07:00
|
|
|
mGoButton = (ImageButton) findViewById(R.id.awesomebar_button);
|
|
|
|
mText = (AwesomeBarEditText) findViewById(R.id.awesomebar_text);
|
2011-12-15 11:53:04 -08:00
|
|
|
|
2012-01-27 11:58:18 -08:00
|
|
|
TabWidget tabWidget = (TabWidget) findViewById(android.R.id.tabs);
|
|
|
|
tabWidget.setDividerDrawable(null);
|
|
|
|
|
2011-11-18 10:28:17 -08:00
|
|
|
mAwesomeTabs = (AwesomeBarTabs) findViewById(R.id.awesomebar_tabs);
|
|
|
|
mAwesomeTabs.setOnUrlOpenListener(new AwesomeBarTabs.OnUrlOpenListener() {
|
2011-12-07 17:52:05 -08:00
|
|
|
public void onUrlOpen(String url) {
|
2012-02-01 16:25:08 -08:00
|
|
|
openUrlAndFinish(url);
|
2011-11-18 10:28:17 -08:00
|
|
|
}
|
2011-12-07 17:52:07 -08:00
|
|
|
|
2012-06-05 14:07:14 -07:00
|
|
|
public void onSearch(String engine, String text) {
|
|
|
|
openSearchAndFinish(text, engine);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void onEditSuggestion(final String text) {
|
|
|
|
GeckoApp.mAppContext.mMainHandler.post(new Runnable() {
|
|
|
|
public void run() {
|
|
|
|
mText.setText(text);
|
|
|
|
mText.setSelection(mText.getText().length());
|
|
|
|
mText.requestFocus();
|
|
|
|
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
|
|
|
imm.showSoftInput(mText, InputMethodManager.SHOW_IMPLICIT);
|
|
|
|
}
|
|
|
|
});
|
2011-12-07 17:52:07 -08:00
|
|
|
}
|
2011-11-18 10:28:17 -08:00
|
|
|
});
|
|
|
|
|
2011-11-24 08:54:39 -08:00
|
|
|
mGoButton.setOnClickListener(new Button.OnClickListener() {
|
|
|
|
public void onClick(View v) {
|
2012-02-22 18:26:04 -08:00
|
|
|
openUserEnteredAndFinish(mText.getText().toString());
|
2011-11-24 08:54:39 -08:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2011-11-18 10:28:17 -08:00
|
|
|
Intent intent = getIntent();
|
|
|
|
String currentUrl = intent.getStringExtra(CURRENT_URL_KEY);
|
2012-06-07 10:13:38 -07:00
|
|
|
mTarget = intent.getStringExtra(TARGET_KEY);
|
2011-11-18 10:28:17 -08:00
|
|
|
if (currentUrl != null) {
|
|
|
|
mText.setText(currentUrl);
|
|
|
|
mText.selectAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
mText.setOnKeyPreImeListener(new AwesomeBarEditText.OnKeyPreImeListener() {
|
|
|
|
public boolean onKeyPreIme(View v, int keyCode, KeyEvent event) {
|
2012-02-23 10:48:48 -08:00
|
|
|
// We only want to process one event per tap
|
|
|
|
if (event.getAction() != KeyEvent.ACTION_DOWN)
|
|
|
|
return false;
|
2011-11-18 10:28:17 -08:00
|
|
|
|
2012-02-23 10:48:48 -08:00
|
|
|
if (keyCode == KeyEvent.KEYCODE_ENTER) {
|
2012-03-22 17:37:32 -07:00
|
|
|
// If the AwesomeBar has a composition string, don't submit the text yet.
|
|
|
|
// ENTER is needed to commit the composition string.
|
|
|
|
Editable content = mText.getText();
|
|
|
|
if (!hasCompositionString(content)) {
|
|
|
|
openUserEnteredAndFinish(content.toString());
|
|
|
|
return true;
|
|
|
|
}
|
2011-12-20 19:44:17 -08:00
|
|
|
}
|
|
|
|
|
2011-11-18 10:28:17 -08:00
|
|
|
// If input method is in fullscreen mode, we want to dismiss
|
|
|
|
// it instead of closing awesomebar straight away.
|
2012-02-23 10:48:48 -08:00
|
|
|
InputMethodManager imm =
|
|
|
|
(InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
|
|
|
if (keyCode == KeyEvent.KEYCODE_BACK && !imm.isFullscreenMode()) {
|
|
|
|
// Let mAwesomeTabs try to handle the back press, since we may be in a
|
|
|
|
// bookmarks sub-folder.
|
|
|
|
if (mAwesomeTabs.onBackPressed())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// If mAwesomeTabs.onBackPressed() returned false, we didn't move up
|
|
|
|
// a folder level, so just exit the activity.
|
2011-11-18 10:28:17 -08:00
|
|
|
cancelAndFinish();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
mText.addTextChangedListener(new TextWatcher() {
|
|
|
|
public void afterTextChanged(Editable s) {
|
2012-03-11 19:30:35 -07:00
|
|
|
String text = s.toString();
|
|
|
|
mAwesomeTabs.filter(text);
|
|
|
|
|
2012-03-22 17:37:32 -07:00
|
|
|
// If the AwesomeBar has a composition string, don't call updateGoButton().
|
|
|
|
// That method resets IME and composition state will be broken.
|
|
|
|
if (hasCompositionString(s)) {
|
|
|
|
return;
|
2012-03-11 19:30:35 -07:00
|
|
|
}
|
2012-03-22 17:37:32 -07:00
|
|
|
|
2012-03-11 19:30:35 -07:00
|
|
|
// no composition string. It is safe to update IME flags.
|
|
|
|
updateGoButton(text);
|
2012-06-05 14:07:14 -07:00
|
|
|
|
|
|
|
// cancel previous query
|
|
|
|
if (mSuggestTask != null) {
|
|
|
|
mSuggestTask.cancel(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mSuggestClient != null) {
|
|
|
|
mSuggestTask = new AsyncTask<String, Void, ArrayList<String>>() {
|
|
|
|
protected ArrayList<String> doInBackground(String... query) {
|
|
|
|
return mSuggestClient.query(query[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void onPostExecute(ArrayList<String> suggestions) {
|
|
|
|
mAwesomeTabs.setSuggestions(suggestions);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
mSuggestTask.execute(text);
|
|
|
|
}
|
2011-11-18 10:28:17 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
public void beforeTextChanged(CharSequence s, int start, int count,
|
|
|
|
int after) {
|
|
|
|
// do nothing
|
|
|
|
}
|
|
|
|
|
|
|
|
public void onTextChanged(CharSequence s, int start, int before,
|
|
|
|
int count) {
|
2012-03-11 19:30:35 -07:00
|
|
|
// do nothing
|
2011-11-18 10:28:17 -08:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
mText.setOnKeyListener(new View.OnKeyListener() {
|
|
|
|
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
|
|
|
if (keyCode == KeyEvent.KEYCODE_ENTER) {
|
|
|
|
if (event.getAction() != KeyEvent.ACTION_DOWN)
|
|
|
|
return true;
|
|
|
|
|
2012-02-22 18:26:04 -08:00
|
|
|
openUserEnteredAndFinish(mText.getText().toString());
|
2011-11-18 10:28:17 -08:00
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2011-12-20 07:28:12 -08:00
|
|
|
registerForContextMenu(mAwesomeTabs.findViewById(R.id.all_pages_list));
|
|
|
|
registerForContextMenu(mAwesomeTabs.findViewById(R.id.bookmarks_list));
|
|
|
|
registerForContextMenu(mAwesomeTabs.findViewById(R.id.history_list));
|
|
|
|
|
2011-12-07 17:52:07 -08:00
|
|
|
GeckoAppShell.registerGeckoEventListener("SearchEngines:Data", this);
|
2012-02-08 23:18:27 -08:00
|
|
|
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("SearchEngines:Get", null));
|
2011-12-07 17:52:07 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
public void handleMessage(String event, JSONObject message) {
|
|
|
|
try {
|
|
|
|
if (event.equals("SearchEngines:Data")) {
|
2012-06-05 14:07:14 -07:00
|
|
|
final String suggestEngine = message.optString("suggestEngine");
|
|
|
|
final String suggestTemplate = message.optString("suggestTemplate");
|
2012-06-12 10:03:14 -07:00
|
|
|
if (suggestTemplate != null)
|
|
|
|
mSuggestClient = new SuggestClient(GeckoApp.mAppContext, suggestTemplate, SUGGESTION_TIMEOUT, SUGGESTION_MAX);
|
2012-06-05 14:07:14 -07:00
|
|
|
mAwesomeTabs.setSearchEngines(suggestEngine, message.getJSONArray("searchEngines"));
|
2011-12-07 17:52:07 -08:00
|
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
|
|
// do nothing
|
|
|
|
Log.i(LOGTAG, "handleMessage throws " + e + " for message: " + event);
|
|
|
|
}
|
2011-11-18 10:28:17 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onConfigurationChanged(Configuration newConfiguration) {
|
|
|
|
super.onConfigurationChanged(newConfiguration);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean onSearchRequested() {
|
|
|
|
cancelAndFinish();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-12-13 08:31:35 -08:00
|
|
|
/*
|
|
|
|
* This method tries to guess if the given string could be a search query or URL
|
|
|
|
* Search examples:
|
|
|
|
* foo
|
|
|
|
* foo bar.com
|
|
|
|
* foo http://bar.com
|
|
|
|
*
|
|
|
|
* URL examples
|
|
|
|
* foo.com
|
|
|
|
* foo.c
|
|
|
|
* :foo
|
|
|
|
* http://foo.com bar
|
|
|
|
*/
|
|
|
|
private boolean isSearchUrl(String text) {
|
|
|
|
text = text.trim();
|
|
|
|
if (text.length() == 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
int colon = text.indexOf(':');
|
|
|
|
int dot = text.indexOf('.');
|
|
|
|
int space = text.indexOf(' ');
|
|
|
|
|
|
|
|
// If a space is found before any dot or colon, we assume this is a search query
|
|
|
|
boolean spacedOut = space > -1 && (space < colon || space < dot);
|
|
|
|
|
2011-12-13 16:17:58 -08:00
|
|
|
return spacedOut || (dot == -1 && colon == -1);
|
2011-12-13 08:31:35 -08:00
|
|
|
}
|
|
|
|
|
2011-11-24 08:54:39 -08:00
|
|
|
private void updateGoButton(String text) {
|
|
|
|
if (text.length() == 0) {
|
|
|
|
mGoButton.setVisibility(View.GONE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mGoButton.setVisibility(View.VISIBLE);
|
|
|
|
|
|
|
|
int imageResource = R.drawable.ic_awesomebar_go;
|
2011-12-14 23:34:54 -08:00
|
|
|
int imeAction = EditorInfo.IME_ACTION_GO;
|
|
|
|
if (isSearchUrl(text)) {
|
2011-11-24 08:54:39 -08:00
|
|
|
imageResource = R.drawable.ic_awesomebar_search;
|
2011-12-14 23:34:54 -08:00
|
|
|
imeAction = EditorInfo.IME_ACTION_SEARCH;
|
|
|
|
}
|
2011-11-24 08:54:39 -08:00
|
|
|
mGoButton.setImageResource(imageResource);
|
2011-12-14 23:34:54 -08:00
|
|
|
|
2012-03-02 17:00:22 -08:00
|
|
|
int actionBits = mText.getImeOptions() & EditorInfo.IME_MASK_ACTION;
|
|
|
|
if (actionBits != imeAction) {
|
2011-12-14 23:34:54 -08:00
|
|
|
InputMethodManager imm = (InputMethodManager) mText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
2012-03-02 17:00:22 -08:00
|
|
|
int optionBits = mText.getImeOptions() & ~EditorInfo.IME_MASK_ACTION;
|
|
|
|
mText.setImeOptions(optionBits | imeAction);
|
2011-12-14 23:34:54 -08:00
|
|
|
imm.restartInput(mText);
|
|
|
|
}
|
2011-11-24 08:54:39 -08:00
|
|
|
}
|
|
|
|
|
2011-11-18 10:28:17 -08:00
|
|
|
private void cancelAndFinish() {
|
|
|
|
setResult(Activity.RESULT_CANCELED);
|
|
|
|
finish();
|
2012-02-14 16:55:18 -08:00
|
|
|
overridePendingTransition(0, 0);
|
2011-11-18 10:28:17 -08:00
|
|
|
}
|
|
|
|
|
2011-12-07 17:52:07 -08:00
|
|
|
private void finishWithResult(Intent intent) {
|
|
|
|
setResult(Activity.RESULT_OK, intent);
|
|
|
|
finish();
|
|
|
|
overridePendingTransition(0, 0);
|
|
|
|
}
|
|
|
|
|
2011-11-18 10:28:17 -08:00
|
|
|
private void openUrlAndFinish(String url) {
|
2012-02-22 18:26:04 -08:00
|
|
|
Intent resultIntent = new Intent();
|
|
|
|
resultIntent.putExtra(URL_KEY, url);
|
2012-06-07 10:13:38 -07:00
|
|
|
resultIntent.putExtra(TARGET_KEY, mTarget);
|
2012-02-22 18:26:04 -08:00
|
|
|
finishWithResult(resultIntent);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void openUserEnteredAndFinish(String url) {
|
2012-02-07 18:15:20 -08:00
|
|
|
int index = url.indexOf(' ');
|
|
|
|
if (index != -1) {
|
|
|
|
String keywordUrl = BrowserDB.getUrlForKeyword(mResolver, url.substring(0, index));
|
|
|
|
if (keywordUrl != null && keywordUrl.contains("%s")) {
|
|
|
|
String search = URLEncoder.encode(url.substring(index + 1));
|
|
|
|
url = keywordUrl.replace("%s", search);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-18 10:28:17 -08:00
|
|
|
Intent resultIntent = new Intent();
|
|
|
|
resultIntent.putExtra(URL_KEY, url);
|
2012-06-07 10:13:38 -07:00
|
|
|
resultIntent.putExtra(TARGET_KEY, mTarget);
|
2012-02-22 18:26:04 -08:00
|
|
|
resultIntent.putExtra(USER_ENTERED_KEY, true);
|
2011-12-07 17:52:07 -08:00
|
|
|
finishWithResult(resultIntent);
|
|
|
|
}
|
2011-11-18 10:28:17 -08:00
|
|
|
|
2011-12-07 17:52:07 -08:00
|
|
|
private void openSearchAndFinish(String url, String engine) {
|
|
|
|
Intent resultIntent = new Intent();
|
|
|
|
resultIntent.putExtra(URL_KEY, url);
|
2012-06-07 10:13:38 -07:00
|
|
|
resultIntent.putExtra(TARGET_KEY, mTarget);
|
2011-12-07 17:52:07 -08:00
|
|
|
resultIntent.putExtra(SEARCH_KEY, engine);
|
|
|
|
finishWithResult(resultIntent);
|
2011-11-18 10:28:17 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
|
|
|
// This method is called only if the key event was not handled
|
|
|
|
// by any of the views, which usually means the edit box lost focus
|
|
|
|
if (keyCode == KeyEvent.KEYCODE_BACK ||
|
|
|
|
keyCode == KeyEvent.KEYCODE_MENU ||
|
|
|
|
keyCode == KeyEvent.KEYCODE_SEARCH ||
|
|
|
|
keyCode == KeyEvent.KEYCODE_DPAD_UP ||
|
|
|
|
keyCode == KeyEvent.KEYCODE_DPAD_DOWN ||
|
|
|
|
keyCode == KeyEvent.KEYCODE_DPAD_LEFT ||
|
|
|
|
keyCode == KeyEvent.KEYCODE_DPAD_RIGHT ||
|
2011-11-24 18:19:52 -08:00
|
|
|
keyCode == KeyEvent.KEYCODE_DPAD_CENTER ||
|
2011-12-16 13:19:48 -08:00
|
|
|
keyCode == KeyEvent.KEYCODE_DEL ||
|
|
|
|
keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
|
|
|
|
keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
|
2011-11-18 10:28:17 -08:00
|
|
|
return super.onKeyDown(keyCode, event);
|
|
|
|
} else {
|
2011-11-23 23:11:06 -08:00
|
|
|
int selStart = -1;
|
|
|
|
int selEnd = -1;
|
|
|
|
if (mText.hasSelection()) {
|
|
|
|
selStart = mText.getSelectionStart();
|
|
|
|
selEnd = mText.getSelectionEnd();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (selStart >= 0) {
|
|
|
|
// Restore the selection, which gets lost due to the focus switch
|
|
|
|
mText.setSelection(selStart, selEnd);
|
|
|
|
}
|
|
|
|
|
2012-05-01 14:56:08 -07:00
|
|
|
// Manually dispatch the key event to the AwesomeBar before restoring (default) input
|
|
|
|
// focus. dispatchKeyEvent() will update AwesomeBar's cursor position.
|
2011-11-18 10:28:17 -08:00
|
|
|
mText.dispatchKeyEvent(event);
|
2012-05-01 14:56:08 -07:00
|
|
|
int newCursorPos = mText.getSelectionEnd();
|
|
|
|
|
|
|
|
// requestFocusFromTouch() will select all AwesomeBar text, so we must restore cursor
|
|
|
|
// position so subsequent typing does not overwrite all text.
|
|
|
|
mText.requestFocusFromTouch();
|
|
|
|
mText.setSelection(newCursorPos);
|
|
|
|
|
2011-11-18 10:28:17 -08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-23 11:40:17 -08:00
|
|
|
@Override
|
|
|
|
public void onResume() {
|
|
|
|
super.onResume();
|
|
|
|
if (mText != null && mText.getText() != null)
|
|
|
|
updateGoButton(mText.getText().toString());
|
2012-03-19 11:22:33 -07:00
|
|
|
|
|
|
|
// Invlidate the cached value that keeps track of whether or
|
|
|
|
// not desktop bookmarks exist
|
|
|
|
BrowserDB.invalidateCachedState();
|
2011-12-23 11:40:17 -08:00
|
|
|
}
|
|
|
|
|
2011-11-18 10:28:17 -08:00
|
|
|
@Override
|
|
|
|
public void onDestroy() {
|
|
|
|
super.onDestroy();
|
|
|
|
mAwesomeTabs.destroy();
|
2011-12-07 17:52:07 -08:00
|
|
|
GeckoAppShell.unregisterGeckoEventListener("SearchEngines:Data", this);
|
2011-11-18 10:28:17 -08:00
|
|
|
}
|
|
|
|
|
2012-03-15 10:01:11 -07:00
|
|
|
@Override
|
|
|
|
public void onBackPressed() {
|
|
|
|
// Let mAwesomeTabs try to handle the back press, since we may be in a
|
|
|
|
// bookmarks sub-folder.
|
|
|
|
if (mAwesomeTabs.onBackPressed())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Otherwise, just exit the awesome screen
|
|
|
|
cancelAndFinish();
|
|
|
|
}
|
|
|
|
|
2012-03-05 13:31:55 -08:00
|
|
|
private class ContextMenuSubject {
|
|
|
|
public int id;
|
|
|
|
public String url;
|
|
|
|
public byte[] favicon;
|
|
|
|
public String title;
|
2012-03-12 13:01:09 -07:00
|
|
|
public String keyword;
|
2012-03-05 13:31:55 -08:00
|
|
|
|
2012-03-12 13:01:09 -07:00
|
|
|
public ContextMenuSubject(int id, String url, byte[] favicon, String title, String keyword) {
|
2012-03-05 13:31:55 -08:00
|
|
|
this.id = id;
|
|
|
|
this.url = url;
|
|
|
|
this.favicon = favicon;
|
|
|
|
this.title = title;
|
2012-03-12 13:01:09 -07:00
|
|
|
this.keyword = keyword;
|
2012-03-05 13:31:55 -08:00
|
|
|
}
|
|
|
|
};
|
2011-12-20 07:28:12 -08:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
|
|
|
|
super.onCreateContextMenu(menu, view, menuInfo);
|
|
|
|
ListView list = (ListView) view;
|
2012-03-05 13:31:55 -08:00
|
|
|
mContextMenuSubject = null;
|
2011-12-26 22:07:46 -08:00
|
|
|
|
2012-02-23 10:48:48 -08:00
|
|
|
if (list == findViewById(R.id.history_list)) {
|
2012-02-07 17:27:25 -08:00
|
|
|
if (!(menuInfo instanceof ExpandableListView.ExpandableListContextMenuInfo)) {
|
2012-01-12 09:59:59 -08:00
|
|
|
Log.e(LOGTAG, "menuInfo is not ExpandableListContextMenuInfo");
|
|
|
|
return;
|
|
|
|
}
|
2012-02-07 17:27:25 -08:00
|
|
|
|
2011-12-26 22:07:46 -08:00
|
|
|
ExpandableListView.ExpandableListContextMenuInfo info = (ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
|
2012-01-24 09:16:48 -08:00
|
|
|
int childPosition = ExpandableListView.getPackedPositionChild(info.packedPosition);
|
|
|
|
int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
|
2012-01-25 08:58:16 -08:00
|
|
|
|
|
|
|
// Check if long tap is on a header row
|
|
|
|
if (groupPosition < 0 || childPosition < 0)
|
|
|
|
return;
|
|
|
|
|
2012-02-07 17:27:25 -08:00
|
|
|
ExpandableListView exList = (ExpandableListView) list;
|
2011-12-26 22:07:46 -08:00
|
|
|
|
2012-02-23 10:48:48 -08:00
|
|
|
// The history list is backed by a SimpleExpandableListAdapter
|
|
|
|
@SuppressWarnings("rawtypes")
|
2012-03-05 13:31:55 -08:00
|
|
|
Map map = (Map) exList.getExpandableListAdapter().getChild(groupPosition, childPosition);
|
2012-05-14 11:44:34 -07:00
|
|
|
mContextMenuSubject = new ContextMenuSubject((Integer) map.get(Combined.HISTORY_ID),
|
|
|
|
(String) map.get(URLColumns.URL),
|
|
|
|
(byte[]) map.get(URLColumns.FAVICON),
|
|
|
|
(String) map.get(URLColumns.TITLE),
|
|
|
|
null);
|
2012-02-23 10:48:48 -08:00
|
|
|
} else {
|
|
|
|
if (!(menuInfo instanceof AdapterView.AdapterContextMenuInfo)) {
|
|
|
|
Log.e(LOGTAG, "menuInfo is not AdapterContextMenuInfo");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
|
2012-03-05 13:31:55 -08:00
|
|
|
Object selectedItem = list.getItemAtPosition(info.position);
|
2012-02-23 10:48:48 -08:00
|
|
|
|
|
|
|
if (!(selectedItem instanceof Cursor)) {
|
|
|
|
Log.e(LOGTAG, "item at " + info.position + " is not a Cursor");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Cursor cursor = (Cursor) selectedItem;
|
|
|
|
|
|
|
|
// Don't show the context menu for folders
|
2012-03-20 04:44:32 -07:00
|
|
|
if (!(list == findViewById(R.id.bookmarks_list) &&
|
|
|
|
cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks.TYPE)) == Bookmarks.TYPE_FOLDER)) {
|
2012-03-12 13:01:09 -07:00
|
|
|
String keyword = null;
|
|
|
|
int keywordCol = cursor.getColumnIndex(URLColumns.KEYWORD);
|
|
|
|
if (keywordCol != -1)
|
|
|
|
keyword = cursor.getString(keywordCol);
|
|
|
|
|
2012-05-14 11:44:34 -07:00
|
|
|
// Use the bookmark id for the Bookmarks tab and the history id for the Top Sites tab
|
|
|
|
int id = (list == findViewById(R.id.bookmarks_list)) ? cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks._ID)) :
|
|
|
|
cursor.getInt(cursor.getColumnIndexOrThrow(Combined.HISTORY_ID));
|
|
|
|
|
|
|
|
mContextMenuSubject = new ContextMenuSubject(id,
|
2012-03-05 13:31:55 -08:00
|
|
|
cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.URL)),
|
|
|
|
cursor.getBlob(cursor.getColumnIndexOrThrow(URLColumns.FAVICON)),
|
2012-03-12 13:01:09 -07:00
|
|
|
cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.TITLE)),
|
2012-05-14 11:44:34 -07:00
|
|
|
keyword);
|
2012-01-12 09:59:59 -08:00
|
|
|
}
|
2011-12-26 22:07:46 -08:00
|
|
|
}
|
2011-12-20 07:28:12 -08:00
|
|
|
|
2012-03-05 13:31:55 -08:00
|
|
|
if (mContextMenuSubject == null)
|
2011-12-20 07:28:12 -08:00
|
|
|
return;
|
|
|
|
|
|
|
|
MenuInflater inflater = getMenuInflater();
|
|
|
|
inflater.inflate(R.menu.awesomebar_contextmenu, menu);
|
2012-01-15 13:51:23 -08:00
|
|
|
|
2012-02-07 17:27:25 -08:00
|
|
|
if (list != findViewById(R.id.bookmarks_list)) {
|
2012-03-12 13:01:09 -07:00
|
|
|
menu.findItem(R.id.remove_bookmark).setVisible(false);
|
|
|
|
menu.findItem(R.id.edit_bookmark).setVisible(false);
|
2012-05-14 11:44:34 -07:00
|
|
|
|
|
|
|
// Hide "Remove" item if there isn't a valid history ID
|
|
|
|
if (mContextMenuSubject.id < 0)
|
|
|
|
menu.findItem(R.id.remove_history).setVisible(false);
|
|
|
|
} else {
|
|
|
|
menu.findItem(R.id.remove_history).setVisible(false);
|
2012-01-15 13:51:23 -08:00
|
|
|
}
|
2011-12-20 07:28:12 -08:00
|
|
|
|
2012-03-05 13:31:55 -08:00
|
|
|
menu.setHeaderTitle(mContextMenuSubject.title);
|
2011-12-20 07:28:12 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean onContextItemSelected(MenuItem item) {
|
2011-12-26 22:07:46 -08:00
|
|
|
if (mContextMenuSubject == null)
|
2011-12-20 07:28:12 -08:00
|
|
|
return false;
|
|
|
|
|
2012-03-05 13:31:55 -08:00
|
|
|
final int id = mContextMenuSubject.id;
|
|
|
|
final String url = mContextMenuSubject.url;
|
|
|
|
final byte[] b = mContextMenuSubject.favicon;
|
|
|
|
final String title = mContextMenuSubject.title;
|
2012-03-12 13:01:09 -07:00
|
|
|
final String keyword = mContextMenuSubject.keyword;
|
2011-12-20 07:28:12 -08:00
|
|
|
|
|
|
|
switch (item.getItemId()) {
|
|
|
|
case R.id.open_new_tab: {
|
2012-05-14 14:38:18 -07:00
|
|
|
if (url == null) {
|
|
|
|
Log.e(LOGTAG, "Can't open in new tab because URL is null");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-06-07 10:13:38 -07:00
|
|
|
GeckoApp.mAppContext.loadUrl(url, AwesomeBar.Target.NEW_TAB);
|
2012-02-02 07:49:46 -08:00
|
|
|
Toast.makeText(this, R.string.new_tab_opened, Toast.LENGTH_SHORT).show();
|
2011-12-20 07:28:12 -08:00
|
|
|
break;
|
|
|
|
}
|
2012-03-12 13:01:09 -07:00
|
|
|
case R.id.edit_bookmark: {
|
|
|
|
AlertDialog.Builder editPrompt = new AlertDialog.Builder(this);
|
|
|
|
View editView = getLayoutInflater().inflate(R.layout.bookmark_edit, null);
|
|
|
|
editPrompt.setTitle(R.string.bookmark_edit_title);
|
|
|
|
editPrompt.setView(editView);
|
|
|
|
|
|
|
|
final EditText nameText = ((EditText) editView.findViewById(R.id.edit_bookmark_name));
|
|
|
|
final EditText locationText = ((EditText) editView.findViewById(R.id.edit_bookmark_location));
|
|
|
|
final EditText keywordText = ((EditText) editView.findViewById(R.id.edit_bookmark_keyword));
|
|
|
|
nameText.setText(title);
|
|
|
|
locationText.setText(url);
|
|
|
|
keywordText.setText(keyword);
|
|
|
|
|
|
|
|
editPrompt.setPositiveButton(R.string.button_ok, new DialogInterface.OnClickListener() {
|
|
|
|
public void onClick(DialogInterface dialog, int whichButton) {
|
|
|
|
(new GeckoAsyncTask<Void, Void, Void>() {
|
|
|
|
@Override
|
|
|
|
public Void doInBackground(Void... params) {
|
|
|
|
String newUrl = locationText.getText().toString().trim();
|
2012-05-07 15:33:07 -07:00
|
|
|
BrowserDB.updateBookmark(mResolver, id, newUrl, nameText.getText().toString(),
|
2012-03-12 13:01:09 -07:00
|
|
|
keywordText.getText().toString());
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onPostExecute(Void result) {
|
|
|
|
Toast.makeText(AwesomeBar.this, R.string.bookmark_updated, Toast.LENGTH_SHORT).show();
|
|
|
|
}
|
|
|
|
}).execute();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
editPrompt.setNegativeButton(R.string.button_cancel, new DialogInterface.OnClickListener() {
|
|
|
|
public void onClick(DialogInterface dialog, int whichButton) {
|
|
|
|
// do nothing
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
final AlertDialog dialog = editPrompt.create();
|
|
|
|
|
|
|
|
// disable OK button if the URL is empty
|
|
|
|
locationText.addTextChangedListener(new TextWatcher() {
|
|
|
|
private boolean mEnabled = true;
|
|
|
|
|
|
|
|
public void afterTextChanged(Editable s) {}
|
|
|
|
|
|
|
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
|
|
|
|
|
|
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
|
|
|
boolean enabled = (s.toString().trim().length() > 0);
|
|
|
|
if (mEnabled != enabled) {
|
|
|
|
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(enabled);
|
|
|
|
mEnabled = enabled;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
dialog.show();
|
|
|
|
break;
|
|
|
|
}
|
2012-01-15 13:51:23 -08:00
|
|
|
case R.id.remove_bookmark: {
|
2012-06-02 11:23:45 -07:00
|
|
|
(new AsyncTask<Void, Void, Void>() {
|
|
|
|
private boolean mInReadingList;
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onPreExecute() {
|
|
|
|
mInReadingList = mAwesomeTabs.isInReadingList();
|
|
|
|
}
|
|
|
|
|
2012-02-14 16:23:06 -08:00
|
|
|
@Override
|
|
|
|
public Void doInBackground(Void... params) {
|
2012-02-09 10:01:57 -08:00
|
|
|
BrowserDB.removeBookmark(mResolver, id);
|
2012-02-14 16:23:06 -08:00
|
|
|
return null;
|
|
|
|
}
|
2012-02-07 06:47:54 -08:00
|
|
|
|
2012-02-14 16:23:06 -08:00
|
|
|
@Override
|
|
|
|
public void onPostExecute(Void result) {
|
2012-06-02 11:23:45 -07:00
|
|
|
int messageId = R.string.bookmark_removed;
|
2012-06-12 07:03:06 -07:00
|
|
|
if (mInReadingList) {
|
2012-06-02 11:23:45 -07:00
|
|
|
messageId = R.string.reading_list_removed;
|
|
|
|
|
2012-06-12 07:03:06 -07:00
|
|
|
GeckoEvent e = GeckoEvent.createBroadcastEvent("Reader:Remove", url);
|
|
|
|
GeckoAppShell.sendEventToGecko(e);
|
|
|
|
}
|
|
|
|
|
2012-06-02 11:23:45 -07:00
|
|
|
Toast.makeText(AwesomeBar.this, messageId, Toast.LENGTH_SHORT).show();
|
2012-02-07 06:47:54 -08:00
|
|
|
}
|
2012-02-14 16:23:06 -08:00
|
|
|
}).execute();
|
2012-01-15 13:51:23 -08:00
|
|
|
break;
|
2012-05-14 11:44:34 -07:00
|
|
|
}
|
|
|
|
case R.id.remove_history: {
|
|
|
|
(new GeckoAsyncTask<Void, Void, Void>() {
|
|
|
|
@Override
|
|
|
|
public Void doInBackground(Void... params) {
|
|
|
|
BrowserDB.removeHistoryEntry(mResolver, id);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onPostExecute(Void result) {
|
|
|
|
Toast.makeText(AwesomeBar.this, R.string.history_removed, Toast.LENGTH_SHORT).show();
|
|
|
|
}
|
|
|
|
}).execute();
|
|
|
|
break;
|
2012-01-15 13:51:23 -08:00
|
|
|
}
|
2011-12-20 07:28:12 -08:00
|
|
|
case R.id.add_to_launcher: {
|
2012-05-14 14:38:18 -07:00
|
|
|
if (url == null) {
|
|
|
|
Log.e(LOGTAG, "Can't add to home screen because URL is null");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2011-12-20 07:28:12 -08:00
|
|
|
Bitmap bitmap = null;
|
|
|
|
if (b != null)
|
|
|
|
bitmap = BitmapFactory.decodeByteArray(b, 0, b.length);
|
2012-04-20 11:49:30 -07:00
|
|
|
|
|
|
|
String shortcutTitle = TextUtils.isEmpty(title) ? url.replaceAll("^([a-z]+://)?(www\\.)?", "") : title;
|
|
|
|
GeckoAppShell.createShortcut(shortcutTitle, url, bitmap, "");
|
2011-12-20 07:28:12 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case R.id.share: {
|
2012-05-14 14:38:18 -07:00
|
|
|
if (url == null) {
|
|
|
|
Log.e(LOGTAG, "Can't share because URL is null");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2011-12-20 07:28:12 -08:00
|
|
|
GeckoAppShell.openUriExternal(url, "text/plain", "", "",
|
|
|
|
Intent.ACTION_SEND, title);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default: {
|
|
|
|
return super.onContextItemSelected(item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-03-22 17:37:32 -07:00
|
|
|
private static boolean hasCompositionString(Editable content) {
|
|
|
|
Object[] spans = content.getSpans(0, content.length(), Object.class);
|
|
|
|
if (spans != null) {
|
|
|
|
for (Object span : spans) {
|
|
|
|
if ((content.getSpanFlags(span) & Spanned.SPAN_COMPOSING) != 0) {
|
|
|
|
// Found composition string.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-11-18 10:28:17 -08:00
|
|
|
public static class AwesomeBarEditText extends EditText {
|
|
|
|
OnKeyPreImeListener mOnKeyPreImeListener;
|
|
|
|
|
|
|
|
public interface OnKeyPreImeListener {
|
|
|
|
public boolean onKeyPreIme(View v, int keyCode, KeyEvent event);
|
|
|
|
}
|
|
|
|
|
|
|
|
public AwesomeBarEditText(Context context, AttributeSet attrs) {
|
|
|
|
super(context, attrs);
|
|
|
|
mOnKeyPreImeListener = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
|
|
|
|
if (mOnKeyPreImeListener != null)
|
|
|
|
return mOnKeyPreImeListener.onKeyPreIme(this, keyCode, event);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setOnKeyPreImeListener(OnKeyPreImeListener listener) {
|
|
|
|
mOnKeyPreImeListener = listener;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|