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.util.Log; 36 import android.webkit.URLUtil; 37 38 import com.android.internal.R; 39 40 /** 41 * This class is registered by NfcService to handle 42 * ACTION_SHARE intents. It tries to parse data contained 43 * in ACTION_SHARE intents in either a content/file Uri, 44 * which can be sent using NFC handover, or alternatively 45 * it tries to parse texts and URLs to store them in a simple 46 * Text or Uri NdefRecord. The data is then passed on into 47 * NfcService to transmit on NFC tap. 48 * 49 */ 50 public class BeamShareActivity extends Activity { 51 static final String TAG ="BeamShareActivity"; 52 static final boolean DBG = false; 53 54 ArrayList<Uri> mUris; 55 NdefMessage mNdefMessage; 56 NfcAdapter mNfcAdapter; 57 Intent mLaunchIntent; 58 59 @Override 60 protected void onCreate(Bundle savedInstanceState) { 61 super.onCreate(savedInstanceState); 62 mUris = new ArrayList<Uri>(); 63 mNdefMessage = null; 64 mNfcAdapter = NfcAdapter.getDefaultAdapter(this); 65 mLaunchIntent = getIntent(); 66 if (mNfcAdapter == null) { 67 Log.e(TAG, "NFC adapter not present."); 68 finish(); 69 } else { 70 if (!mNfcAdapter.isEnabled()) { 71 showNfcDialogAndExit(com.android.nfc.R.string.beam_requires_nfc_enabled); 72 } else { 73 parseShareIntentAndFinish(mLaunchIntent); 74 } 75 } 76 } 77 78 79 private void showNfcDialogAndExit(int msgId) { 80 IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED); 81 registerReceiver(mReceiver, filter); 82 83 AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this, 84 AlertDialog.THEME_DEVICE_DEFAULT_LIGHT); 85 dialogBuilder.setMessage(msgId); 86 dialogBuilder.setOnCancelListener(new DialogInterface.OnCancelListener() { 87 @Override 88 public void onCancel(DialogInterface dialogInterface) { 89 finish(); 90 } 91 }); 92 dialogBuilder.setPositiveButton(R.string.yes, 93 new DialogInterface.OnClickListener() { 94 @Override 95 public void onClick(DialogInterface dialog, int id) { 96 if (!mNfcAdapter.isEnabled()) { 97 mNfcAdapter.enable(); 98 // Wait for enable broadcast 99 } else { 100 parseShareIntentAndFinish(mLaunchIntent); 101 } 102 } 103 }); 104 dialogBuilder.setNegativeButton(R.string.no, 105 new DialogInterface.OnClickListener() { 106 @Override 107 public void onClick(DialogInterface dialogInterface, int i) { 108 finish(); 109 } 110 }); 111 dialogBuilder.show(); 112 } 113 114 void tryUri(Uri uri) { 115 if (uri.getScheme().equalsIgnoreCase("content") || 116 uri.getScheme().equalsIgnoreCase("file")) { 117 // Typically larger data, this can be shared using NFC handover 118 mUris.add(uri); 119 } else { 120 // Just put this Uri in an NDEF message 121 mNdefMessage = new NdefMessage(NdefRecord.createUri(uri)); 122 } 123 } 124 125 void tryText(String text) { 126 if (URLUtil.isValidUrl(text)) { 127 Uri parsedUri = Uri.parse(text); 128 tryUri(parsedUri); 129 } else { 130 mNdefMessage = new NdefMessage(NdefRecord.createTextRecord(null, text)); 131 } 132 } 133 134 public void parseShareIntentAndFinish(Intent intent) { 135 if (intent == null || (!intent.getAction().equalsIgnoreCase(Intent.ACTION_SEND) && 136 !intent.getAction().equalsIgnoreCase(Intent.ACTION_SEND_MULTIPLE))) return; 137 138 // First, see if the intent contains clip-data, and if so get data from there 139 ClipData clipData = intent.getClipData(); 140 if (clipData != null && clipData.getItemCount() > 0) { 141 for (int i = 0; i < clipData.getItemCount(); i++) { 142 ClipData.Item item = clipData.getItemAt(i); 143 // First try to get an Uri 144 Uri uri = item.getUri(); 145 String plainText = item.coerceToText(this).toString(); 146 if (uri != null) { 147 if (DBG) Log.d(TAG, "Found uri in ClipData."); 148 tryUri(uri); 149 } else if (plainText != null) { 150 if (DBG) Log.d(TAG, "Found text in ClipData."); 151 tryText(plainText); 152 } else { 153 if (DBG) Log.d(TAG, "Did not find any shareable data in ClipData."); 154 } 155 } 156 } else { 157 if (intent.getAction().equalsIgnoreCase(Intent.ACTION_SEND)) { 158 final Uri uri = intent.getParcelableExtra(Intent.EXTRA_STREAM); 159 final CharSequence text = intent.getCharSequenceExtra(Intent.EXTRA_TEXT); 160 if (uri != null) { 161 if (DBG) Log.d(TAG, "Found uri in ACTION_SEND intent."); 162 tryUri(uri); 163 } else if (text != null) { 164 if (DBG) Log.d(TAG, "Found EXTRA_TEXT in ACTION_SEND intent."); 165 tryText(text.toString()); 166 } else { 167 if (DBG) Log.d(TAG, "Did not find any shareable data in ACTION_SEND intent."); 168 } 169 } else { 170 final ArrayList<Uri> uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); 171 final ArrayList<CharSequence> texts = intent.getCharSequenceArrayListExtra( 172 Intent.EXTRA_TEXT); 173 174 if (uris != null && uris.size() > 0) { 175 for (Uri uri : uris) { 176 if (DBG) Log.d(TAG, "Found uri in ACTION_SEND_MULTIPLE intent."); 177 tryUri(uri); 178 } 179 } else if (texts != null && texts.size() > 0) { 180 // Try EXTRA_TEXT, but just for the first record 181 if (DBG) Log.d(TAG, "Found text in ACTION_SEND_MULTIPLE intent."); 182 tryText(texts.get(0).toString()); 183 } else { 184 if (DBG) Log.d(TAG, "Did not find any shareable data in " + 185 "ACTION_SEND_MULTIPLE intent."); 186 } 187 } 188 } 189 190 BeamShareData shareData = null; 191 if (mUris.size() > 0) { 192 // Uris have our first preference for sharing 193 Uri[] uriArray = new Uri[mUris.size()]; 194 int numValidUris = 0; 195 for (Uri uri : mUris) { 196 try { 197 grantUriPermission("com.android.nfc", uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); 198 uriArray[numValidUris++] = uri; 199 if (DBG) Log.d(TAG, "Found uri: " + uri); 200 } catch (SecurityException e) { 201 Log.e(TAG, "Security exception granting uri permission to NFC process."); 202 numValidUris = 0; 203 break; 204 } 205 } 206 if (numValidUris > 0) { 207 shareData = new BeamShareData(null, uriArray, 0); 208 } else { 209 // No uris left 210 shareData = new BeamShareData(null, null, 0); 211 } 212 } else if (mNdefMessage != null) { 213 shareData = new BeamShareData(mNdefMessage, null, 0); 214 if (DBG) Log.d(TAG, "Created NDEF message:" + mNdefMessage.toString()); 215 } else { 216 if (DBG) Log.d(TAG, "Could not find any data to parse."); 217 // Activity may have set something to share over NFC, so pass on anyway 218 shareData = new BeamShareData(null, null, 0); 219 } 220 mNfcAdapter.invokeBeam(shareData); 221 finish(); 222 } 223 224 final BroadcastReceiver mReceiver = new BroadcastReceiver() { 225 @Override 226 public void onReceive(Context context, Intent intent) { 227 String action = intent.getAction(); 228 if (NfcAdapter.ACTION_ADAPTER_STATE_CHANGED.equals(intent.getAction())) { 229 int state = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE, 230 NfcAdapter.STATE_OFF); 231 if (state == NfcAdapter.STATE_ON) { 232 parseShareIntentAndFinish(mLaunchIntent); 233 } 234 } 235 } 236 }; 237 } 238