From 8d6d4328f23b527be9ea794433b3f160cd58e5a5 Mon Sep 17 00:00:00 2001 From: Kent Date: Fri, 20 Mar 2015 06:31:18 +0800 Subject: Implemented #1162: Invoke NFC in Advanced View - Extracted NFC code from ViewKeyActivity to NfcHelper to share code between classes - Changed the private anonymous Handler for NFC into a static private subclass, that uses WeakReference to avoid memory leaks - Added resources needed (retrieved from Graphics) for the NFC button inside ViewKeyAdvShareFragment. - Fixed the ripple boundary of the Share With... button to prevent it from bleeding to other buttons on the right (UX improvement) --- .../keychain/util/NfcHelper.java | 204 +++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/NfcHelper.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/NfcHelper.java') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/NfcHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/NfcHelper.java new file mode 100644 index 000000000..bae42d965 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/NfcHelper.java @@ -0,0 +1,204 @@ +package org.sufficientlysecure.keychain.util; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.nfc.NdefMessage; +import android.nfc.NdefRecord; +import android.nfc.NfcAdapter; +import android.nfc.NfcEvent; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Handler; +import android.os.Message; +import android.provider.Settings; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.ui.util.Notify; + +import java.lang.ref.WeakReference; + +/** + * This class contains NFC functionality that can be shared across Fragments or Activities. + *

+ * Created on Mar 20, 2015. + * + * @author Kent + */ + +public class NfcHelper { + + private Activity mActivity; + private ProviderHelper mProviderHelper; + + /** + * NFC: This handler receives a message from onNdefPushComplete + */ + private static NfcHandler mNfcHandler; + + private NfcAdapter mNfcAdapter; + private NfcAdapter.CreateNdefMessageCallback mNdefCallback; + private NfcAdapter.OnNdefPushCompleteCallback mNdefCompleteCallback; + private byte[] mNfcKeyringBytes; + private static final int NFC_SENT = 1; + + /** + * Initializes the NfcHelper. + */ + public NfcHelper(final Activity activity, final ProviderHelper providerHelper) { + mActivity = activity; + mProviderHelper = providerHelper; + + mNfcHandler = new NfcHandler(mActivity); + } + + /** + * Return true if the NFC Adapter of this Helper has any features enabled. + * + * @return true if this NFC Adapter has any features enabled + */ + public boolean isEnabled() { + return mNfcAdapter.isEnabled(); + } + + /** + * NFC: Initialize NFC sharing if OS and device supports it + */ + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + public void initNfc(final Uri dataUri) { + // check if NFC Beam is supported (>= Android 4.1) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + + // Implementation for the CreateNdefMessageCallback interface + mNdefCallback = new NfcAdapter.CreateNdefMessageCallback() { + @Override + public NdefMessage createNdefMessage(NfcEvent event) { + /* + * When a device receives a push with an AAR in it, the application specified in the AAR is + * guaranteed to run. The AAR overrides the tag dispatch system. You can add it back in to + * guarantee that this activity starts when receiving a beamed message. For now, this code + * uses the tag dispatch system. + */ + return new NdefMessage(NdefRecord.createMime(Constants.NFC_MIME, + mNfcKeyringBytes), NdefRecord.createApplicationRecord(Constants.PACKAGE_NAME)); + } + }; + + // Implementation for the OnNdefPushCompleteCallback interface + mNdefCompleteCallback = new NfcAdapter.OnNdefPushCompleteCallback() { + @Override + public void onNdefPushComplete(NfcEvent event) { + // A handler is needed to send messages to the activity when this + // callback occurs, because it happens from a binder thread + mNfcHandler.obtainMessage(NFC_SENT).sendToTarget(); + } + }; + + // Check for available NFC Adapter + mNfcAdapter = NfcAdapter.getDefaultAdapter(mActivity); + if (mNfcAdapter != null) { + /* + * Retrieve mNfcKeyringBytes here asynchronously (to not block the UI) + * and init nfc adapter afterwards. + * mNfcKeyringBytes can not be retrieved in createNdefMessage, because this process + * has no permissions to query the Uri. + */ + AsyncTask initTask = + new AsyncTask() { + protected Void doInBackground(Void... unused) { + try { + Uri blobUri = + KeychainContract.KeyRingData.buildPublicKeyRingUri(dataUri); + mNfcKeyringBytes = (byte[]) mProviderHelper.getGenericData( + blobUri, + KeychainContract.KeyRingData.KEY_RING_DATA, + ProviderHelper.FIELD_TYPE_BLOB); + } catch (ProviderHelper.NotFoundException e) { + Log.e(Constants.TAG, "key not found!", e); + } + + // no AsyncTask return (Void) + return null; + } + + protected void onPostExecute(Void unused) { + // Register callback to set NDEF message + mNfcAdapter.setNdefPushMessageCallback(mNdefCallback, + mActivity); + // Register callback to listen for message-sent success + mNfcAdapter.setOnNdefPushCompleteCallback(mNdefCompleteCallback, + mActivity); + } + }; + + initTask.execute(); + } + } + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public void invokeNfcBeam() { + // Check if device supports NFC + if (!mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) { + Notify.createNotify(mActivity, R.string.no_nfc_support, Notify.LENGTH_LONG, Notify.Style.ERROR).show(); + return; + } + // Check for available NFC Adapter + mNfcAdapter = NfcAdapter.getDefaultAdapter(mActivity); + if (mNfcAdapter == null || !mNfcAdapter.isEnabled()) { + Notify.createNotify(mActivity, R.string.error_nfc_needed, Notify.LENGTH_LONG, Notify.Style.ERROR, new Notify.ActionListener() { + @Override + public void onAction() { + Intent intentSettings = new Intent(Settings.ACTION_NFC_SETTINGS); + mActivity.startActivity(intentSettings); + } + }, R.string.menu_nfc_preferences).show(); + + return; + } + + if (!mNfcAdapter.isNdefPushEnabled()) { + Notify.createNotify(mActivity, R.string.error_beam_needed, Notify.LENGTH_LONG, Notify.Style.ERROR, new Notify.ActionListener() { + @Override + public void onAction() { + Intent intentSettings = new Intent(Settings.ACTION_NFCSHARING_SETTINGS); + mActivity.startActivity(intentSettings); + } + }, R.string.menu_beam_preferences).show(); + + return; + } + + mNfcAdapter.invokeBeam(mActivity); + } + + /** + * A static subclass of {@link Handler} with a {@link WeakReference} to an {@link Activity} to avoid memory leaks. + */ + private static class NfcHandler extends Handler { + private final WeakReference mActivityReference; + + public NfcHandler(Activity activity) { + mActivityReference = new WeakReference<>(activity); + } + + @Override + public void handleMessage(Message msg) { + Activity activity = mActivityReference.get(); + + if (activity != null) { + switch (msg.what) { + case NFC_SENT: + Notify.showNotify( + activity, R.string.nfc_successful, Notify.Style.INFO); + break; + } + } + } + } + +} \ No newline at end of file -- cgit v1.2.3