package no.topi.fiken.display; import android.app.ActionBar; import android.app.Activity; import android.app.ListActivity; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.Handler; 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.ListView; import android.widget.TextView; import android.widget.Toast; import com.crashlytics.android.Crashlytics; import io.fabric.sdk.android.Fabric; import java.util.ArrayList; import java.util.List; import static java.lang.String.valueOf; import static no.topi.fiken.display.DisplayService.IntentAction; import static no.topi.fiken.display.DisplayService.IntentExtra; import static no.topi.fiken.display.ExceptionHandler.EXCEPTION_HANDLER; public class MainActivity extends ListActivity { private final static String TAG = MainActivity.class.getSimpleName(); // Stops scanning after 10 seconds. private static final long SCAN_PERIOD = 3 * 1000; private static final int REQUEST_ENABLE_BT = 1; private DisplayListAdapter displayList; private Handler handler; private BluetoothAdapter mBluetoothAdapter; private boolean mScanning; private DisplayService displayService; private String deviceToShow; 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); } handler = new Handler(); // Use this check to determine whether BLE is supported on the device. Then you can // selectively disable BLE-related features. if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show(); finish(); } // Initializes a Bluetooth adapter. For API level 18 and above, get a reference to // BluetoothAdapter through BluetoothManager. final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter(); // Checks if Bluetooth is supported on the device. if (mBluetoothAdapter == null) { Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show(); finish(); } serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder service) { displayService = ((DisplayService.LocalBinder) service).getService(); if (!displayService.initialize()) { finish(); } startScan(); } @Override public void onServiceDisconnected(ComponentName componentName) { stopScan(); displayService = null; } }; Intent displayServiceIntent = new Intent(this, DefaultDisplayService.class); bindService(displayServiceIntent, serviceConnection, BIND_AUTO_CREATE); } @Override protected void onDestroy() { unbindService(serviceConnection); } @Override protected void onResume() { Log.i(TAG, "onResume"); super.onResume(); // Ensures Bluetooth is enabled on the device. If Bluetooth is not currently enabled, // fire an intent to display a dialog asking the user to grant permission to enable it. if (!mBluetoothAdapter.isEnabled()) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); } registerReceiver(displayServiceBroadcastReceiver, IntentAction.ALL_FILTER); } @Override protected void onPause() { Log.i(TAG, "onPause"); super.onPause(); stopScan(); unregisterReceiver(displayServiceBroadcastReceiver); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { Log.i(TAG, "onActivityResult"); // User chose not to enable Bluetooth. if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) { finish(); return; } 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 (!mScanning) { 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() { displayList = new DisplayListAdapter(); setListAdapter(displayList); displayService.startScan(); // Stops scanning after a pre-defined scan period. handler.postDelayed(new Runnable() { @Override public void run() { displayService.stopScan(); invalidateOptionsMenu(); } }, SCAN_PERIOD); } private void stopScan() { if (displayService != null) { displayService.stopScan(); } } @Override protected void onListItemClick(ListView l, View v, int position, long id) { stopScan(); DisplayService.DeviceInfo state = displayList.getDevice(position); if (!displayService.connect(state.address)) { Toast.makeText(this, "Could not connect to " + state.address, Toast.LENGTH_SHORT).show(); } else { deviceToShow = state.address; } } private final BroadcastReceiver displayServiceBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(final Context context, final Intent intent) { runOnUiThread(new Runnable() { @Override public void run() { IntentAction action = IntentAction.valueOf(intent); String deviceAddress = intent.getStringExtra(IntentExtra.DEVICE_ADDRESS.name()); if (action == IntentAction.DEVICE_UPDATE && deviceAddress != null) { DisplayService.DeviceInfo device = displayList.getDevice(deviceAddress, true); device.update(intent); if (intent.hasExtra(IntentExtra.CONNECTED.name())) { boolean connected = intent.getBooleanExtra(IntentExtra.CONNECTED.name(), false); if (connected) { if (deviceToShow != null && deviceToShow.equals(device.address)) { Log.i(TAG, "connected to " + deviceToShow); final Intent intent = new Intent(context, DisplayControlActivity.class); intent.putExtra(IntentExtra.DEVICE_ADDRESS.name(), device.address); intent.putExtra(IntentExtra.DEVICE_NAME.name(), device.name); startActivity(intent); } } } displayList.notifyDataSetChanged(); } if (intent.hasExtra(IntentExtra.SCANNING.name())) { mScanning = intent.getBooleanExtra(IntentExtra.SCANNING.name(), false); invalidateOptionsMenu(); } } }); } }; static class ViewHolder { final TextView deviceName; final TextView deviceAddress; final TextView rssi; final TextView isDisplay; ViewHolder(TextView deviceName, TextView deviceAddress, TextView rssi, TextView isDisplay) { this.deviceName = deviceName; this.deviceAddress = deviceAddress; this.rssi = rssi; this.isDisplay = isDisplay; } } private class DisplayListAdapter extends BaseAdapter { private List devices = new ArrayList(); private LayoutInflater inflater = MainActivity.this.getLayoutInflater(); public DisplayService.DeviceInfo getDevice(int position) { return devices.get(position); } public DisplayService.DeviceInfo getDevice(String address, boolean create) { for (DisplayService.DeviceInfo device : devices) { if (device.address.equals(address)) { return device; } } DisplayService.DeviceInfo deviceInfo = null; if (create) { deviceInfo = new DisplayService.DeviceInfo(address, 0); devices.add(deviceInfo); } return deviceInfo; } @Override public int getCount() { return devices.size(); } @Override public Object getItem(int i) { return devices.get(i); } @Override public long getItemId(int i) { return i; } @Override public View getView(int i, View view, ViewGroup viewGroup) { ViewHolder viewHolder; if (view == null) { view = inflater.inflate(R.layout.listitem_device, null); viewHolder = new ViewHolder( (TextView) view.findViewById(R.id.device_name), (TextView) view.findViewById(R.id.device_address), (TextView) view.findViewById(R.id.device_rssi), (TextView) view.findViewById(R.id.device_isDisplay)); view.setTag(viewHolder); } else { viewHolder = (ViewHolder) view.getTag(); } DisplayService.DeviceInfo state = devices.get(i); if (state.name != null && state.name.length() > 0) { viewHolder.deviceName.setText(state.name); } else { viewHolder.deviceName.setText(R.string.unknown_device); } viewHolder.deviceAddress.setText(state.address); viewHolder.rssi.setText(getText(R.string.rssi) + ": " + (state.rssi != 0 ? valueOf(state.rssi) : getText(R.string.rssi_unknown))); viewHolder.isDisplay.setText("Is display: " + (state.isDisplay != null ? state.isDisplay : "unknown")); view.setClickable(state.isDisplay != null && state.isDisplay); return view; } } }