package io.trygvis.soilmoisture; import android.app.ActionBar; import android.app.ListActivity; import android.app.ProgressDialog; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.database.DataSetObservable; import android.database.DataSetObserver; 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.Button; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import com.crashlytics.android.Crashlytics; import java.util.ArrayList; import java.util.List; import io.fabric.sdk.android.Fabric; import io.trygvis.android.LocalBinder; import io.trygvis.android.bt.BtActivitySupport; import static io.trygvis.soilmoisture.ExceptionHandler.EXCEPTION_HANDLER; import static io.trygvis.soilmoisture.SoilMoistureService.SoilMoistureListener; 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 final SoilMoistureListener serviceListener = new MySoilMoistureListener(); private final MainActivity context = this; private DeviceListAdapter deviceList; private ServiceConnection serviceConnection; private SoilMoistureService soilMoistureService; private ProgressDialog initializing; private boolean ready; @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) { Log.i(TAG, "onServiceConnected"); soilMoistureService = ((LocalBinder) service).getService(); registerReceiver(serviceListener, SoilMoistureListener.INTENT_FILTER); } @Override public void onServiceDisconnected(ComponentName componentName) { Log.i(TAG, "onServiceDisconnected"); soilMoistureService = null; stopScan(); } }; bindService(new Intent(this, DefaultSoilMoistureService.class), serviceConnection, BIND_AUTO_CREATE); initializing = ProgressDialog. show(this, "Initializing", "Connecting to Bluetooth system.", true); setContentView(R.layout.main); } @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(serviceListener, SoilMoistureListener.INTENT_FILTER); } @Override protected void onPause() { Log.i(TAG, "onPause"); super.onPause(); stopScan(); unregisterReceiver(serviceListener); } @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); MenuItem stop = menu.findItem(R.id.menu_stop); MenuItem scan = menu.findItem(R.id.menu_scan); MenuItem refresh = menu.findItem(R.id.menu_refresh); MenuItem showAll = menu.findItem(R.id.menu_show_all); MenuItem groupByDevice = menu.findItem(R.id.menu_group_by_device); if (ready) { if (!soilMoistureService.isScanning()) { stop.setVisible(false); scan.setVisible(true); refresh.setActionView(null); } else { stop.setVisible(true); scan.setVisible(false); refresh.setActionView(R.layout.actionbar_indeterminate_progress); } showAll.setChecked(deviceList.isShowAll()); groupByDevice.setChecked(deviceList.isGroupByDevice()); } else { stop.setVisible(false); scan.setVisible(true); refresh.setActionView(null); showAll.setVisible(false); groupByDevice.setVisible(false); } return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { Log.i(TAG, "onOptionsItemSelected"); boolean consumed = true; switch (item.getItemId()) { case R.id.menu_scan: startScan(); break; case R.id.menu_stop: stopScan(); break; case R.id.menu_show_all: item.setChecked(!item.isChecked()); deviceList.setShowAll(item.isChecked()); break; case R.id.menu_group_by_device: item.setChecked(!item.isChecked()); deviceList.setGroupByDevice(item.isChecked()); break; default: consumed = super.onOptionsItemSelected(item); } return consumed; } private void startScan() { soilMoistureService.startScanning(SCAN_PERIOD); } private void stopScan() { if (soilMoistureService != null) { soilMoistureService.stopScanning(); } } @Override protected void onListItemClick(ListView l, View v, int position, long id) { stopScan(); } // ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- static class DeviceItem { final TextView deviceName; final TextView deviceAddress; final TextView rssi; final TextView info; final ProgressBar spinner; final Button connect; DeviceItem(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.info = (TextView) view.findViewById(R.id.device_info); this.spinner = (ProgressBar) view.findViewById(R.id.device_spinner); this.connect = (Button) view.findViewById(R.id.button_connect); } } private class DeviceListAdapter implements ListAdapter { private final DataSetObservable dataSetObservable = new DataSetObservable(); private List devices = new ArrayList<>(); private List current = new ArrayList<>(); private LayoutInflater inflater = MainActivity.this.getLayoutInflater(); private boolean groupByDevice = true; private boolean showAll = true; public void sort() { Log.i(TAG, "sort(), groupByDevice=" + groupByDevice + ", showAll=" + showAll); current = new ArrayList<>(); List usefulDevices = new ArrayList<>(devices.size()); List unusefulDevices = new ArrayList<>(devices.size()); for (SmDevice d : devices) { (d.isUseful() ? usefulDevices : unusefulDevices).add(d); } List monitors = new ArrayList<>(); for (SmDevice d : devices) { monitors.addAll(d.getSensors()); } if (groupByDevice) { current.addAll(usefulDevices); if (showAll) { current.addAll(unusefulDevices); } } else { current.addAll(monitors); } dataSetObservable.notifyChanged(); } public void notifyDataSetChanged() { dataSetObservable.notifyChanged(); } public void setShowAll(boolean showAll) { if (showAll == this.showAll) { return; } this.showAll = showAll; sort(); } public boolean isShowAll() { return showAll; } public void setGroupByDevice(boolean groupByDevice) { if (groupByDevice == this.groupByDevice) { return; } this.groupByDevice = groupByDevice; sort(); } public boolean isGroupByDevice() { return groupByDevice; } // ----------------------------------------------------------------------- // ListAdapter Implementation // ----------------------------------------------------------------------- @Override public void registerDataSetObserver(DataSetObserver observer) { dataSetObservable.registerObserver(observer); } @Override public void unregisterDataSetObserver(DataSetObserver observer) { dataSetObservable.unregisterObserver(observer); } @Override public boolean areAllItemsEnabled() { return true; } @Override public boolean isEnabled(int position) { return true; } @Override public boolean hasStableIds() { return true; } @Override public int getItemViewType(int position) { Object o = current.get(position); if (o instanceof SmDevice) { return 0; } else if (o instanceof SmSensor) { return 1; } throw new RuntimeException("Unknown kind: " + o.getClass()); } @Override public int getViewTypeCount() { return 2; } @Override public boolean isEmpty() { return current.isEmpty(); } @Override public int getCount() { return current.size(); } @Override public Object getItem(int position) { return current.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View view, ViewGroup viewGroup) { Object o = current.get(position); if (o instanceof SmDevice) { return getSmDeviceView((SmDevice) o, view); } else if (o instanceof SmSensor) { return getSoilMonitorView((SmSensor) o, view); } throw new RuntimeException("Not implemented"); } private View getSmDeviceView(SmDevice smDevice, View view) { DeviceItem item; if (view == null) { view = inflater.inflate(R.layout.listitem_device, null); item = new DeviceItem(view); view.setTag(item); view.setClickable(false); } else { item = (DeviceItem) view.getTag(); } if (smDevice.getName() != null) { item.deviceName.setText(smDevice.getName()); } else { item.deviceName.setText(R.string.unknown_device); } item.deviceAddress.setText(smDevice.getBtDevice().getAddress()); item.rssi.setText(getText(R.string.rssi) + ": " + (smDevice.getBtDevice().getRssi() != 0 ? valueOf(smDevice.getBtDevice().getRssi()) : getText(R.string.unknown))); if (smDevice.isUseful()) { item.info.setText("number of sensors: " + smDevice.getSensors().size()); } else { item.info.setText(""); } 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; } private View getSoilMonitorView(SmSensor smSensor, View view) { throw new RuntimeException("Not implemented"); } } private class MySoilMoistureListener extends SoilMoistureListener { @Override public void onReady(boolean ok) { if (!ok) { Toast.makeText(context, "Could not initialize services.", Toast.LENGTH_SHORT). show(); finish(); } else { ready = true; deviceList = new DeviceListAdapter(); setListAdapter(deviceList); deviceList.notifyDataSetChanged(); startScan(); initializing.dismiss(); } } @Override public void onScanStarted() { invalidateOptionsMenu(); } @Override public void onScanStopped() { invalidateOptionsMenu(); } @Override public void onNewDevice(String address) { SmDevice device = soilMoistureService.getDevice(address); deviceList.devices.add(device); deviceList.sort(); } } }