diff --git a/embedding/android/AndroidManifest.xml.in b/embedding/android/AndroidManifest.xml.in index 87e6bdc2035..5061c3f5091 100644 --- a/embedding/android/AndroidManifest.xml.in +++ b/embedding/android/AndroidManifest.xml.in @@ -143,15 +143,5 @@ - - - diff --git a/embedding/android/BrowserToolbar.java b/embedding/android/BrowserToolbar.java index 09760cb81eb..7d01bead355 100644 --- a/embedding/android/BrowserToolbar.java +++ b/embedding/android/BrowserToolbar.java @@ -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) { diff --git a/embedding/android/GeckoApp.java b/embedding/android/GeckoApp.java index c79c3951071..84e15434a20 100644 --- a/embedding/android/GeckoApp.java +++ b/embedding/android/GeckoApp.java @@ -22,6 +22,7 @@ * Vladimir Vukicevic * Matt Brubeck * Vivien Nicolas + * Sriram Ramasubramanian * * 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 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 { 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 tabs) { + mContext = context; + mTabs = new ArrayList(); + + 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 mTabs; + private LayoutInflater mInflater; + } } diff --git a/embedding/android/GeckoAppShell.java b/embedding/android/GeckoAppShell.java index 9cd50d05a5d..5f400933d74 100644 --- a/embedding/android/GeckoAppShell.java +++ b/embedding/android/GeckoAppShell.java @@ -20,6 +20,7 @@ * * Contributor(s): * Vladimir Vukicevic + * Sriram Ramasubramanian * * 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> mEventListeners; + /* The Android-side API: API methods that Android calls */ // Initialization methods @@ -1502,6 +1505,28 @@ public class GeckoAppShell static SynchronousQueue sPromptQueue = null; + public static void registerGeckoEventListener(String event, GeckoEventListener listener) { + if (mEventListeners == null) + mEventListeners = new HashMap>(); + + if (!mEventListeners.containsKey(event)) + mEventListeners.put(event, new ArrayList()); + + ArrayList 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 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(); getHandler().post(new Runnable() { @@ -1586,9 +1556,23 @@ public class GeckoAppShell } return promptServiceResult; } + + if (mEventListeners == null) + return ""; + + if (!mEventListeners.containsKey(type)) + return ""; + + ArrayList 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 ""; } diff --git a/embedding/android/GeckoEventListener.java b/embedding/android/GeckoEventListener.java new file mode 100644 index 00000000000..670513f2cfb --- /dev/null +++ b/embedding/android/GeckoEventListener.java @@ -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 + * + * 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); +} diff --git a/embedding/android/Makefile.in b/embedding/android/Makefile.in index 7ae29f56eee..d8c2334ee97 100644 --- a/embedding/android/Makefile.in +++ b/embedding/android/Makefile.in @@ -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) diff --git a/embedding/android/ShowTabs.java b/embedding/android/ShowTabs.java deleted file mode 100644 index 21de8f71623..00000000000 --- a/embedding/android/ShowTabs.java +++ /dev/null @@ -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 - * - * 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 > 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 tabs = Tabs.getInstance().getTabs(); - tabsList = new ArrayList> (); - - if (tabs != null) { - Iterator keys = tabs.keySet().iterator(); - HashMap map; - Tab tab; - while (keys.hasNext()) { - tab = tabs.get(keys.next()); - map = new HashMap(); - 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 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(); - } -} diff --git a/embedding/android/Tab.java b/embedding/android/Tab.java index d450d2dece4..f25483f61cf 100644 --- a/embedding/android/Tab.java +++ b/embedding/android/Tab.java @@ -95,6 +95,10 @@ public class Tab { return title; } + public Drawable getFavicon() { + return favicon; + } + public boolean isLoading() { return loading; } diff --git a/embedding/android/Tabs.java b/embedding/android/Tabs.java index eeb32a49096..0c369144e1f 100644 --- a/embedding/android/Tabs.java +++ b/embedding/android/Tabs.java @@ -47,9 +47,11 @@ public class Tabs { private static final String LOG_FILE_NAME = "Tabs"; private static int selectedTab = -1; private HashMap tabs; + private ArrayList order; private Tabs() { tabs = new HashMap(); + order = new ArrayList(); } 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); } diff --git a/embedding/android/locales/en-US/android_strings.dtd b/embedding/android/locales/en-US/android_strings.dtd index cb2c0acfb26..a846ce0a599 100644 --- a/embedding/android/locales/en-US/android_strings.dtd +++ b/embedding/android/locales/en-US/android_strings.dtd @@ -23,7 +23,9 @@ - + + + diff --git a/embedding/android/resources/layout/browser_toolbar.xml b/embedding/android/resources/layout/browser_toolbar.xml index 30eb5010458..e3db012cd25 100644 --- a/embedding/android/resources/layout/browser_toolbar.xml +++ b/embedding/android/resources/layout/browser_toolbar.xml @@ -9,19 +9,18 @@ + +