Android Open Source - Bluebit Activity Weight






From Project

Back to project page Bluebit.

License

The source code is released under:

Apache License

If you think the Android project Bluebit 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

// vim: et sw=4 sts=4 tabstop=4
/*/*from   w ww  .  j  a  v  a 2 s.c  om*/
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package com.issc.ui;

import com.issc.Bluebit;
import com.issc.gatt.Gatt;
import com.issc.gatt.GattAdapter;
import com.issc.gatt.GattCharacteristic;
import com.issc.gatt.GattDescriptor;
import com.issc.gatt.GattService;
import com.issc.impl.LeService;
import com.issc.impl.GattTransaction;
import com.issc.R;
import com.issc.util.Log;
import com.issc.util.Util;
import com.issc.util.TransactionQueue;

import java.nio.ByteBuffer;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class ActivityWeight extends Activity implements
    TransactionQueue.Consumer<GattTransaction> {

    private LeService mService;
    private Gatt.Listener mListener;
    private SrvConnection mConn;
    private ScanCallback mScanCallback;

    private final static double LB_BASE = 2.2046; // 1 kg is about 2.2046 lb
    private final static double ST_BASE = 0.1574; // 1 kg is about 0.1574 st

    private final static int UPDATE_VALUE = 0x9527; // random-unique number
    private final static int UPDATE_NAME  = 0x1984;
    private final static int SHOW_LOADER  = 0x2013;
    private final static int HIDE_LOADER  = 0x2014;

    private final static String VALUE_IN_MSG = "value_in_message_instance";
    private final static String NAME_IN_MSG  = "name_message_instance";

    private final static String SEARCH_PATTERN = "Electronic Scales";

    /* use 0xFFF4 descriptor of 0xFFF0 characteristic to enable
     * notification from target */
    private final static UUID mUuidFFF0 = Util.uuidFromStr("FFF0");
    private final static UUID mUuidFFF4 = Util.uuidFromStr("FFF4");

    private BluetoothDevice mDevice;
    private GattService        mFFF0;
    private GattCharacteristic mFFF4;
    private GattDescriptor     mCCC;
    private GattService        mProprietary;
    private GattCharacteristic mAirPatch;

    private TransactionQueue mQueue;

    private TextView mKg;
    private TextView mLb;
    private TextView mSt;
    private TextView mName;
    private View     mLoader;

    private ViewHandler mViewHandler;
    private final static DecimalFormat sDF = new DecimalFormat("0.0");

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_weight);

        mKg = (TextView) findViewById(R.id.kg);
        mLb = (TextView) findViewById(R.id.lb);
        mSt = (TextView) findViewById(R.id.st);
        mLoader = findViewById(R.id.loader);
        mName = (TextView) findViewById(R.id.weight_name);

        mQueue = new TransactionQueue(this);

        mViewHandler = new ViewHandler();
        mListener = new GattListener();
        mScanCallback = new ScanCallback();
        mConn = new SrvConnection();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mQueue.clear();
    }

    @Override
    protected void onResume() {
        super.onResume();
        bindService(new Intent(this, LeService.class), mConn, 0);
    }

    @Override
    protected void onPause() {
        super.onPause();
        stopScanningTarget();
        mService.rmListener(mListener);
        mService.disconnect(mDevice);
        mService.closeGatt(mDevice);
        mQueue.clear();
        unbindService(mConn);
    }

    /**
     * Prompt a dialog for input to change name of device.
     */
    public void onClickName(View v) {
        if (!isProprietary()) {
            Toast.makeText(this, "This device does not support changing name",
                    Toast.LENGTH_SHORT).show();
            return;
        }

        /*TODO: using Dialog is not good, we should improve it */
        AlertDialog.Builder alert = new AlertDialog.Builder(this);

        alert.setTitle("Set New Name");

        // Set an EditText view to get user input 
        final EditText input = new EditText(this);
        alert.setView(input);
        input.setText(mName.getText());

        alert.setPositiveButton("Done", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int whichButton) {
                if(input.getText().length() <= 0) {
                    // input nothing, aborting
                    return;
                }
                onChangingName(input.getText());
            }
        });

        alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int whichButton) {
                // Canceled.
            }
        });

        alert.show();
    }

    private void onChangingName(CharSequence newName) {
        mName.setText(newName);
        writeName(newName);
    }

    private void connectDevice() {
        mService.connectGatt(this, false, mDevice);
        mService.connect(mDevice, false);
    }

    private void scanTarget() {
        mDevice = null;
        Log.d("Scanning Target");
        mService.startScan(mScanCallback);
        updateView(SHOW_LOADER, null);
    }

    private void stopScanningTarget() {
        Log.d("Stop scanning");
        updateView(HIDE_LOADER, null);
        mService.stopScan(mScanCallback);
    }

    private void onConnected() {
        List<GattService> list = mService.getServices(mDevice);
        if ((list == null) || (list.size() == 0)) {
            Log.d("no services, do discovery");
            mService.discoverServices(mDevice);
        } else {
            onDiscovered();
        }
    }

    private void onDisconnected() {
        mFFF0 = null;
        mFFF4 = null;
        mCCC = null;
        mProprietary = null;
        scanTarget();
    }

    private void onDiscovered() {
        Log.d("Discovered services, enable notification");
        mQueue.clear();
        diggServices();

        // enable notification to get update from Weight Scale
        mService.setCharacteristicNotification(mFFF4, true);
        GattTransaction t = new GattTransaction(mCCC,
                mCCC.getConstantBytes(GattDescriptor.ENABLE_NOTIFICATION_VALUE));
        mQueue.add(t);

        enableAirPatch();
    }

    private boolean isProprietary() {
        return (mProprietary != null) && (mAirPatch != null);
    }

    /**
     * To enable Air Patch for ISSC device.
     *
     * If connected to a ISSC device, we could use Air Patch via
     * proprietary service.
     */
    private void enableAirPatch() {
        if (!isProprietary()) {
            return;
        }

        Log.d("proprietary, enabling air patch");
        byte[] enable = {(byte)0x03};
        GattTransaction t = new GattTransaction(mAirPatch, enable);
        mQueue.add(t);
    }

    /**
     * Change name of ISSC device.
     *
     * By using ISSC proprietary service.
     */
    private void writeName(CharSequence name) {
        if (!isProprietary()) {
            Log.d("not proprietary, do not write name");
            return;
        }
        updateView(SHOW_LOADER, null);
        Log.d("proprietary, write name:" + name);

        // Get name and put into byte array, padding with zero
        // if the name is not long enough.
        final int max = Bluebit.NAME_MAX_SIZE;
        final byte empty = (byte)0x00;
        byte[] nameData = name.toString().getBytes();
        ByteBuffer data = ByteBuffer.allocate(max);
        for (int i = 0; i < data.limit(); i++) {
            if (i >= nameData.length) {
                data.put(empty);
            } else {
                data.put(nameData[i]);
            }
        }

        // update E2PROM
        ByteBuffer e2prom = ByteBuffer.allocate(
                Bluebit.CMD_WRITE_E2PROM.length +
                Bluebit.ADDR_E2PROM_NAME.length +
                1 +  // 1 byte for length
                max);
        e2prom.put(Bluebit.CMD_WRITE_E2PROM);  // write e2prom
        e2prom.put(Bluebit.ADDR_E2PROM_NAME);  // 0x000b -> addr of name on e2prom
        e2prom.put((byte)max);
        e2prom.put(data.array());
        GattTransaction t1 = new GattTransaction(mAirPatch, e2prom.array());
        mQueue.add(t1);

        // update RAM to update immediately.
        ByteBuffer memory = ByteBuffer.allocate(
                Bluebit.CMD_WRITE_MEMORY.length +
                Bluebit.ADDR_MEMORY_NAME.length +
                1 +  // 1 byte for length
                max);
        memory.put(Bluebit.CMD_WRITE_MEMORY);  // write memory
        memory.put(Bluebit.ADDR_MEMORY_NAME);  // 0x4e0b -> addr of name on memory
        memory.put((byte)max);
        memory.put(data.array());
        GattTransaction t2 = new GattTransaction(mAirPatch, memory.array());
        mQueue.add(t2);
    }


    /**
     * To get needed Gatt Service/Characteristic from Device.
     */
    private void diggServices() {
        mFFF0 = null;
        mFFF4 = null;
        mCCC  = null;
        mProprietary = null;

        List<GattService> list = mService.getServices(mDevice);
        Iterator<GattService> it = list.iterator();
        while(it.hasNext()) {
            GattService srv = it.next();
            if (srv.getUuid().equals(mUuidFFF0)) {
                mFFF0 = srv;
                mFFF4 = mFFF0.getCharacteristic(mUuidFFF4);
                mCCC  = mFFF4.getDescriptor(Bluebit.DES_CLIENT_CHR_CONFIG);
            } else if (srv.getUuid().equals(Bluebit.SERVICE_ISSC_PROPRIETARY)) {
                mProprietary = srv;
                mAirPatch = mProprietary.getCharacteristic(Bluebit.CHR_AIR_PATCH);
            }
        }
    }

    private void onFoundTarget(BluetoothDevice dev, byte[] records) {
        if (mDevice == null) {
            mDevice = dev;
            stopScanningTarget();
            connectDevice();
        }
    }

    /**
     * To check whether the device is our target.
     *
     * The device which advertising data with SEARCH_PATTERN is our target.
     */
    private boolean isTheTarget(BluetoothDevice device, byte[] records) {
        String response = new String(records);
        if (response.contains(SEARCH_PATTERN)) {
            return true;
        }
        return false;
    }

    /**
     * Send message to handler of UI thread.
     */
    public void updateView(int tag, Bundle info) {
        if (info == null) {
            info = new Bundle();
        }
        mViewHandler.removeMessages(tag);
        Message msg = mViewHandler.obtainMessage(tag);
        msg.what = tag;
        msg.setData(info);
        mViewHandler.sendMessage(msg);
    }

    public void updateValue(int value) {
        Bundle info = new Bundle();
        info.putInt(VALUE_IN_MSG, value);
        updateView(UPDATE_VALUE, info);
    }

    class ViewHandler extends Handler {
        public void handleMessage(Message msg) {
            Bundle bundle = msg.getData();
            if (bundle == null) {
                Log.d("ViewHandler handled a message without information");
                return;
            }

            int tag = msg.what;
            if (tag == UPDATE_VALUE) {
                int value = bundle.getInt(VALUE_IN_MSG, 0);
                onUpdateValue(value);
            } else if (tag == UPDATE_NAME) {
            } else if (tag == SHOW_LOADER) {
                mLoader.setVisibility(View.VISIBLE);
            } else if (tag == HIDE_LOADER) {
                mLoader.setVisibility(View.INVISIBLE);
            }
        }
    }

    /**
     * Update UI for latest value.
     */
    private void onUpdateValue(int value) {
        double f = (double)value;
        double carry = 10.0; // if we got 102, it means 10.2 kg
        double kg = f / carry;
        double lb = (f * LB_BASE) / carry;
        double st = (f * ST_BASE) / carry;

        mKg.setText(sDF.format(kg));
        mLb.setText(sDF.format(lb));
        mSt.setText(sDF.format(st));
    }

    @Override
    public void onTransact(GattTransaction t) {
        if (t.isForCharacteristic()) {
            if (t.isWrite) {
                Log.d("gatt writing characteristic");
                t.chr.setValue(t.value);
                mService.writeCharacteristic(t.chr);
            } else {
                t.chr.setValue(t.value);
                boolean r = mService.readCharacteristic(t.chr);
                Log.d("gatt reading characteristic:" + r);
            }
        } else if (t.isForDescriptor()) {
            if (t.isWrite) {
                t.desc.setValue(t.value);
                mService.writeDescriptor(t.desc);
            }
        }
    }

    class ScanCallback implements GattAdapter.LeScanCallback {
        @Override
        public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
            Log.d("Scanned:" + device.getAddress());
            if (isTheTarget(device, scanRecord)) {
                onFoundTarget(device, scanRecord);
            }
        }
    }

    class GattListener extends Gatt.ListenerHelper {
        GattListener() {
            super("ActivityWeight");
        }

        @Override
        public void onConnectionStateChange(Gatt gatt,
                int status, int newState) {
            BluetoothDevice device = gatt.getDevice();
            if (!mDevice.getAddress().equals(device.getAddress())) {
                // not the device I care about
                return;
            }
            if (newState ==  BluetoothProfile.STATE_CONNECTED) {
                Log.d("connected to device");
                onConnected();
            } else if (newState ==  BluetoothProfile.STATE_DISCONNECTED) {
                onDisconnected();
            }
        }

        @Override
        public void onCharacteristicChanged(Gatt gatt, GattCharacteristic chrc) {
            Log.d("on chr changed");
            if (chrc.getUuid().equals(mFFF4.getUuid())) {
                Log.d("Got Weight value, update view");
                final int index = 4; // got this index from Frontline
                byte[] data = chrc.getValue();
                int value = ((0xFF & data[index]) << 8) + (0xFF & data[index + 1]);
                updateValue(value);
            } else if (chrc.getUuid().equals(mAirPatch.getUuid())){
                Log.d("Got Value update from AirPatch");
                byte[] data = chrc.getValue();
                for (int i = 0; i < data.length; i++) {
                    Log.d(String.format("[%d]: 0x%02x", i, data[i]));
                }

                if (data.length >= 2 && data[1] == Bluebit.CMD_WRITE_MEMORY[0]) {
                    // finished writing memory
                    updateView(HIDE_LOADER, null);
                }
            } else {
                Log.d("Unknown chr:" + chrc.getUuid().toString());
            }
        }

        @Override
        public void onCharacteristicRead(Gatt gatt, GattCharacteristic charac, int status) {
            Log.d("on chr read:" + status);
            byte[] value = charac.getValue();
            mQueue.onConsumed();
        }

        @Override
        public void onCharacteristicWrite(Gatt gatt, GattCharacteristic charac, int status) {
            Log.d("on chr write:" + status);
            mQueue.onConsumed();
        }

        @Override
        public void onDescriptorRead(Gatt gatt, GattDescriptor desc, int status) {
            Log.d("on desc read:" + status);
            mQueue.onConsumed();
        }

        @Override
        public void onDescriptorWrite(Gatt gatt, GattDescriptor desc, int status) {
            Log.d("on desc write:" + status);
            mQueue.onConsumed();
        }

        @Override
        public void onServicesDiscovered(Gatt gatt, int status) {
            onDiscovered();
        }
    }

    class SrvConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder service) {
            mService = ((LeService.LocalBinder)service).getService();
            mService.addListener(mListener);
            scanTarget();
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Log.e("Gatt Service disconnected");
        }
    }
}




Java Source Code List

com.issc.Bluebit.java
com.issc.data.BLEDevice.java
com.issc.data.UuidsHandler.java
com.issc.gatt.GattAdapter.java
com.issc.gatt.GattCharacteristic.java
com.issc.gatt.GattDescriptor.java
com.issc.gatt.GattServer.java
com.issc.gatt.GattService.java
com.issc.gatt.Gatt.java
com.issc.impl.AlgorithmAIO.java
com.issc.impl.FunctionAdapter.java
com.issc.impl.GattTransaction.java
com.issc.impl.LeService.java
com.issc.impl.aosp.AospGattAdapter.java
com.issc.impl.aosp.AospGattCharacteristic.java
com.issc.impl.aosp.AospGattDescriptor.java
com.issc.impl.aosp.AospGattServer.java
com.issc.impl.aosp.AospGattService.java
com.issc.impl.aosp.AospGatt.java
com.issc.ui.ActivityAIO.java
com.issc.ui.ActivityDeviceChooser.java
com.issc.ui.ActivityDeviceDetail.java
com.issc.ui.ActivityFileChooser.java
com.issc.ui.ActivityFunctionPicker.java
com.issc.ui.ActivityGattServer.java
com.issc.ui.ActivityKeyboard.java
com.issc.ui.ActivityMain.java
com.issc.ui.ActivityServerList.java
com.issc.ui.ActivityTransparent.java
com.issc.ui.ActivityWeight.java
com.issc.ui.FragGattServer.java
com.issc.util.Log.java
com.issc.util.TransactionQueue.java
com.issc.util.Util.java
com.issc.util.UuidMatcher.java
com.issc.widget.LoadingFragment.java