diff --git a/code/boot.js b/code/boot.js index c66dcadf..220e1531 100644 --- a/code/boot.js +++ b/code/boot.js @@ -566,8 +566,8 @@ function boot() { window.iitcLoaded = true; window.runHooks('iitcLoaded'); - if (typeof android !== 'undefined' && android && android.removeSplashScreen) { - android.removeSplashScreen(); + if (typeof android !== 'undefined' && android && android.bootFinished) { + android.bootFinished(); } } diff --git a/external/taphold.js b/external/taphold.js index cf16e280..e889b126 100644 --- a/external/taphold.js +++ b/external/taphold.js @@ -106,12 +106,16 @@ $(this).bind((touchSupported ? "touchstart" : "mousedown"), data, startHandler) .bind((touchSupported ? "touchend" : "mouseup"), stopHandler) .bind((touchSupported ? "touchmove" : "mouseleave"), leaveHandler); + if(touchSupported) + $(this).bind("touchcancel", leaveHandler); }, teardown: function(namespaces) { $(this).unbind((touchSupported ? "touchstart" : "mousedown"), startHandler) .unbind((touchSupported ? "touchend" : "mouseup"), stopHandler) .unbind((touchSupported ? "touchmove" : "mouseleave"), leaveHandler); + if(touchSupported) + $(this).unbind("touchcancel", leaveHandler); } }; })(jQuery); diff --git a/mobile/AndroidManifest.xml b/mobile/AndroidManifest.xml index fda15a23..29831632 100644 --- a/mobile/AndroidManifest.xml +++ b/mobile/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="51" + android:versionName="0.7.1"> + android:theme="@style/AppTheme"> - \ No newline at end of file + diff --git a/mobile/libs/android-support-v4.jar b/mobile/libs/android-support-v4.jar index 428bdbc0..cf12d283 100644 Binary files a/mobile/libs/android-support-v4.jar and b/mobile/libs/android-support-v4.jar differ diff --git a/mobile/res/drawable-hdpi/collections_view_as_list.png b/mobile/res/drawable-hdpi/collections_view_as_list.png new file mode 100644 index 00000000..e45ea1fd Binary files /dev/null and b/mobile/res/drawable-hdpi/collections_view_as_list.png differ diff --git a/mobile/res/drawable-hdpi/collections_view_as_list_compact.png b/mobile/res/drawable-hdpi/collections_view_as_list_compact.png new file mode 100644 index 00000000..4a92e2c0 Binary files /dev/null and b/mobile/res/drawable-hdpi/collections_view_as_list_compact.png differ diff --git a/mobile/res/drawable-hdpi/drawer_shadow.9.png b/mobile/res/drawable-hdpi/drawer_shadow.9.png new file mode 100644 index 00000000..224cc4ff Binary files /dev/null and b/mobile/res/drawable-hdpi/drawer_shadow.9.png differ diff --git a/mobile/res/drawable-hdpi/ic_debug.png b/mobile/res/drawable-hdpi/ic_debug.png new file mode 100644 index 00000000..8ed186b4 Binary files /dev/null and b/mobile/res/drawable-hdpi/ic_debug.png differ diff --git a/mobile/res/drawable-hdpi/ic_drawer.png b/mobile/res/drawable-hdpi/ic_drawer.png new file mode 100644 index 00000000..ff7b1def Binary files /dev/null and b/mobile/res/drawable-hdpi/ic_drawer.png differ diff --git a/mobile/res/drawable-hdpi/social_cc_bcc.png b/mobile/res/drawable-hdpi/social_cc_bcc.png new file mode 100644 index 00000000..8c4f8b31 Binary files /dev/null and b/mobile/res/drawable-hdpi/social_cc_bcc.png differ diff --git a/mobile/res/drawable-mdpi/collections_view_as_list.png b/mobile/res/drawable-mdpi/collections_view_as_list.png new file mode 100644 index 00000000..9c748b0b Binary files /dev/null and b/mobile/res/drawable-mdpi/collections_view_as_list.png differ diff --git a/mobile/res/drawable-mdpi/collections_view_as_list_compact.png b/mobile/res/drawable-mdpi/collections_view_as_list_compact.png new file mode 100644 index 00000000..a53ef7c0 Binary files /dev/null and b/mobile/res/drawable-mdpi/collections_view_as_list_compact.png differ diff --git a/mobile/res/drawable-mdpi/drawer_shadow.9.png b/mobile/res/drawable-mdpi/drawer_shadow.9.png new file mode 100644 index 00000000..3797f99c Binary files /dev/null and b/mobile/res/drawable-mdpi/drawer_shadow.9.png differ diff --git a/mobile/res/drawable-mdpi/ic_debug.png b/mobile/res/drawable-mdpi/ic_debug.png new file mode 100644 index 00000000..ac20ea13 Binary files /dev/null and b/mobile/res/drawable-mdpi/ic_debug.png differ diff --git a/mobile/res/drawable-mdpi/ic_drawer.png b/mobile/res/drawable-mdpi/ic_drawer.png new file mode 100644 index 00000000..fb681ba2 Binary files /dev/null and b/mobile/res/drawable-mdpi/ic_drawer.png differ diff --git a/mobile/res/drawable-mdpi/social_cc_bcc.png b/mobile/res/drawable-mdpi/social_cc_bcc.png new file mode 100644 index 00000000..9ec127ed Binary files /dev/null and b/mobile/res/drawable-mdpi/social_cc_bcc.png differ diff --git a/mobile/res/drawable-xhdpi/collections_view_as_list.png b/mobile/res/drawable-xhdpi/collections_view_as_list.png new file mode 100644 index 00000000..95708234 Binary files /dev/null and b/mobile/res/drawable-xhdpi/collections_view_as_list.png differ diff --git a/mobile/res/drawable-xhdpi/collections_view_as_list_compact.png b/mobile/res/drawable-xhdpi/collections_view_as_list_compact.png new file mode 100644 index 00000000..c8f46e41 Binary files /dev/null and b/mobile/res/drawable-xhdpi/collections_view_as_list_compact.png differ diff --git a/mobile/res/drawable-xhdpi/drawer_shadow.9.png b/mobile/res/drawable-xhdpi/drawer_shadow.9.png new file mode 100644 index 00000000..fa3d853e Binary files /dev/null and b/mobile/res/drawable-xhdpi/drawer_shadow.9.png differ diff --git a/mobile/res/drawable-xhdpi/ic_debug.png b/mobile/res/drawable-xhdpi/ic_debug.png new file mode 100644 index 00000000..aeb96b06 Binary files /dev/null and b/mobile/res/drawable-xhdpi/ic_debug.png differ diff --git a/mobile/res/drawable-xhdpi/ic_drawer.png b/mobile/res/drawable-xhdpi/ic_drawer.png new file mode 100644 index 00000000..b9bc3d70 Binary files /dev/null and b/mobile/res/drawable-xhdpi/ic_drawer.png differ diff --git a/mobile/res/drawable-xhdpi/social_cc_bcc.png b/mobile/res/drawable-xhdpi/social_cc_bcc.png new file mode 100644 index 00000000..4153807e Binary files /dev/null and b/mobile/res/drawable-xhdpi/social_cc_bcc.png differ diff --git a/mobile/res/layout/activity_main.xml b/mobile/res/layout/activity_main.xml index 01392b66..d4a8ccbc 100644 --- a/mobile/res/layout/activity_main.xml +++ b/mobile/res/layout/activity_main.xml @@ -1,18 +1,45 @@ - + - + android:layout_width="match_parent" + android:layout_height="match_parent"/> - \ No newline at end of file + + + + + + + + + + + \ No newline at end of file diff --git a/mobile/res/layout/list_item_narrow.xml b/mobile/res/layout/list_item_narrow.xml new file mode 100644 index 00000000..64771748 --- /dev/null +++ b/mobile/res/layout/list_item_narrow.xml @@ -0,0 +1,10 @@ + + diff --git a/mobile/res/layout/list_item_selectable.xml b/mobile/res/layout/list_item_selectable.xml new file mode 100644 index 00000000..a82df550 --- /dev/null +++ b/mobile/res/layout/list_item_selectable.xml @@ -0,0 +1,11 @@ + + diff --git a/mobile/res/layout/map_options_header.xml b/mobile/res/layout/map_options_header.xml new file mode 100644 index 00000000..6efdd616 --- /dev/null +++ b/mobile/res/layout/map_options_header.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mobile/res/menu/main.xml b/mobile/res/menu/main.xml index 479e4446..941cd9bc 100644 --- a/mobile/res/menu/main.xml +++ b/mobile/res/menu/main.xml @@ -6,64 +6,24 @@ android:showAsAction="ifRoom|collapseActionView" android:actionViewClass="android.widget.SearchView" /> - - - - - - - - - - - - + android:title="@string/locate"> - - + android:title="@string/layer_chooser"> - - - - + + \ No newline at end of file diff --git a/mobile/res/values/strings.xml b/mobile/res/values/strings.xml index 2e067082..8699ebec 100644 --- a/mobile/res/values/strings.xml +++ b/mobile/res/values/strings.xml @@ -7,6 +7,8 @@ Share using… Copy to clipboard Settings + Show navigation menu + Hide navigation menu Reload IITC Print Version Toggle fullscreen @@ -53,7 +55,17 @@ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.]]> - + +
+ • Navigation: swipe from the left edge of your screen (or click the app icon) to evoke the Navigation Drawer. + Click the app icon again to return to the map.

+ • Layers/Highlights: swipe from the right edge of your screen (or click the layer-chooser icon in the ActionBar) + to evoke the Layer Drawer]]> +
+ + + + UI Misc IITC Plugins @@ -63,7 +75,7 @@ Show zoom control Shows +/- buttons even on multitouch capable devices. Hide Action Bar in fullscreen mode - Nice for screenshots. Note: IITCM cannot be controlled without Action Bar. + Nice for screenshots. Note: IITCm can still be controlled via the Navigation Drawers Force desktop mode Nice for tablets, looks awful on smartphones Force https @@ -81,13 +93,6 @@ IITC source Load IITC main script from url or use local script. Currently used source: - Chat - Full - Compact - Public - Faction - Info - Debug Clear Cookies Search Choose account to login @@ -100,8 +105,8 @@ Share Browser - Base Layers - Overlay Layers - Deselect/Select all + Highlighter + Base Layer + Overlay Layers \ No newline at end of file diff --git a/mobile/res/values/styles.xml b/mobile/res/values/styles.xml index 7843ae20..d8351bc3 100644 --- a/mobile/res/values/styles.xml +++ b/mobile/res/values/styles.xml @@ -17,4 +17,6 @@ + #99111111 + \ No newline at end of file diff --git a/mobile/res/xml/preferences.xml b/mobile/res/xml/preferences.xml index 49c89c40..7fc502d4 100644 --- a/mobile/res/xml/preferences.xml +++ b/mobile/res/xml/preferences.xml @@ -26,7 +26,7 @@ android:key="pref_fullscreen_actionbar" android:title="@string/pref_fullscreen_actionbar" android:summary="@string/pref_fullscreen_actionbar_sum" - android:defaultValue="false" /> + android:defaultValue="true" /> { - public HighlighterAdapter() { - super(mIitc, android.R.layout.simple_list_item_1); - clear(); - } - - @Override - public void add(String object) { - super.remove(object); // to avoid duplicates - super.add(object); - } - - @Override - public void clear() { - super.clear(); - add("No Highlights");// Probably must be the same as window._no_highlighter - } - } - - private IITC_Mobile mIitc; - private ActionBar mActionBar; - private SharedPreferences mPrefs; - private HighlighterAdapter mHighlighters; - - private String mActiveHighlighter = null; - private boolean mDesktopMode = false; - private boolean mFullscreen = false; - private boolean mHideInFullscreen = false; - private int mPane = android.R.id.home; - - public IITC_ActionBarHelper(IITC_Mobile activity, ActionBar bar) { - mIitc = activity; - mActionBar = bar; - mPrefs = PreferenceManager.getDefaultSharedPreferences(activity); - mHighlighters = new HighlighterAdapter(); - - mActionBar.setDisplayShowHomeEnabled(true); // show icon - mActionBar.setListNavigationCallbacks(mHighlighters, this); - - onPrefChanged(); // also calls updateActionBar() - } - - private void updateActionBar() { - boolean showHighlighter = true; - - if (mDesktopMode) { - mActionBar.setDisplayHomeAsUpEnabled(false); // Hide "up" indicator - mActionBar.setHomeButtonEnabled(false); // Make icon unclickable - mActionBar.setTitle(mIitc.getString(R.string.app_name)); - } else { - if (mPane != android.R.id.home) { - mActionBar.setDisplayHomeAsUpEnabled(true); // Show "up" indicator - mActionBar.setHomeButtonEnabled(true);// Make icon clickable - showHighlighter = false; - } - else { - mActionBar.setDisplayHomeAsUpEnabled(false); // Hide "up" indicator - mActionBar.setHomeButtonEnabled(false); // Make icon unclickable - } - mActionBar.setTitle(IITC_Mobile.PANE_TITLES.get(mPane, mIitc.getString(R.string.app_name))); - } - - if (mHighlighters.getCount() < 2) // there should always be "No Highlights" - showHighlighter = false; - - if (showHighlighter) { - mActionBar.setDisplayShowTitleEnabled(false); // Hide title - mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); - setActiveHighlighter(mActiveHighlighter); - } else { - mActionBar.setDisplayShowTitleEnabled(true); // Show title - mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); - } - if (mFullscreen && mHideInFullscreen) - mActionBar.hide(); - else - mActionBar.show(); - } - - public void addPortalHighlighter(String name) { - mHighlighters.add(name); - - if (name.equals(mActiveHighlighter)) - setActiveHighlighter(name); - - updateActionBar(); - } - - public boolean hideInFullscreen() { - return mHideInFullscreen; - } - - @Override - public boolean onNavigationItemSelected(int position, long itemId) { - String name = mHighlighters.getItem(position); - mIitc.getWebView().loadUrl("javascript: window.changePortalHighlights('" + name + "')"); - return true; - } - - public void onPrefChanged() { - mDesktopMode = mPrefs.getBoolean("pref_force_desktop", false); - mHideInFullscreen = mPrefs.getBoolean("pref_fullscreen_actionbar", false); - updateActionBar(); - } - - public void reset() { - mHighlighters.clear(); - mPane = android.R.id.home; - updateActionBar(); - } - - public void setActiveHighlighter(String name) { - mActiveHighlighter = name; - - if (mActionBar.getNavigationMode() == ActionBar.NAVIGATION_MODE_LIST) { - int position = mHighlighters.getPosition(mActiveHighlighter); - if (position >= 0 && position < mActionBar.getNavigationItemCount()) - mActionBar.setSelectedNavigationItem(position); - } - } - - public void setFullscreen(boolean fullscreen) { - mFullscreen = fullscreen; - if (mFullscreen && mHideInFullscreen) { - // show a toast with instructions to exit the fullscreen mode again - Toast.makeText(mIitc, "Press back button to exit fullscreen", Toast.LENGTH_SHORT).show(); - } - - updateActionBar(); - } - - public void switchTo(int button) { - mPane = button; - updateActionBar(); - } -} diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_JSInterface.java b/mobile/src/com/cradle/iitc_mobile/IITC_JSInterface.java index cd3cbf94..c26c8e21 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_JSInterface.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_JSInterface.java @@ -1,66 +1,48 @@ package com.cradle.iitc_mobile; -import android.app.Activity; -import android.app.AlertDialog; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; -import android.content.DialogInterface.OnMultiChoiceClickListener; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.util.Log; -import android.view.View; import android.webkit.JavascriptInterface; -import android.widget.AdapterView; -import android.widget.ListView; import android.widget.Toast; +import com.cradle.iitc_mobile.IITC_NavigationHelper.Pane; import com.cradle.iitc_mobile.share.ShareActivity; -import org.json.JSONArray; -import org.json.JSONException; - -import java.util.HashMap; +import java.util.Locale; // provide communication between IITC script and android app public class IITC_JSInterface { - // context of main activity - private final Context mContext; - private final HashMap mLayerIds; - private boolean[] mOverlayIsActive; - private int mActiveBaseLayer; - private String[] mOverlayLayers, mBaseLayers; - private int mNumBaseLayers; - private int mNumOverlayLayers; + private IITC_Mobile mIitc; - IITC_JSInterface(Context c) { - mLayerIds = new HashMap(); - mContext = c; + IITC_JSInterface(IITC_Mobile iitc) { + mIitc = iitc; } // open dialog to send geo intent for navigation apps like gmaps or waze etc... @JavascriptInterface public void intentPosLink(double lat, double lng, int zoom, String title, boolean isPortal) { - Intent intent = new Intent(mContext, ShareActivity.class); + Intent intent = new Intent(mIitc, ShareActivity.class); intent.putExtra("lat", lat); intent.putExtra("lng", lng); intent.putExtra("zoom", zoom); intent.putExtra("title", title); intent.putExtra("isPortal", isPortal); - mContext.startActivity(intent); + mIitc.startActivity(intent); } // share a string to the IITC share activity. only uses the share tab. @JavascriptInterface public void shareString(String str) { - Intent intent = new Intent(mContext, ShareActivity.class); + Intent intent = new Intent(mIitc, ShareActivity.class); intent.putExtra("shareString", str); intent.putExtra("onlyShare", true); - mContext.startActivity(intent); + mIitc.startActivity(intent); } // disable javascript injection while spinner is enabled @@ -68,26 +50,25 @@ public class IITC_JSInterface { @JavascriptInterface public void spinnerEnabled(boolean en) { Log.d("iitcm", "disableJS? " + en); - ((IITC_Mobile) mContext).getWebView().disableJS(en); + mIitc.getWebView().disableJS(en); } // copy link to specific portal to android clipboard @JavascriptInterface public void copy(String s) { - ClipboardManager clipboard = (ClipboardManager) mContext + ClipboardManager clipboard = (ClipboardManager) mIitc .getSystemService(Context.CLIPBOARD_SERVICE); ClipData clip = ClipData.newPlainText("Copied Text ", s); clipboard.setPrimaryClip(clip); - Toast.makeText(mContext, "copied to clipboard", Toast.LENGTH_SHORT) - .show(); + Toast.makeText(mIitc, "copied to clipboard", Toast.LENGTH_SHORT).show(); } @JavascriptInterface public int getVersionCode() { int versionCode = 0; try { - PackageInfo pInfo = mContext.getPackageManager() - .getPackageInfo(mContext.getPackageName(), 0); + PackageInfo pInfo = mIitc.getPackageManager() + .getPackageInfo(mIitc.getPackageName(), 0); versionCode = pInfo.versionCode; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); @@ -97,250 +78,72 @@ public class IITC_JSInterface { @JavascriptInterface public void switchToPane(final String id) { - final IITC_Mobile iitcm = (IITC_Mobile) mContext; - - iitcm.runOnUiThread(new Runnable() { + mIitc.runOnUiThread(new Runnable() { @Override public void run() { - IITC_ActionBarHelper actionbar = iitcm.getActionBarHelper(); - Integer button = IITC_Mobile.PANES.get(id); + Pane pane; + try { + pane = Pane.valueOf(id.toUpperCase(Locale.getDefault())); + } catch (IllegalArgumentException e) { + pane = Pane.MAP; + } - if (button == null) - button = android.R.id.home; - - actionbar.switchTo(button); - iitcm.backStackUpdate(button); + mIitc.setCurrentPane(pane); } }); } - @JavascriptInterface - public void dialogOpened(String id, boolean open) { - ((IITC_Mobile) mContext).dialogOpened(id, open); - } - @JavascriptInterface public void dialogFocused(String id) { - ((IITC_Mobile) mContext).setFocusedDialog(id); + mIitc.setFocusedDialog(id); } @JavascriptInterface - public void removeSplashScreen() { - Log.d("iitcm", "removing splash screen"); - final IITC_Mobile iitc = ((IITC_Mobile) mContext); + public void dialogOpened(String id, boolean open) { + mIitc.dialogOpened(id, open); + } - iitc.runOnUiThread(new Runnable() { + @JavascriptInterface + public void bootFinished() { + Log.d("iitcm", "...boot finished"); + + mIitc.runOnUiThread(new Runnable() { @Override public void run() { - iitc.findViewById(R.id.iitc_webview).setVisibility(View.VISIBLE); - iitc.findViewById(R.id.imageLoading).setVisibility(View.GONE); + mIitc.setLoadingState(false); + + mIitc.getMapSettings().onBootFinished(); } }); } // get layers and list them in a dialog @JavascriptInterface - public void setLayers(String base_layer, String overlay_layer) { - - /* - * the layer strings have a form like: - * [{"layerId":27,"name":"MapQuest OSM","active":true}, - * {"layerId":28,"name":"Default Ingress Map","active":false}] - * Put it in a JSONArray and parse it - */ - JSONArray base_layersJSON = null; - JSONArray overlay_layersJSON = null; - try { - base_layersJSON = new JSONArray(base_layer); - overlay_layersJSON = new JSONArray(overlay_layer); - } catch (JSONException e) { - e.printStackTrace(); - } - - // get length and initialize arrays - mNumBaseLayers = base_layersJSON.length(); - mNumOverlayLayers = overlay_layersJSON.length(); - mOverlayIsActive = new boolean[mNumOverlayLayers]; - mOverlayLayers = new String[mNumOverlayLayers]; - mBaseLayers = new String[mNumBaseLayers]; - mLayerIds.clear(); - - // --------------- base layers ------------------------ - for (int i = 0; i < mNumBaseLayers; ++i) { - try { - String layer = base_layersJSON.getString(i); - layer = layer.replace("{", ""); - layer = layer.replace("}", ""); - /* - * we now should have a string like - * ["layerId":27,"name":"MapQuest OSM","active":true] - * split it on , - */ - String[] layers = layer.split(","); - /* - * we should have 3 strings in a form like - * "name":"MapQuest OSM" - * get the values and get rid of the quotation marks - */ - String id = ""; - String name = ""; - boolean isActive = false; - for (String b_layer : layers) { - String[] values = b_layer.split(":"); - if (values[0].contains("active")) isActive = values[1].equals("true"); - if (values[0].contains("layerId")) id = values[1]; - if (values[0].contains("name")) name = values[1]; - } - name = name.replace("\"", ""); - mLayerIds.put(name, id); - this.mBaseLayers[i] = name; - if (isActive) mActiveBaseLayer = i; - } catch (JSONException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + public void setLayers(final String base_layer, final String overlay_layer) { + mIitc.runOnUiThread(new Runnable() { + @Override + public void run() { + mIitc.getMapSettings().setLayers(base_layer, overlay_layer); } - } - - // --------------- overlay layers ------------------------ - for (int i = 0; i < mNumOverlayLayers; ++i) { - try { - String layer = overlay_layersJSON.getString(i); - layer = layer.replace("{", ""); - layer = layer.replace("}", ""); - String[] layers = layer.split(","); - String id = ""; - String name = ""; - boolean isActive = false; - for (String o_layer : layers) { - String[] values = o_layer.split(":"); - if (values[0].contains("active")) isActive = values[1].equals("true"); - if (values[0].contains("layerId")) id = values[1]; - if (values[0].contains("name")) name = values[1]; - } - name = name.replace("\"", ""); - mLayerIds.put(name, id); - this.mOverlayLayers[i] = name; - this.mOverlayIsActive[i] = isActive; - } catch (JSONException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - // show overlay layers by default - showMultiSelection(); + }); } @JavascriptInterface public void addPortalHighlighter(final String name) { - final IITC_Mobile iitc = ((IITC_Mobile) mContext); - iitc.runOnUiThread(new Runnable() { + mIitc.runOnUiThread(new Runnable() { @Override public void run() { - iitc.getActionBarHelper().addPortalHighlighter(name); + mIitc.getMapSettings().addPortalHighlighter(name); } }); } @JavascriptInterface public void setActiveHighlighter(final String name) { - final IITC_Mobile iitc = ((IITC_Mobile) mContext); - iitc.runOnUiThread(new Runnable() { + mIitc.runOnUiThread(new Runnable() { @Override public void run() { - iitc.getActionBarHelper().setActiveHighlighter(name); - } - }); - } - - // show all overlay layers in a multi selection list dialog - private void showMultiSelection() { - // build the layer chooser dialog - AlertDialog.Builder d_m = new AlertDialog.Builder(mContext); - OnMultiChoiceClickListener m_listener = new OnMultiChoiceClickListener() { - @Override - public void onClick(DialogInterface dialog, int which, boolean isChecked) { - // activate clicked layer - showLayer(mLayerIds.get(mOverlayLayers[which]), isChecked); - } - }; - d_m.setMultiChoiceItems(mOverlayLayers, mOverlayIsActive, m_listener); - // switch to base layers - d_m.setPositiveButton(R.string.base_layers, new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - showSingleSelection(); - dialog.dismiss(); - } - }); - d_m.setNegativeButton(R.string.close, new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - }); - d_m.setTitle(R.string.overlay_layers); - final AlertDialog dialog = d_m.create(); - final ListView list = dialog.getListView(); - list.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { - boolean disable = false; - - @Override - public boolean onItemLongClick(AdapterView adapterView, View view, int i, long l) { - int j = 0; - for (String layer : mOverlayLayers) { - if (!mOverlayLayers[j].contains("DEBUG")) { - // uncheck the item + set the boolean in the isActive array - mOverlayIsActive[j] = disable; - list.setItemChecked(j, disable); - showLayer(mLayerIds.get(layer), disable); - } - ++j; - } - disable = !disable; - return true; - } - }); - dialog.show(); - } - - // show all base layers in a single selection list dialog - private void showSingleSelection() { - OnClickListener s_listener = new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - // activate clicked layer - showLayer(mLayerIds.get(mBaseLayers[which]), true); - mActiveBaseLayer = which; - } - }; - AlertDialog.Builder d_s = new AlertDialog.Builder(mContext); - d_s.setSingleChoiceItems(mBaseLayers, mActiveBaseLayer, s_listener); - // switch to overlay layers - d_s.setPositiveButton(R.string.overlay_layers, new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - showMultiSelection(); - dialog.dismiss(); - } - }); - d_s.setNegativeButton(R.string.close, new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - }); - d_s.setTitle(R.string.base_layers); - final AlertDialog dialog = d_s.create(); - dialog.show(); - } - - private void showLayer(final String id, final boolean enable) { - ((Activity) mContext).runOnUiThread(new Runnable() { - @Override - public void run() { - ((IITC_Mobile) mContext).getWebView().loadUrl("javascript: " + - "window.layerChooser.showLayer(" - + id + "," + enable + ");"); + mIitc.getMapSettings().setActiveHighlighter(name); } }); } diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_MapSettings.java b/mobile/src/com/cradle/iitc_mobile/IITC_MapSettings.java new file mode 100644 index 00000000..c97c65a6 --- /dev/null +++ b/mobile/src/com/cradle/iitc_mobile/IITC_MapSettings.java @@ -0,0 +1,286 @@ +package com.cradle.iitc_mobile; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.AdapterView.OnItemLongClickListener; +import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.ArrayAdapter; +import android.widget.CheckedTextView; +import android.widget.ListView; +import android.widget.Spinner; +import android.widget.TextView; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.Comparator; + +public class IITC_MapSettings implements OnItemSelectedListener, OnItemClickListener, OnItemLongClickListener { + private class HighlighterAdapter extends ArrayAdapter { + private HighlighterComparator mComparator = new HighlighterComparator(); + + private HighlighterAdapter(int resource) { + super(mIitc, resource); + clear(); + } + + @Override + public void add(String object) { + super.remove(object); // to avoid duplicates + super.add(object); + super.sort(mComparator); + } + + @Override + public void clear() { + super.clear(); + add("No Highlights");// Probably must be the same as window._no_highlighter + } + } + + private class HighlighterComparator implements Comparator { + @Override + public int compare(String lhs, String rhs) { + // Move "No Highlights" on top. Sort the rest alphabetically + if (lhs.equals("No Highlights")) + return -1000; + else if (rhs.equals("No Highlights")) + return 1000; + else + return lhs.compareTo(rhs); + } + } + + private class Layer { + boolean active; + int id; + String name; + + @Override + public String toString() { + return name; + } + } + + private class LayerAdapter extends ArrayAdapter + { + public LayerAdapter(int resource) { + super(mIitc, resource); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + Layer item = getItem(position); + View view = (TextView) super.getView(position, convertView, parent); + + if (view instanceof CheckedTextView) + ((CheckedTextView) view).setChecked(item.active); + return view; + } + } + + private IITC_Mobile mIitc; + + private ArrayAdapter mHighlighters; + private ArrayAdapter mBaseLayers; + private ArrayAdapter mOverlayLayers; + + private Spinner mSpinnerBaseMap; + private Spinner mSpinnerHighlighter; + private ListView mListViewOverlayLayers; + + private String mActiveHighlighter; + private int mActiveLayer; + + private boolean mLoading = true; + + public IITC_MapSettings(IITC_Mobile activity) { + mIitc = activity; + + mHighlighters = new HighlighterAdapter(R.layout.list_item_narrow); + mBaseLayers = new LayerAdapter(R.layout.list_item_narrow); + mOverlayLayers = new LayerAdapter(android.R.layout.simple_list_item_multiple_choice); + + mHighlighters.setDropDownViewResource(R.layout.list_item_selectable); + mBaseLayers.setDropDownViewResource(R.layout.list_item_selectable); + + LayoutInflater inflater = (LayoutInflater) mIitc.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View header = inflater.inflate(R.layout.map_options_header, null); + + mSpinnerHighlighter = (Spinner) header.findViewById(R.id.spinnerHighlighter); + mSpinnerBaseMap = (Spinner) header.findViewById(R.id.spinnerBaseLayer); + mListViewOverlayLayers = (ListView) mIitc.findViewById(R.id.right_drawer); + + mListViewOverlayLayers.addHeaderView(header); + + mSpinnerHighlighter.setAdapter(mHighlighters); + mSpinnerBaseMap.setAdapter(mBaseLayers); + mListViewOverlayLayers.setAdapter(mOverlayLayers); + + mSpinnerHighlighter.setOnItemSelectedListener(this); + mSpinnerBaseMap.setOnItemSelectedListener(this); + mListViewOverlayLayers.setOnItemClickListener(this); + mListViewOverlayLayers.setOnItemLongClickListener(this); + } + + private void setLayer(Layer layer) { + if (!mLoading) + mIitc.getWebView().loadUrl( + "javascript: window.layerChooser.showLayer(" + layer.id + "," + layer.active + ");"); + } + + public void addPortalHighlighter(String name) { + mHighlighters.add(name); + + // to select active highlighter. must be called every time because of sorting + setActiveHighlighter(mActiveHighlighter); + } + + public void onBootFinished() { + mLoading = false; + updateLayers(); + } + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + position--; // The ListView header counts as an item as well. + + Layer item = mOverlayLayers.getItem(position); + item.active = !item.active; + setLayer(item); + mOverlayLayers.notifyDataSetChanged(); + } + + @Override + public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { + position--; // The ListView header counts as an item as well. + boolean active = !mOverlayLayers.getItem(position).active; + + for (int i = 0; i < mOverlayLayers.getCount(); i++) { + Layer item = mOverlayLayers.getItem(i); + if (item.name.contains("DEBUG")) continue; + if (active == item.active) continue; // no need to set same value again + item.active = active; + setLayer(item); + } + + mOverlayLayers.notifyDataSetChanged(); + + return true; + } + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + if (mLoading) return; + + if (parent.equals(mSpinnerHighlighter)) { + String name = mHighlighters.getItem(position); + mIitc.getWebView().loadUrl("javascript: window.changePortalHighlights('" + name + "')"); + } + else if (parent.equals(mSpinnerBaseMap)) { + mBaseLayers.getItem(mActiveLayer).active = false; // set old layer to hidden, but no need to really hide + + Layer layer = mBaseLayers.getItem(position); + layer.active = true; + setLayer(layer); + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + // ignore + } + + public void reset() { + mHighlighters.clear(); + mBaseLayers.clear(); + mOverlayLayers.clear(); + + mIitc.getNavigationHelper().setHighlighter(null); + + mLoading = true; + } + + public void setActiveHighlighter(String name) { + mActiveHighlighter = name; + + int position = mHighlighters.getPosition(mActiveHighlighter); + if (position >= 0 && position < mHighlighters.getCount()) + mSpinnerHighlighter.setSelection(position); + + mIitc.getNavigationHelper().setHighlighter(name); + } + + public void setLayers(String base_layer, String overlay_layer) { + /* + * the layer strings have a form like: + * [{"layerId":27,"name":"MapQuest OSM","active":true}, + * {"layerId":28,"name":"Default Ingress Map","active":false}] + * Put it in a JSONArray and parse it + */ + JSONArray base_layers = null; + JSONArray overlay_layers = null; + + try { + base_layers = new JSONArray(base_layer); + overlay_layers = new JSONArray(overlay_layer); + } catch (JSONException e) { + e.printStackTrace(); + return; + } + + mActiveLayer = 0; + mBaseLayers.setNotifyOnChange(false); + mBaseLayers.clear(); + for (int i = 0; i < base_layers.length(); i++) { + try { + JSONObject layerObj = base_layers.getJSONObject(i); + Layer layer = new Layer(); + + layer.id = layerObj.getInt("layerId"); + layer.name = layerObj.getString("name"); + layer.active = layerObj.getBoolean("active"); + + if (layer.active) + // getCount() will be the index of the layer we are about to add + mActiveLayer = mBaseLayers.getCount(); + + mBaseLayers.add(layer); + } catch (JSONException e) { + e.printStackTrace(); + continue; + } + } + mBaseLayers.notifyDataSetChanged(); + mSpinnerBaseMap.setSelection(mActiveLayer); + + mOverlayLayers.setNotifyOnChange(false); + mOverlayLayers.clear(); + for (int i = 0; i < overlay_layers.length(); i++) { + try { + JSONObject layerObj = overlay_layers.getJSONObject(i); + Layer layer = new Layer(); + + layer.id = layerObj.getInt("layerId"); + layer.name = layerObj.getString("name"); + layer.active = layerObj.getBoolean("active"); + + mOverlayLayers.add(layer); + } catch (JSONException e) { + e.printStackTrace(); + continue; + } + } + mOverlayLayers.notifyDataSetChanged(); + } + + public void updateLayers() { + if (!mLoading) + mIitc.getWebView().loadUrl("javascript: window.layerChooser.getLayers()"); + } +} diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java b/mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java index b8d84c83..af56d2c4 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java @@ -18,7 +18,6 @@ import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceManager; import android.util.Log; -import android.util.SparseArray; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -30,10 +29,12 @@ import android.webkit.WebView; import android.widget.SearchView; import android.widget.Toast; +import com.cradle.iitc_mobile.IITC_NavigationHelper.Pane; + import java.io.IOException; import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.HashMap; +import java.util.Locale; +import java.util.Stack; public class IITC_Mobile extends Activity { @@ -52,37 +53,17 @@ public class IITC_Mobile extends Activity { private boolean mDesktopMode = false; private boolean mAdvancedMenu = false; private boolean mReloadNeeded = false; - private final ArrayList mDialogStack = new ArrayList(); + private final Stack mDialogStack = new Stack(); private SharedPreferences mSharedPrefs; - private IITC_ActionBarHelper mActionBarHelper; + private IITC_NavigationHelper mNavigationHelper; + private IITC_MapSettings mMapSettings; // Used for custom back stack handling - private final ArrayList mBackStack = new ArrayList(); + private final Stack mBackStack = new Stack(); private boolean mBackStackPush = true; - private int mCurrentPane = android.R.id.home; + private Pane mCurrentPane = Pane.MAP; private boolean mBackButtonPressed = false; - public static final SparseArray PANE_TITLES = new SparseArray(); - public static final HashMap PANES = new HashMap(); - - static { - PANES.put("map", android.R.id.home); - PANES.put("info", R.id.menu_info); - PANES.put("full", R.id.menu_full); - PANES.put("compact", R.id.menu_compact); - PANES.put("public", R.id.menu_public); - PANES.put("faction", R.id.menu_faction); - PANES.put("debug", R.id.menu_debug); - - // No need to declare android.R.id.home - that title is default - PANE_TITLES.append(R.id.menu_info, "Info"); - PANE_TITLES.append(R.id.menu_full, "Full"); - PANE_TITLES.append(R.id.menu_compact, "Compact"); - PANE_TITLES.append(R.id.menu_public, "Public"); - PANE_TITLES.append(R.id.menu_faction, "Faction"); - PANE_TITLES.append(R.id.menu_debug, "Debug"); - } - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -94,7 +75,9 @@ public class IITC_Mobile extends Activity { mIitcWebView = (IITC_WebView) findViewById(R.id.iitc_webview); // pass ActionBar to helper because we deprecated getActionBar - mActionBarHelper = new IITC_ActionBarHelper(this, super.getActionBar()); + mNavigationHelper = new IITC_NavigationHelper(this, super.getActionBar()); + + mMapSettings = new IITC_MapSettings(this); // do something if user changed something in the settings mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); @@ -104,24 +87,27 @@ public class IITC_Mobile extends Activity { SharedPreferences sharedPreferences, String key) { if (key.equals("pref_force_desktop")) { mDesktopMode = sharedPreferences.getBoolean("pref_force_desktop", false); - mActionBarHelper.onPrefChanged(); - invalidateOptionsMenu(); + mNavigationHelper.onPrefChanged(); } if (key.equals("pref_user_loc")) mIsLocEnabled = sharedPreferences.getBoolean("pref_user_loc", false); if (key.equals("pref_fullscreen_actionbar")) { - mActionBarHelper.onPrefChanged(); + mNavigationHelper.onPrefChanged(); return; } if (key.equals("pref_advanced_menu")) { mAdvancedMenu = sharedPreferences.getBoolean("pref_advanced_menu", false); + mNavigationHelper.setDebugMode(mAdvancedMenu); invalidateOptionsMenu(); // no reload needed return; } - // no reload needed - if (key.equals("pref_press_twice_to_exit") || key.equals("pref_share_selected_tab")) + + if (key.equals("pref_press_twice_to_exit") + || key.equals("pref_share_selected_tab") + || key.equals("pref_messages")) + // no reload needed return; mReloadNeeded = true; @@ -224,8 +210,8 @@ public class IITC_Mobile extends Activity { (SearchView) mSearchMenuItem.getActionView(); searchView.setQuery(query, false); searchView.clearFocus(); - mActionBarHelper.switchTo(android.R.id.home); - backStackUpdate(android.R.id.home); + + switchToPane(Pane.MAP); mIitcWebView.loadUrl("javascript:search('" + query + "');"); return; } @@ -318,45 +304,63 @@ public class IITC_Mobile extends Activity { public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); + mNavigationHelper.onConfigurationChanged(newConfig); + Log.d("iitcm", "configuration changed...restoring...reset idleTimer"); mIitcWebView.loadUrl("javascript: window.idleTime = 0"); mIitcWebView.loadUrl("javascript: window.renderUpdateStatus()"); } + @Override + protected void onPostCreate(Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + mNavigationHelper.onPostCreate(savedInstanceState); + } + // we want a self defined behavior for the back button @Override public void onBackPressed() { - // first kill all open iitc dialogs + // exit fullscreen mode if it is enabled and action bar is disabled or the back stack is empty + if (mFullscreenMode && (mBackStack.isEmpty() || mNavigationHelper.hideInFullscreen())) { + toggleFullscreen(); + return; + } + + // close drawer if opened + if (mNavigationHelper.isDrawerOpened()) { + mNavigationHelper.closeDrawers(); + return; + } + + // kill all open iitc dialogs if (!mDialogStack.isEmpty()) { - int last = mDialogStack.size() - 1; - String id = mDialogStack.get(last); + String id = mDialogStack.pop(); mIitcWebView.loadUrl("javascript: " + "var selector = $(window.DIALOGS['" + id + "']); " + "selector.dialog('close'); " + "selector.remove();"); return; } - // exit fullscreen mode if it is enabled and action bar is disabled - // or the back stack is empty - if (mFullscreenMode && (mBackStack.isEmpty() || mActionBarHelper.hideInFullscreen())) { - this.toggleFullscreen(); - } else if (!mBackStack.isEmpty()) { - // Pop last item from backstack and pretend the relevant menu item was clicked + + // Pop last item from backstack and pretend the relevant menu item was clicked + if (!mBackStack.isEmpty()) { backStackPop(); + return; + } + + if (mBackButtonPressed || !mSharedPrefs.getBoolean("pref_press_twice_to_exit", false)) { + super.onBackPressed(); + return; } else { - if (mBackButtonPressed || !mSharedPrefs.getBoolean("pref_press_twice_to_exit", false)) - super.onBackPressed(); - else { - mBackButtonPressed = true; - Toast.makeText(this, "Press twice to exit", Toast.LENGTH_SHORT).show(); - // reset back button after 2 seconds - new Handler().postDelayed(new Runnable() { - @Override - public void run() { - mBackButtonPressed = false; - } - }, 2000); - } + mBackButtonPressed = true; + Toast.makeText(this, "Press twice to exit", Toast.LENGTH_SHORT).show(); + // reset back button after 2 seconds + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + mBackButtonPressed = false; + } + }, 2000); } } @@ -364,31 +368,30 @@ public class IITC_Mobile extends Activity { // shouldn't be called when back stack is empty // catch wrong usage if (mBackStack.isEmpty()) { - // Empty back stack means we should be at home (ie map) screen - mActionBarHelper.switchTo(android.R.id.home); - mIitcWebView.loadUrl("javascript: window.show('map');"); - return; + mBackStack.push(Pane.MAP); } - int index = mBackStack.size() - 1; - int itemId = mBackStack.remove(index); + + Pane pane = mBackStack.pop(); mBackStackPush = false; - handleMenuItemSelected(itemId); + switchToPane(pane); } - public void backStackUpdate(int itemId) { + public void setCurrentPane(Pane pane) { // ensure no double adds - if (itemId == mCurrentPane) return; - if (itemId == android.R.id.home) { - mBackStack.clear(); - mBackStackPush = true; - } else { - if (mBackStackPush) - mBackStack.add(mCurrentPane); - else - mBackStackPush = true; - } + if (pane == mCurrentPane) return; - mCurrentPane = itemId; + if (mBackStackPush) + mBackStack.push(mCurrentPane); + else + mBackStackPush = true; + + mCurrentPane = pane; + mNavigationHelper.switchTo(pane); + } + + public void switchToPane(Pane pane) { + String name = pane.name().toLowerCase(Locale.getDefault()); + mIitcWebView.loadUrl("javascript: window.show('" + name + "');"); } @Override @@ -397,30 +400,47 @@ public class IITC_Mobile extends Activity { getMenuInflater().inflate(R.menu.main, menu); // Get the SearchView and set the searchable configuration SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); - this.mSearchMenuItem = menu.findItem(R.id.menu_search); + mSearchMenuItem = menu.findItem(R.id.menu_search); final SearchView searchView = (SearchView) mSearchMenuItem.getActionView(); // Assumes current activity is the searchable activity searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); searchView.setIconifiedByDefault(false); // Do not iconify the widget; expand it by default - // enable/disable mDesktopMode menu - enableDesktopUI(menu); - enableAdvancedMenu(menu); return true; } @Override - public boolean onOptionsItemSelected(MenuItem item) { - // Handle item selection - final int itemId = item.getItemId(); - boolean result = handleMenuItemSelected(itemId); - return result || super.onOptionsItemSelected(item); + public boolean onPrepareOptionsMenu(Menu menu) { + if (mNavigationHelper != null) { + boolean visible = !mNavigationHelper.isDrawerOpened(); + + for (int i = 0; i < menu.size(); i++) + if (menu.getItem(i).getItemId() != R.id.action_settings) { + // clear cookies is part of the advanced menu + if (menu.getItem(i).getItemId() == R.id.menu_clear_cookies) { + menu.getItem(i).setVisible(mAdvancedMenu & visible); + } else { + menu.getItem(i).setVisible(visible); + } + } + } + + return super.onPrepareOptionsMenu(menu); } - public boolean handleMenuItemSelected(int itemId) { + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (mNavigationHelper.onOptionsItemSelected(item)) + return true; + + // Handle item selection + final int itemId = item.getItemId(); + switch (itemId) { case android.R.id.home: - mIitcWebView.loadUrl("javascript: window.show('map');"); + mBackStack.clear(); + mBackStackPush = false; + switchToPane(Pane.MAP); return true; case R.id.reload_button: reloadIITC(); @@ -429,14 +449,10 @@ public class IITC_Mobile extends Activity { toggleFullscreen(); return true; case R.id.layer_chooser: - // Force map view to handle potential issue with back stack - if (!mBackStack.isEmpty() && mCurrentPane != android.R.id.home) - mIitcWebView.loadUrl("javascript: window.show('map');"); - // the getLayers function calls the setLayers method of IITC_JSInterface - mIitcWebView.loadUrl("javascript: window.layerChooser.getLayers()"); + mNavigationHelper.openRightDrawer(); return true; case R.id.locate: // get the users current location and focus it on map - mIitcWebView.loadUrl("javascript: window.show('map');"); + switchToPane(Pane.MAP); // get location from network by default if (!mIsLocEnabled) { mIitcWebView.loadUrl("javascript: " + @@ -455,24 +471,6 @@ public class IITC_Mobile extends Activity { .getIITCVersion()); startActivity(intent); return true; - case R.id.menu_info: - mIitcWebView.loadUrl("javascript: window.show('info');"); - return true; - case R.id.menu_full: - mIitcWebView.loadUrl("javascript: window.show('full');"); - return true; - case R.id.menu_compact: - mIitcWebView.loadUrl("javascript: window.show('compact');"); - return true; - case R.id.menu_public: - mIitcWebView.loadUrl("javascript: window.show('public');"); - return true; - case R.id.menu_faction: - mIitcWebView.loadUrl("javascript: window.show('faction');"); - return true; - case R.id.menu_debug: - mIitcWebView.loadUrl("javascript: window.show('debug')"); - return true; case R.id.menu_clear_cookies: CookieManager cm = CookieManager.getInstance(); cm.removeAllCookie(); @@ -483,10 +481,12 @@ public class IITC_Mobile extends Activity { } public void reloadIITC() { - mActionBarHelper.reset(); + mNavigationHelper.reset(); + mMapSettings.reset(); mBackStack.clear(); + mBackStackPush = true; // iitc starts on map after reload - mCurrentPane = android.R.id.home; + mCurrentPane = Pane.MAP; loadUrl(mIntelUrl); mReloadNeeded = false; } @@ -512,7 +512,7 @@ public class IITC_Mobile extends Activity { // inject the iitc-script and load the intel url // plugins are injected onPageFinished public void loadUrl(String url) { - showSplashScreen(); + setLoadingState(true); url = addUrlParam(url); loadIITC(); mIitcWebView.loadUrl(url); @@ -534,7 +534,7 @@ public class IITC_Mobile extends Activity { public void toggleFullscreen() { mFullscreenMode = !mFullscreenMode; - mActionBarHelper.setFullscreen(mFullscreenMode); + mNavigationHelper.setFullscreen(mFullscreenMode); // toggle notification bar WindowManager.LayoutParams attrs = getWindow().getAttributes(); @@ -584,18 +584,7 @@ public class IITC_Mobile extends Activity { public void loginSucceeded() { // garbage collection mLogin = null; - showSplashScreen(); - } - - // disable/enable some menu buttons... - public void enableDesktopUI(Menu menu) { - MenuItem item; - item = menu.findItem(R.id.menu_chat); - item.setVisible(!mDesktopMode); - item = menu.findItem(R.id.menu_info); - item.setVisible(!mDesktopMode); - item = menu.findItem(R.id.menu_debug); - item.setVisible(!mDesktopMode); + setLoadingState(true); } // remove dialog and add it back again @@ -604,37 +593,35 @@ public class IITC_Mobile extends Activity { public void setFocusedDialog(String id) { Log.d("iitcm", "Dialog " + id + " focused"); mDialogStack.remove(id); - mDialogStack.add(id); + mDialogStack.push(id); } // called by the javascript interface public void dialogOpened(String id, boolean open) { if (open) { Log.d("iitcm", "Dialog " + id + " added"); - mDialogStack.add(id); + mDialogStack.push(id); } else { Log.d("iitcm", "Dialog " + id + " closed"); mDialogStack.remove(id); } } - public void showSplashScreen() { - if (!mSharedPrefs.getBoolean("pref_disable_splash", false)) { + public void setLoadingState(boolean isLoading) { + mNavigationHelper.setLoadingState(isLoading); + + if (isLoading && !mSharedPrefs.getBoolean("pref_disable_splash", false)) { findViewById(R.id.iitc_webview).setVisibility(View.GONE); findViewById(R.id.imageLoading).setVisibility(View.VISIBLE); + } else { + findViewById(R.id.iitc_webview).setVisibility(View.VISIBLE); + findViewById(R.id.imageLoading).setVisibility(View.GONE); } } - public void enableAdvancedMenu(Menu menu) { - MenuItem item; - item = menu.findItem(R.id.menu_debug); - item.setVisible(mAdvancedMenu); - item = menu.findItem(R.id.menu_clear_cookies); - item.setVisible(mAdvancedMenu); - } - /** - * @deprecated ActionBar related stuff should be handled by ActionBarHelper + * @deprecated ActionBar related stuff should be handled by IITC_NavigationHelper + * @see getNavigationHelper() */ @Deprecated @Override @@ -642,7 +629,11 @@ public class IITC_Mobile extends Activity { return super.getActionBar(); } - public IITC_ActionBarHelper getActionBarHelper() { - return mActionBarHelper; + public IITC_NavigationHelper getNavigationHelper() { + return mNavigationHelper; + } + + public IITC_MapSettings getMapSettings() { + return mMapSettings; } } diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_NavigationHelper.java b/mobile/src/com/cradle/iitc_mobile/IITC_NavigationHelper.java new file mode 100644 index 00000000..d0676fd1 --- /dev/null +++ b/mobile/src/com/cradle/iitc_mobile/IITC_NavigationHelper.java @@ -0,0 +1,351 @@ +package com.cradle.iitc_mobile; + +import android.app.ActionBar; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.content.DialogInterface.OnDismissListener; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.os.Handler; +import android.preference.PreferenceManager; +import android.support.v4.app.ActionBarDrawerToggle; +import android.support.v4.widget.DrawerLayout; +import android.text.Html; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.ArrayAdapter; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.Toast; + +public class IITC_NavigationHelper extends ActionBarDrawerToggle implements OnItemClickListener { + // Show/hide the up arrow on the very left + // getActionBar().setDisplayHomeAsUpEnabled(enabled); + + // Show/hide the activity icon/logo + // getActionBar().setDisplayShowHomeEnabled(enabled); + + // Show/hide the activity title + // getActionBar().setDisplayShowTitleEnabled(enabled); + + // Makes the icon/title clickable + // getActionBar().setHomeButtonEnabled(enabled); + + private class NavigationAdapter extends ArrayAdapter { + public NavigationAdapter() { + super(mIitc, R.layout.list_item_selectable); + + add(Pane.MAP); + add(Pane.INFO); + add(Pane.FULL); + add(Pane.COMPACT); + add(Pane.PUBLIC); + add(Pane.FACTION); + + if (mPrefs.getBoolean("pref_advanced_menu", false)) + add(Pane.DEBUG); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + TextView view = (TextView) super.getView(position, convertView, parent); + Pane item = getItem(position); + if (item == Pane.MAP) + view.setText("Map"); + else + view.setText(getPaneTitle(item)); + + int icon = 0; + switch (item) + { + case MAP: + icon = R.drawable.location_map; + break; + case INFO: + icon = R.drawable.action_about; + break; + case FULL: + icon = R.drawable.collections_view_as_list; + break; + case COMPACT: + icon = R.drawable.collections_view_as_list_compact; + break; + case PUBLIC: + icon = R.drawable.social_group; + break; + case FACTION: + icon = R.drawable.social_cc_bcc; + break; + case DEBUG: + icon = R.drawable.ic_debug; + break; + } + + if (icon != 0) + view.setCompoundDrawablesWithIntrinsicBounds(icon, 0, 0, 0); + + return view; + } + } + + public static enum Pane { + COMPACT, DEBUG, FACTION, FULL, INFO, MAP, PUBLIC + } + + public static final int NOTICE_DRAWERS = 1 << 0; + public static final int NOTICE_INFO = 1 << 1; + // next one would be 1<<2; (this results in 1,2,4,8,...) + + private IITC_Mobile mIitc; + private ActionBar mActionBar; + private SharedPreferences mPrefs; + private NavigationAdapter mNavigationAdapter; + private DrawerLayout mDrawerLayout; + private ListView mDrawerLeft; + private View mDrawerRight; + + private boolean mDesktopMode = false; + private boolean mFullscreen = false; + private boolean mIsLoading; + private boolean mHideInFullscreen = false; + private Pane mPane = Pane.MAP; + private String mHighlighter = null; + + public IITC_NavigationHelper(IITC_Mobile activity, ActionBar bar) { + super(activity, (DrawerLayout) activity.findViewById(R.id.drawer_layout), + R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close); + + mIitc = activity; + mActionBar = bar; + mDrawerLeft = (ListView) activity.findViewById(R.id.left_drawer); + mDrawerRight = activity.findViewById(R.id.right_drawer); + mDrawerLayout = (DrawerLayout) activity.findViewById(R.id.drawer_layout); + + mPrefs = PreferenceManager.getDefaultSharedPreferences(activity); + + mActionBar.setDisplayShowHomeEnabled(true); // show icon + + mNavigationAdapter = new NavigationAdapter(); + mDrawerLeft.setAdapter(mNavigationAdapter); + mDrawerLeft.setOnItemClickListener(this); + mDrawerLeft.setItemChecked(0, true); + mDrawerLayout.setDrawerListener(this); + + onPrefChanged(); // also calls updateActionBar() + + showNotice(NOTICE_DRAWERS); + } + + private void showNotice(final int which) { + if ((mPrefs.getInt("pref_messages", 0) & which) != 0) + return; + + String text; + switch (which) { + case NOTICE_DRAWERS: + text = mIitc.getText(R.string.notice_drawers).toString(); + break; + case NOTICE_INFO: + text = mIitc.getText(R.string.notice_info).toString(); + break; + default: + return; + } + + TextView message = new TextView(mIitc); + message.setPadding(20, 20, 20, 20); + message.setText(Html.fromHtml(text)); + + AlertDialog dialog = new AlertDialog.Builder(mIitc) + .setView(message) + .setCancelable(true) + .setPositiveButton(android.R.string.ok, new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.cancel(); + } + }) + .create(); + dialog.setOnDismissListener(new OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + int value = mPrefs.getInt("pref_messages", 0); + value |= which; + + mPrefs + .edit() + .putInt("pref_messages", value) + .commit(); + } + }); + dialog.show(); + } + + private void updateActionBar() { + int position = mNavigationAdapter.getPosition(mPane); + if (position >= 0 && position < mNavigationAdapter.getCount()) + mDrawerLeft.setItemChecked(position, true); + + if (mDesktopMode) { + mActionBar.setDisplayHomeAsUpEnabled(false); // Hide "up" indicator + mActionBar.setHomeButtonEnabled(false); // Make icon unclickable + mActionBar.setTitle(mIitc.getString(R.string.app_name)); + mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, mDrawerLeft); + mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, mDrawerRight); + setDrawerIndicatorEnabled(false); + } else { + if (mIsLoading) { + mActionBar.setDisplayHomeAsUpEnabled(false); // Hide "up" indicator + mActionBar.setHomeButtonEnabled(false);// Make icon unclickable + mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); + setDrawerIndicatorEnabled(false); + } else { + mActionBar.setDisplayHomeAsUpEnabled(true); // Show "up" indicator + mActionBar.setHomeButtonEnabled(true);// Make icon clickable + mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED); + + if (mPane == Pane.MAP || mDrawerLayout.isDrawerOpen(mDrawerLeft)) + setDrawerIndicatorEnabled(true); + else + setDrawerIndicatorEnabled(false); + } + + if (mDrawerLayout.isDrawerOpen(mDrawerLeft)) + mActionBar.setTitle(mIitc.getString(R.string.app_name)); + else + mActionBar.setTitle(getPaneTitle(mPane)); + } + + boolean mapVisible = mDesktopMode || mPane == Pane.MAP; + if ("No Highlights".equals(mHighlighter) || isDrawerOpened() || !mapVisible) + mActionBar.setSubtitle(null); + else + mActionBar.setSubtitle(mHighlighter); + + if (mFullscreen && mHideInFullscreen) + mActionBar.hide(); + else + mActionBar.show(); + } + + public void closeDrawers() { + mDrawerLayout.closeDrawers(); + } + + public String getPaneTitle(Pane pane) + { + switch (pane) { + case INFO: + return "Info"; + case FULL: + return "Full"; + case COMPACT: + return "Compact"; + case PUBLIC: + return "Public"; + case FACTION: + return "Faction"; + case DEBUG: + return "Debug"; + default: + return mIitc.getString(R.string.app_name); + } + } + + public boolean hideInFullscreen() { + return mHideInFullscreen; + } + + public boolean isDrawerOpened() { + return mDrawerLayout.isDrawerOpen(mDrawerLeft) || mDrawerLayout.isDrawerOpen(mDrawerRight); + } + + @Override + public void onDrawerClosed(View drawerView) { + super.onDrawerClosed(drawerView); + + // delay invalidating to prevent flickering in case another drawer is opened + (new Handler()).postDelayed(new Runnable() { + @Override + public void run() { + mIitc.invalidateOptionsMenu(); + updateActionBar(); + } + }, 200); + } + + @Override + public void onDrawerOpened(View drawerView) { + super.onDrawerOpened(drawerView); + mIitc.invalidateOptionsMenu(); + updateActionBar(); + mDrawerLayout.closeDrawer(drawerView.equals(mDrawerLeft) ? mDrawerRight : mDrawerLeft); + } + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + Pane item = mNavigationAdapter.getItem(position); + mIitc.switchToPane(item); + + if (item == Pane.INFO) + showNotice(NOTICE_INFO); + + mDrawerLayout.closeDrawer(mDrawerLeft); + } + + public void onPostCreate(Bundle savedInstanceState) { + // Sync the toggle state after onRestoreInstanceState has occurred. + syncState(); + } + + public void onPrefChanged() { + mDesktopMode = mPrefs.getBoolean("pref_force_desktop", false); + mHideInFullscreen = mPrefs.getBoolean("pref_fullscreen_actionbar", true); + updateActionBar(); + } + + public void openRightDrawer() { + if (mDrawerLayout.getDrawerLockMode(mDrawerRight) == DrawerLayout.LOCK_MODE_UNLOCKED) + mDrawerLayout.openDrawer(mDrawerRight); + } + + public void reset() { + mPane = Pane.MAP; + updateActionBar(); + } + + public void setDebugMode(boolean enabled) { + mNavigationAdapter.remove(Pane.DEBUG); // avoid duplicates + if (enabled) + mNavigationAdapter.add(Pane.DEBUG); + } + + public void setFullscreen(boolean fullscreen) { + mFullscreen = fullscreen; + if (mFullscreen && mHideInFullscreen) { + // show a toast with instructions to exit the fullscreen mode again + Toast.makeText(mIitc, "Press back button to exit fullscreen", Toast.LENGTH_SHORT).show(); + } + + updateActionBar(); + } + + public void setHighlighter(String name) { + mHighlighter = name; + updateActionBar(); + } + + public void setLoadingState(boolean isLoading) { + mIsLoading = isLoading; + updateActionBar(); + } + + public void switchTo(Pane pane) { + mPane = pane; + + updateActionBar(); + } +} diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_WebView.java b/mobile/src/com/cradle/iitc_mobile/IITC_WebView.java index 6e932266..c0966c28 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_WebView.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_WebView.java @@ -42,7 +42,7 @@ public class IITC_WebView extends WebView { + "/databases/"); mSettings.setAppCachePath(this.getContext().getCacheDir() .getAbsolutePath()); - this.mJsInterface = new IITC_JSInterface(mContext); + this.mJsInterface = new IITC_JSInterface((IITC_Mobile) mContext); this.addJavascriptInterface(mJsInterface, "android"); this.setWebChromeClient(new WebChromeClient() { @@ -53,7 +53,7 @@ public class IITC_WebView extends WebView { */ @Override public void onGeolocationPermissionsShowPrompt(String origin, - GeolocationPermissions.Callback callback) { + GeolocationPermissions.Callback callback) { callback.invoke(origin, true, false); } @@ -76,7 +76,7 @@ public class IITC_WebView extends WebView { public boolean onConsoleMessage(ConsoleMessage consoleMessage) { if (consoleMessage.messageLevel() == ConsoleMessage.MessageLevel.ERROR) { Log.d("iitcm", consoleMessage.message()); - mJsInterface.removeSplashScreen(); + ((IITC_Mobile) getContext()).setLoadingState(false); } return super.onConsoleMessage(consoleMessage); } diff --git a/mobile/src/com/cradle/iitc_mobile/async/CheckHttpResponse.java b/mobile/src/com/cradle/iitc_mobile/async/CheckHttpResponse.java index e7df6056..431dee3c 100644 --- a/mobile/src/com/cradle/iitc_mobile/async/CheckHttpResponse.java +++ b/mobile/src/com/cradle/iitc_mobile/async/CheckHttpResponse.java @@ -43,7 +43,7 @@ public class CheckHttpResponse extends AsyncTask { int code = response.getStatusLine().getStatusCode(); if (code != HttpStatus.SC_OK) { Log.d("iitcm", "received error code: " + code); - mJsInterface.removeSplashScreen(); + ((IITC_Mobile) mContext).setLoadingState(false); // TODO: remove when google login issue is fixed if (urls[0].contains("uberauth=WILL_NOT_SIGN_IN")) { return true;