diff options
author | Trygve Laugstøl <trygvis@inamo.no> | 2014-12-31 16:31:49 +0100 |
---|---|---|
committer | Trygve Laugstøl <trygvis@inamo.no> | 2014-12-31 16:31:49 +0100 |
commit | ed559834ccddafa955df5b528f08fba964e57699 (patch) | |
tree | 3fcbcedaf49232de5c8fe8b6f67bd2fd8c52afa9 /app/src/main/java/io/trygvis/soilmoisture | |
download | io.trygvis.soilmoisture-android-ed559834ccddafa955df5b528f08fba964e57699.tar.gz io.trygvis.soilmoisture-android-ed559834ccddafa955df5b528f08fba964e57699.tar.bz2 io.trygvis.soilmoisture-android-ed559834ccddafa955df5b528f08fba964e57699.tar.xz io.trygvis.soilmoisture-android-ed559834ccddafa955df5b528f08fba964e57699.zip |
o Initial import of Soil Moisture app.
Diffstat (limited to 'app/src/main/java/io/trygvis/soilmoisture')
5 files changed, 406 insertions, 0 deletions
diff --git a/app/src/main/java/io/trygvis/soilmoisture/Constants.java b/app/src/main/java/io/trygvis/soilmoisture/Constants.java new file mode 100644 index 0000000..0b891b7 --- /dev/null +++ b/app/src/main/java/io/trygvis/soilmoisture/Constants.java @@ -0,0 +1,13 @@ +package io.trygvis.soilmoisture; + +import java.util.UUID; + +public interface Constants { + String TRYGVIS_IO_BASE_UUID = "32D0xxxx-035D-59C5-70D3-BC8E4A1FD83F"; + UUID TRYGVIS_IO_FIKEN_STATUS_PANEL_UUID = UUID.fromString(TRYGVIS_IO_BASE_UUID.replace("xxxx", "0001")); + UUID TRYGVIS_IO_GAUGE_DATA_UUID = UUID.fromString(TRYGVIS_IO_BASE_UUID.replace("xxxx", "0002")); + UUID TRYGVIS_IO_GAUGE_CTRL_UUID = UUID.fromString(TRYGVIS_IO_BASE_UUID.replace("xxxx", "0004")); + UUID TRYGVIS_IO_LED_UUID = UUID.fromString(TRYGVIS_IO_BASE_UUID.replace("xxxx", "0003")); + + UUID CLIENT_CHARACTERISTIC_CONFIG = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); +} diff --git a/app/src/main/java/io/trygvis/soilmoisture/ExceptionHandler.java b/app/src/main/java/io/trygvis/soilmoisture/ExceptionHandler.java new file mode 100644 index 0000000..faa95e1 --- /dev/null +++ b/app/src/main/java/io/trygvis/soilmoisture/ExceptionHandler.java @@ -0,0 +1,22 @@ +package io.trygvis.soilmoisture; + +import android.util.Log; + +public class ExceptionHandler implements Thread.UncaughtExceptionHandler { + private final static String TAG = ExceptionHandler.class.getSimpleName(); + + public static final ExceptionHandler EXCEPTION_HANDLER = new ExceptionHandler(); + + @Override + public void uncaughtException(Thread thread, Throwable ex) { + Log.e(TAG, "Uncaught", ex); + + if (ex instanceof RuntimeException) { + throw (RuntimeException) ex; + } + if (ex instanceof Error) { + throw (Error) ex; + } + throw new RuntimeException(ex); + } +} diff --git a/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java b/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java new file mode 100644 index 0000000..18c96b8 --- /dev/null +++ b/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java @@ -0,0 +1,307 @@ +package io.trygvis.soilmoisture; + +import android.app.ActionBar; +import android.app.ListActivity; +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattService; +import android.content.ComponentName; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.Button; +import android.widget.ListView; +import android.widget.ProgressBar; +import android.widget.TextView; + +import com.crashlytics.android.Crashlytics; + +import io.fabric.sdk.android.Fabric; +import io.trygvis.android.bt.BtActionExecutor; +import io.trygvis.android.bt.BtActivitySupport; +import io.trygvis.android.bt.BtDevice; +import io.trygvis.android.bt.BtDeviceListener; +import io.trygvis.android.bt.BtService; +import io.trygvis.android.bt.DefaultBtService; +import io.trygvis.bluetooth.TrygvisIoUuids; + +import static io.trygvis.android.bt.BtService.BtServiceListener; +import static io.trygvis.soilmoisture.ExceptionHandler.EXCEPTION_HANDLER; +import static java.lang.String.valueOf; + +public class MainActivity extends ListActivity { + private final static String TAG = MainActivity.class.getSimpleName(); + + private static final long SCAN_PERIOD = 3 * 1000; + + private static final int REQUEST_ENABLE_BT = 1; + + private final BtActivitySupport btActivitySupport = new BtActivitySupport(this, REQUEST_ENABLE_BT); + + private DeviceListAdapter deviceList; + private BtService<SmDevice> btService; + + private ServiceConnection serviceConnection; + + @Override + protected void onCreate(Bundle savedInstanceState) { + Log.i(TAG, "onCreate"); + Thread.setDefaultUncaughtExceptionHandler(EXCEPTION_HANDLER); + super.onCreate(savedInstanceState); + Fabric.with(this, new Crashlytics()); + + ActionBar actionBar = getActionBar(); + if (actionBar != null) { + actionBar.setTitle(R.string.title_devices); + } + + if (!btActivitySupport.onCreate()) { + finish(); + return; + } + + serviceConnection = new ServiceConnection() { + @SuppressWarnings("unchecked") + @Override + public void onServiceConnected(ComponentName componentName, IBinder service) { + btService = ((BtService.LocalBinder<SmDevice>) service).getService(); + if (!btService.initialize(serviceListener, SmDevice::new)) { + finish(); + } + + deviceList = new DeviceListAdapter(); + deviceList.notifyDataSetChanged(); + setListAdapter(deviceList); + + startScan(); + } + + @Override + public void onServiceDisconnected(ComponentName componentName) { + btService = null; + stopScan(); + } + }; + + bindService(new Intent(this, DefaultBtService.class), serviceConnection, BIND_AUTO_CREATE); + } + + @Override + protected void onDestroy() { + Log.i(TAG, "onDestroy"); + super.onDestroy(); + + if (serviceConnection != null) { + unbindService(serviceConnection); + } + } + + @Override + protected void onResume() { + Log.i(TAG, "onResume"); + + super.onResume(); + + if (!btActivitySupport.enableBt()) { + finish(); + return; + } + + // registerReceiver(btServiceBroadcastReceiver, IntentAction.ALL_FILTER); + } + + @Override + protected void onPause() { + Log.i(TAG, "onPause"); + + super.onPause(); + stopScan(); + // unregisterReceiver(ntServiceBroadcastReceiver); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + Log.i(TAG, "onActivityResult"); + + if (!btActivitySupport.onActivityResult(requestCode, resultCode, data)) { + finish(); + } + + super.onActivityResult(requestCode, resultCode, data); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + Log.i(TAG, "onCreateOptionsMenu"); + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.main, menu); + + if (!btService.isScanning()) { + menu.findItem(R.id.menu_stop).setVisible(false); + menu.findItem(R.id.menu_scan).setVisible(true); + menu.findItem(R.id.menu_refresh).setActionView(null); + } else { + menu.findItem(R.id.menu_stop).setVisible(true); + menu.findItem(R.id.menu_scan).setVisible(false); + menu.findItem(R.id.menu_refresh).setActionView(R.layout.actionbar_indeterminate_progress); + } + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + Log.i(TAG, "onOptionsItemSelected"); + + switch (item.getItemId()) { + case R.id.menu_scan: + startScan(); + break; + case R.id.menu_stop: + stopScan(); + break; + } + return super.onOptionsItemSelected(item); + } + + private void startScan() { + btService.startScanning(SCAN_PERIOD); + } + + private void stopScan() { + if (btService != null) { + btService.stopScanning(); + } + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) { + stopScan(); + + BtDevice<SmDevice> state = btService.getDevices().get(position); + + BtActionExecutor executor = new BtActionExecutor(). + onConnectionStateChange((gatt, newState) -> { + if (newState == BluetoothGatt.STATE_CONNECTED) { + Intent intent = new Intent(this, SoilActivity.class); + startActivity(intent); + return true; + } + return false; + }). + onServicesDiscovered(gatt -> false); + + state.connect(executor); + } + + BtServiceListener<SmDevice> serviceListener = new BtServiceListener<SmDevice>() { + @Override + public void onScanStarted() { + invalidateOptionsMenu(); + } + + @Override + public void onNewDevice(BtDevice<SmDevice> device) { + device.addListener(deviceListener); + + BtActionExecutor executor = new BtActionExecutor(). + onConnectionStateChange((gatt, newState) -> gatt.discoverServices()). + onServicesDiscovered(gatt -> { + BluetoothGattService service = gatt.getService(TrygvisIoUuids.Services.SOIL_MOISTURE_SERVICE); + + boolean useful = service != null; + device.getTag().setIsUseful(useful); + runOnUiThread(deviceList::notifyDataSetChanged); + return useful; + }); + device.connect(executor); + } + + @Override + public void onScanStopped() { + invalidateOptionsMenu(); + } + }; + + BtDeviceListener deviceListener = new BtDeviceListener() { + }; + + // ----------------------------------------------------------------------- + // + // ----------------------------------------------------------------------- + + static class DeviceListItem { + final TextView deviceName; + final TextView deviceAddress; + final TextView rssi; + final ProgressBar spinner; + final Button connect; + + DeviceListItem(View view) { + this.deviceName = (TextView) view.findViewById(R.id.device_name); + this.deviceAddress = (TextView) view.findViewById(R.id.device_address); + this.rssi = (TextView) view.findViewById(R.id.device_rssi); + this.spinner = (ProgressBar) view.findViewById(R.id.device_spinner); + this.connect = (Button) view.findViewById(R.id.button_connect); + } + } + + private class DeviceListAdapter extends BaseAdapter { + private LayoutInflater inflater = MainActivity.this.getLayoutInflater(); + + @Override + public int getCount() { + return btService.getDevices().size(); + } + + @Override + public Object getItem(int i) { + return btService.getDevices().get(i); + } + + @Override + public long getItemId(int i) { + return i; + } + + @Override + public View getView(int i, View view, ViewGroup viewGroup) { + DeviceListItem item; + + if (view == null) { + view = inflater.inflate(R.layout.listitem_device, null); + item = new DeviceListItem(view); + view.setTag(item); + view.setClickable(false); + } else { + item = (DeviceListItem) view.getTag(); + } + + BtDevice<SmDevice> btDevice = btService.getDevices().get(i); + if (btDevice.getName() != null && btDevice.getName().length() > 0) { + item.deviceName.setText(btDevice.getName()); + } else { + item.deviceName.setText(R.string.unknown_device); + } + item.deviceAddress.setText(btDevice.getAddress()); + + item.rssi.setText(getText(R.string.rssi) + ": " + + (btDevice.getRssi() != 0 ? valueOf(btDevice.getRssi()) : getText(R.string.unknown))); + + SmDevice smDevice = btDevice.getTag(); + + boolean useful = smDevice.isUseful(); + item.spinner.setVisibility(useful ? View.GONE : View.VISIBLE); + item.connect.setVisibility(useful ? View.VISIBLE : View.GONE); + view.setClickable(useful); + + return view; + } + } +} diff --git a/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java b/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java new file mode 100644 index 0000000..6bc522d --- /dev/null +++ b/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java @@ -0,0 +1,26 @@ +package io.trygvis.soilmoisture; + +import android.util.Log; + +class SmDevice { + private final static String TAG = SmDevice.class.getSimpleName(); + + public SmDevice() { + Log.i(TAG, "new device"); + } + + private Boolean isUseful; + + public boolean isUseful() { + return isUseful != null && isUseful; + } + + public Boolean getIsUseful() { + return isUseful; + } + + public void setIsUseful(Boolean isUseful) { + Log.i(TAG, "useful=" + isUseful); + this.isUseful = isUseful; + } +} diff --git a/app/src/main/java/io/trygvis/soilmoisture/SoilActivity.java b/app/src/main/java/io/trygvis/soilmoisture/SoilActivity.java new file mode 100644 index 0000000..4c66f7b --- /dev/null +++ b/app/src/main/java/io/trygvis/soilmoisture/SoilActivity.java @@ -0,0 +1,38 @@ +package io.trygvis.soilmoisture; + +import android.app.Activity; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; + + +public class SoilActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_soil); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_soil, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + return true; + } + + return super.onOptionsItemSelected(item); + } +} |