Android Open Source - MSMBle Device Activity






From Project

Back to project page MSMBle.

License

The source code is released under:

Created by Javier Montaner (twitter: @tumaku_) during M-week (February 2014) of MakeSpaceMadrid http://www.makespacemadrid.org @ 2014 Javier Montaner Licensed under the MIT Open Source License htt...

If you think the Android project MSMBle listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Created by Javier Montaner  (twitter: @tumaku_) during M-week (February 2014) of MakeSpaceMadrid
 * http://www.makespacemadrid.org/*from  ww w  .ja v a2s. c om*/
 * @ 2014 Javier Montaner
 * 
 * Licensed under the MIT Open Source License 
 * http://opensource.org/licenses/MIT
 * 
 * Many thanks to Yeelight (special mention to Daping Liu) and Double Encore (Dave Smith)
 * for their support and shared knowlegde
 * 
 * Based on the API released by Yeelight:
 * http://www.yeelight.com/en_US/info/download
 * 
 * Based on the code created by Dave Smith (Double Encore):
 * https://github.com/devunwired/accessory-samples/tree/master/BluetoothGatt
 * http://www.doubleencore.com/2013/12/bluetooth-smart-for-android/
 * 
 * 
 * Scan Bluetooth Low Energy devices and their services and characteristics.
 * If the Yeelight Service is found, an activity can be launched to control colour and intensity of Yeelight Blue bulb
 * 
 * Tested on a Nexus 7 (2013)
 * 
 */

package com.tumaku.msmble;

import java.util.ArrayList;
import java.util.List;

import android.os.Bundle;
import android.app.Activity;
import android.app.ListActivity;
import android.bluetooth.BluetoothGattCharacteristic;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color;
import android.graphics.Typeface;
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.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;


public class DeviceActivity extends ListActivity 
  implements OnItemClickListener{

  private static final int RECONNECT_ITEM =1;

  private BLEDeviceBroadcastReceiver mBroadcastReceiver;
  private List <TumakuBLE.ServiceType> mServices=null;
  private ListView mListView;
  private TextView mHeaderText;
  private MySimpleArrayAdapter mAdapter=null;
  private Context mContext=null;
  private String mAddress;
  private String mDeviceName;
  private TumakuBLE mTumakuBLE;
  private int mState=STATE_DUMMY;
  
  private final static int STATE_DUMMY=0;
  private final static int STATE_CONNECT=1;
  private final static int STATE_CONNECTING=2;
  private final static int STATE_CONNECTED=3; 
  private final static int STATE_RETRIEVING_SERVICES=4;
  private final static int STATE_SERVICES_RETRIEVED=5;   
  private final static int STATE_DISCONNECTING=6;   
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Log.i("JMG","DeviceActivity triggered OnCreate()");
    mContext=this;
        setContentView(R.layout.device_list);           
    mBroadcastReceiver= new BLEDeviceBroadcastReceiver();
    mHeaderText= (TextView) findViewById(R.id.header);
    mServices=new ArrayList<TumakuBLE.ServiceType>();
    mTumakuBLE=((TumakuBLEApplication)getApplication()).getTumakuBLEInstance(this);

    mAddress=getIntent().getStringExtra(TumakuBLE.EXTRA_ADDRESS);
    if (mAddress==null) {
      // return to main activity if no device address received
        if (Constant.DEBUG) Log.i("JMG", "No device address data defined in intent");
          Toast.makeText(this, "No  device address defined in intent", Toast.LENGTH_SHORT).show();            
      finish();
    }    
    
    mDeviceName=getIntent().getStringExtra(TumakuBLE.EXTRA_NAME);
    if (mDeviceName==null) {
      // return to main activity if no device address received
        if (Constant.DEBUG) Log.i("JMG", "No device name data defined in intent");
          Toast.makeText(this, "No  device name defined in intent", Toast.LENGTH_SHORT).show();            
      finish();
    }    

    
      if (Constant.DEBUG) {
        Log.i("JMG", "Device address: "+ mAddress);
        Log.i("JMG", "Device name: "+ mDeviceName);
      }

    mHeaderText.setText("Device Name: "+ mDeviceName + " - Addresss: " + mAddress);
    mState=STATE_DUMMY;
    mTumakuBLE.setDeviceAddress(mAddress);
    
        mListView = getListView();
        mAdapter=new MySimpleArrayAdapter(this, mServices);
        mListView.setAdapter(mAdapter);
        mListView.setOnItemClickListener(this);        
  }
  
  @Override
    public boolean onCreateOptionsMenu(Menu menu) {
    String menuTitle= getResources().getString(R.string.reconnect);    
      menu.add(0,RECONNECT_ITEM,0,menuTitle);
      return true;
    }

  @Override
    public boolean onOptionsItemSelected (MenuItem item){
      switch (item.getItemId()){
        case RECONNECT_ITEM:
              if (mTumakuBLE.isConnected()){
                if (Constant.DEBUG) Log.i("JMG", "Disconnecting GATT Reconnect DeviceActivity");
                TumakuBLE.disconnect();
              }
            if (Constant.DEBUG) Log.i("JMG", "ReStart Connection Reconnect DeviceActivity");
              restartConnection(false);
    
          break;
      }
      return true;
    }    
   
  public void restartConnection(boolean fullResetFlag){
    mState=STATE_CONNECT;
        TumakuBLE.resetTumakuBLE();
    mTumakuBLE.setDeviceAddress(mAddress);
    mAdapter.clear();
      if (Constant.DEBUG) Log.i("JMG", "Reset mTumakuBLE restartConnection() DeviceActivity");
      if (fullResetFlag) {
        
      }
      
    IntentFilter filter = new IntentFilter(TumakuBLE.DEVICE_CONNECTED);  
    filter.addAction(TumakuBLE.DEVICE_DISCONNECTED);  
    filter.addAction(TumakuBLE.SERVICES_DISCOVERED);  
    this.registerReceiver(mBroadcastReceiver, filter);
    mTumakuBLE.connect();  
    mState=STATE_CONNECTING;
    updateTitle();
  }
  
  public void onResume(){
    super.onResume();
      if (Constant.DEBUG) Log.i("JMG", "ReStart Connection onResume DeviceActivity");    
    restartConnection(false);
  }
  
  @Override
  public void onPause(){
    // The application Disconnects onPause 
    // but the TumakuBLE service tree is not cleared until next connect() call
    super.onPause();
    mState=STATE_DISCONNECTING;
        this.unregisterReceiver(this.mBroadcastReceiver);
        if (mTumakuBLE.isConnected()){
          if (Constant.DEBUG) Log.i("JMG", "Disconnecting GATT onPause DeviceActivity");
          TumakuBLE.disconnect();
        }
  }
  
    @Override
    public void onItemClick(AdapterView<?> adapter, View view,
                int position, long id) {       
      if (Constant.DEBUG) Log.i("JMG", "Selected service " + mServices.get(position).getService().getUuid().toString());
      Intent intentActivity= new Intent(mContext, ServiceActivity.class);
    intentActivity.putExtra(TumakuBLE.EXTRA_SERVICE, mServices.get(position).getService().getUuid().toString());
      mContext.startActivity(intentActivity);

    }
  
    //called from BroadacastReceiver (maybe outside UI thread, so this mechanism must be used
    protected void updateServiceList(){
      this.runOnUiThread(new Runnable() {
          @Override
          public void run() {
            mAdapter.clear();
          mAdapter.addAll(mTumakuBLE.getServices());               
          if (Constant.DEBUG) Log.i("JMG", "Added new services ");
          }
    }
       );
    }
    
    //called from BroadacastReceiver (maybe outside UI thread, so this mechanism must be used
    protected void updateTitle(){
      this.runOnUiThread(new Runnable() {
          @Override
          public void run() {
            String stateString="";
            switch (mState){
            case(STATE_DUMMY):
              stateString="Dummy";
              break;
            case(STATE_CONNECT):
              stateString="Connect";
              break;            
            case(STATE_CONNECTING):
              stateString="Connecting";
              break;
            case(STATE_CONNECTED):
              stateString="Connected";
              break;
            case(STATE_RETRIEVING_SERVICES):
              stateString="Retrieving Services";
              break;
            case(STATE_SERVICES_RETRIEVED):
              stateString="Services discovered";
              break;
            case(STATE_DISCONNECTING):
              stateString="Disconnecting";
              break;
            }
            mHeaderText.setText("Addr: " + mAddress + "  - Name: "+ mDeviceName+ "  - State: " + stateString);          
          }
    }
       );
    }
    
    public class MySimpleArrayAdapter extends ArrayAdapter<TumakuBLE.ServiceType> {
      private final Context context;
      public MySimpleArrayAdapter(Context context, List <TumakuBLE.ServiceType> serviceList) {
        super(context, R.layout.device_list_item, serviceList);
        this.context = context;
      }

      @Override
      public View getView(int position, View convertView, ViewGroup parent) {
        LayoutInflater inflater = (LayoutInflater) context
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View rowView = inflater.inflate(R.layout.device_list_item, parent, false);
        TextView serviceText = (TextView) rowView.findViewById(R.id.deviceName);
        TextView characteristicsText = (TextView) rowView.findViewById(R.id.deviceAddress);
        String serviceUUID=mServices.get(position).getService().getUuid().toString();
        serviceText.setText(serviceUUID);
        if (serviceUUID.equalsIgnoreCase(TumakuBLE.YEELIGHT_SERVICE)) rowView.setBackgroundColor(Color.GREEN);
        if (serviceUUID.equalsIgnoreCase(TumakuBLE.SENSORTAG_HUMIDITY_SERVICE)) rowView.setBackgroundColor(Color.RED);
        if (serviceUUID.equalsIgnoreCase(TumakuBLE.SENSORTAG_KEY_SERVICE)) rowView.setBackgroundColor(Color.YELLOW);
        if (serviceUUID.equalsIgnoreCase(TumakuBLE.SENSORTAG_IR_TEMPERATURE_SERVICE)) rowView.setBackgroundColor(Color.RED);
        if (serviceUUID.equalsIgnoreCase(TumakuBLE.TETHERCELL_SERVICE)) rowView.setBackgroundColor(Color.BLUE);
        if (serviceUUID.equalsIgnoreCase(TumakuBLE.BLEDUINO_UART_SERVICE)) rowView.setBackgroundColor(Color.BLUE);

        serviceText.setTypeface(null, Typeface.BOLD);
        String characteristicsString ="";
        for (BluetoothGattCharacteristic characteristicInList : mServices.get(position).getCharacteristics()){
          characteristicsString+=characteristicInList.getUuid().toString()+"\n";
        }
        characteristicsText.setText(characteristicsString);
        return rowView;
      }
    } 

 
    private class BLEDeviceBroadcastReceiver extends BroadcastReceiver {
  
       @Override
       public void onReceive(Context context, Intent intent) {
           if (intent.getAction().equals(TumakuBLE.DEVICE_CONNECTED)) {
           if (Constant.DEBUG) Log.i("JMG","DEVICE_CONNECTED message received DeviceActivity");
           //once connected to device, discover services
           mState=STATE_CONNECTED;
           mTumakuBLE.discoverServices();
           mState= STATE_RETRIEVING_SERVICES;
         updateTitle();
             return;
           }
           if (intent.getAction().equals(TumakuBLE.DEVICE_DISCONNECTED)) {
           if (Constant.DEBUG) Log.i("JMG","DEVICE_DISCONNECTED message received");
           //This is an unexpected device disconnect situation generated by Android BLE stack
           //Usually happens on the service discovery step :-(
           //Try to reconnect
           
           String fullReset=intent.getStringExtra(TumakuBLE.EXTRA_FULL_RESET);
           if (fullReset!=null){
             if (Constant.DEBUG) Log.i("JMG","DEVICE_DISCONNECTED message received with full reset flag");
             Toast.makeText(mContext, "Urecoverable BT error received. Launching full reset", Toast.LENGTH_SHORT).show();    
           restartConnection(true);
               return;             
           } else {                      
             if (mState!=STATE_CONNECTING){
             Toast.makeText(mContext, "Device disconnected unexpectedly. Reconnecting.", Toast.LENGTH_SHORT).show();                  
             restartConnection(false);
                 return;
             }
           }
           }
           if (intent.getAction().equals(TumakuBLE.SERVICES_DISCOVERED)) {
           if (Constant.DEBUG) Log.i("JMG","SERVICES_DISCOVERED message received  DeviceActivity");
           mState= STATE_SERVICES_RETRIEVED;
           updateServiceList();
         updateTitle();
           return;
           }
        }
       
    }
    
    
}




Java Source Code List

com.tumaku.msmble.BLEduinoUartActivity.java
com.tumaku.msmble.Constant.java
com.tumaku.msmble.ControlLightActivity.java
com.tumaku.msmble.DeviceActivity.java
com.tumaku.msmble.HM10Activity.java
com.tumaku.msmble.MainActivity.java
com.tumaku.msmble.SensorTagActivity.java
com.tumaku.msmble.ServiceActivity.java
com.tumaku.msmble.TethercellActivity.java
com.tumaku.msmble.TumakuBLEApplication.java
com.tumaku.msmble.TumakuBLE.java