1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.nfc; 18 19 import java.util.ArrayList; 20 21 import android.app.Activity; 22 import android.app.AlertDialog; 23 import android.content.BroadcastReceiver; 24 import android.content.Context; 25 import android.content.DialogInterface; 26 import android.content.ClipData; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.net.Uri; 30 import android.nfc.BeamShareData; 31 import android.nfc.NdefMessage; 32 import android.nfc.NdefRecord; 33 import android.nfc.NfcAdapter; 34 import android.os.Bundle; 35 import android.os.UserHandle; 36 import android.util.Log; 37 import android.webkit.URLUtil; 38 39 import com.android.internal.R; 40 41 /** 42 * This class is registered by NfcService to handle 43 * ACTION_SHARE intents. It tries to parse data contained 44 * in ACTION_SHARE intents in either a content/file Uri, 45 * which can be sent using NFC handover, or alternatively 46 * it tries to parse texts and URLs to store them in a simple 47 * Text or Uri NdefRecord. The data is then passed on into 48 * NfcService to transmit on NFC tap. 49 * 50 */ 51 public class BeamShareActivity extends Activity { 52 static final String TAG ="BeamShareActivity"; 53 static final boolean DBG = false; 54 55 ArrayList<Uri> mUris; 56 NdefMessage mNdefMessage; 57 NfcAdapter mNfcAdapter; 58 Intent mLaunchIntent; 59 60 @Override 61 protected void onCreate(Bundle savedInstanceState) { 62 super.onCreate(savedInstanceState); 63 mUris = new ArrayList<Uri>(); 64 mNdefMessage = null; 65 mNfcAdapter = NfcAdapter.getDefaultAdapter(this); 66 mLaunchIntent = getIntent(); 67 if (mNfcAdapter == null) { 68 Log.e(TAG, "NFC adapter not present."); 69 finish(); 70 } else { 71 if (!mNfcAdapter.isEnabled()) { 72 showNfcDialogAndExit(com.android.nfc.R.string.beam_requires_nfc_enabled); 73 } else { 74 parseShareIntentAndFinish(mLaunchIntent); 75 } 76 } 77 } 78 79 @Override 80 protected void onDestroy() { 81 try { 82 unregisterReceiver(mReceiver); 83 } catch (Exception e) { 84 Log.w(TAG, e.getMessage()); 85 } 86 super.onDestroy(); 87 } 88 89 private void showNfcDialogAndExit(int msgId) { 90 IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED); 91 registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, null); 92 93 AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this, 94 AlertDialog.THEME_DEVICE_DEFAULT_LIGHT); 95 dialogBuilder.setMessage(msgId); 96 dialogBuilder.setOnCancelListener(new DialogInterface.OnCancelListener() { 97 @Override 98 public void onCancel(DialogInterface dialogInterface) { 99 finish(); 100 } 101 }); 102 dialogBuilder.setPositiveButton(R.string.yes, 103 new DialogInterface.OnClickListener() { 104 @Override 105 public void onClick(DialogInterface dialog, int id) { 106 if (!mNfcAdapter.isEnabled()) { 107 mNfcAdapter.enable(); 108 // Wait for enable broadcast 109 } else { 110 parseShareIntentAndFinish(mLaunchIntent); 111 } 112 } 113 }); 114 dialogBuilder.setNegativeButton(R.string.no, 115 new DialogInterface.OnClickListener() { 116 @Override 117 public void onClick(DialogInterface dialogInterface, int i) { 118 finish(); 119 } 120 }); 121 dialogBuilder.show(); 122 } 123 124 void tryUri(Uri uri) { 125 if (uri.getScheme().equalsIgnoreCase("content") || 126 uri.getScheme().equalsIgnoreCase("file")) { 127 // Typically larger data, this can be shared using NFC handover 128 mUris.add(uri); 129 } else { 130 // Just put this Uri in an NDEF message 131 mNdefMessage = new NdefMessage(NdefRecord.createUri(uri)); 132 } 133 } 134 135 void tryText(String text) { 136 if (URLUtil.isValidUrl(text)) { 137 Uri parsedUri = Uri.parse(text); 138 tryUri(parsedUri); 139 } else { 140 mNdefMessage = new NdefMessage(NdefRecord.createTextRecord(null, text)); 141 } 142 } 143 144 public void parseShareIntentAndFinish(Intent intent) { 145 if (intent == null || (!intent.getAction().equalsIgnoreCase(Intent.ACTION_SEND) && 146 !intent.getAction().equalsIgnoreCase(Intent.ACTION_SEND_MULTIPLE))) return; 147 148 // First, see if the intent contains clip-data, and if so get data from there 149 ClipData clipData = intent.getClipData(); 150 if (clipData != null && clipData.getItemCount() > 0) { 151 for (int i = 0; i < clipData.getItemCount(); i++) { 152 ClipData.Item item = clipData.getItemAt(i); 153 // First try to get an Uri 154 Uri uri = item.getUri(); 155 String plainText = null; 156 try { 157 plainText = item.coerceToText(this).toString(); 158 } catch (IllegalStateException e) { 159 if (DBG) Log.d(TAG, e.getMessage()); 160 continue; 161 } 162 if (uri != null) { 163 if (DBG) Log.d(TAG, "Found uri in ClipData."); 164 tryUri(uri); 165 } else if (plainText != null) { 166 if (DBG) Log.d(TAG, "Found text in ClipData."); 167 tryText(plainText); 168 } else { 169 if (DBG) Log.d(TAG, "Did not find any shareable data in ClipData."); 170 } 171 } 172 } else { 173 if (intent.getAction().equalsIgnoreCase(Intent.ACTION_SEND)) { 174 final Uri uri = intent.getParcelableExtra(Intent.EXTRA_STREAM); 175 final CharSequence text = intent.getCharSequenceExtra(Intent.EXTRA_TEXT); 176 if (uri != null) { 177 if (DBG) Log.d(TAG, "Found uri in ACTION_SEND intent."); 178 tryUri(uri); 179 } else if (text != null) { 180 if (DBG) Log.d(TAG, "Found EXTRA_TEXT in ACTION_SEND intent."); 181 tryText(text.toString()); 182 } else { 183 if (DBG) Log.d(TAG, "Did not find any shareable data in ACTION_SEND intent."); 184 } 185 } else { 186 final ArrayList<Uri> uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); 187 final ArrayList<CharSequence> texts = intent.getCharSequenceArrayListExtra( 188 Intent.EXTRA_TEXT); 189 190 if (uris != null && uris.size() > 0) { 191 for (Uri uri : uris) { 192 if (DBG) Log.d(TAG, "Found uri in ACTION_SEND_MULTIPLE intent."); 193 tryUri(uri); 194 } 195 } else if (texts != null && texts.size() > 0) { 196 // Try EXTRA_TEXT, but just for the first record 197 if (DBG) Log.d(TAG, "Found text in ACTION_SEND_MULTIPLE intent."); 198 tryText(texts.get(0).toString()); 199 } else { 200 if (DBG) Log.d(TAG, "Did not find any shareable data in " + 201 "ACTION_SEND_MULTIPLE intent."); 202 } 203 } 204 } 205 206 BeamShareData shareData = null; 207 UserHandle myUserHandle = new UserHandle(UserHandle.myUserId()); 208 if (mUris.size() > 0) { 209 // Uris have our first preference for sharing 210 Uri[] uriArray = new Uri[mUris.size()]; 211 int numValidUris = 0; 212 for (Uri uri : mUris) { 213 try { 214 grantUriPermission("com.android.nfc", uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); 215 uriArray[numValidUris++] = uri; 216 if (DBG) Log.d(TAG, "Found uri: " + uri); 217 } catch (SecurityException e) { 218 Log.e(TAG, "Security exception granting uri permission to NFC process."); 219 numValidUris = 0; 220 break; 221 } 222 } 223 if (numValidUris > 0) { 224 shareData = new BeamShareData(null, uriArray, myUserHandle, 0); 225 } else { 226 // No uris left 227 shareData = new BeamShareData(null, null, myUserHandle, 0); 228 } 229 } else if (mNdefMessage != null) { 230 shareData = new BeamShareData(mNdefMessage, null, myUserHandle, 0); 231 if (DBG) Log.d(TAG, "Created NDEF message:" + mNdefMessage.toString()); 232 } else { 233 if (DBG) Log.d(TAG, "Could not find any data to parse."); 234 // Activity may have set something to share over NFC, so pass on anyway 235 shareData = new BeamShareData(null, null, myUserHandle, 0); 236 } 237 mNfcAdapter.invokeBeam(shareData); 238 finish(); 239 } 240 241 final BroadcastReceiver mReceiver = new BroadcastReceiver() { 242 @Override 243 public void onReceive(Context context, Intent intent) { 244 String action = intent.getAction(); 245 if (NfcAdapter.ACTION_ADAPTER_STATE_CHANGED.equals(intent.getAction())) { 246 int state = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE, 247 NfcAdapter.STATE_OFF); 248 if (state == NfcAdapter.STATE_ON) { 249 parseShareIntentAndFinish(mLaunchIntent); 250 } 251 } 252 } 253 }; 254 } 255