Commit a9c25cc5 authored by kirk's avatar kirk

prepare for versin 201; various code style and javadocs improvements

parent e6ba823d
......@@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="us.rader.wyfy"
android:versionCode="200"
android:versionName="2.0" >
android:versionCode="201"
android:versionName="2.1" >
<uses-sdk
android:minSdkVersion="10"
......
......@@ -43,8 +43,6 @@ public final class WifiSettingsFragment extends Fragment {
/**
* Interface implemented by any {@link Activity} to which this
* {@link WifiSettingsFragment} may be attached
*
* @author Kirk
*/
public interface OnWifiSettingsChangedListener {
......@@ -59,8 +57,6 @@ public final class WifiSettingsFragment extends Fragment {
/**
* {@link android.widget.CompoundButton.OnCheckedChangeListener} for changes
* to the state of the checkbox denoting a hidden SSID
*
* @author Kirk
*/
private class HiddenCheckedChangeListener implements
CompoundButton.OnCheckedChangeListener {
......@@ -76,6 +72,7 @@ public final class WifiSettingsFragment extends Fragment {
*
* @see android.widget.CompoundButton.OnCheckedChangeListener#onCheckedChanged(android.widget.CompoundButton,
* boolean)
* @see WifiSettingsFragment#onControlsChanged(boolean)
*/
@Override
public void onCheckedChanged(CompoundButton button, boolean checked) {
......@@ -85,7 +82,7 @@ public final class WifiSettingsFragment extends Fragment {
case R.id.hidden_checkbox:
wifiSettings.setHidden(checked);
onControlsChanged();
onControlsChanged(true);
break;
default:
......@@ -101,8 +98,6 @@ public final class WifiSettingsFragment extends Fragment {
/**
* Invoke {@link WifiSettingsDatabaseHelper#lookupPassword()} in a worker
* thread, update the UI in the main thread
*
* @author Kirk
*/
private class LookupPasswordTask extends AsyncTask<Void, Void, String> {
......@@ -157,8 +152,6 @@ public final class WifiSettingsFragment extends Fragment {
/**
* {@link TextWatcher} for edits to password control
*
* @author Kirk
*/
private final class PasswordTextWatcher extends WifiSettingsTextWatcher {
......@@ -169,12 +162,13 @@ public final class WifiSettingsFragment extends Fragment {
* ignored due to ill-thought out Android API
*
* @see android.text.TextWatcher#afterTextChanged(android.text.Editable)
* @see WifiSettingsFragment#onControlsChanged(boolean)
*/
@Override
public void afterTextChanged(Editable editable) {
wifiSettings.setPassword(editable.toString());
onControlsChanged();
onControlsChanged(true);
}
......@@ -183,8 +177,6 @@ public final class WifiSettingsFragment extends Fragment {
/**
* {@Link RadioGroup.OnCheckedChangeListener} used to track selected
* security protocol
*
* @author Kirk
*/
private class SecurityCheckedChangeListener implements
RadioGroup.OnCheckedChangeListener {
......@@ -202,6 +194,7 @@ public final class WifiSettingsFragment extends Fragment {
*
* @see android.widget.RadioGroup.OnCheckedChangeListener#onCheckedChanged(android
* .widget.RadioGroup, int)
* @see WifiSettingsFragment#onControlsChanged(boolean)
*/
@Override
public void onCheckedChanged(RadioGroup group, int id) {
......@@ -225,7 +218,7 @@ public final class WifiSettingsFragment extends Fragment {
}
onControlsChanged();
onControlsChanged(true);
}
......@@ -233,8 +226,6 @@ public final class WifiSettingsFragment extends Fragment {
/**
* {@link TextWatcher} for edits to SSID control
*
* @author Kirk
*/
private final class SsidTextWatcher extends WifiSettingsTextWatcher {
......@@ -245,12 +236,13 @@ public final class WifiSettingsFragment extends Fragment {
* ignored due to ill-thought out Android API
*
* @see android.text.TextWatcher#afterTextChanged(android.text.Editable)
* @see WifiSettingsFragment#onControlsChanged(boolean)
*/
@Override
public void afterTextChanged(Editable editable) {
wifiSettings.setSsid(editable.toString());
onControlsChanged();
onControlsChanged(false);
}
......@@ -259,8 +251,6 @@ public final class WifiSettingsFragment extends Fragment {
/**
* Invoke {@link WifiSettingsDatabaseHelper#storeWifiSettings()} in a worker
* thread
*
* @author Kirk
*/
private class StoreWifiSettingsTask extends AsyncTask<Void, Void, Void> {
......@@ -294,21 +284,14 @@ public final class WifiSettingsFragment extends Fragment {
* {@link TextWatcher} used to track changes to text controls in this
* fragment's layout
*
* @author Kirk
* This is just a convenience skeleton to provide implementations of
* required methods that aren't needed by this app.
*
* @see PasswordTextWatcher
* @see SsidTextWatcher
*/
private abstract class WifiSettingsTextWatcher implements TextWatcher {
/**
* Handle new value of {@link Editable} control
*
* @param editable
* new value of {@link Editable} control
*
* @see android.text.TextWatcher#afterTextChanged(android.text.Editable)
*/
@Override
public abstract void afterTextChanged(Editable editable);
/**
* Ignored
*
......@@ -328,8 +311,8 @@ public final class WifiSettingsFragment extends Fragment {
* int, int, int)
*/
@Override
public final void beforeTextChanged(CharSequence s, int start,
int count, int after) {
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// nothing to do here
......@@ -354,7 +337,7 @@ public final class WifiSettingsFragment extends Fragment {
* int, int, int)
*/
@Override
public final void onTextChanged(CharSequence s, int start, int before,
public void onTextChanged(CharSequence s, int start, int before,
int count) {
// nothing to do here
......@@ -599,20 +582,41 @@ public final class WifiSettingsFragment extends Fragment {
/**
* Notify {@link #listener} that the wi fi wifiSettings habe been changed by
* the user
*
* <p>
* Also update the database if and only if <code>updateDatabase</code> is
* <code>true</code>
* </p>
*
* <p>
* This is used as the common helper method by all of the UI widget event
* handlers
* </p>
*
* @param updateDatabase
* update the database, if <code>true</code>
*
* @see HiddenCheckedChangeListener#onCheckedChanged(CompoundButton,
* boolean)
* @see PasswordTextWatcher#afterTextChanged(Editable)
* @see SecurityCheckedChangeListener#onCheckedChanged(RadioGroup, int)
* @see SsidTextWatcher#afterTextChanged(Editable)
*/
private void onControlsChanged() {
private void onControlsChanged(boolean updateDatabase) {
try {
new StoreWifiSettingsTask().execute();
if (listener == null) {
if (updateDatabase) {
return;
new StoreWifiSettingsTask().execute();
}
listener.onWifiSettingsChanged();
if (listener != null) {
listener.onWifiSettingsChanged();
}
} catch (Exception e) {
......
......@@ -16,11 +16,10 @@
package us.rader.wyfy.db;
import us.rader.wyfy.model.WifiSettings;
import android.content.ContentValues;
import android.provider.BaseColumns;
/**
* SQLlite contract for persisting instances of {@link WifiSettings}
* SQLlite contract for persisting {@link WifiSettings}
*
* @author Kirk
*/
......@@ -28,8 +27,6 @@ public final class WiFiSettingsContract {
/**
* Table for storing instances of {@link WifiSettings}
*
* @author Kirk
*/
public static final class WifiSettingsEntry implements BaseColumns {
......@@ -59,52 +56,7 @@ public final class WiFiSettingsContract {
public static final String TABLE_NAME = "wifi_settings"; //$NON-NLS-1$
/**
* Get a {@link ContentValues} to use to persist the current state of
* the {@link WifiSettings} singleton
*
* @return {@link ContentValues}
*
* @see #updateWifiSettings(ContentValues)
*/
public static ContentValues getContentValues() {
WifiSettings settings = WifiSettings.getInstance();
ContentValues values = new ContentValues();
values.put(COLUMN_NAME_HIDDEN, settings.isHidden());
values.put(COLUMN_NAME_PASSWORD, settings.getPassword());
values.put(COLUMN_NAME_SECURITY, settings.getSecurity().toString());
values.put(COLUMN_NAME_SSID, settings.getSsid());
return values;
}
/**
* Update the state of the singleton {@link WifiSettings} from the given
* {@link ContentValues}
*
* @param values
* {@link ContentValues}
*
* @see #getContentValues()
*/
public static void updateWifiSettings(ContentValues values) {
String ssid = values.getAsString(COLUMN_NAME_SSID);
String password = values.getAsString(COLUMN_NAME_PASSWORD);
WifiSettings.Security security = Enum.valueOf(
WifiSettings.Security.class,
values.getAsString(COLUMN_NAME_SECURITY));
boolean hidden = values.getAsBoolean(COLUMN_NAME_HIDDEN);
WifiSettings settings = WifiSettings.getInstance();
settings.setSsid(ssid);
settings.setPassword(password);
settings.setSecurity(security);
settings.setHidden(hidden);
}
/**
* Prevent casual instantiation of contract member
* Prevent casual instantiation of contract member class
*/
private WifiSettingsEntry() {
......
......@@ -22,6 +22,7 @@ import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.provider.BaseColumns;
/**
* {@link SQLiteOpenHelper} for the WyFy datbase
......@@ -51,7 +52,7 @@ public final class WifiSettingsDatabaseHelper extends SQLiteOpenHelper {
private static final String SQL_CREATE_ENTRIES = "CREATE TABLE " //$NON-NLS-1$
+ WiFiSettingsContract.WifiSettingsEntry.TABLE_NAME
+ " (" //$NON-NLS-1$
+ WiFiSettingsContract.WifiSettingsEntry._ID
+ BaseColumns._ID
+ " INTEGER PRIMARY KEY," //$NON-NLS-1$
+ WiFiSettingsContract.WifiSettingsEntry.COLUMN_NAME_SSID
+ " TEXT," //$NON-NLS-1$
......@@ -241,7 +242,11 @@ public final class WifiSettingsDatabaseHelper extends SQLiteOpenHelper {
public Cursor query(SQLiteDatabase db, String selection,
String... selectionArgs) {
String[] columns = createColumnsForSelection();
String[] columns = new String[] { BaseColumns._ID,
WiFiSettingsContract.WifiSettingsEntry.COLUMN_NAME_SSID,
WiFiSettingsContract.WifiSettingsEntry.COLUMN_NAME_PASSWORD,
WiFiSettingsContract.WifiSettingsEntry.COLUMN_NAME_SECURITY,
WiFiSettingsContract.WifiSettingsEntry.COLUMN_NAME_HIDDEN };
Cursor cursor = db.query(
WiFiSettingsContract.WifiSettingsEntry.TABLE_NAME, columns,
selection, selectionArgs, null, null, null);
......@@ -290,22 +295,6 @@ public final class WifiSettingsDatabaseHelper extends SQLiteOpenHelper {
}
}
/**
* Return a string array for use with the SQLite API representing all of the
* columns of the {@link WiFiSettingsContract.WifiSettingsEntry} table
*
* @return column name array
*/
private String[] createColumnsForSelection() {
return new String[] { WiFiSettingsContract.WifiSettingsEntry._ID,
WiFiSettingsContract.WifiSettingsEntry.COLUMN_NAME_SSID,
WiFiSettingsContract.WifiSettingsEntry.COLUMN_NAME_PASSWORD,
WiFiSettingsContract.WifiSettingsEntry.COLUMN_NAME_SECURITY,
WiFiSettingsContract.WifiSettingsEntry.COLUMN_NAME_HIDDEN };
}
/**
* Insert a new row for the current state of the {@link WifiSettings}
* singleton
......@@ -315,10 +304,9 @@ public final class WifiSettingsDatabaseHelper extends SQLiteOpenHelper {
*/
private void insert(SQLiteDatabase db) {
ContentValues values = WiFiSettingsContract.WifiSettingsEntry
.getContentValues();
db.insert(WiFiSettingsContract.WifiSettingsEntry.TABLE_NAME,
"null", values); //$NON-NLS-1$
ContentValues values = WifiSettings.getInstance().getContentValues();
db.insert(WiFiSettingsContract.WifiSettingsEntry.TABLE_NAME, null,
values);
}
......@@ -331,8 +319,7 @@ public final class WifiSettingsDatabaseHelper extends SQLiteOpenHelper {
*/
private void update(SQLiteDatabase db) {
ContentValues values = WiFiSettingsContract.WifiSettingsEntry
.getContentValues();
ContentValues values = WifiSettings.getInstance().getContentValues();
String whereClause = WiFiSettingsContract.WifiSettingsEntry.COLUMN_NAME_SSID
+ " LIKE ?"; //$NON-NLS-1$
String[] whereArgs = { values
......
......@@ -29,14 +29,19 @@ import android.util.Log;
import android.widget.Toast;
/**
* Abstract base class of activities that use the {@link NfcAdapter} foreground
* dispatch mechanism to read or write NFC tags
* Abstract ancestor for any class of {@link FragmentActivity} that uses the
* {@link NfcAdapter} foreground dispatch mechanism to read or write NFC tags
*
* Note that <code>ResultType</code> is constrained to extend {@link Parcelable}
* so that instances can be passed between activities via {@link Intent} extras
*
* @param <ResultType>
* The {@link Parcelable} type returned by
* {@link #processTag(Tag, ProcessTagTask)} and expected by
* {@link #onTagProcessed(Parcelable, boolean)}
*
* @see #onTagProcessed(Parcelable, boolean)
*
* @author Kirk
*/
public abstract class ForegroundDispatchActivity<ResultType extends Parcelable>
......@@ -47,8 +52,6 @@ public abstract class ForegroundDispatchActivity<ResultType extends Parcelable>
* on a worker thread,
* {@link ForegroundDispatchActivity#onTagProcessed(Parcelable, boolean)} on
* the UI thread
*
* @author Kirk
*/
protected final class ProcessTagTask extends
AsyncTask<Tag, String, ResultType> {
......@@ -69,6 +72,10 @@ public abstract class ForegroundDispatchActivity<ResultType extends Parcelable>
* Invoke
* {@link ForegroundDispatchActivity#processTag(Tag, ProcessTagTask)}
*
* If {@link ForegroundDispatchActivity#processTag(Tag, ProcessTagTask)}
* returns <code>null</code>, this will call {@link #cancel(boolean)}
* before returning.
*
* @param tags
* <code>tags[0]</code> is the {@link Tag} to process
*
......@@ -77,13 +84,26 @@ public abstract class ForegroundDispatchActivity<ResultType extends Parcelable>
* or <code><code>null</code> if an error occurs
*
* @see android.os.AsyncTask#doInBackground(Tag...)
* @see #onPostExecute(Parcelable)
* @see #onCancelled(Parcelable)
* @see ForegroundDispatchActivity#processTag(Tag, ProcessTagTask)
*/
@Override
protected ResultType doInBackground(Tag... tags) {
try {
return processTag(tags[0], this);
ResultType result = processTag(tags[0], this);
// interpret a null result as equivalent to processTag() having
// called cancel()
if (result == null) {
cancel(false);
}
return result;
} catch (Exception e) {
......@@ -94,24 +114,49 @@ public abstract class ForegroundDispatchActivity<ResultType extends Parcelable>
}
/**
* Pass <code>null</code> and <code>true</code> to
* {@link ForegroundDispatchActivity#onTagProcessed(Parcelable, boolean)}
* Pass <code>null</code> to {@link #onCancelled(Parcelable)}
*
* This override is provided on behalf of devices running versions of
* Android based on SDK 10 or earlier
*
* @see android.os.AsyncTask#onCancelled()
* @see #onCancelled(Parcelable)
*/
@Override
protected void onCancelled() {
onCancelled(null);
}
/**
* Pass <code>result</code> and <code>true</code> to
* {@link ForegroundDispatchActivity#onTagProcessed(Parcelable, boolean)}
*
* Note that the overload of <code>onCancelled</code> that takes a
* parameter was added in SDK 11, while this code strives to be
* backwards-compatible to SDK 10, hence the override of
* {@link #onCancelled()} that calls this one
*
* @param result
* the value to pass to
* {@link ForegroundDispatchActivity#onTagProcessed(Parcelable, boolean)}
*
* @see android.os.AsyncTask#onCancelled(java.lang.Object)
* @see #onCancelled()
*/
@Override
protected void onCancelled(ResultType result) {
try {
onTagProcessed(null, true);
onTagProcessed(result, true);
} catch (Exception e) {
Log.e(getClass().getName(), "onCancelled", e); //$NON-NLS-1$
}
}
/**
......@@ -149,23 +194,8 @@ public abstract class ForegroundDispatchActivity<ResultType extends Parcelable>
@Override
protected void onProgressUpdate(String... strings) {
if (strings.length > 0) {
final String message = strings[0];
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(ForegroundDispatchActivity.this,
message, Toast.LENGTH_SHORT).show();
}
});
}
Toast.makeText(ForegroundDispatchActivity.this, strings[0],
Toast.LENGTH_SHORT).show();
}
......@@ -176,7 +206,13 @@ public abstract class ForegroundDispatchActivity<ResultType extends Parcelable>
* {@link #onTagProcessed(Parcelable, boolean)} to the {@link Activity} that
* launched this one
*/
public static final String EXTRA_RESULT = "us.rader.wyfy.nfc.result"; //$NON-NLS-1$
public static final String EXTRA_RESULT = "us.rader.wyfy.nfc.result"; //$NON-NLS-1$
/**
* Result code passed to {@link #setResult(int)} or
* {@link #setResult(int, Intent)} to indicate an error
*/
public static final int RESULT_ERROR_PROCESSING_TAG = RESULT_FIRST_USER;
/**
* Cached {@link NfcAdapter}
......@@ -235,13 +271,10 @@ public abstract class ForegroundDispatchActivity<ResultType extends Parcelable>
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
adapter = NfcAdapter.getDefaultAdapter(this);
Intent intent = new Intent(this, getClass());
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
pendingIntent = PendingIntent.getActivity(this, requestCode, intent, 0);
IntentFilter ndefFilter = new IntentFilter(
NfcAdapter.ACTION_NDEF_DISCOVERED);
IntentFilter tagFilter = new IntentFilter(
......@@ -312,9 +345,47 @@ public abstract class ForegroundDispatchActivity<ResultType extends Parcelable>
*
* <p>
* Override this method if you wish to handle the result of processing a tag
* without terminating the current activity
* without terminating the current activity. If you override this method,
* you can interpret the parameters as follows:
* </p>
*
* <table>
*
* <tr>
* <th><code>result</code></th>
* <th><code>cancelled</code></th>
* <th>Outcome</th>
* </tr>
*
* <tr>
* <td><code>null</code></td>
* <td><code>true</code></td>
* <td>{@link #processTag(Tag, ProcessTagTask)} returned <code>null</code>,
* with or without calling {@link AsyncTask#cancel(boolean)}</td>
* </tr>
*
* <tr>
* <td><code>null</code></td>
* <td><code>false</code></td>
* <td>{@link #processTag(Tag, ProcessTagTask)} threw an exception</td>
* </tr>
*
* <tr>
* <td>non-<code>null</code></td>
* <td><code>true</code></td>
* <td>{@link #processTag(Tag, ProcessTagTask)} called
* {@link AsyncTask#cancel(boolean)} but also returned non-
* <code>null</code></td>
* </tr>
*
* <tr>
* <td>non-<code>null</code></td>
* <td><code>false</code></td>
* <td>successful return from {@link #processTag(Tag, ProcessTagTask)}</td>
* </tr>
*
* </table>
*
* @param result
* the value returned from
* {@link #processTag(Tag, ProcessTagTask)} or <code>null</code>
......@@ -322,16 +393,26 @@ public abstract class ForegroundDispatchActivity<ResultType extends Parcelable>
* @param cancelled
* <code>true</code> if and only if the {@link ProcessTagTask}
* running {@link #processTag(Tag, ProcessTagTask)} was cancelled
*
* @see #processTag(Tag, ProcessTagTask)
*/
protected void onTagProcessed(ResultType result, boolean cancelled) {
int resultCode = (cancelled ? RESULT_CANCELED : RESULT_OK);
if (result == null) {
setResult(RESULT_CANCELED);
if (resultCode != RESULT_CANCELED) {
Log.e(getClass().getName(), "result is null but not cancelled"); //$NON-NLS-1$
resultCode = RESULT_ERROR_PROCESSING_TAG;
}
setResult(resultCode);
} else {
int resultCode = (cancelled ? RESULT_CANCELED : RESULT_OK);
Intent intent = new Intent();
intent.putExtra(EXTRA_RESULT, result);
setResult(resultCode, intent);
......
......@@ -45,6 +45,11 @@ public abstract class NdefReaderActivity extends
*
* @return the decoded payload or <code>null</code> if <code>record</code>
* isn't supported by this method
*
* @see #decodeMime(String, byte[])
* @see #decodeText(byte[])
* @see #decodeUri(byte[])
* @see #decodeWellKnown(String, byte[])
*/
public static String decodePayload(NdefRecord record) {
......@@ -60,6 +65,8 @@ public abstract class NdefReaderActivity extends
case NdefRecord.TNF_ABSOLUTE_URI:
// oddly, the URI in such records is in the type field
// rather than the payload for this kind of record
return new String(record.getType(), "US-ASCII"); //$NON-NLS-1$
case NdefRecord.TNF_MIME_MEDIA:
......@@ -88,11 +95,12 @@ public abstract class NdefReaderActivity extends
/**
* Return <code>payload</code> decoded as a UTF-8 string if
* <code>type</code> starts with "text/"
* <code>type</code> starts with "text/" or is one of a small number of
* other known MIME types
*
* <p>
* Return <code>null</code> for any MIME type that doesn't start with
* "text/"
* Return <code>null</code> for any MIME type that isn't supported by this
* method
* </p>
*
* @param type
......@@ -106,20 +114,20 @@ public abstract class NdefReaderActivity extends
* @throws UnsupportedEncodingException
* if there is a bug in the Java virtual machine
*/
private static String decodeMime(String type, byte[] payload)
static private String decodeMime(String type, byte[] payload)
throws UnsupportedEncodingException {
if (type.equalsIgnoreCase("application/xml")) { //$NON-NLS-1$
if (type.equalsIgnoreCase(MIME_XML)) {
return new String(payload, "UTF-8"); //$NON-NLS-1$
}
if (type.equalsIgnoreCase("application/json")) { //$NON-NLS-1$
if (type.equalsIgnoreCase(MIME_JSON)) {
return new String(payload, "UTF-8"); //$NON-NLS-1$
}
if (type.toLowerCase(Locale.US).startsWith("text/")) { //$NON-NLS-1$
if (type.toLowerCase(Locale.US).startsWith(MIME_TEXT_PREFIX)) {
return new String(payload, "UTF-8"); //$NON-NLS-1$
......@@ -133,8 +141,7 @@ public abstract class NdefReaderActivity extends
* Decode the <code>payload</code> of a "T" record
*
* <p>
* This parses and discards any language-code prefix in the
* <code>payload</code>
* This discards any language-code prefix in the <code>payload</code>
* </p>
*
* @param payload
......@@ -216,12 +223,12 @@ public abstract class NdefReaderActivity extends
private static String decodeWellKnown(String type, byte[] payload)
throws UnsupportedEncodingException {
if (TYPE_TEXT.equals(type)) {
if (RECORD_TYPE_TEXT.equals(type)) {
return decodeText(payload);
}
if (TYPE_URI.equals(type)) {
if (RECORD_TYPE_URI.equals(type)) {
return decodeUri(payload);
......
......@@ -27,6 +27,8 @@ import android.nfc.tech.NdefFormatable;