/* * Copyright (C) 2016 Glucosio Foundation * * This file is part of Glucosio. * * Glucosio is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * Glucosio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Glucosio. If not, see <http://www.gnu.org/licenses/>. * * */ package org.glucosio.android.activity; import android.app.Activity; import android.app.PendingIntent; import android.content.Intent; import android.content.IntentFilter; import android.nfc.NfcAdapter; import android.nfc.Tag; import android.nfc.tech.NfcV; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import org.glucosio.android.R; import org.glucosio.android.db.DatabaseHandler; import org.glucosio.android.object.PredictionData; import org.glucosio.android.object.ReadingData; import org.glucosio.android.tools.AlgorithmUtil; import org.glucosio.android.tools.AnimationTools; import java.io.IOException; import java.util.Arrays; public class FreestyleLibreActivity extends Activity { private static final String TAG = "FreestyleLibreActivity"; private NfcAdapter mNfcAdapter; private TextView readingTextView; /** * @param activity The corresponding {@link Activity} requesting the foreground dispatch. * @param adapter The {@link NfcAdapter} used for the foreground dispatch. */ public static void setupForegroundDispatch(final Activity activity, NfcAdapter adapter) { final Intent intent = new Intent(activity.getApplicationContext(), activity.getClass()); intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); final PendingIntent pendingIntent = PendingIntent.getActivity(activity.getApplicationContext(), 0, intent, 0); IntentFilter[] filters = new IntentFilter[1]; String[][] techList = new String[][]{}; // Notice that this is the same filter as in our manifest. filters[0] = new IntentFilter(); filters[0].addAction(NfcAdapter.ACTION_NDEF_DISCOVERED); filters[0].addCategory(Intent.CATEGORY_DEFAULT); adapter.enableForegroundDispatch(activity, pendingIntent, filters, techList); } /** * @param activity The corresponding {@link Activity} requesting to stop the foreground dispatch. * @param adapter The {@link NfcAdapter} used for the foreground dispatch. */ public static void stopForegroundDispatch(final Activity activity, NfcAdapter adapter) { adapter.disableForegroundDispatch(activity); } private ReadingData mResult = new ReadingData(PredictionData.Result.ERROR_NO_NFC); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_freestyle_libre); mNfcAdapter = NfcAdapter.getDefaultAdapter(this); readingTextView = (TextView) findViewById(R.id.activity_freestyle_textview_reading); Button saveButton = (Button) findViewById(R.id.activity_freestyle_button_save); saveButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { openAddGlucoseActivity(); } }); if (mNfcAdapter == null) { // Stop here, we definitely need NFC Toast.makeText(this, R.string.freestylelibre_nfc_not_supported, Toast.LENGTH_LONG).show(); finish(); return; } if (!mNfcAdapter.isEnabled()) { Toast.makeText(this, R.string.freestylelibre_nfc_not_enabled, Toast.LENGTH_LONG).show(); } handleIntent(getIntent()); } @Override protected void onResume() { super.onResume(); /** * It's important, that the activity is in the foreground (resumed). Otherwise * an IllegalStateException is thrown. */ setupForegroundDispatch(this, mNfcAdapter); } @Override protected void onPause() { /** * Call this before onPause, otherwise an IllegalArgumentException is thrown as well. */ stopForegroundDispatch(this, mNfcAdapter); super.onPause(); } @Override protected void onNewIntent(Intent intent) { /** * This method gets called, when a new Intent gets associated with the current activity instance. * Instead of creating a new activity, onNewIntent will be called. For more information have a look * at the documentation. * * In our case this method gets called, when the user attaches a Tag to the device. */ handleIntent(intent); } private void handleIntent(Intent intent) { String action = intent.getAction(); if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) { Log.d("glucosio", "NfcAdapter.ACTION_TECH_DISCOVERED"); // In case we would still use the Tech Discovered Intent Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); new NfcVReaderTask().execute(tag); } } private void showReadingLayout() { /* SimpleDatabase database = new SimpleDatabase(this); long id = database.saveMessage(mResult); ReadingData.TransferObject transferObject = new ReadingData.TransferObject(id, mResult); database.close(); WearableApi.sendMessage(mGoogleApiClient, WearableApi.GLUCOSE, new Gson().toJson(transferObject), mMessageListener); mMessagesBeingSent++; mFinishAfterSentMessages = true;*/ // Apply values in TextViews // TODO: Add check for mmol/L readingTextView.setText(mResult.trend.get(0).glucose(false)); new Runnable() { @Override public void run() { View view = findViewById(R.id.activity_freestyle_reading); view.setVisibility(View.INVISIBLE); AnimationTools.startCircularReveal(view); } }.run(); // Reveal ReadingLayout } private void openAddGlucoseActivity() { DatabaseHandler dB = new DatabaseHandler(getApplicationContext()); if (dB.getUser(1) != null) { // Start AddGlucose Activity passing the reading value Intent intent = new Intent(getApplicationContext(), AddGlucoseActivity.class); Bundle bundle = new Bundle(); String currentGlucose = mResult.trend.get(0).glucose(false); bundle.putString("reading", currentGlucose + ""); intent.putExtras(bundle); startActivity(intent); FreestyleLibreActivity.this.finish(); } else { Intent intent = new Intent(getApplicationContext(), HelloActivity.class); startActivity(intent); } } private class NfcVReaderTask extends AsyncTask<Tag, Void, Tag> { private byte[] data = new byte[360]; @Override protected void onPostExecute(Tag tag) { if (tag == null) return; String tagId = bytesToHexString(tag.getId()); int attempt = 1; mResult = AlgorithmUtil.parseData(attempt, tagId, data); showReadingLayout(); } @Override protected Tag doInBackground(Tag... params) { Tag tag = params[0]; NfcV nfcvTag = NfcV.get(tag); try { nfcvTag.connect(); final byte[] uid = tag.getId(); for (int i = 0; i <= 40; i++) { byte[] cmd = new byte[]{0x60, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, (byte) i, 0}; System.arraycopy(uid, 0, cmd, 2, 8); byte[] oneBlock; Long time = System.currentTimeMillis(); while (true) { try { oneBlock = nfcvTag.transceive(cmd); break; } catch (IOException e) { if ((System.currentTimeMillis() > time + 2000)) { Log.e(TAG, "tag read timeout"); return null; } } } oneBlock = Arrays.copyOfRange(oneBlock, 2, oneBlock.length); System.arraycopy(oneBlock, 0, data, i * 8, 8); } } catch (Exception e) { Log.i(TAG, e.toString()); return null; } finally { try { nfcvTag.close(); } catch (Exception e) { Log.e(TAG, "Error closing tag!"); } } return tag; } } private String bytesToHexString(byte[] src) { StringBuilder builder = new StringBuilder(""); if (src == null || src.length <= 0) { return ""; } char[] buffer = new char[2]; for (byte b : src) { buffer[0] = Character.forDigit((b >>> 4) & 0x0F, 16); buffer[1] = Character.forDigit(b & 0x0F, 16); builder.append(buffer); } return builder.toString(); } }