diff --git a/code/portal_detail_display.js b/code/portal_detail_display.js index cb2e79a7..37eb45a9 100644 --- a/code/portal_detail_display.js +++ b/code/portal_detail_display.js @@ -52,7 +52,6 @@ window.renderPortalDetails = function(guid) { var poslinks = 'window.showPortalPosLinks('+lat+','+lng+',\''+escapeJavascriptString(d.portalV2.descriptiveText.TITLE)+'\')'; var portalDetailObj = window.getPortalDescriptionFromDetailsExtended(d); - var portalDetailedDescription = ''; if(portalDetailObj) { @@ -101,8 +100,12 @@ window.renderPortalDetails = function(guid) { + randDetails + resoDetails + '
' - + '' - + '' + + ( + typeof android !== 'undefined' && android && android.intentPosLink // Android handles both links via a dialog + ? '' + : '' + + '' + ) + '' + '
' ); diff --git a/code/utils_misc.js b/code/utils_misc.js index 781fd378..541342bd 100644 --- a/code/utils_misc.js +++ b/code/utils_misc.js @@ -176,7 +176,7 @@ window.showPortalPosLinks = function(lat, lng, name) { } if (typeof android !== 'undefined' && android && android.intentPosLink) { - android.intentPosLink(lat, lng, encoded_name); + android.intentPosLink(lat, lng, map.getZoom(), name); } else { var qrcode = '
'; var script = ''; @@ -200,6 +200,15 @@ window.androidCopy = function(text) { return false; } +window.androidPermalink = function() { + if(typeof android === 'undefined' || !android || !android.copy) + return true; // i.e. execute other actions + + var center = map.getCenter(); + android.intentPosLink(center.lat, center.lng, map.getZoom(), null); + return false; +} + window.reportPortalIssue = function(info) { var t = 'Redirecting you to a Google Help Page.\n\nThe text box contains all necessary information. Press CTRL+C to copy it.'; var d = window.portals[window.selectedPortal].options.details; diff --git a/main.js b/main.js index 38136170..1256988f 100644 --- a/main.js +++ b/main.js @@ -96,7 +96,7 @@ document.getElementsByTagName('body')[0].innerHTML = '' // redeeming removed from stock site, so commented out for now. it may return... // + ' ' + '
' - + ' Permalink' + + ' Permalink' + ' About IITC' + '
' + ' ' diff --git a/mobile/res/drawable-hdpi/ic_dialog_browser.png b/mobile/res/drawable-hdpi/ic_dialog_browser.png new file mode 100644 index 00000000..e154afdb Binary files /dev/null and b/mobile/res/drawable-hdpi/ic_dialog_browser.png differ diff --git a/mobile/res/drawable-hdpi/ic_dialog_copy.png b/mobile/res/drawable-hdpi/ic_dialog_copy.png new file mode 100644 index 00000000..72c6bc6e Binary files /dev/null and b/mobile/res/drawable-hdpi/ic_dialog_copy.png differ diff --git a/mobile/res/drawable-hdpi/ic_dialog_share.png b/mobile/res/drawable-hdpi/ic_dialog_share.png new file mode 100644 index 00000000..c329f58d Binary files /dev/null and b/mobile/res/drawable-hdpi/ic_dialog_share.png differ diff --git a/mobile/res/drawable-mdpi/ic_dialog_browser.png b/mobile/res/drawable-mdpi/ic_dialog_browser.png new file mode 100644 index 00000000..41b56ec9 Binary files /dev/null and b/mobile/res/drawable-mdpi/ic_dialog_browser.png differ diff --git a/mobile/res/drawable-mdpi/ic_dialog_copy.png b/mobile/res/drawable-mdpi/ic_dialog_copy.png new file mode 100644 index 00000000..d93968e5 Binary files /dev/null and b/mobile/res/drawable-mdpi/ic_dialog_copy.png differ diff --git a/mobile/res/drawable-mdpi/ic_dialog_share.png b/mobile/res/drawable-mdpi/ic_dialog_share.png new file mode 100644 index 00000000..056deb57 Binary files /dev/null and b/mobile/res/drawable-mdpi/ic_dialog_share.png differ diff --git a/mobile/res/drawable-xhdpi/ic_dialog_browser.png b/mobile/res/drawable-xhdpi/ic_dialog_browser.png new file mode 100644 index 00000000..9b77be96 Binary files /dev/null and b/mobile/res/drawable-xhdpi/ic_dialog_browser.png differ diff --git a/mobile/res/drawable-xhdpi/ic_dialog_copy.png b/mobile/res/drawable-xhdpi/ic_dialog_copy.png new file mode 100644 index 00000000..04e290d8 Binary files /dev/null and b/mobile/res/drawable-xhdpi/ic_dialog_copy.png differ diff --git a/mobile/res/drawable-xhdpi/ic_dialog_share.png b/mobile/res/drawable-xhdpi/ic_dialog_share.png new file mode 100644 index 00000000..15549b04 Binary files /dev/null and b/mobile/res/drawable-xhdpi/ic_dialog_share.png differ diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_JSInterface.java b/mobile/src/com/cradle/iitc_mobile/IITC_JSInterface.java index 4f6dceb7..7a4d7419 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_JSInterface.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_JSInterface.java @@ -1,5 +1,11 @@ package com.cradle.iitc_mobile; +import java.util.HashMap; + +import org.json.JSONArray; +import org.json.JSONException; + +import android.app.Activity; import android.app.AlertDialog; import android.content.ClipData; import android.content.ClipboardManager; @@ -7,17 +13,11 @@ import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.DialogInterface.OnMultiChoiceClickListener; -import android.content.Intent; -import android.net.Uri; +import android.os.Bundle; import android.util.Log; import android.webkit.JavascriptInterface; import android.widget.Toast; -import org.json.JSONArray; -import org.json.JSONException; - -import java.util.HashMap; - // provide communication between IITC script and android app public class IITC_JSInterface { @@ -35,13 +35,18 @@ public class IITC_JSInterface { context = c; } - // send geo intent for navigation apps like gmaps or waze etc... + // open dialog to send geo intent for navigation apps like gmaps or waze etc... @JavascriptInterface - public void intentPosLink(String lat, String lng, String portal_name) { - String uri = "geo:" + lat + "," + lng + "?q=" + lat + "," + lng; - Intent intent = new Intent(android.content.Intent.ACTION_VIEW, - Uri.parse(uri)); - context.startActivity(intent); + public void intentPosLink(double lat, double lng, int zoom, String portalName) { + Bundle args = new Bundle(); + args.putDouble("lat", lat); + args.putDouble("lng", lng); + args.putInt("zoom", zoom); + args.putString("title", portalName); + + IITC_ShareDialog dialog = new IITC_ShareDialog(); + dialog.setArguments(args); + dialog.show(((Activity) context).getFragmentManager(), "ShareDialog"); } // disable javascript injection while spinner is enabled diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_ShareDialog.java b/mobile/src/com/cradle/iitc_mobile/IITC_ShareDialog.java new file mode 100644 index 00000000..cf0c9a2d --- /dev/null +++ b/mobile/src/com/cradle/iitc_mobile/IITC_ShareDialog.java @@ -0,0 +1,194 @@ +package com.cradle.iitc_mobile; + +import java.util.ArrayList; +import java.util.List; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.ComponentInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.net.Uri; +import android.os.Bundle; +import android.os.Parcelable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.TextView; +import android.widget.Toast; + +public class IITC_ShareDialog extends DialogFragment { + private abstract class Action { + private int mIcon; + private String mLabel; + + private Action(String label, int icon) { + mLabel = label; + mIcon = icon; + } + + abstract void invoke(); + } + + public class OnClickListener implements DialogInterface.OnClickListener { + @Override + public void onClick(DialogInterface dialog, int which) { + mAdapter.getItem(which).invoke(); + } + } + + class ActionAdapter extends ArrayAdapter { + private LayoutInflater mInflater; + + public ActionAdapter(Context context) { + super(context, android.R.layout.simple_list_item_1, ACTIONS); + mInflater = LayoutInflater.from(context); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + Action action = getItem(position); + + if (convertView == null) + convertView = mInflater.inflate(android.R.layout.simple_list_item_1, null); + + TextView tv = (TextView) convertView; + tv.setText(action.mLabel); + tv.setCompoundDrawablePadding(8); + tv.setCompoundDrawablesWithIntrinsicBounds(action.mIcon, 0, 0, 0); + return convertView; + } + } + + private final Action[] ACTIONS = { + new Action("Share…", R.drawable.ic_dialog_share) { + void invoke() { + Intent intent = new Intent(Intent.ACTION_SEND); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); + intent.setType("text/plain"); + intent.putExtra(Intent.EXTRA_TEXT, getUrl()); + intent.putExtra(Intent.EXTRA_SUBJECT, mTitle); + + startIntent(intent); + } + }, + new Action("View map with app…", R.drawable.location_map) { + void invoke() { + String geoUri = "geo:" + mLl; + Intent intent = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse(geoUri)); + + startIntent(intent); + } + }, + new Action("Open with browser…", R.drawable.ic_dialog_browser) { + void invoke() { + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(getUrl())); + + startIntent(intent); + } + }, + new Action("Copy to clipboard", R.drawable.ic_dialog_copy) { + void invoke() { + ClipData clip = ClipData.newPlainText("Copied text", getUrl()); + + ClipboardManager clipboard = (ClipboardManager) getActivity().getSystemService( + Activity.CLIPBOARD_SERVICE); + clipboard.setPrimaryClip(clip); + + Toast.makeText(getActivity(), "copied to clipboard", Toast.LENGTH_SHORT).show(); + } + } + }; + + private ActionAdapter mAdapter; + private boolean mIsPortal = true; + private String mLl; + private String mTitle; + private int mZoom; + + private String getUrl() { + String url = "http://www.ingress.com/intel?ll=" + mLl + "&z=" + mZoom; + if (mIsPortal) + url += "&pll=" + mLl; + return url; + } + + private void startIntent(Intent intent) { + // for geo: and Intel Map intents, the user may choose a default application to handle the intent. Since we have + // suitable intent filters declared, it might be that we *are* the default application. In theses cases, a list + // of designated applications is presented to the user + + String packageName = getActivity().getPackageName(); + + PackageManager pm = getActivity().getPackageManager(); + ResolveInfo mInfo = pm.resolveActivity(intent, 0); + + if (mInfo.activityInfo.packageName.equals(packageName)) { + // note: Intent.createChooser would be shorter, but it also includes IITCm. + // Therefore, we'll filter the available activities + String label = null; + if (intent.getAction().equals(Intent.ACTION_SEND)) + label = "Share via"; + else if (intent.getAction().equals(Intent.ACTION_VIEW)) + label = "Open with"; + + List intents = new ArrayList(); + List activities = getActivity().getPackageManager().queryIntentActivities(intent, 0); + + if (!activities.isEmpty()) { + for (ResolveInfo resolveInfo : activities) { + ComponentInfo info; + if (resolveInfo.activityInfo != null) + info = resolveInfo.activityInfo; + else + // Exactly one if these two must be non-null (according to docs) + info = resolveInfo.serviceInfo; + + if (info.packageName.equals(packageName)) // don't show IITCm + continue; + + // setComponent is used to route the intent towards the component + // setPackage is used to prevent Android from showing IITCm + // (without a package set, Android would still show all available components, including IITCm) + intents.add(new Intent(intent) + .setComponent(new ComponentName(info.packageName, info.name)) + .setPackage(info.packageName)); + } + + intent = Intent.createChooser(intents.remove(intents.size() - 1), label); + intent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intents.toArray(new Parcelable[] {})); + } + } + + startActivity(intent); + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + Bundle args = getArguments(); + mTitle = args.getString("title"); + mLl = args.getDouble("lat") + "," + args.getDouble("lng"); + mZoom = args.getInt("zoom"); + + if (mTitle == null) { + mTitle = "Intel Map"; + mIsPortal = false; + } + + mAdapter = new ActionAdapter(getActivity()); + + return new AlertDialog.Builder(getActivity()) + .setTitle(mTitle) + .setAdapter(mAdapter, new OnClickListener()) + .create(); + } +}