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 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) 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 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 serviceListener = new BtServiceListener() { @Override public void onScanStarted() { invalidateOptionsMenu(); } @Override public void onNewDevice(BtDevice 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 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; } } }