summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoraurimas <aurimas@chromium.org>2014-09-30 16:48:42 -0700
committerCommit bot <commit-bot@chromium.org>2014-09-30 23:49:11 +0000
commita683d267055e8b1cfd054986b8964b1a3456df55 (patch)
treef5b38e441a1061a4d051e28a66541ce312e4dd31
parent9fd4726662eacd649b3071410d5d4c1d5f58c84d (diff)
downloadchromium_src-a683d267055e8b1cfd054986b8964b1a3456df55.zip
chromium_src-a683d267055e8b1cfd054986b8964b1a3456df55.tar.gz
chromium_src-a683d267055e8b1cfd054986b8964b1a3456df55.tar.bz2
Upstream NFC beam URL sharing code and add it to ChromeShell.
BUG=417543 Review URL: https://codereview.chromium.org/615213003 Cr-Commit-Position: refs/heads/master@{#297549}
-rw-r--r--chrome/android/java/src/org/chromium/chrome/browser/UmaBridge.java13
-rw-r--r--chrome/android/java/src/org/chromium/chrome/browser/nfc/BeamCallback.java151
-rw-r--r--chrome/android/java/src/org/chromium/chrome/browser/nfc/BeamController.java34
-rw-r--r--chrome/android/java/src/org/chromium/chrome/browser/nfc/BeamProvider.java15
-rw-r--r--chrome/android/java/src/org/chromium/chrome/browser/nfc/OWNERS2
-rw-r--r--chrome/android/java/strings/android_chrome_strings.grd8
-rw-r--r--chrome/android/shell/java/AndroidManifest.xml1
-rw-r--r--chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellActivity.java11
-rw-r--r--chrome/android/sync_shell/java/AndroidManifest.xml1
-rw-r--r--chrome/browser/android/uma_bridge.cc9
10 files changed, 245 insertions, 0 deletions
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/UmaBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/UmaBridge.java
index 0e80f48..ed6fbe9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/UmaBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/UmaBridge.java
@@ -29,6 +29,19 @@ public class UmaBridge {
nativeRecordUsingMenu(isByHwButton, isDragging);
}
+ // Android beam
+ public static void beamCallbackSuccess() {
+ nativeRecordBeamCallbackSuccess();
+ }
+
+ public static void beamInvalidAppState() {
+ nativeRecordBeamInvalidAppState();
+ }
+
private static native void nativeRecordMenuShow();
private static native void nativeRecordUsingMenu(boolean isByHwButton, boolean isDragging);
+
+ // Android Beam
+ private static native void nativeRecordBeamInvalidAppState();
+ private static native void nativeRecordBeamCallbackSuccess();
}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/nfc/BeamCallback.java b/chrome/android/java/src/org/chromium/chrome/browser/nfc/BeamCallback.java
new file mode 100644
index 0000000..4811d20
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/nfc/BeamCallback.java
@@ -0,0 +1,151 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.nfc;
+
+import android.app.Activity;
+import android.nfc.NdefMessage;
+import android.nfc.NdefRecord;
+import android.nfc.NfcAdapter.CreateNdefMessageCallback;
+import android.nfc.NfcAdapter.OnNdefPushCompleteCallback;
+import android.nfc.NfcEvent;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.text.TextUtils;
+import android.widget.Toast;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.UmaBridge;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Beam callback that gets passed to Android to get triggered when devices are tapped to
+ * each other.
+ */
+class BeamCallback implements CreateNdefMessageCallback, OnNdefPushCompleteCallback {
+
+ private static class Status {
+ public final Integer errorStrID;
+ public final String result;
+
+ Status(Integer errorStrID) {
+ assert errorStrID != null;
+ this.errorStrID = errorStrID;
+ this.result = null;
+ }
+
+ Status(String result) {
+ assert result != null;
+ this.result = result;
+ this.errorStrID = null;
+ }
+ }
+
+ // In ICS returning null from createNdefMessage will cause beam to send our market
+ // link so we need to hook to the return from the beam overlay to display the error.
+ // But in SDK_INT >= 16, beam won't activate, so the hook wouldn't go off. (b/5943350)
+ private static final boolean NFC_BUGS_ACTIVE = Build.VERSION.SDK_INT < 16;
+
+ // Arbitrarily chosen interval to delay toast to allow NFC animations to finish
+ // and our app to return to foreground.
+ private static final int TOAST_ERROR_DELAY_MS = 400;
+
+ private final Activity mActivity;
+ private final BeamProvider mProvider;
+
+ // We use this to delay the error message in ICS because it would be hidden behind
+ // the system beam overlay. It is only accessed by the NFC thread
+ private Runnable mErrorRunnableIfBeamSent;
+
+ BeamCallback(Activity activity, BeamProvider provider) {
+ mActivity = activity;
+ mProvider = provider;
+ }
+
+ @Override
+ public NdefMessage createNdefMessage(NfcEvent event) {
+ // Default status is an error
+ Status status = new Status(R.string.nfc_beam_error_bad_url);
+ try {
+ status = ThreadUtils.runOnUiThread(new Callable<Status>() {
+ @Override
+ public Status call() {
+ String url = mProvider.getTabUrlForBeam();
+ if (url == null) return new Status(R.string.nfc_beam_error_overlay_active);
+ if (!isValidUrl(url)) return new Status(R.string.nfc_beam_error_bad_url);
+ return new Status(url);
+ }
+ }).get(2000, TimeUnit.MILLISECONDS); // Arbitrarily chosen timeout for query.
+ } catch (TimeoutException e) {
+ // Squelch this exception, we'll treat it as a bad tab
+ } catch (ExecutionException e) {
+ // And this
+ } catch (InterruptedException e) {
+ // And squelch this one too
+ }
+
+ if (status.errorStrID != null) {
+ onInvalidBeam(status.errorStrID);
+ return null;
+ }
+
+ UmaBridge.beamCallbackSuccess();
+ mErrorRunnableIfBeamSent = null;
+ return new NdefMessage(new NdefRecord[] {NdefRecord.createUri(status.result)});
+ }
+
+ /**
+ * Trigger an error about NFC if we don't want to send anything. Also
+ * records a UMA stat. On ICS we only show the error if they attempt to
+ * beam, since the recipient will receive the market link. On JB we'll
+ * always show the error, since the beam animation won't trigger, which
+ * could be confusing to the user.
+ *
+ * @param errorStringId The resid of the string to display as error.
+ */
+ private void onInvalidBeam(final int errorStringId) {
+ UmaBridge.beamInvalidAppState();
+ Runnable errorRunnable = new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(mActivity, errorStringId, Toast.LENGTH_SHORT).show();
+ }
+ };
+ if (NFC_BUGS_ACTIVE) {
+ mErrorRunnableIfBeamSent = errorRunnable;
+ } else {
+ ThreadUtils.runOnUiThread(errorRunnable);
+ }
+ }
+
+ @Override
+ public void onNdefPushComplete(NfcEvent event) {
+ if (mErrorRunnableIfBeamSent != null) {
+ Handler h = new Handler(Looper.getMainLooper());
+ h.postDelayed(mErrorRunnableIfBeamSent, TOAST_ERROR_DELAY_MS);
+ mErrorRunnableIfBeamSent = null;
+ }
+ }
+
+ /**
+ * @return Whether given URL is valid and sharable via Beam.
+ */
+ private static boolean isValidUrl(String url) {
+ if (TextUtils.isEmpty(url)) return false;
+ try {
+ String urlProtocol = (new URL(url)).getProtocol();
+ return urlProtocol.matches("http|https");
+ } catch (MalformedURLException e) {
+ return false;
+ }
+ }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/nfc/BeamController.java b/chrome/android/java/src/org/chromium/chrome/browser/nfc/BeamController.java
new file mode 100644
index 0000000..105daa6
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/nfc/BeamController.java
@@ -0,0 +1,34 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.nfc;
+
+import android.app.Activity;
+import android.nfc.NfcAdapter;
+import android.util.Log;
+
+/**
+ * Initializes Android Beam (sharing URL via NFC) for devices that have NFC. If user taps their
+ * device with another Beam capable device, then Chrome gets the current URL, filters for security
+ * and returns the result to Android.
+ */
+public final class BeamController {
+ /**
+ * If the device has NFC, construct a BeamCallback and pass it to Android.
+ *
+ * @param activity Activity that is sending out beam messages.
+ * @param provider Provider that returns the URL that should be shared.
+ */
+ public static void registerForBeam(final Activity activity, final BeamProvider provider) {
+ final NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(activity);
+ if (nfcAdapter == null) return;
+ try {
+ final BeamCallback beamCallback = new BeamCallback(activity, provider);
+ nfcAdapter.setNdefPushMessageCallback(beamCallback, activity);
+ nfcAdapter.setOnNdefPushCompleteCallback(beamCallback, activity);
+ } catch (IllegalStateException e) {
+ Log.w("BeamController", "NFC registration failure. Can't retry, giving up.");
+ }
+ }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/nfc/BeamProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/nfc/BeamProvider.java
new file mode 100644
index 0000000..4d95e20
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/nfc/BeamProvider.java
@@ -0,0 +1,15 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.nfc;
+
+/**
+ * Interface for Activities that use Beam (sharing URL via NFC) controller.
+ */
+public interface BeamProvider {
+ /**
+ * @return URL of the current tab, null otherwise (e.g. user is in tab switcher).
+ */
+ String getTabUrlForBeam();
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/nfc/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/nfc/OWNERS
new file mode 100644
index 0000000..577bf98
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/nfc/OWNERS
@@ -0,0 +1,2 @@
+aurimas@chromium.org
+newt@chromium.org
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index ecff791..9eabb65 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -389,6 +389,14 @@ You are signing in with a managed account and giving its administrator control o
Settings
</message>
+ <!-- Android NFC Beam strings -->
+ <message name="IDS_NFC_BEAM_ERROR_OVERLAY_ACTIVE" desc="Android Beam error - a tab is not in the foreground [CHAR-LIMIT=40]">
+ Select a tab to beam
+ </message>
+ <message name="IDS_NFC_BEAM_ERROR_BAD_URL" desc="Android Beam error - the current tab has an invalid url [CHAR-LIMIT=40]">
+ Can’t beam current tab
+ </message>
+
</messages>
</release>
</grit>
diff --git a/chrome/android/shell/java/AndroidManifest.xml b/chrome/android/shell/java/AndroidManifest.xml
index e18acc7..aebe584 100644
--- a/chrome/android/shell/java/AndroidManifest.xml
+++ b/chrome/android/shell/java/AndroidManifest.xml
@@ -19,6 +19,7 @@
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
+ <uses-permission android:name="android.permission.NFC"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
diff --git a/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellActivity.java b/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellActivity.java
index 48627d2..3de8cf7 100644
--- a/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellActivity.java
+++ b/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellActivity.java
@@ -28,6 +28,8 @@ import org.chromium.chrome.browser.FileProviderHelper;
import org.chromium.chrome.browser.appmenu.AppMenuHandler;
import org.chromium.chrome.browser.appmenu.AppMenuPropertiesDelegate;
import org.chromium.chrome.browser.dom_distiller.DomDistillerTabUtils;
+import org.chromium.chrome.browser.nfc.BeamController;
+import org.chromium.chrome.browser.nfc.BeamProvider;
import org.chromium.chrome.browser.printing.PrintingControllerFactory;
import org.chromium.chrome.browser.printing.TabPrinter;
import org.chromium.chrome.browser.share.ShareHelper;
@@ -172,6 +174,15 @@ public class ChromeShellActivity extends Activity implements AppMenuPropertiesDe
// SyncController that we have started.
mSyncController.onStart();
ContentUriUtils.setFileProviderUtil(new FileProviderHelper());
+
+ BeamController.registerForBeam(this, new BeamProvider() {
+ @Override
+ public String getTabUrlForBeam() {
+ ChromeShellTab tab = getActiveTab();
+ if (tab == null) return null;
+ return tab.getUrl();
+ }
+ });
}
@Override
diff --git a/chrome/android/sync_shell/java/AndroidManifest.xml b/chrome/android/sync_shell/java/AndroidManifest.xml
index 358b88b..e756c75 100644
--- a/chrome/android/sync_shell/java/AndroidManifest.xml
+++ b/chrome/android/sync_shell/java/AndroidManifest.xml
@@ -19,6 +19,7 @@
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
+ <uses-permission android:name="android.permission.NFC"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
diff --git a/chrome/browser/android/uma_bridge.cc b/chrome/browser/android/uma_bridge.cc
index ab345bb..4b2406e 100644
--- a/chrome/browser/android/uma_bridge.cc
+++ b/chrome/browser/android/uma_bridge.cc
@@ -36,6 +36,15 @@ static void RecordUsingMenu(JNIEnv*,
}
}
+// Android Beam
+static void RecordBeamCallbackSuccess(JNIEnv*, jclass) {
+ RecordAction(UserMetricsAction("MobileBeamCallbackSuccess"));
+}
+
+static void RecordBeamInvalidAppState(JNIEnv*, jclass) {
+ RecordAction(UserMetricsAction("MobileBeamInvalidAppState"));
+}
+
namespace chrome {
namespace android {