Bug 695152 - Tabs tray implementation for native UI. r=mfinkle

--HG--
extra : rebase_source : 81efd1c8844a1ecdf49be1ee28060c4e8198e23d
This commit is contained in:
Sriram Ramasubramanian 2011-10-24 12:20:27 -07:00
parent dedcb28dbb
commit 03c42af896
18 changed files with 608 additions and 279 deletions

View File

@ -143,15 +143,5 @@
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity android:name="org.mozilla.gecko.ShowTabs"
android:theme="@android:style/Theme.Translucent"
android:windowSoftInputMode="stateUnspecified|adjustResize"
android:windowIsTranslucent="true"
android:windowContentOverlay="@null"
android:windowNoTitle="true"
android:windowIsFloating="true"
android:backgroundDimEnabled="false">
</activity>
</application>
</manifest>

View File

@ -56,8 +56,8 @@ import org.mozilla.gecko.R;
public class BrowserToolbar extends LinearLayout {
final private ProgressBar mProgressBar;
final private Button mAwesomeBar;
final private Button mTabs;
final private ImageButton mFavicon;
final private ImageButton mReloadButton;
public BrowserToolbar(Context context, AttributeSet attrs) {
super(context, attrs);
@ -77,22 +77,27 @@ public class BrowserToolbar extends LinearLayout {
}
});
mFavicon = (ImageButton) findViewById(R.id.favimage);
mReloadButton = (ImageButton) findViewById(R.id.reload);
mReloadButton.setOnClickListener(new ImageButton.OnClickListener() {
mTabs = (Button) findViewById(R.id.tabs);
mTabs.setText("1");
mTabs.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
onReload();
showTabs();
}
});
mFavicon = (ImageButton) findViewById(R.id.favimage);
}
private void onAwesomeBarSearch() {
GeckoApp.mAppContext.onSearchRequested();
}
private void onReload() {
GeckoApp.mAppContext.doReload();
private void showTabs() {
GeckoApp.mAppContext.showTabs();
}
public void updateTabs(int count) {
mTabs.setText("" + count);
}
public void updateProgress(int progress, int total) {

View File

@ -22,6 +22,7 @@
* Vladimir Vukicevic <vladimir@pobox.com>
* Matt Brubeck <mbrubeck@mozilla.com>
* Vivien Nicolas <vnicolas@mozilla.com>
* Sriram Ramasubramanian <sriram@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -49,6 +50,8 @@ import java.nio.channels.FileChannel;
import java.util.concurrent.*;
import java.lang.reflect.*;
import org.json.*;
import android.os.*;
import android.app.*;
import android.text.*;
@ -58,6 +61,7 @@ import android.content.*;
import android.content.res.*;
import android.graphics.*;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.BitmapDrawable;
import android.widget.*;
import android.hardware.*;
@ -72,7 +76,7 @@ import android.content.SharedPreferences.*;
import dalvik.system.*;
abstract public class GeckoApp
extends Activity
extends Activity implements GeckoEventListener
{
private static final String LOG_FILE_NAME = "GeckoApp";
@ -93,6 +97,9 @@ abstract public class GeckoApp
private IntentFilter mConnectivityFilter;
private BroadcastReceiver mConnectivityReceiver;
private BrowserToolbar mBrowserToolbar;
private PopupWindow mTabsTray;
private TabsAdapter mTabsAdapter;
private static boolean isTabsTrayShowing;
enum LaunchState {Launching, WaitButton,
Launched, GeckoRunning, GeckoExiting};
@ -102,7 +109,6 @@ abstract public class GeckoApp
private static final int FILE_PICKER_REQUEST = 1;
private static final int AWESOMEBAR_REQUEST = 2;
private static final int CAMERA_CAPTURE_REQUEST = 3;
private static final int SHOW_TABS_REQUEST = 4;
static boolean checkLaunchState(LaunchState checkState) {
synchronized(sLaunchState) {
@ -296,7 +302,7 @@ abstract public class GeckoApp
boolean launch(Intent intent)
{
Log.w(LOGTAG, "zerdatime " + new Date().getTime() + " - launch");
if (!checkAndSetLaunchState(LaunchState.Launching, LaunchState.Launched))
return false;
@ -408,10 +414,8 @@ abstract public class GeckoApp
Intent.ACTION_SEND, he.mTitle);
}
return true;
case R.id.show_tabs:
Intent showTabsIntent = new Intent(this, ShowTabs.class);
showTabsIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION | Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivityForResult(showTabsIntent, SHOW_TABS_REQUEST);
case R.id.reload:
doReload();
return true;
default:
return super.onOptionsItemSelected(item);
@ -477,6 +481,177 @@ abstract public class GeckoApp
});
}
void showTabs() {
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int width = metrics.widthPixels;
int height = (int) (metrics.widthPixels * 0.75);
LayoutInflater inflater = (LayoutInflater) mAppContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mTabsTray = new PopupWindow(inflater.inflate(R.layout.tabs_tray, null, false),
width,
height,
true);
mTabsTray.setBackgroundDrawable(new BitmapDrawable());
mTabsTray.setOutsideTouchable(true);
ListView list = (ListView) mTabsTray.getContentView().findViewById(R.id.list);
Button addTab = new Button(this);
addTab.setText(R.string.new_tab);
addTab.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
hideTabs();
Intent intent = new Intent(mAppContext, AwesomeBar.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION | Intent.FLAG_ACTIVITY_NO_HISTORY);
intent.putExtra(AwesomeBar.TYPE_KEY, AwesomeBar.Type.ADD.name());
startActivityForResult(intent, AWESOMEBAR_REQUEST);
}
});
list.addFooterView(addTab);
isTabsTrayShowing = true;
onTabsChanged();
mTabsTray.showAsDropDown(mBrowserToolbar.findViewById(R.id.tabs));
}
void hideTabs() {
if (mTabsTray.isShowing()) {
mTabsAdapter = null;
((ListView) mTabsTray.getContentView().findViewById(R.id.list)).invalidateViews();
mTabsTray.dismiss();
isTabsTrayShowing = false;
}
}
public void onTabsChanged() {
if (mTabsTray == null)
return;
if (!isTabsTrayShowing)
return;
final HashMap<Integer, Tab> tabs = Tabs.getInstance().getTabs();
if (mTabsAdapter != null) {
mTabsAdapter = new TabsAdapter(mAppContext, tabs);
mTabsAdapter.notifyDataSetChanged();
ListView list = (ListView) mTabsTray.getContentView().findViewById(R.id.list);
list.invalidateViews();
list.setAdapter(mTabsAdapter);
} else {
mTabsAdapter = new TabsAdapter(mAppContext, tabs);
ListView list = (ListView) mTabsTray.getContentView().findViewById(R.id.list);
list.setAdapter(mTabsAdapter);
}
}
public void handleMessage(String event, JSONObject message) {
try {
if (event.equals("DOMContentLoaded")) {
final int tabId = message.getInt("tabID");
final String uri = message.getString("uri");
final String title = message.getString("title");
final CharSequence titleText = title;
handleContentLoaded(tabId, uri, title);
Log.i("GeckoShell", "URI - " + uri + ", title - " + title);
} else if (event.equals("DOMTitleChanged")) {
final int tabId = message.getInt("tabID");
final String title = message.getString("title");
final CharSequence titleText = title;
handleTitleChanged(tabId, title);
Log.i("GeckoShell", "title - " + title);
} else if (event.equals("DOMLinkAdded")) {
final int tabId = message.getInt("tabID");
final String rel = message.getString("rel");
final String href = message.getString("href");
Log.i("GeckoShell", "link rel - " + rel + ", href - " + href);
handleLinkAdded(tabId, rel, href);
} else if (event.equals("log")) {
// generic log listener
final String msg = message.getString("msg");
Log.i("GeckoShell", "Log: " + msg);
} else if (event.equals("onLocationChange")) {
final int tabId = message.getInt("tabID");
final String uri = message.getString("uri");
Log.i("GeckoShell", "URI - " + uri);
handleLocationChange(tabId, uri);
} else if (event.equals("onStateChange")) {
final int tabId = message.getInt("tabID");
int state = message.getInt("state");
Log.i("GeckoShell", "State - " + state);
if ((state & GeckoAppShell.WPL_STATE_IS_DOCUMENT) != 0) {
if ((state & GeckoAppShell.WPL_STATE_START) != 0) {
Log.i("GeckoShell", "Got a document start");
handleDocumentStart(tabId);
} else if ((state & GeckoAppShell.WPL_STATE_STOP) != 0) {
Log.i("GeckoShell", "Got a document stop");
handleDocumentStop(tabId);
}
}
} else if (event.equals("onProgressChange")) {
final int tabId = message.getInt("tabID");
final int current = message.getInt("current");
final int total = message.getInt("total");
handleProgressChange(tabId, current, total);
Log.i("GeckoShell", "progress - " + current + "/" + total);
} else if (event.equals("onCameraCapture")) {
//GeckoApp.mAppContext.doCameraCapture(message.getString("path"));
doCameraCapture();
} else if (event.equals("Tab:Added")) {
Log.i("GeckoShell", "Created a new tab");
int tabId = message.getInt("tabID");
String uri = message.getString("uri");
handleAddTab(tabId, uri);
} else if (event.equals("Tab:Closed")) {
Log.i("GeckoShell", "Destroyed a tab");
int tabId = message.getInt("tabID");
handleCloseTab(tabId);
} else if (event.equals("Tab:Selected")) {
int tabId = message.getInt("tabID");
Log.i("GeckoShell", "Switched to tab: " + tabId);
handleSelectTab(tabId);
}
} catch (Exception e) {
Log.i("GeckoApp", "handleMessage throws " + e + " for message: " + event);
}
}
void handleAddTab(final int tabId, final String uri) {
Tab tab = Tabs.getInstance().addTab(tabId, uri);
tab.updateFavicon(mAppContext.getResources().getDrawable(R.drawable.favicon));
mMainHandler.post(new Runnable() {
public void run() {
onTabsChanged();
mBrowserToolbar.updateTabs(Tabs.getInstance().getCount());
}
});
}
void handleCloseTab(final int tabId) {
Tabs.getInstance().removeTab(tabId);
mMainHandler.post(new Runnable() {
public void run() {
onTabsChanged();
mBrowserToolbar.updateTabs(Tabs.getInstance().getCount());
}
});
}
void handleSelectTab(final int tabId) {
final Tab tab = Tabs.getInstance().selectTab(tabId);
if (tab == null)
return;
mMainHandler.post(new Runnable() {
public void run() {
mBrowserToolbar.setTitle(tab.getTitle());
mBrowserToolbar.setFavicon(tab.getFavicon());
mBrowserToolbar.setProgressVisibility(tab.isLoading());
}
});
}
void handleDocumentStart(final int tabId) {
Tab tab = Tabs.getInstance().getTab(tabId);
@ -485,6 +660,12 @@ abstract public class GeckoApp
tab.setLoading(true);
mMainHandler.post(new Runnable() {
public void run() {
onTabsChanged();
}
});
if (!Tabs.getInstance().isSelectedTab(tab))
return;
@ -498,7 +679,17 @@ abstract public class GeckoApp
void handleDocumentStop(final int tabId) {
Tab tab = Tabs.getInstance().getTab(tabId);
tab.setLoading(false);
if (tab == null)
return;
tab.setLoading(false);
mMainHandler.post(new Runnable() {
public void run() {
onTabsChanged();
}
});
if (!Tabs.getInstance().isSelectedTab(tab))
return;
@ -513,6 +704,9 @@ abstract public class GeckoApp
void handleProgressChange(final int tabId, final int current, final int total) {
Tab tab = Tabs.getInstance().getTab(tabId);
if (tab == null)
return;
if (!Tabs.getInstance().isSelectedTab(tab))
return;
@ -525,9 +719,18 @@ abstract public class GeckoApp
void handleContentLoaded(final int tabId, final String uri, final String title) {
Tab tab = Tabs.getInstance().getTab(tabId);
if (tab == null)
return;
tab.updateTitle(title);
tab.addHistory(new Tab.HistoryEntry(uri, title));
mMainHandler.post(new Runnable() {
public void run() {
onTabsChanged();
}
});
if (!Tabs.getInstance().isSelectedTab(tab))
return;
@ -540,6 +743,9 @@ abstract public class GeckoApp
void handleTitleChanged(final int tabId, final String title) {
Tab tab = Tabs.getInstance().getTab(tabId);
if (tab == null)
return;
tab.updateTitle(title);
if (!Tabs.getInstance().isSelectedTab(tab))
@ -547,12 +753,13 @@ abstract public class GeckoApp
mMainHandler.post(new Runnable() {
public void run() {
onTabsChanged();
mBrowserToolbar.setTitle(title);
}
});
}
void handleLinkAdded(String rel, final String href) {
void handleLinkAdded(final int tabId, String rel, final String href) {
class DownloadFaviconTask extends AsyncTask<URL, Void, Drawable> {
protected Drawable doInBackground(URL... url) {
Drawable image = null;
@ -566,6 +773,21 @@ abstract public class GeckoApp
}
protected void onPostExecute(Drawable image) {
if (image != null) {
Tab tab = Tabs.getInstance().getTab(tabId);
if (tab == null)
return;
tab.updateFavicon(image);
mMainHandler.post(new Runnable() {
public void run() {
onTabsChanged();
}
});
if (!Tabs.getInstance().isSelectedTab(tab))
return;
final Drawable postImage = image;
mMainHandler.post(new Runnable() {
public void run() {
@ -585,7 +807,7 @@ abstract public class GeckoApp
}
}
}
void addPluginView(final View view,
final double x, final double y,
final double w, final double h) {
@ -699,6 +921,20 @@ abstract public class GeckoApp
mMainLayout = (LinearLayout) findViewById(R.id.mainLayout);
mBrowserToolbar = (BrowserToolbar) findViewById(R.id.browserToolbar);
//register for events
GeckoAppShell.registerGeckoEventListener("DOMContentLoaded", GeckoApp.mAppContext);
GeckoAppShell.registerGeckoEventListener("DOMTitleChanged", GeckoApp.mAppContext);
GeckoAppShell.registerGeckoEventListener("DOMLinkAdded", GeckoApp.mAppContext);
GeckoAppShell.registerGeckoEventListener("log", GeckoApp.mAppContext);
GeckoAppShell.registerGeckoEventListener("onLocationChange", GeckoApp.mAppContext);
GeckoAppShell.registerGeckoEventListener("onStateChange", GeckoApp.mAppContext);
GeckoAppShell.registerGeckoEventListener("onProgressChange", GeckoApp.mAppContext);
GeckoAppShell.registerGeckoEventListener("onCameraCapture", GeckoApp.mAppContext);
GeckoAppShell.registerGeckoEventListener("Tab:Added", GeckoApp.mAppContext);
GeckoAppShell.registerGeckoEventListener("Tab:Closed", GeckoApp.mAppContext);
GeckoAppShell.registerGeckoEventListener("Tab:Selected", GeckoApp.mAppContext);
mConnectivityFilter = new IntentFilter();
mConnectivityFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
@ -874,6 +1110,23 @@ abstract public class GeckoApp
GeckoAppShell.getPromptService().onDestroy();
}
});
if (mTabsTray != null && mTabsTray.isShowing()) {
hideTabs();
mTabsTray = null;
}
GeckoAppShell.unregisterGeckoEventListener("DOMContentLoaded", GeckoApp.mAppContext);
GeckoAppShell.unregisterGeckoEventListener("DOMTitleChanged", GeckoApp.mAppContext);
GeckoAppShell.unregisterGeckoEventListener("DOMLinkAdded", GeckoApp.mAppContext);
GeckoAppShell.unregisterGeckoEventListener("log", GeckoApp.mAppContext);
GeckoAppShell.unregisterGeckoEventListener("onLocationChange", GeckoApp.mAppContext);
GeckoAppShell.unregisterGeckoEventListener("onStateChange", GeckoApp.mAppContext);
GeckoAppShell.unregisterGeckoEventListener("onProgressChange", GeckoApp.mAppContext);
GeckoAppShell.unregisterGeckoEventListener("onCameraCapture", GeckoApp.mAppContext);
GeckoAppShell.unregisterGeckoEventListener("Tab:Added", GeckoApp.mAppContext);
GeckoAppShell.unregisterGeckoEventListener("Tab:Closed", GeckoApp.mAppContext);
GeckoAppShell.unregisterGeckoEventListener("Tab:Selected", GeckoApp.mAppContext);
super.onDestroy();
}
@ -1145,24 +1398,6 @@ abstract public class GeckoApp
"{\"ok\": false, \"path\": \"" + file.getPath() + "\" }");
GeckoAppShell.sendEventToGecko(e);
break;
case SHOW_TABS_REQUEST:
if (data != null) {
ShowTabs.Type type = ShowTabs.Type.valueOf(data.getStringExtra(ShowTabs.TYPE));
if (type == ShowTabs.Type.ADD) {
Intent intent = new Intent(this, AwesomeBar.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION | Intent.FLAG_ACTIVITY_NO_HISTORY);
intent.putExtra(AwesomeBar.TYPE_KEY, AwesomeBar.Type.ADD.name());
startActivityForResult(intent, AWESOMEBAR_REQUEST);
} else {
int id = Integer.parseInt(data.getStringExtra(ShowTabs.ID));
Tab tab = Tabs.getInstance().selectTab(id);
if (tab != null) {
mBrowserToolbar.setTitle(tab.getTitle());
mBrowserToolbar.setProgressVisibility(tab.isLoading());
}
GeckoAppShell.sendEventToGecko(new GeckoEvent("tab-select", "" + id));
}
}
}
}
@ -1179,9 +1414,109 @@ abstract public class GeckoApp
mBrowserToolbar.setTitle(url);
Log.d(LOG_FILE_NAME, type.name());
if (type == AwesomeBar.Type.ADD) {
GeckoAppShell.sendEventToGecko(new GeckoEvent("tab-add", url));
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Add", url));
} else {
GeckoAppShell.sendEventToGecko(new GeckoEvent("tab-load", url));
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Load", url));
}
}
}
// Adapter to bind tabs into a list
private class TabsAdapter extends BaseAdapter {
public TabsAdapter(Context context, HashMap<Integer, Tab> tabs) {
mContext = context;
mTabs = new ArrayList<Tab>();
if (tabs != null) {
Iterator keys = tabs.keySet().iterator();
Tab tab;
while (keys.hasNext()) {
tab = tabs.get(keys.next());
mTabs.add(tab);
}
}
mInflater = LayoutInflater.from(mContext);
}
@Override
public int getCount() {
return mTabs.size();
}
@Override
public Tab getItem(int position) {
return mTabs.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null)
convertView = mInflater.inflate(R.layout.tabs_row, null);
Tab tab = mTabs.get(position);
LinearLayout info = (LinearLayout) convertView.findViewById(R.id.info);
info.setTag("" + tab.getId());
info.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
hideTabs();
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Select", "" + v.getTag()));
}
});
ImageView favicon = (ImageView) convertView.findViewById(R.id.favicon);
favicon.setImageDrawable(tab.getFavicon());
TextView title = (TextView) convertView.findViewById(R.id.title);
title.setText(tab.getTitle());
TextView url = (TextView) convertView.findViewById(R.id.url);
url.setText(tab.getURL());
ImageButton close = (ImageButton) convertView.findViewById(R.id.close);
if (mTabs.size() > 1) {
close.setTag("" + tab.getId());
close.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
int tabId = Integer.parseInt("" + v.getTag());
Tabs tabs = Tabs.getInstance();
Tab tab = tabs.getTab(tabId);
if (tabs.isSelectedTab(tab)) {
int index = tabs.getIndexOf(tab);
if (index >= 1)
index--;
else
index = 1;
int id = tabs.getTabAt(index).getId();
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Select", "" + id));
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Close", "" + v.getTag()));
} else {
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Close", "" + v.getTag()));
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Select", "" + tabs.getSelectedTabId()));
}
}
});
} else {
close.setVisibility(View.GONE);
}
return convertView;
}
@Override
public void notifyDataSetChanged() {
}
private Context mContext;
private ArrayList<Tab> mTabs;
private LayoutInflater mInflater;
}
}

View File

@ -20,6 +20,7 @@
*
* Contributor(s):
* Vladimir Vukicevic <vladimir@pobox.com>
* Sriram Ramasubramanian <sriram@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -99,13 +100,15 @@ public class GeckoAppShell
/* Keep in sync with constants found here:
http://mxr.mozilla.org/mozilla-central/source/uriloader/base/nsIWebProgressListener.idl
*/
static private final int WPL_STATE_START = 0x00000001;
static private final int WPL_STATE_STOP = 0x00000010;
static private final int WPL_STATE_IS_DOCUMENT = 0x00020000;
static public final int WPL_STATE_START = 0x00000001;
static public final int WPL_STATE_STOP = 0x00000010;
static public final int WPL_STATE_IS_DOCUMENT = 0x00020000;
static private File sCacheFile = null;
static private int sFreeSpace = -1;
private static HashMap<String, ArrayList<GeckoEventListener>> mEventListeners;
/* The Android-side API: API methods that Android calls */
// Initialization methods
@ -1502,6 +1505,28 @@ public class GeckoAppShell
static SynchronousQueue<String> sPromptQueue = null;
public static void registerGeckoEventListener(String event, GeckoEventListener listener) {
if (mEventListeners == null)
mEventListeners = new HashMap<String, ArrayList<GeckoEventListener>>();
if (!mEventListeners.containsKey(event))
mEventListeners.put(event, new ArrayList<GeckoEventListener>());
ArrayList<GeckoEventListener> listeners = mEventListeners.get(event);
listeners.add(listener);
}
public static void unregisterGeckoEventListener(String event, GeckoEventListener listener) {
if (mEventListeners == null)
return;
if (!mEventListeners.containsKey(event))
return;
ArrayList<GeckoEventListener> listeners = mEventListeners.get(event);
listeners.remove(listener);
}
public static String handleGeckoMessage(String message) {
//
// {"gecko": {
@ -1512,63 +1537,8 @@ public class GeckoAppShell
JSONObject json = new JSONObject(message);
final JSONObject geckoObject = json.getJSONObject("gecko");
String type = geckoObject.getString("type");
if (type.equals("DOMContentLoaded")) {
final int tabId = geckoObject.getInt("tabID");
final String uri = geckoObject.getString("uri");
final String title = geckoObject.getString("title");
final CharSequence titleText = title;
GeckoApp.mAppContext.handleContentLoaded(tabId, uri, title);
Log.i("GeckoShell", "URI - " + uri + ", title - " + title);
} else if (type.equals("DOMTitleChanged")) {
final int tabId = geckoObject.getInt("tabID");
final String title = geckoObject.getString("title");
final CharSequence titleText = title;
GeckoApp.mAppContext.handleTitleChanged(tabId, title);
Log.i("GeckoShell", "title - " + title);
} else if (type.equals("DOMLinkAdded")) {
final String rel = geckoObject.getString("rel");
final String href = geckoObject.getString("href");
Log.i("GeckoShell", "link rel - " + rel + ", href - " + href);
GeckoApp.mAppContext.handleLinkAdded(rel, href);
} else if (type.equals("log")) {
// generic log listener
final String msg = geckoObject.getString("msg");
Log.i("GeckoShell", "Log: " + msg);
} else if (type.equals("onLocationChange")) {
final int tabId = geckoObject.getInt("tabID");
final String uri = geckoObject.getString("uri");
Log.i("GeckoShell", "URI - " + uri);
GeckoApp.mAppContext.handleLocationChange(tabId, uri);
} else if (type.equals("onStateChange")) {
final int tabId = geckoObject.getInt("tabID");
int state = geckoObject.getInt("state");
Log.i("GeckoShell", "State - " + state);
if ((state & WPL_STATE_IS_DOCUMENT) != 0) {
if ((state & WPL_STATE_START) != 0) {
Log.i("GeckoShell", "Got a document start");
GeckoApp.mAppContext.handleDocumentStart(tabId);
} else if ((state & WPL_STATE_STOP) != 0) {
Log.i("GeckoShell", "Got a document stop");
GeckoApp.mAppContext.handleDocumentStop(tabId);
}
}
} else if (type.equals("onProgressChange")) {
final int tabId = geckoObject.getInt("tabID");
final int current = geckoObject.getInt("current");
final int total = geckoObject.getInt("total");
GeckoApp.mAppContext.handleProgressChange(tabId, current, total);
Log.i("GeckoShell", "progress - " + current + "/" + total);
} else if (type.equals("onCameraCapture")) {
//GeckoApp.mAppContext.doCameraCapture(geckoObject.getString("path"));
GeckoApp.mAppContext.doCameraCapture();
} else if (type.equals("onCreateTab")) {
Log.i("GeckoShell", "Created a new tab");
int tabId = geckoObject.getInt("tabID");
String uri = geckoObject.getString("uri");
Tabs.getInstance().addTab(tabId, uri);
} else if (type.equals("prompt")) {
if (type.equals("prompt")) {
if (sPromptQueue == null)
sPromptQueue = new SynchronousQueue<String>();
getHandler().post(new Runnable() {
@ -1586,9 +1556,23 @@ public class GeckoAppShell
}
return promptServiceResult;
}
if (mEventListeners == null)
return "";
if (!mEventListeners.containsKey(type))
return "";
ArrayList<GeckoEventListener> listeners = mEventListeners.get(type);
Iterator items = listeners.iterator();
while (items.hasNext()) {
((GeckoEventListener) items.next()).handleMessage(type, geckoObject);
}
} catch (Exception e) {
Log.i("GeckoShell", "handleGeckoMessage throws " + e);
}
return "";
}

View File

@ -0,0 +1,44 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Android code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Sriram Ramasubramanian <sriram@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
package org.mozilla.gecko;
import org.json.JSONObject;
public interface GeckoEventListener {
public void handleMessage(String event, JSONObject message);
}

View File

@ -58,7 +58,7 @@ JAVAFILES = \
GeckoBookmarks.java \
Tab.java \
Tabs.java \
ShowTabs.java \
GeckoEventListener.java \
PromptService.java \
SurfaceInfo.java \
$(NULL)
@ -139,7 +139,8 @@ RES_LAYOUT = \
res/layout/browser_toolbar.xml \
res/layout/bookmarks.xml \
res/layout/bookmark_list_row.xml \
res/layout/show_tabs.xml \
res/layout/tabs_tray.xml \
res/layout/tabs_row.xml \
res/layout/dialog_checkbox.xml \
$(NULL)

View File

@ -1,121 +0,0 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Android code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Sriram Ramasubramanian <sriram@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
package org.mozilla.gecko;
import java.io.*;
import java.util.*;
import org.mozilla.gecko.*;
import android.os.*;
import android.content.*;
import android.app.*;
import android.text.*;
import android.util.*;
import android.widget.*;
import android.database.sqlite.*;
import android.database.*;
import android.view.*;
import android.view.View.*;
import android.net.Uri;
import android.graphics.*;
public class ShowTabs extends ListActivity {
private static final String LOG_FILE_NAME = "ShowTabs";
public static final String ID = "id";
public static final String TYPE = "type";
private ArrayList <HashMap<String, String>> tabsList = null;
public static enum Type { ADD, SWITCH };
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.show_tabs);
ListView list = (ListView) findViewById(android.R.id.list);
Button addTab = new Button(this);
addTab.setText("+ add tab");
addTab.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent resultIntent = new Intent();
resultIntent.putExtra(TYPE, Type.ADD.name());
setResult(Activity.RESULT_OK, resultIntent);
finish();
}
});
list.addHeaderView(addTab);
HashMap<Integer, Tab> tabs = Tabs.getInstance().getTabs();
tabsList = new ArrayList<HashMap<String, String>> ();
if (tabs != null) {
Iterator keys = tabs.keySet().iterator();
HashMap<String, String> map;
Tab tab;
while (keys.hasNext()) {
tab = tabs.get(keys.next());
map = new HashMap<String, String>();
map.put("id", "" + tab.getId());
map.put("title", tab.getTitle());
map.put("url", tab.getURL());
tabsList.add(map);
}
}
list.setAdapter(new SimpleAdapter(
ShowTabs.this,
tabsList,
R.layout.awesomebar_row,
new String[] { "title", "url" },
new int[] { R.id.title, R.id.url }
));
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
HashMap<String, String> map = tabsList.get((int) id);
Intent resultIntent = new Intent();
resultIntent.putExtra(TYPE, Type.SWITCH.name());
resultIntent.putExtra(ID, map.get("id"));
setResult(Activity.RESULT_OK, resultIntent);
finish();
}
}

View File

@ -95,6 +95,10 @@ public class Tab {
return title;
}
public Drawable getFavicon() {
return favicon;
}
public boolean isLoading() {
return loading;
}

View File

@ -47,9 +47,11 @@ public class Tabs {
private static final String LOG_FILE_NAME = "Tabs";
private static int selectedTab = -1;
private HashMap<Integer, Tab> tabs;
private ArrayList<Tab> order;
private Tabs() {
tabs = new HashMap<Integer, Tab>();
order = new ArrayList<Tab>();
}
public int getCount() {
@ -62,6 +64,7 @@ public class Tabs {
Tab tab = new Tab(id, url);
tabs.put(id, tab);
order.add(tab);
Log.i(LOG_FILE_NAME, "Added a tab with id: " + id + ", url: " + url);
selectedTab = id;
return tab;
@ -69,6 +72,7 @@ public class Tabs {
public void removeTab(int id) {
if (tabs.containsKey(id)) {
order.remove(getTab(id));
tabs.remove(id);
Log.i(LOG_FILE_NAME, "Removed a tab with id: " + id);
}
@ -82,6 +86,17 @@ public class Tabs {
return tabs.get(id);
}
public int getIndexOf(Tab tab) {
return order.lastIndexOf(tab);
}
public Tab getTabAt(int index) {
if (index < order.size())
return order.get(index);
else
return null;
}
public Tab getSelectedTab() {
return tabs.get(selectedTab);
}

View File

@ -23,7 +23,9 @@
<!ENTITY bookmarks "Bookmarks">
<!ENTITY bookmark_add "Add Bookmark">
<!ENTITY tabs "Tabs">
<!ENTITY reload "Reload">
<!ENTITY new_tab "New Tab">
<!ENTITY quit "Quit">

View File

@ -9,19 +9,18 @@
<LinearLayout android:id="@+id/address_bar"
style="@style/AddressBar">
<Button android:id="@+id/tabs"
style="@style/AddressBar.Button.Left"/>
<ImageButton android:id="@+id/favimage"
style="@style/AddressBar.ImageButton.Left"
style="@style/AddressBar.ImageButton.Middle"
android:src="@drawable/favicon"/>
<Button android:id="@+id/awesome_bar"
style="@style/AddressBar.URL"
android:textColor="#666"/>
<ImageButton android:id="@+id/reload"
style="@style/AddressBar.ImageButton.Right"
android:src="@drawable/reload"/>
</LinearLayout>
</merge>

View File

@ -1,9 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/quit"
android:icon="@drawable/quit"
android:title="@string/quit" />
<item android:id="@+id/reload"
android:title="@string/reload"/>
<item android:id="@+id/bookmarks"
android:title="@string/bookmarks"
@ -13,8 +12,8 @@
android:icon="@drawable/share"
android:title="@string/share" />
<item android:id="@+id/show_tabs"
android:title="@string/tabs"
android:showAsAction="ifRoom"/>
<item android:id="@+id/quit"
android:icon="@drawable/quit"
android:title="@string/quit" />
</menu>

View File

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/awesomeScreen"
style="@style/Screen">
<ListView android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:background="#fff"
android:cacheColorHint="#fff"
android:divider="#666666"
android:dividerHeight="1dp"/>
</LinearLayout>

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#fefefe"
android:padding="6dip"
android:orientation="horizontal">
<LinearLayout android:id="@+id/info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1">
<ImageView android:id="@+id/favicon"
android:layout_width="32dip"
android:layout_height="match_parent"
android:layout_marginRight="6dip"
android:layout_gravity="center_vertical"
android:scaleType="fitCenter"/>
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginLeft="6dip"
android:layout_marginRight="6dip"
android:orientation="vertical">
<TextView android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMediumInverse"
android:textColor="#000"
android:singleLine="true"
android:ellipsize="middle"/>
<TextView android:id="@+id/url"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmallInverse"
android:textColor="?android:attr/textColorSecondaryInverse"
android:layout_marginTop="2dip"
android:includeFontPadding="false"
android:singleLine="true"
android:ellipsize="middle"/>
</LinearLayout>
</LinearLayout>
<ImageButton android:id="@+id/close"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="#00000000"
android:src="@drawable/quit"/>
</LinearLayout>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/address_bar_button_middle"
android:cacheColorHint="#fff"
android:divider="#666666"
android:dividerHeight="1dp"/>

View File

@ -45,23 +45,23 @@
<!-- Address bar - Button -->
<style name="AddressBar.Button">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_width">40dip</item>
<item name="android:layout_height">43dip</item>
<item name="android:textSize">16sp</item>
</style>
<!-- Address bar - Image Button -->
<style name="AddressBar.ImageButton" parent="AddressBar.Button">
<item name="android:scaleType">fitXY</item>
<item name="android:layout_width">40dip</item>
<item name="android:scaleType">fitCenter</item>
</style>
<!-- Address bar - Left Button -->
<style name="AddressBar.ImageButton.Left">
<style name="AddressBar.Button.Left">
<item name="android:background">@drawable/address_bar_button_left</item>
</style>
<!-- Address bar - Right Button -->
<style name="AddressBar.ImageButton.Right">
<style name="AddressBar.Button.Right">
<item name="android:background">@drawable/address_bar_button_right</item>
</style>
@ -71,7 +71,8 @@
</style>
<!-- Address bar - URL -->
<style name="AddressBar.URL" parent="AddressBar.ImageButton.Middle">
<style name="AddressBar.URL" parent="AddressBar.Button.Right">
<item name="android:layout_width">wrap_content</item>
<item name="android:orientation">horizontal</item>
<item name="android:layout_weight">1</item>
<item name="android:paddingLeft">10dip</item>

View File

@ -32,5 +32,6 @@
<string name="share">&share;</string>
<string name="tabs">&tabs;</string>
<string name="reload">&reload;</string>
<string name="new_tab">&new_tab;</string>
</resources>

View File

@ -67,9 +67,10 @@ var BrowserApp = {
this.deck = document.getElementById("browsers");
BrowserEventHandler.init();
Services.obs.addObserver(this, "tab-add", false);
Services.obs.addObserver(this, "tab-load", false);
Services.obs.addObserver(this, "tab-select", false);
Services.obs.addObserver(this, "Tab:Add", false);
Services.obs.addObserver(this, "Tab:Load", false);
Services.obs.addObserver(this, "Tab:Select", false);
Services.obs.addObserver(this, "Tab:Close", false);
Services.obs.addObserver(this, "session-back", false);
Services.obs.addObserver(this, "session-reload", false);
@ -170,11 +171,18 @@ var BrowserApp = {
this._tabs.splice(this._tabs.indexOf(aTab), 1);
},
selectTab: function selectTab(aTabId) {
let tab = this.getTabForId(aTabId);
if (tab != null) {
this.selectedTab = tab;
tab.active = true;
selectTab: function selectTab(aTab) {
if (aTab != null) {
this.selectedTab = aTab;
aTab.active = true;
let message = {
gecko: {
type: "Tab:Selected",
tabID: aTab.id
}
};
sendMessageToJava(message);
}
},
@ -187,13 +195,15 @@ var BrowserApp = {
browser.goBack();
else if (aTopic == "session-reload")
browser.reload();
else if (aTopic == "tab-add") {
else if (aTopic == "Tab:Add") {
let newTab = this.addTab(aData);
newTab.active = true;
} else if (aTopic == "tab-load")
} else if (aTopic == "Tab:Load")
browser.loadURI(aData);
else if (aTopic == "tab-select")
this.selectTab(parseInt(aData));
else if (aTopic == "Tab:Select")
this.selectTab(this.getTabForId(parseInt(aData)));
else if (aTopic == "Tab:Close")
this.closeTab(this.getTabForId(parseInt(aData)));
}
}
@ -254,7 +264,7 @@ Tab.prototype = {
this.id = ++gTabIDFactory;
let message = {
gecko: {
type: "onCreateTab",
type: "Tab:Added",
tabID: this.id,
uri: aURL
}
@ -270,6 +280,14 @@ Tab.prototype = {
this.browser.removeProgressListener(this);
BrowserApp.deck.removeChild(this.browser);
this.browser = null;
let message = {
gecko: {
type: "Tab:Closed",
tabID: this.id
}
};
sendMessageToJava(message);
},
set active(aActive) {
@ -394,8 +412,12 @@ var BrowserEventHandler = {
if (!target.href || target.disabled)
return;
let browser = BrowserApp.getBrowserForDocument(target.ownerDocument);
let tabID = BrowserApp.getTabForBrowser(browser).id;
let json = {
type: "DOMLinkAdded",
tabID: tabID,
windowId: target.ownerDocument.defaultView.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID,
href: resolveGeckoURI(target.href),
charset: target.ownerDocument.characterSet,