Merge "Checkpoint work on the Tag app." into gingerbread
This commit is contained in:
committed by
Android (Google) Code Review
commit
4f6f93d594
@@ -20,26 +20,33 @@
|
|||||||
own application, the package name must be changed from "com.example.*"
|
own application, the package name must be changed from "com.example.*"
|
||||||
to come from a domain that you own or have control over. -->
|
to come from a domain that you own or have control over. -->
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.android.apps.tag">
|
package="com.android.apps.tag"
|
||||||
|
>
|
||||||
|
|
||||||
|
<uses-permission android:name="com.trustedlogic.trustednfc.permission.NFC_NOTIFY" />
|
||||||
|
<uses-permission android:name="com.trustedlogic.trustednfc.permission.NFC_RAW" />
|
||||||
|
|
||||||
<application android:label="Tags">
|
<application android:label="Tags">
|
||||||
<activity android:name="TagBrowserActivity"
|
<activity android:name="TagBrowserActivity"
|
||||||
android:icon="@drawable/ic_launcher_nfc"
|
android:icon="@drawable/ic_launcher_nfc"
|
||||||
android:theme="@style/Tags.TagBrowserTheme" >
|
android:theme="@android:style/Theme.NoTitleBar"
|
||||||
|
>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity android:name="TagList"></activity>
|
<activity android:name="TagList" />
|
||||||
<activity android:name="SaveTag"></activity>
|
|
||||||
<receiver android:name=".TagBroadcastReceiver">
|
<activity android:name="TagViewer"
|
||||||
|
android:theme="@android:style/Theme.Dialog"
|
||||||
|
>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name= "com.trustedlogic.trustednfc.android.action.NDEF_TAG_DISCOVERED"/>
|
<action android:name="android.nfc.action.NDEF_TAG_DISCOVERED"/>
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</activity>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
<uses-permission android:name="com.trustedlogic.trustednfc.permission.NFC_NOTIFY"></uses-permission>
|
|
||||||
<uses-permission android:name="com.trustedlogic.trustednfc.permission.NFC_RAW"></uses-permission>
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -14,10 +14,9 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
<resources>
|
|
||||||
|
|
||||||
<style name="Tags.TagBrowserTheme" parent="@android:style/Theme.NoTitleBar">
|
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
</style>
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?android:attr/listDivider"
|
||||||
</resources>
|
/>
|
||||||
26
apps/Tag/res/layout/tag_text.xml
Normal file
26
apps/Tag/res/layout/tag_text.xml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2010 The Android Open Source Project
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/text"
|
||||||
|
android:padding="4dip"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?android:attr/listPreferredItemHeight"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
/>
|
||||||
21
apps/Tag/res/layout/tag_viewer_list.xml
Normal file
21
apps/Tag/res/layout/tag_viewer_list.xml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2010 The Android Open Source Project
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
/>
|
||||||
@@ -20,4 +20,10 @@
|
|||||||
<string name="help_and_info">help and info</string>
|
<string name="help_and_info">help and info</string>
|
||||||
<string name="saved">Saved</string>
|
<string name="saved">Saved</string>
|
||||||
|
|
||||||
|
<!-- The title of the tab that displays all recently scanned NFC tags -->
|
||||||
|
<string name="tab_recent">Recent</string>
|
||||||
|
|
||||||
|
<!-- The title of the tab that displays all saved NFC tags -->
|
||||||
|
<string name="tab_saved">Saved</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -16,17 +16,17 @@
|
|||||||
|
|
||||||
package com.android.apps.tag;
|
package com.android.apps.tag;
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.BiMap;
|
import com.google.common.collect.BiMap;
|
||||||
import com.google.common.collect.ImmutableBiMap;
|
import com.google.common.collect.ImmutableBiMap;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.primitives.Bytes;
|
import com.google.common.primitives.Bytes;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
import android.nfc.NdefMessage;
|
import android.nfc.NdefMessage;
|
||||||
import android.nfc.NdefRecord;
|
import android.nfc.NdefRecord;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URI;
|
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.nio.charset.Charsets;
|
import java.nio.charset.Charsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -47,8 +47,7 @@ public class NdefUtil {
|
|||||||
* This is a mapping of "URI Identifier Codes" to URI string prefixes,
|
* This is a mapping of "URI Identifier Codes" to URI string prefixes,
|
||||||
* per section 3.2.2 of the NFC Forum URI Record Type Definition document.
|
* per section 3.2.2 of the NFC Forum URI Record Type Definition document.
|
||||||
*/
|
*/
|
||||||
private static final
|
private static final BiMap<Byte, String> URI_PREFIX_MAP = ImmutableBiMap.<Byte, String>builder()
|
||||||
BiMap<Byte, String> URI_PREFIX_MAP = ImmutableBiMap.<Byte, String>builder()
|
|
||||||
.put((byte) 0x00, "")
|
.put((byte) 0x00, "")
|
||||||
.put((byte) 0x01, "http://www.")
|
.put((byte) 0x01, "http://www.")
|
||||||
.put((byte) 0x02, "https://www.")
|
.put((byte) 0x02, "https://www.")
|
||||||
@@ -88,9 +87,9 @@ public class NdefUtil {
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link NdefRecord} containing the supplied {@link URI}.
|
* Create a new {@link NdefRecord} containing the supplied {@link Uri}.
|
||||||
*/
|
*/
|
||||||
public static NdefRecord toUriRecord(URI uri) {
|
public static NdefRecord toUriRecord(Uri uri) {
|
||||||
byte[] uriBytes = uri.toString().getBytes(Charsets.UTF_8);
|
byte[] uriBytes = uri.toString().getBytes(Charsets.UTF_8);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -110,18 +109,16 @@ public class NdefUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert {@link NdefRecord} into a {@link URI}.
|
* Convert {@link NdefRecord} into a {@link Uri}.
|
||||||
*
|
*
|
||||||
* TODO: This class does not handle NdefRecords where the TNF
|
* TODO: This class does not handle NdefRecords where the TNF
|
||||||
* (Type Name Format) of the class is {@link NdefRecord#TNF_ABSOLUTE_URI}.
|
* (Type Name Format) of the class is {@link NdefRecord#TNF_ABSOLUTE_URI}.
|
||||||
* This should be fixed.
|
* This should be fixed.
|
||||||
*
|
*
|
||||||
* @throws URISyntaxException if the {@code NdefRecord} contains an
|
|
||||||
* invalid URI.
|
|
||||||
* @throws IllegalArgumentException if the NdefRecord is not a
|
* @throws IllegalArgumentException if the NdefRecord is not a
|
||||||
* record containing a URI.
|
* record containing a URI.
|
||||||
*/
|
*/
|
||||||
public static URI toURI(NdefRecord record) throws URISyntaxException {
|
public static Uri toUri(NdefRecord record) {
|
||||||
Preconditions.checkArgument(record.getTnf() == NdefRecord.TNF_WELL_KNOWN);
|
Preconditions.checkArgument(record.getTnf() == NdefRecord.TNF_WELL_KNOWN);
|
||||||
Preconditions.checkArgument(Arrays.equals(record.getType(), NdefRecord.RTD_URI));
|
Preconditions.checkArgument(Arrays.equals(record.getType(), NdefRecord.RTD_URI));
|
||||||
|
|
||||||
@@ -140,17 +137,15 @@ public class NdefUtil {
|
|||||||
prefix.getBytes(Charsets.UTF_8),
|
prefix.getBytes(Charsets.UTF_8),
|
||||||
Arrays.copyOfRange(payload, 1, payload.length));
|
Arrays.copyOfRange(payload, 1, payload.length));
|
||||||
|
|
||||||
return new URI(new String(fullUri, Charsets.UTF_8));
|
return Uri.parse(new String(fullUri, Charsets.UTF_8));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isURI(NdefRecord record) {
|
public static boolean isUri(NdefRecord record) {
|
||||||
try {
|
try {
|
||||||
toURI(record);
|
toUri(record);
|
||||||
return true;
|
return true;
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
return false;
|
return false;
|
||||||
} catch (URISyntaxException e) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,33 +202,29 @@ public class NdefUtil {
|
|||||||
return Iterables.filter(getObjects(message), String.class);
|
return Iterables.filter(getObjects(message), String.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Iterable<URI> getURIs(NdefMessage message) {
|
public static Iterable<Uri> getUris(NdefMessage message) {
|
||||||
return Iterables.filter(getObjects(message), URI.class);
|
return Iterables.filter(getObjects(message), Uri.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the provided {@code NdefMessage}, extracting all known
|
* Parse the provided {@code NdefMessage}, extracting all known
|
||||||
* objects from the message. Typically this list will consist of
|
* objects from the message. Typically this list will consist of
|
||||||
* {@link String}s corresponding to NDEF text records, or {@link URI}s
|
* {@link String}s corresponding to NDEF text records, or {@link Uri}s
|
||||||
* corresponding to NDEF URI records.
|
* corresponding to NDEF URI records.
|
||||||
* <p>
|
* <p>
|
||||||
* TODO: Is this API too generic? Should we keep it?
|
* TODO: Is this API too generic? Should we keep it?
|
||||||
*/
|
*/
|
||||||
private static Iterable<Object> getObjects(NdefMessage message) {
|
public static Iterable<Object> getObjects(NdefMessage message) {
|
||||||
try {
|
List<Object> retval = new ArrayList<Object>();
|
||||||
List<Object> retval = new ArrayList<Object>();
|
for (NdefRecord record : message.getRecords()) {
|
||||||
for (NdefRecord record : message.getRecords()) {
|
if (isUri(record)) {
|
||||||
if (isURI(record)) {
|
retval.add(toUri(record));
|
||||||
retval.add(toURI(record));
|
} else if (isText(record)) {
|
||||||
} else if (isText(record)) {
|
retval.add(toText(record));
|
||||||
retval.add(toText(record));
|
} else if (SmartPoster.isPoster(record)) {
|
||||||
} else if (SmartPoster.isPoster(record)) {
|
retval.add(SmartPoster.from(record));
|
||||||
retval.add(SmartPoster.from(record));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return retval;
|
|
||||||
} catch (URISyntaxException e) {
|
|
||||||
throw new IllegalArgumentException(e);
|
|
||||||
}
|
}
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,96 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.apps.tag;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.nfc.NdefMessage;
|
|
||||||
import android.nfc.NdefTag;
|
|
||||||
import android.nfc.NfcAdapter;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.WindowManager;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An {@code Activity} which handles a broadcast of a new tag that the device just discovered.
|
|
||||||
*/
|
|
||||||
public class SaveTag extends Activity implements DialogInterface.OnClickListener {
|
|
||||||
private static final String TAG = "SaveTag";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStart() {
|
|
||||||
super.onStart();
|
|
||||||
|
|
||||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
|
|
||||||
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
|
|
||||||
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
|
|
||||||
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
|
|
||||||
| WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
|
|
||||||
| WindowManager.LayoutParams.FLAG_DIM_BEHIND
|
|
||||||
);
|
|
||||||
|
|
||||||
showDialog(1);
|
|
||||||
NdefTag tag = getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG);
|
|
||||||
NdefMessage[] msgs = tag.getNdefMessages();
|
|
||||||
|
|
||||||
if (msgs.length == 0) {
|
|
||||||
Log.d(TAG, "No NDEF messages");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (msgs.length > 1) {
|
|
||||||
Log.d(TAG, "Multiple NDEF messages, only saving first");
|
|
||||||
}
|
|
||||||
String s = toHexString(msgs[0].toByteArray());
|
|
||||||
Log.d("SaveTag", s);
|
|
||||||
Toast.makeText(this.getBaseContext(), "SaveTag: " + s, Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Dialog onCreateDialog(int id, Bundle args) {
|
|
||||||
return new AlertDialog.Builder(this)
|
|
||||||
.setTitle("Welcome! T2000 Festival")
|
|
||||||
.setPositiveButton("Save", this)
|
|
||||||
.setNegativeButton("Cancel", this)
|
|
||||||
.create();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStop() {
|
|
||||||
super.onStop();
|
|
||||||
dismissDialog(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final char[] hexDigits = "0123456789abcdef".toCharArray();
|
|
||||||
|
|
||||||
private static String toHexString(byte[] bytes) {
|
|
||||||
StringBuilder sb = new StringBuilder(3 * bytes.length);
|
|
||||||
for (byte b : bytes) {
|
|
||||||
sb.append("(byte) 0x")
|
|
||||||
.append(hexDigits[(b >> 4) & 0xf])
|
|
||||||
.append(hexDigits[b & 0xf]).append(", ");
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -18,13 +18,15 @@ package com.android.apps.tag;
|
|||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.nfc.FormatException;
|
||||||
import android.nfc.NdefMessage;
|
import android.nfc.NdefMessage;
|
||||||
import android.nfc.NdefRecord;
|
import android.nfc.NdefRecord;
|
||||||
import android.nfc.FormatException;
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.net.URI;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A representation of an NFC Forum "Smart Poster".
|
* A representation of an NFC Forum "Smart Poster".
|
||||||
@@ -39,7 +41,7 @@ public class SmartPoster {
|
|||||||
* This record is optional."
|
* This record is optional."
|
||||||
|
|
||||||
*/
|
*/
|
||||||
private final String titleRecord;
|
private final String mTitleRecord;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NFC Forum Smart Poster Record Type Definition section 3.2.1.
|
* NFC Forum Smart Poster Record Type Definition section 3.2.1.
|
||||||
@@ -48,22 +50,22 @@ public class SmartPoster {
|
|||||||
* records are just metadata about this record. There MUST be one URI
|
* records are just metadata about this record. There MUST be one URI
|
||||||
* record and there MUST NOT be more than one."
|
* record and there MUST NOT be more than one."
|
||||||
*/
|
*/
|
||||||
private final URI uriRecord;
|
private final Uri mUriRecord;
|
||||||
|
|
||||||
private SmartPoster(URI uri, @Nullable String title) {
|
private SmartPoster(Uri uri, @Nullable String title) {
|
||||||
uriRecord = Preconditions.checkNotNull(uri);
|
mUriRecord = Preconditions.checkNotNull(uri);
|
||||||
titleRecord = title;
|
mTitleRecord = title;
|
||||||
}
|
}
|
||||||
|
|
||||||
public URI getURI() {
|
public Uri getUri() {
|
||||||
return uriRecord;
|
return mUriRecord;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the title of the smartposter. This may be {@code null}.
|
* Returns the title of the smart poster. This may be {@code null}.
|
||||||
*/
|
*/
|
||||||
public String getTitle() {
|
public String getTitle() {
|
||||||
return titleRecord;
|
return mTitleRecord;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SmartPoster from(NdefRecord record) {
|
public static SmartPoster from(NdefRecord record) {
|
||||||
@@ -71,7 +73,7 @@ public class SmartPoster {
|
|||||||
Preconditions.checkArgument(Arrays.equals(record.getType(), NdefRecord.RTD_SMART_POSTER));
|
Preconditions.checkArgument(Arrays.equals(record.getType(), NdefRecord.RTD_SMART_POSTER));
|
||||||
try {
|
try {
|
||||||
NdefMessage subRecords = new NdefMessage(record.getPayload());
|
NdefMessage subRecords = new NdefMessage(record.getPayload());
|
||||||
URI uri = Iterables.getOnlyElement(NdefUtil.getURIs(subRecords));
|
Uri uri = Iterables.getOnlyElement(NdefUtil.getUris(subRecords));
|
||||||
Iterable<String> textFields = NdefUtil.getTextFields(subRecords);
|
Iterable<String> textFields = NdefUtil.getTextFields(subRecords);
|
||||||
String title = null;
|
String title = null;
|
||||||
if (!Iterables.isEmpty(textFields)) {
|
if (!Iterables.isEmpty(textFields)) {
|
||||||
|
|||||||
86
apps/Tag/src/com/android/apps/tag/TagAdapter.java
Normal file
86
apps/Tag/src/com/android/apps/tag/TagAdapter.java
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.apps.tag;
|
||||||
|
|
||||||
|
import com.android.apps.tag.TagDBHelper.NdefMessagesTable;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.nfc.FormatException;
|
||||||
|
import android.nfc.NdefMessage;
|
||||||
|
import android.nfc.NdefRecord;
|
||||||
|
import android.text.format.DateUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Adapter;
|
||||||
|
import android.widget.CursorAdapter;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom {@link Adapter} that renders tag entries for a list.
|
||||||
|
*/
|
||||||
|
public class TagAdapter extends CursorAdapter {
|
||||||
|
|
||||||
|
private final LayoutInflater mInflater;
|
||||||
|
|
||||||
|
public TagAdapter(Context context) {
|
||||||
|
super(context, null, false);
|
||||||
|
mInflater = LayoutInflater.from(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bindView(View view, Context context, Cursor cursor) {
|
||||||
|
TextView mainLine = (TextView) view.findViewById(R.id.title);
|
||||||
|
TextView dateLine = (TextView) view.findViewById(R.id.date);
|
||||||
|
|
||||||
|
NdefMessage msg = null;
|
||||||
|
try {
|
||||||
|
msg = new NdefMessage(cursor.getBlob(cursor.getColumnIndex(NdefMessagesTable.BYTES)));
|
||||||
|
} catch (FormatException e) {
|
||||||
|
Log.e("foo", "poorly formatted message", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg == null) {
|
||||||
|
mainLine.setText("Invalid tag");
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
SmartPoster poster = SmartPoster.from(msg.getRecords()[0]);
|
||||||
|
mainLine.setText(poster.getTitle());
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
// Not a smart poster
|
||||||
|
NdefRecord record = msg.getRecords()[0];
|
||||||
|
Uri uri = null;
|
||||||
|
try {
|
||||||
|
uri = NdefUtil.toUri(record);
|
||||||
|
mainLine.setText(uri.toString());
|
||||||
|
} catch (IllegalArgumentException e2) {
|
||||||
|
mainLine.setText("Not a smart poster or URL");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dateLine.setText(DateUtils.getRelativeTimeSpanString(
|
||||||
|
context, cursor.getLong(cursor.getColumnIndex(NdefMessagesTable.DATE))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
||||||
|
return mInflater.inflate(R.layout.tag_list_item, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.apps.tag;
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.nfc.NdefTag;
|
|
||||||
|
|
||||||
import android.nfc.NdefMessage;
|
|
||||||
import android.nfc.NfcAdapter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When we receive a new NDEF tag, start the activity to
|
|
||||||
* process the tag.
|
|
||||||
*/
|
|
||||||
public class TagBroadcastReceiver extends BroadcastReceiver {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
if (intent.getAction().equals(NfcAdapter.ACTION_NDEF_TAG_DISCOVERED)) {
|
|
||||||
NdefTag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
|
|
||||||
Intent i = new Intent(context, SaveTag.class)
|
|
||||||
.putExtra(NfcAdapter.EXTRA_TAG, tag)
|
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
context.startActivity(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.apps.tag;
|
package com.android.apps.tag;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.app.TabActivity;
|
import android.app.TabActivity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
@@ -23,34 +24,27 @@ import android.os.Bundle;
|
|||||||
import android.widget.TabHost;
|
import android.widget.TabHost;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A browsing {@code Activity} that displays the saved tags in categories under tabs.
|
* A browsing {@link Activity} that displays the saved tags in categories under tabs.
|
||||||
*/
|
*/
|
||||||
public class TagBrowserActivity extends TabActivity {
|
public class TagBrowserActivity extends TabActivity {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
// While we're doing development, delete the database every time we start.
|
|
||||||
getBaseContext().getDatabasePath("Tags.db").delete();
|
|
||||||
|
|
||||||
setContentView(R.layout.main);
|
setContentView(R.layout.main);
|
||||||
|
|
||||||
Resources res = getResources();
|
Resources res = getResources();
|
||||||
TabHost tabHost = getTabHost();
|
TabHost tabHost = getTabHost();
|
||||||
Intent i = new Intent().setClass(this, TagList.class);
|
|
||||||
|
|
||||||
Intent iSavedList = new Intent().setClass(this, TagList.class)
|
TabHost.TabSpec spec1 = tabHost.newTabSpec("saved")
|
||||||
.putExtra(TagList.SHOW_SAVED_ONLY, true);
|
.setIndicator(getText(R.string.tab_saved), res.getDrawable(R.drawable.ic_menu_tag))
|
||||||
Intent iRecentList = new Intent().setClass(this, TagList.class);
|
.setContent(new Intent().setClass(this, TagList.class)
|
||||||
|
.putExtra(TagList.EXTRA_SHOW_SAVED_ONLY, true));
|
||||||
TabHost.TabSpec spec1 = tabHost.newTabSpec("1")
|
|
||||||
.setIndicator("Saved", res.getDrawable(R.drawable.ic_menu_tag))
|
|
||||||
.setContent(iSavedList);
|
|
||||||
tabHost.addTab(spec1);
|
tabHost.addTab(spec1);
|
||||||
|
|
||||||
TabHost.TabSpec spec2 = tabHost.newTabSpec("2")
|
TabHost.TabSpec spec2 = tabHost.newTabSpec("recent")
|
||||||
.setIndicator("Recent", res.getDrawable(R.drawable.ic_menu_desk_clock))
|
.setIndicator(getText(R.string.tab_recent), res.getDrawable(R.drawable.ic_menu_desk_clock))
|
||||||
.setContent(iRecentList);
|
.setContent(new Intent().setClass(this, TagList.class));
|
||||||
tabHost.addTab(spec2);
|
tabHost.addTab(spec2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
package com.android.apps.tag;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.text.format.DateUtils;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.Adapter;
|
|
||||||
import android.widget.CursorAdapter;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A custom {@link Adapter} that renders tag entries for a list.
|
|
||||||
*/
|
|
||||||
public class TagCursorAdapter extends CursorAdapter {
|
|
||||||
|
|
||||||
private final LayoutInflater mInflater;
|
|
||||||
|
|
||||||
public TagCursorAdapter(Context context, Cursor c) {
|
|
||||||
super(context, c);
|
|
||||||
|
|
||||||
mInflater = LayoutInflater.from(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bindView(View view, Context context, Cursor cursor) {
|
|
||||||
TextView mainLine = (TextView) view.findViewById(R.id.title);
|
|
||||||
TextView dateLine = (TextView) view.findViewById(R.id.date);
|
|
||||||
|
|
||||||
// TODO(benkomalo): either write a cursor abstraction, or use constants for column indices.
|
|
||||||
mainLine.setText(cursor.getString(cursor.getColumnIndex("bytes")));
|
|
||||||
dateLine.setText(DateUtils.getRelativeTimeSpanString(
|
|
||||||
context, cursor.getLong(cursor.getColumnIndex("date"))));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
|
||||||
return mInflater.inflate(R.layout.tag_list_item, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -22,34 +22,32 @@ import android.content.Context;
|
|||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
import android.database.sqlite.SQLiteStatement;
|
import android.database.sqlite.SQLiteStatement;
|
||||||
|
import android.net.Uri;
|
||||||
import android.nfc.FormatException;
|
import android.nfc.FormatException;
|
||||||
import android.nfc.NdefMessage;
|
import android.nfc.NdefMessage;
|
||||||
import android.nfc.NdefRecord;
|
import android.nfc.NdefRecord;
|
||||||
|
|
||||||
import java.net.URI;
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Database utilities for the saved tags.
|
* Database utilities for the saved tags.
|
||||||
*/
|
*/
|
||||||
public class TagDBHelper extends SQLiteOpenHelper {
|
public class TagDBHelper extends SQLiteOpenHelper {
|
||||||
|
|
||||||
private static final int DATABASE_VERSION = 1;
|
private static final String DATABASE_NAME = "tags.db";
|
||||||
|
private static final int DATABASE_VERSION = 3;
|
||||||
|
|
||||||
private static final String NDEF_MSG = "create table NdefMessage ("
|
public interface NdefMessagesTable {
|
||||||
+ "_id INTEGER NOT NULL, "
|
public static final String TABLE_NAME = "nedf_msg";
|
||||||
+ "bytes BLOB NOT NULL, "
|
|
||||||
+ "date INTEGER NOT NULL, "
|
|
||||||
+ "saved TEXT NOT NULL default 0," // boolean
|
|
||||||
+ "PRIMARY KEY(_id)"
|
|
||||||
+ ")";
|
|
||||||
|
|
||||||
private static final String INSERT =
|
|
||||||
"INSERT INTO NdefMessage (bytes, date, saved) values (?, ?, ?)";
|
|
||||||
|
|
||||||
|
public static final String _ID = "_id";
|
||||||
|
public static final String TITLE = "title";
|
||||||
|
public static final String BYTES = "bytes";
|
||||||
|
public static final String DATE = "date";
|
||||||
|
public static final String SAVED = "saved";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A real NFC tag containing an NFC "smart poster". This smart poster
|
* A real NFC tag containing an NFC "smart poster". This smart poster
|
||||||
* consists of the text "NFC Forum Type 4 Tag" in english combined with
|
* consists of the text "NFC Forum Type 4 Tag" in English combined with
|
||||||
* the URL "http://www.nxp.com/nfc"
|
* the URL "http://www.nxp.com/nfc"
|
||||||
*/
|
*/
|
||||||
public static final byte[] REAL_NFC_MSG = new byte[] {
|
public static final byte[] REAL_NFC_MSG = new byte[] {
|
||||||
@@ -115,53 +113,85 @@ public class TagDBHelper extends SQLiteOpenHelper {
|
|||||||
// end smart poster payload
|
// end smart poster payload
|
||||||
};
|
};
|
||||||
|
|
||||||
public TagDBHelper(Context context) {
|
private static TagDBHelper sInstance;
|
||||||
this(context, "Tags.db");
|
|
||||||
|
public static synchronized TagDBHelper getInstance(Context context) {
|
||||||
|
if (sInstance == null) {
|
||||||
|
sInstance = new TagDBHelper(context.getApplicationContext());
|
||||||
|
}
|
||||||
|
return sInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TagDBHelper(Context context) {
|
||||||
|
this(context, DATABASE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public TagDBHelper(Context context, String dbFile) {
|
TagDBHelper(Context context, String dbFile) {
|
||||||
super(context, dbFile, null, DATABASE_VERSION);
|
super(context, dbFile, null, DATABASE_VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(SQLiteDatabase db) {
|
public void onCreate(SQLiteDatabase db) {
|
||||||
db.execSQL(NDEF_MSG);
|
db.execSQL("CREATE TABLE " + NdefMessagesTable.TABLE_NAME + " (" +
|
||||||
|
NdefMessagesTable._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
|
||||||
|
NdefMessagesTable.TITLE + " TEXT NOT NULL DEFAULT ''," +
|
||||||
|
NdefMessagesTable.BYTES + " BLOB NOT NULL, " +
|
||||||
|
NdefMessagesTable.DATE + " INTEGER NOT NULL, " +
|
||||||
|
NdefMessagesTable.SAVED + " INTEGER NOT NULL DEFAULT 0" + // boolean
|
||||||
|
");");
|
||||||
|
|
||||||
|
db.execSQL("CREATE INDEX msgIndex ON " + NdefMessagesTable.TABLE_NAME + " (" +
|
||||||
|
NdefMessagesTable.DATE + " DESC, " +
|
||||||
|
NdefMessagesTable.SAVED + " ASC" +
|
||||||
|
")");
|
||||||
|
|
||||||
|
addTestData(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||||
|
// Drop everything and recreate it for now
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS " + NdefMessagesTable.TABLE_NAME);
|
||||||
|
onCreate(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addTestData(SQLiteDatabase db) {
|
||||||
// A fake message containing 1 URL
|
// A fake message containing 1 URL
|
||||||
NdefMessage msg1 = new NdefMessage(new NdefRecord[] {
|
NdefMessage msg1 = new NdefMessage(new NdefRecord[] {
|
||||||
NdefUtil.toUriRecord(URI.create("http://www.google.com"))
|
NdefUtil.toUriRecord(Uri.parse("http://www.google.com"))
|
||||||
});
|
});
|
||||||
|
|
||||||
// A fake message containing 2 URLs
|
// A fake message containing 2 URLs
|
||||||
NdefMessage msg2 = new NdefMessage(new NdefRecord[] {
|
NdefMessage msg2 = new NdefMessage(new NdefRecord[] {
|
||||||
NdefUtil.toUriRecord(URI.create("http://www.youtube.com")),
|
NdefUtil.toUriRecord(Uri.parse("http://www.youtube.com")),
|
||||||
NdefUtil.toUriRecord(URI.create("http://www.android.com"))
|
NdefUtil.toUriRecord(Uri.parse("http://www.android.com"))
|
||||||
});
|
});
|
||||||
|
|
||||||
insert(db, msg1, false);
|
insertNdefMessage(db, msg1, false);
|
||||||
insert(db, msg2, true);
|
insertNdefMessage(db, msg2, true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// A real message obtained from an NFC Forum Type 4 tag.
|
// A real message obtained from an NFC Forum Type 4 tag.
|
||||||
NdefMessage msg3 = new NdefMessage(REAL_NFC_MSG);
|
NdefMessage msg3 = new NdefMessage(REAL_NFC_MSG);
|
||||||
insert(db, msg3, false);
|
insertNdefMessage(db, msg3, false);
|
||||||
} catch (FormatException e) {
|
} catch (FormatException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void insert(SQLiteDatabase db, NdefMessage msg, boolean isSaved) {
|
public void insertNdefMessage(SQLiteDatabase db, NdefMessage msg, boolean isSaved) {
|
||||||
SQLiteStatement stmt = db.compileStatement(INSERT);
|
SQLiteStatement stmt = null;
|
||||||
stmt.bindString(1, new String(msg.toByteArray())); // TODO: This should be a blob
|
try {
|
||||||
stmt.bindLong(2, System.currentTimeMillis());
|
stmt = db.compileStatement("INSERT INTO " + NdefMessagesTable.TABLE_NAME +
|
||||||
String isSavedStr = isSaved ? "1" : "0";
|
"(" + NdefMessagesTable.BYTES + ", " + NdefMessagesTable.DATE + ", " +
|
||||||
stmt.bindString(3, isSavedStr);
|
NdefMessagesTable.SAVED + ") values (?, ?, ?)");
|
||||||
stmt.executeInsert();
|
stmt.bindBlob(1, msg.toByteArray());
|
||||||
stmt.close();
|
stmt.bindLong(2, System.currentTimeMillis());
|
||||||
}
|
stmt.bindLong(3, isSaved ? 1 : 0);
|
||||||
|
stmt.executeInsert();
|
||||||
@Override
|
} finally {
|
||||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
if (stmt != null) stmt.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,42 +16,47 @@
|
|||||||
|
|
||||||
package com.android.apps.tag;
|
package com.android.apps.tag;
|
||||||
|
|
||||||
|
import com.android.apps.tag.TagDBHelper.NdefMessagesTable;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.app.ListActivity;
|
import android.app.ListActivity;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.nfc.FormatException;
|
||||||
|
import android.nfc.NdefMessage;
|
||||||
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
import android.widget.SimpleCursorAdapter;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@code Activity} that displays a flat list of tags that can be "opened".
|
* An {@link Activity} that displays a flat list of tags that can be "opened".
|
||||||
*/
|
*/
|
||||||
public class TagList extends ListActivity implements DialogInterface.OnClickListener {
|
public class TagList extends ListActivity implements DialogInterface.OnClickListener {
|
||||||
private SQLiteDatabase db;
|
static final String TAG = "TagList";
|
||||||
private Cursor cursor;
|
|
||||||
static final String SHOW_SAVED_ONLY = "show_saved_only";
|
static final String EXTRA_SHOW_SAVED_ONLY = "show_saved_only";
|
||||||
|
|
||||||
|
SQLiteDatabase mDatabase;
|
||||||
|
TagAdapter mAdapter;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
boolean showSavedOnly = getIntent().getBooleanExtra(SHOW_SAVED_ONLY, false);
|
boolean showSavedOnly = getIntent().getBooleanExtra(EXTRA_SHOW_SAVED_ONLY, false);
|
||||||
db = new TagDBHelper(getBaseContext()).getReadableDatabase();
|
mDatabase = TagDBHelper.getInstance(this).getReadableDatabase();
|
||||||
String selection = showSavedOnly ? "saved=1" : null;
|
String selection = showSavedOnly ? NdefMessagesTable.SAVED + "=1" : null;
|
||||||
|
|
||||||
// TODO: Use an AsyncQueryHandler so that DB queries are not done on UI thread.
|
new TagLoaderTask().execute(selection);
|
||||||
cursor = db.query(
|
mAdapter = new TagAdapter(this);
|
||||||
"NdefMessage",
|
setListAdapter(mAdapter);
|
||||||
new String[] { "_id", "bytes", "date" },
|
|
||||||
selection,
|
|
||||||
null, null, null, null);
|
|
||||||
|
|
||||||
setListAdapter(new TagCursorAdapter(this, cursor));
|
|
||||||
registerForContextMenu(getListView());
|
registerForContextMenu(getListView());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,22 +80,53 @@ public class TagList extends ListActivity implements DialogInterface.OnClickList
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
if (cursor != null) {
|
if (mAdapter != null) {
|
||||||
cursor.close();
|
mAdapter.changeCursor(null);
|
||||||
}
|
|
||||||
if (db != null) {
|
|
||||||
db.close();
|
|
||||||
}
|
}
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onListItemClick(ListView l, View v, int position, long id) {
|
protected void onListItemClick(ListView l, View v, int position, long id) {
|
||||||
showDialog(1);
|
Cursor cursor = mAdapter.getCursor();
|
||||||
super.onListItemClick(l, v, position, id);
|
cursor.moveToPosition(position);
|
||||||
|
byte[] tagBytes = cursor.getBlob(cursor.getColumnIndexOrThrow(NdefMessagesTable.BYTES));
|
||||||
|
try {
|
||||||
|
NdefMessage msg = new NdefMessage(tagBytes);
|
||||||
|
Intent intent = new Intent(this, TagViewer.class);
|
||||||
|
intent.putExtra(TagViewer.EXTRA_MESSAGE, msg);
|
||||||
|
intent.putExtra(TagViewer.EXTRA_TAG_DB_ID, id);
|
||||||
|
startActivity(intent);
|
||||||
|
} catch (FormatException e) {
|
||||||
|
Log.e(TAG, "bad format for tag " + id + ": " + tagBytes, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final class TagLoaderTask extends AsyncTask<String, Void, Cursor> {
|
||||||
|
@Override
|
||||||
|
public Cursor doInBackground(String... args) {
|
||||||
|
String selection = args[0];
|
||||||
|
Cursor cursor = mDatabase.query(
|
||||||
|
NdefMessagesTable.TABLE_NAME,
|
||||||
|
new String[] {
|
||||||
|
NdefMessagesTable._ID,
|
||||||
|
NdefMessagesTable.BYTES,
|
||||||
|
NdefMessagesTable.DATE,
|
||||||
|
NdefMessagesTable.TITLE },
|
||||||
|
selection,
|
||||||
|
null, null, null, null);
|
||||||
|
cursor.getCount();
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Cursor cursor) {
|
||||||
|
mAdapter.changeCursor(cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
137
apps/Tag/src/com/android/apps/tag/TagViewer.java
Normal file
137
apps/Tag/src/com/android/apps/tag/TagViewer.java
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.apps.tag;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.nfc.NdefMessage;
|
||||||
|
import android.nfc.NdefTag;
|
||||||
|
import android.nfc.NfcAdapter;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.ContextThemeWrapper;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link Activity} which handles a broadcast of a new tag that the device just discovered.
|
||||||
|
*/
|
||||||
|
public class TagViewer extends Activity {
|
||||||
|
static final String TAG = "SaveTag";
|
||||||
|
static final String EXTRA_TAG_DB_ID = "db_id";
|
||||||
|
static final String EXTRA_MESSAGE = "msg";
|
||||||
|
|
||||||
|
long mTagDatabaseId;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
|
||||||
|
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
|
||||||
|
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
|
||||||
|
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
|
||||||
|
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
|
||||||
|
| WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
|
||||||
|
| WindowManager.LayoutParams.FLAG_DIM_BEHIND
|
||||||
|
);
|
||||||
|
|
||||||
|
Intent intent = getIntent();
|
||||||
|
NdefMessage[] msgs = null;
|
||||||
|
NdefTag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
|
||||||
|
if (tag == null) {
|
||||||
|
// Maybe it came from the database?
|
||||||
|
mTagDatabaseId = intent.getLongExtra(EXTRA_TAG_DB_ID, -1);
|
||||||
|
NdefMessage msg = intent.getParcelableExtra(EXTRA_MESSAGE);
|
||||||
|
if (msg != null) {
|
||||||
|
msgs = new NdefMessage[] { msg };
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
msgs = tag.getNdefMessages();
|
||||||
|
// TODO use a service to avoid the process getting reaped during saving
|
||||||
|
new SaveTagTask().execute(msgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msgs == null || msgs.length == 0) {
|
||||||
|
Log.e(TAG, "No NDEF messages");
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LayoutInflater inflater = LayoutInflater.from(
|
||||||
|
new ContextThemeWrapper(this, android.R.style.Theme_Light));
|
||||||
|
LinearLayout list = (LinearLayout) inflater.inflate(R.layout.tag_viewer_list, null, false);
|
||||||
|
// TODO figure out why the background isn't white, the CTW should force that...
|
||||||
|
list.setBackgroundColor(Color.WHITE);
|
||||||
|
setContentView(list);
|
||||||
|
buildTagViews(list, inflater, msgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildTagViews(LinearLayout list, LayoutInflater inflater, NdefMessage[] msgs) {
|
||||||
|
// The body of the dialog should use the light theme
|
||||||
|
|
||||||
|
// Build the views from the logical records in the messages
|
||||||
|
boolean first = true;
|
||||||
|
for (NdefMessage msg : msgs) {
|
||||||
|
Iterable<Object> objects = NdefUtil.getObjects(msg);
|
||||||
|
for (Object object : objects) {
|
||||||
|
if (!first) {
|
||||||
|
list.addView(inflater.inflate(R.layout.tag_divider, list, false));
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (object instanceof String) {
|
||||||
|
TextView text = (TextView) inflater.inflate(R.layout.tag_text, list, false);
|
||||||
|
text.setText((CharSequence) object);
|
||||||
|
list.addView(text);
|
||||||
|
} else if (object instanceof Uri) {
|
||||||
|
TextView text = (TextView) inflater.inflate(R.layout.tag_text, list, false);
|
||||||
|
text.setText(object.toString());
|
||||||
|
list.addView(text);
|
||||||
|
} else if (object instanceof SmartPoster) {
|
||||||
|
TextView text = (TextView) inflater.inflate(R.layout.tag_text, list, false);
|
||||||
|
SmartPoster poster = (SmartPoster) object;
|
||||||
|
text.setText(poster.getTitle());
|
||||||
|
list.addView(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class SaveTagTask extends AsyncTask<NdefMessage, Void, Void> {
|
||||||
|
@Override
|
||||||
|
public Void doInBackground(NdefMessage... msgs) {
|
||||||
|
TagDBHelper helper = TagDBHelper.getInstance(TagViewer.this);
|
||||||
|
SQLiteDatabase db = helper.getWritableDatabase();
|
||||||
|
db.beginTransaction();
|
||||||
|
try {
|
||||||
|
for (NdefMessage msg : msgs) {
|
||||||
|
helper.insertNdefMessage(db, msg, false);
|
||||||
|
}
|
||||||
|
db.setTransactionSuccessful();
|
||||||
|
} finally {
|
||||||
|
db.endTransaction();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,6 +28,6 @@ public class SmartPosterTest extends AndroidTestCase {
|
|||||||
|
|
||||||
SmartPoster poster = SmartPoster.from(msg.getRecords()[0]);
|
SmartPoster poster = SmartPoster.from(msg.getRecords()[0]);
|
||||||
assertEquals("NFC Forum Type 4 Tag", poster.getTitle());
|
assertEquals("NFC Forum Type 4 Tag", poster.getTitle());
|
||||||
assertEquals("http://www.nxp.com/nfc", poster.getURI().toString());
|
assertEquals("http://www.nxp.com/nfc", poster.getUri().toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user