• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
onCreate(Bundle savedInstanceState)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
onDestroy()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 
showNfcDialogAndExit(int msgId)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 
tryUri(Uri uri)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 
tryText(String text)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 
parseShareIntentAndFinish(Intent intent)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