package com.kuxhausen.huemore.nfc; import android.app.PendingIntent; import android.content.Intent; import android.content.IntentFilter; import android.database.Cursor; import android.nfc.FormatException; import android.nfc.NdefMessage; import android.nfc.NdefRecord; import android.nfc.NfcAdapter; import android.nfc.Tag; import android.nfc.tech.Ndef; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.support.v4.widget.SimpleCursorAdapter; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; import com.kuxhausen.huemore.NavigationDrawerActivity; import com.kuxhausen.huemore.R; import com.kuxhausen.huemore.persistence.Definitions.GroupColumns; import com.kuxhausen.huemore.persistence.Definitions.MoodColumns; import com.kuxhausen.huemore.persistence.HueUrlEncoder; import com.kuxhausen.huemore.persistence.Utils; import com.kuxhausen.huemore.state.DatabaseGroup; import com.kuxhausen.huemore.state.Group; import com.kuxhausen.huemore.state.GroupMoodBrightness; import com.kuxhausen.huemore.state.Mood; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; public class NfcWriterFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor>, OnCheckedChangeListener, OnClickListener { private Button sendButton; private NfcAdapter nfcAdapter; PendingIntent pendingIntent; IntentFilter writeTagFilters[]; boolean writeMode; NavigationDrawerActivity context; // Identifies a particular Loader being used in this component private static final int GROUPS_LOADER = 0, MOODS_LOADER = 1; private static final String[] GROUP_SELECTION = {GroupColumns.COL_GROUP_NAME, GroupColumns._ID}; private static final String[] MOOD_SELECTION = {MoodColumns.COL_MOOD_NAME, MoodColumns._ID}; private SeekBar brightnessBar; private CheckBox brightnessCheckBox; private TextView brightnessDescripterTextView; private Spinner groupSpinner, moodSpinner; private SimpleCursorAdapter groupDataSource, moodDataSource; private GroupMoodBrightness priorGMB; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View myView = inflater.inflate(R.layout.nfc_writer, container, false); context = (NavigationDrawerActivity) this.getActivity(); // TODO deal with NFC not supported on this device /* * if (NfcAdapter.getDefaultAdapter(this) == null) { // hide nfc link if nfc not supported * MenuItem nfcItem = menu.findItem(R.id.action_nfc); if (nfcItem != null) { * nfcItem.setEnabled(false); nfcItem.setVisible(false); } */ // We need to use a different list item layout for devices older than Honeycomb int layout = android.R.layout.simple_list_item_activated_1; brightnessBar = (SeekBar) myView.findViewById(R.id.brightnessBar); brightnessBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { @Override public void onStopTrackingTouch(SeekBar seekBar) { preview(); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } }); brightnessDescripterTextView = (TextView) myView.findViewById(R.id.brightnessDescripterTextView); brightnessCheckBox = (CheckBox) myView.findViewById(R.id.includeBrightnessCheckBox); brightnessCheckBox.setOnCheckedChangeListener(this); groupSpinner = (Spinner) myView.findViewById(R.id.groupSpinner); groupDataSource = new SimpleCursorAdapter(context, layout, null, GROUP_SELECTION, new int[]{android.R.id.text1}, 0); groupDataSource.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); groupSpinner.setAdapter(groupDataSource); moodSpinner = (Spinner) myView.findViewById(R.id.moodSpinner); moodDataSource = new SimpleCursorAdapter(context, layout, null, MOOD_SELECTION, new int[]{android.R.id.text1}, 0); moodDataSource.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); moodSpinner.setAdapter(moodDataSource); nfcAdapter = NfcAdapter.getDefaultAdapter(context); sendButton = (Button) myView.findViewById(R.id.writeToTagButton); sendButton.setOnClickListener(this); pendingIntent = PendingIntent.getActivity(context, 0, new Intent(context, NavigationDrawerActivity.class) .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0 ); IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED); tagDetected.addCategory(Intent.CATEGORY_DEFAULT); writeTagFilters = new IntentFilter[]{tagDetected}; return myView; } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.action_write_nfc, menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle item selection switch (item.getItemId()) { case android.R.id.home: context.onBackPressed(); return true; case R.id.action_help: context.showHelp(this.getResources().getString(R.string.help_title_nfc)); return true; } return super.onOptionsItemSelected(item); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.writeToTagButton: preview(); try { if (context.myTag == null) { Toast.makeText(context, context.getString(R.string.nfc_tag_not_detected), Toast.LENGTH_SHORT).show(); } else { write(getSerializedByValue(), context.myTag); Toast.makeText(context, context.getString(R.string.nfc_tag_write_success), Toast.LENGTH_SHORT).show(); } } catch (IOException e) { Toast.makeText(context, context.getString(R.string.nfc_tag_write_fail), Toast.LENGTH_SHORT).show(); e.printStackTrace(); } catch (FormatException e) { Toast.makeText(context, context.getString(R.string.nfc_tag_write_fail), Toast.LENGTH_SHORT).show(); e.printStackTrace(); } break; } } private void write(String text, Tag tag) throws IOException, FormatException { try { NdefRecord[] records = {createRecord(text)}; NdefMessage message = new NdefMessage(records); // Get an instance of Ndef for the tag. Ndef ndef = Ndef.get(tag); // Enable I/O ndef.connect(); // Write the message ndef.writeNdefMessage(message); // Close the connection ndef.close(); } catch (java.lang.NullPointerException e) { Toast.makeText(context, context.getString(R.string.nfc_tag_not_supported), Toast.LENGTH_LONG) .show(); } } private NdefRecord createRecord(String text) throws UnsupportedEncodingException { byte[] uriField = text.getBytes(Charset.forName("US-ASCII")); byte[] payload = new byte[uriField.length + 1]; // add 1 for the URI // Prefix payload[0] = 0x01; // prefixes http://www. to the URI System.arraycopy(uriField, 0, payload, 1, uriField.length); // appends // URI to // payload NdefRecord rtdUriRecord = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI, new byte[0], payload); return rtdUriRecord; } @Override public void onPause() { super.onPause(); WriteModeOff(); } @Override public void onResume() { super.onResume(); WriteModeOn(); LoaderManager lm = context.getSupportLoaderManager(); lm.initLoader(GROUPS_LOADER, null, this); lm.initLoader(MOODS_LOADER, null, this); setHasOptionsMenu(true); } private void WriteModeOn() { writeMode = true; if (nfcAdapter != null) { nfcAdapter.enableForegroundDispatch(context, pendingIntent, writeTagFilters, null); } } private void WriteModeOff() { writeMode = false; if (nfcAdapter != null) { nfcAdapter.disableForegroundDispatch(context); } } public void preview() { String groupName = ((TextView) groupSpinner.getSelectedView()).getText().toString(); Group g = DatabaseGroup.load(groupName, context); String moodName = ((TextView) moodSpinner.getSelectedView()).getText().toString(); Mood m = Utils.getMoodFromDatabase(moodName, context); Integer brightness = null; if (brightnessBar.getVisibility() == View.VISIBLE) { brightness = brightnessBar.getProgress(); } context.getService().getMoodPlayer().playMood(g, m, moodName, brightness); } public String getSerializedByValue() { String url = "lampshade.io/nfc?"; Group g = DatabaseGroup.load(((TextView) groupSpinner.getSelectedView()).getText().toString(), context); Mood m = Utils.getMoodFromDatabase(((TextView) moodSpinner.getSelectedView()).getText().toString(), context); Integer brightness = null; if (brightnessBar.getVisibility() == View.VISIBLE) { brightness = brightnessBar.getProgress(); } String data = HueUrlEncoder.encode(m, g, brightness, context); return url + data; } @Override public Loader<Cursor> onCreateLoader(int loaderID, Bundle arg1) { switch (loaderID) { case GROUPS_LOADER: return new CursorLoader(context, GroupColumns.URI, GROUP_SELECTION, null, null, null); case MOODS_LOADER: return new CursorLoader(context, MoodColumns.MOODS_URI, MOOD_SELECTION, null, null, null); default: return null; } } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { switch (loader.getId()) { case GROUPS_LOADER: if (groupDataSource != null) { groupDataSource.changeCursor(cursor); } break; case MOODS_LOADER: if (moodDataSource != null) { moodDataSource.changeCursor(cursor); } break; } if (priorGMB != null) { // apply prior state int moodPos = 0; for (int i = 0; i < moodDataSource.getCount(); i++) { if (((Cursor) moodDataSource.getItem(i)).getString(0).equals(priorGMB.mood)) { moodPos = i; } } moodSpinner.setSelection(moodPos); int groupPos = 0; for (int i = 0; i < groupDataSource.getCount(); i++) { if (((Cursor) groupDataSource.getItem(i)).getString(0).equals(priorGMB.group)) { groupPos = i; } } groupSpinner.setSelection(groupPos); if (priorGMB.brightness != null) { brightnessBar.setProgress(priorGMB.brightness); } } } @Override public void onLoaderReset(Loader<Cursor> loader) { /* * Clears out the adapter's reference to the Cursor. This prevents memory leaks. */ // unregisterForContextMenu(getListView()); switch (loader.getId()) { case GROUPS_LOADER: if (groupDataSource != null) { groupDataSource.changeCursor(null); } break; case MOODS_LOADER: if (moodDataSource != null) { moodDataSource.changeCursor(null); } break; } } @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { brightnessBar.setVisibility(View.VISIBLE); brightnessDescripterTextView.setVisibility(View.VISIBLE); } else { brightnessBar.setVisibility(View.INVISIBLE); brightnessDescripterTextView.setVisibility(View.INVISIBLE); } } }