blob: dea9bb6a30e53f57fdc30f95721314831eabd425 [file] [log] [blame]
Jeff Hamilton79495342010-10-18 13:13:10 -05001/*
2 * Copyright (C) 2010 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
17package com.android.apps.tag;
18
Jeff Hamiltonf979f1c2010-10-18 18:06:53 -050019import com.android.apps.tag.TagList.TagQuery;
Jeff Hamilton79495342010-10-18 13:13:10 -050020import com.android.apps.tag.message.NdefMessageParser;
21import com.android.apps.tag.message.ParsedNdefMessage;
Jeff Hamilton0215c982010-10-18 17:27:21 -050022import com.android.apps.tag.provider.TagContract.NdefMessages;
Jeff Hamilton79495342010-10-18 13:13:10 -050023import com.android.apps.tag.record.ParsedNdefRecord;
24
25import android.app.Activity;
Jeff Hamilton79495342010-10-18 13:13:10 -050026import android.content.Intent;
Ben Komalo9330d7c2010-10-19 14:16:49 -070027import android.content.res.AssetFileDescriptor;
Jeff Hamilton0215c982010-10-18 17:27:21 -050028import android.database.Cursor;
Ben Komalo9330d7c2010-10-19 14:16:49 -070029import android.media.AudioSystem;
30import android.media.MediaPlayer;
Jeff Hamilton0215c982010-10-18 17:27:21 -050031import android.net.Uri;
32import android.nfc.FormatException;
Jeff Hamilton79495342010-10-18 13:13:10 -050033import android.nfc.NdefMessage;
34import android.nfc.NdefTag;
35import android.nfc.NfcAdapter;
Jeff Hamilton0215c982010-10-18 17:27:21 -050036import android.os.AsyncTask;
Jeff Hamilton79495342010-10-18 13:13:10 -050037import android.os.Bundle;
38import android.os.Handler;
Jeff Hamilton79495342010-10-18 13:13:10 -050039import android.os.Message;
Jeff Hamiltonf979f1c2010-10-18 18:06:53 -050040import android.text.format.DateUtils;
Jeff Hamilton79495342010-10-18 13:13:10 -050041import android.util.Log;
Jeff Hamilton79495342010-10-18 13:13:10 -050042import android.view.LayoutInflater;
43import android.view.View;
44import android.view.View.OnClickListener;
45import android.view.WindowManager;
46import android.widget.Button;
47import android.widget.CheckBox;
48import android.widget.ImageView;
49import android.widget.LinearLayout;
50import android.widget.TextView;
51
Ben Komalo9330d7c2010-10-19 14:16:49 -070052import java.io.IOException;
53
Jeff Hamilton79495342010-10-18 13:13:10 -050054/**
55 * An {@link Activity} which handles a broadcast of a new tag that the device just discovered.
56 */
57public class TagViewer extends Activity implements OnClickListener, Handler.Callback {
Jeff Hamiltonf979f1c2010-10-18 18:06:53 -050058 static final String TAG = "SaveTag";
Jeff Hamilton79495342010-10-18 13:13:10 -050059 static final String EXTRA_TAG_DB_ID = "db_id";
60 static final String EXTRA_MESSAGE = "msg";
61
62 /** This activity will finish itself in this amount of time if the user doesn't do anything. */
Jeff Hamilton0215c982010-10-18 17:27:21 -050063 static final int ACTIVITY_TIMEOUT_MS = 5 * 1000;
Jeff Hamilton79495342010-10-18 13:13:10 -050064
Jeff Hamilton0215c982010-10-18 17:27:21 -050065 Uri mTagUri;
Jeff Hamilton79495342010-10-18 13:13:10 -050066 ImageView mIcon;
67 TextView mTitle;
Jeff Hamiltonf979f1c2010-10-18 18:06:53 -050068 TextView mDate;
Jeff Hamilton79495342010-10-18 13:13:10 -050069 CheckBox mStar;
70 Button mDeleteButton;
Jeff Hamilton0215c982010-10-18 17:27:21 -050071 Button mDoneButton;
Jeff Hamilton79495342010-10-18 13:13:10 -050072 NdefMessage[] mMessagesToSave = null;
Jeff Hamilton0215c982010-10-18 17:27:21 -050073 LinearLayout mTagContent;
Jeff Hamilton79495342010-10-18 13:13:10 -050074
75 @Override
76 protected void onCreate(Bundle savedInstanceState) {
77 super.onCreate(savedInstanceState);
78
79 getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
80 | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
81 | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
82 | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
83 | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
84 | WindowManager.LayoutParams.FLAG_DIM_BEHIND
85 );
86
87 setContentView(R.layout.tag_viewer);
88
Jeff Hamilton0215c982010-10-18 17:27:21 -050089 mTagContent = (LinearLayout) findViewById(R.id.list);
Jeff Hamilton79495342010-10-18 13:13:10 -050090 mTitle = (TextView) findViewById(R.id.title);
Jeff Hamiltonf979f1c2010-10-18 18:06:53 -050091 mDate = (TextView) findViewById(R.id.date);
Jeff Hamilton79495342010-10-18 13:13:10 -050092 mIcon = (ImageView) findViewById(R.id.icon);
93 mStar = (CheckBox) findViewById(R.id.star);
Jeff Hamilton0215c982010-10-18 17:27:21 -050094 mDeleteButton = (Button) findViewById(R.id.button_delete);
95 mDoneButton = (Button) findViewById(R.id.button_done);
Jeff Hamilton79495342010-10-18 13:13:10 -050096
97 mDeleteButton.setOnClickListener(this);
Jeff Hamilton0215c982010-10-18 17:27:21 -050098 mDoneButton.setOnClickListener(this);
Jeff Hamilton79495342010-10-18 13:13:10 -050099 mIcon.setImageResource(R.drawable.ic_launcher_nfc);
100
Jeff Hamilton0215c982010-10-18 17:27:21 -0500101 resolveIntent(getIntent());
102 }
103
104 void resolveIntent(Intent intent) {
105 // Parse the intent
106 String action = intent.getAction();
107 if (NfcAdapter.ACTION_NDEF_TAG_DISCOVERED.equals(action)) {
108 // Get the messages from the tag
109 //TODO check if the tag is writable and offer to write it?
110 NdefTag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
111 NdefMessage[] msgs = tag.getNdefMessages();
112 if (msgs == null || msgs.length == 0) {
113 Log.e(TAG, "No NDEF messages");
114 finish();
115 return;
Jeff Hamilton79495342010-10-18 13:13:10 -0500116 }
117
Jeff Hamilton0215c982010-10-18 17:27:21 -0500118 // Setup the views
119 setTitle(R.string.title_scanned_tag);
120 mStar.setVisibility(View.GONE);
Jeff Hamiltonf979f1c2010-10-18 18:06:53 -0500121 mDate.setVisibility(View.GONE);
Jeff Hamilton79495342010-10-18 13:13:10 -0500122
Ben Komalo9330d7c2010-10-19 14:16:49 -0700123 // Play notification.
124 try {
125 MediaPlayer player = new MediaPlayer();
126 AssetFileDescriptor file = getResources().openRawResourceFd(
127 R.raw.discovered_tag_notification);
128 player.setDataSource(
129 file.getFileDescriptor(),
130 file.getStartOffset(),
131 file.getLength());
132 file.close();
133 player.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
134 player.prepare();
135 player.start();
136 } catch (IOException ex) {
137 Log.w(TAG, "Sound creation failed for tag discovery");
138 }
139
Jeff Hamilton79495342010-10-18 13:13:10 -0500140 // Set a timer on this activity since it wasn't created by the user
Jeff Hamilton0215c982010-10-18 17:27:21 -0500141// new Handler(this).sendEmptyMessageDelayed(0, ACTIVITY_TIMEOUT_MS);
Jeff Hamilton79495342010-10-18 13:13:10 -0500142
Jeff Hamilton0215c982010-10-18 17:27:21 -0500143 // Mark messages that were just scanned for saving
144 mMessagesToSave = msgs;
145
146 // Build the views for the tag
147 buildTagViews(msgs);
148 } else if (Intent.ACTION_VIEW.equals(action)) {
149 // Setup the views
150 setTitle(R.string.title_existing_tag);
151 mStar.setVisibility(View.VISIBLE);
Jeff Hamiltonf979f1c2010-10-18 18:06:53 -0500152 mDate.setVisibility(View.VISIBLE);
Jeff Hamilton0215c982010-10-18 17:27:21 -0500153
154 // Read the tag from the database asynchronously
155 mTagUri = intent.getData();
156 new LoadTagTask().execute(mTagUri);
157 } else {
158 Log.e(TAG, "Unknown intent " + intent);
Jeff Hamilton79495342010-10-18 13:13:10 -0500159 finish();
160 return;
161 }
Jeff Hamilton79495342010-10-18 13:13:10 -0500162 }
163
Jeff Hamilton0215c982010-10-18 17:27:21 -0500164 void buildTagViews(NdefMessage[] msgs) {
Jeff Hamilton79495342010-10-18 13:13:10 -0500165 if (msgs == null || msgs.length == 0) {
166 return;
167 }
168
Jeff Hamilton0215c982010-10-18 17:27:21 -0500169 LayoutInflater inflater = LayoutInflater.from(this);
170 LinearLayout content = mTagContent;
Jeff Hamilton79495342010-10-18 13:13:10 -0500171
Jeff Hamilton0215c982010-10-18 17:27:21 -0500172 // Clear out any old views in the content area, for example if you scan two tags in a row.
173 content.removeAllViews();
174
175 // Parse the first message in the list
176 //TODO figure out what to do when/if we support multiple messages per tag
177 ParsedNdefMessage parsedMsg = NdefMessageParser.parse(msgs[0]);
Jeff Hamilton79495342010-10-18 13:13:10 -0500178
179 // Build views for all of the sub records
180 for (ParsedNdefRecord record : parsedMsg.getRecords()) {
Jeff Hamilton0215c982010-10-18 17:27:21 -0500181 content.addView(record.getView(this, inflater, content));
182 inflater.inflate(R.layout.tag_divider, content, true);
Jeff Hamilton79495342010-10-18 13:13:10 -0500183 }
184 }
185
186 @Override
Jeff Hamilton0215c982010-10-18 17:27:21 -0500187 public void onNewIntent(Intent intent) {
188 // If we get a new scan while looking at a tag just save off the old tag...
189 if (mMessagesToSave != null) {
190 saveMessages(mMessagesToSave);
191 mMessagesToSave = null;
192 }
193
194 // ... and show the new one.
195 resolveIntent(intent);
196 }
197
198 @Override
Jeff Hamilton79495342010-10-18 13:13:10 -0500199 public void setTitle(CharSequence title) {
200 mTitle.setText(title);
201 }
202
203 @Override
204 public void onClick(View view) {
205 if (view == mDeleteButton) {
Jeff Hamilton0215c982010-10-18 17:27:21 -0500206 if (mTagUri == null) {
207 // The tag hasn't been saved yet, so indicate it shouldn't be saved
208 mMessagesToSave = null;
209 finish();
210 } else {
211 // The tag came from the database, start a service to delete it
212 Intent delete = new Intent(this, TagService.class);
213 delete.putExtra(TagService.EXTRA_DELETE_URI, mTagUri);
214 startService(delete);
215 finish();
216 }
217 } else if (view == mDoneButton) {
Jeff Hamilton79495342010-10-18 13:13:10 -0500218 finish();
219 }
220 }
221
222 @Override
223 public void onStop() {
224 super.onStop();
225 if (mMessagesToSave != null) {
226 saveMessages(mMessagesToSave);
227 }
228 }
229
Jeff Hamilton0215c982010-10-18 17:27:21 -0500230 /**
231 * Starts a service to asynchronously save the messages to the content provider.
232 */
Jeff Hamilton79495342010-10-18 13:13:10 -0500233 void saveMessages(NdefMessage[] msgs) {
234 Intent save = new Intent(this, TagService.class);
235 save.putExtra(TagService.EXTRA_SAVE_MSGS, msgs);
236 startService(save);
237 }
238
239 @Override
240 public boolean handleMessage(Message msg) {
241 finish();
242 return true;
243 }
Jeff Hamilton0215c982010-10-18 17:27:21 -0500244
Jeff Hamiltonf979f1c2010-10-18 18:06:53 -0500245 interface ViewTagQuery {
246 final static String[] PROJECTION = new String[] {
247 NdefMessages.BYTES, // 0
248 NdefMessages.STARRED, // 1
249 NdefMessages.DATE, // 2
250 };
251
252 static final int COLUMN_BYTES = 0;
253 static final int COLUMN_STARRED = 1;
254 static final int COLUMN_DATE = 2;
255 }
256
Jeff Hamilton0215c982010-10-18 17:27:21 -0500257 /**
258 * Loads a tag from the database, parses it, and builds the views
259 */
Jeff Hamiltonf979f1c2010-10-18 18:06:53 -0500260 final class LoadTagTask extends AsyncTask<Uri, Void, Cursor> {
Jeff Hamilton0215c982010-10-18 17:27:21 -0500261 @Override
Jeff Hamiltonf979f1c2010-10-18 18:06:53 -0500262 public Cursor doInBackground(Uri... args) {
263 Cursor cursor = getContentResolver().query(args[0], ViewTagQuery.PROJECTION,
Jeff Hamilton0215c982010-10-18 17:27:21 -0500264 null, null, null);
Jeff Hamiltonf979f1c2010-10-18 18:06:53 -0500265
266 // Ensure the cursor loads its window
267 if (cursor != null) cursor.getCount();
268 return cursor;
269 }
270
271 @Override
272 public void onPostExecute(Cursor cursor) {
273 NdefMessage msg = null;
Jeff Hamilton0215c982010-10-18 17:27:21 -0500274 try {
Jeff Hamiltonf979f1c2010-10-18 18:06:53 -0500275 if (cursor != null && cursor.moveToFirst()) {
276 msg = new NdefMessage(cursor.getBlob(ViewTagQuery.COLUMN_BYTES));
277 if (msg != null) {
278 mDate.setText(DateUtils.getRelativeTimeSpanString(TagViewer.this,
279 cursor.getLong(ViewTagQuery.COLUMN_DATE)));
280 mStar.setChecked(cursor.getInt(ViewTagQuery.COLUMN_STARRED) != 0);
281 buildTagViews(new NdefMessage[] { msg });
282 }
Jeff Hamilton0215c982010-10-18 17:27:21 -0500283 }
284 } catch (FormatException e) {
285 Log.e(TAG, "invalid tag format", e);
286 } finally {
287 if (cursor != null) cursor.close();
288 }
Jeff Hamilton0215c982010-10-18 17:27:21 -0500289 }
290 }
Jeff Hamilton79495342010-10-18 13:13:10 -0500291}