Android Open Source - Bluebit Activity Transparent






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  av  a  2  s  . co m
 * 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.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.io.FileOutputStream;
import java.io.File;
import java.io.OutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Iterator;
import java.util.UUID;
import java.util.List;

import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.content.ServiceConnection;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.text.method.ScrollingMovementMethod;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TabHost;
import android.widget.TextView;
import android.widget.ToggleButton;

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

    private LeService mService;
    private BluetoothDevice mDevice;
    private Gatt.Listener mListener;
    private SrvConnection mConn;

    private ProgressDialog mConnectionDialog;
    private ProgressDialog mTimerDialog;
    protected ViewHandler  mViewHandler;

    private OutputStream mStream;
    private TransactionQueue mQueue;

    private final static int PAYLOAD_MAX = 20; // 90 bytes might be max

    private final static int CONNECTION_DIALOG = 1;
    private final static int TIMER_DIALOG      = 2;
    private final static int CHOOSE_FILE = 0x101;
    private final static int COMPARE_FILE = 0x102;
    private final static int MENU_CLEAR  = 0x501;

    private final static String INFO_CONTENT = "the_information_body";
    private final static String RCV_ENABLED = "could_receive_data_if_enabled";

    private final static int SHOW_CONNECTION_DIALOG     = 0x1000;
    private final static int DISMISS_CONNECTION_DIALOG  = 0x1001;
    private final static int CONSUME_TRANSACTION        = 0x1002;
    private final static int DISMISS_TIMER_DIALOG       = 0x1003;
    private final static int APPEND_MESSAGE             = 0x1004;
    private final static int RCV_STATE                  = 0x1005;


    private TabHost mTabHost;
    private TextView mMsg;
    private EditText mInput;
    private Button   mBtnSend;
    private ToggleButton mToggleEcho;
    private ToggleButton mToggleResponse;
    private CompoundButton mRcvIndicator;

    private Spinner mSpinnerDelta;
    private Spinner mSpinnerSize;
    private Spinner mSpinnerRepeat;

    private int[] mValueDelta;
    private int[] mValueSize;
    private int[] mValueRepeat;

    private GattCharacteristic mTransTx;
    private GattCharacteristic mTransRx;

    private int mSuccess = 0;
    private int mFail    = 0;
    private Calendar mStartTime;

    private final static int MAX_LINES = 50;
    private ArrayList<CharSequence> mLogBuf;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_trans);

        mQueue = new TransactionQueue(this);

        mMsg     = (TextView)findViewById(R.id.trans_msg);
        mInput   = (EditText)findViewById(R.id.trans_input);
        mBtnSend = (Button)findViewById(R.id.trans_btn_send);
        mToggleEcho     = (ToggleButton)findViewById(R.id.echo_toggle);
        mToggleResponse = (ToggleButton)findViewById(R.id.trans_type);
        mRcvIndicator   = (CompoundButton)findViewById(R.id.rcv_indicator);

        mViewHandler = new ViewHandler();

        mTabHost = (TabHost) findViewById(R.id.tabhost);
        mTabHost.setup();
        addTab(mTabHost, "Tab1", "Raw", R.id.tab_raw);
        addTab(mTabHost, "Tab2", "Timer", R.id.tab_timer);
        addTab(mTabHost, "Tab3", "Receive", R.id.tab_rcv);

        mMsg.setMovementMethod(ScrollingMovementMethod.getInstance());
        registerForContextMenu(mMsg);

        mDevice = getIntent().getParcelableExtra(Bluebit.CHOSEN_DEVICE);

        mListener = new GattListener();
        initSpinners();

        mLogBuf = new ArrayList<CharSequence>();

        /* Transparent is not a leaf activity. connect service in onCreate*/
        mConn = new SrvConnection();
        bindService(new Intent(this, LeService.class), mConn, 0);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mQueue.clear();
        closeStream();
        mViewHandler.removeCallbacksAndMessages(null);

        /* Transparent is not a leaf activity. disconnect/unregister-listener in onDestroy*/
        mService.rmListener(mListener);
        mService = null;
        unbindService(mConn);
    }

    private void initSpinners() {
        Resources res = getResources();

        mSpinnerDelta  = (Spinner)findViewById(R.id.timer_delta);
        mSpinnerSize   = (Spinner)findViewById(R.id.timer_size);
        mSpinnerRepeat = (Spinner)findViewById(R.id.timer_repeat);

        mValueDelta  = res.getIntArray(R.array.delta_value);
        mValueSize   = res.getIntArray(R.array.size_value);
        mValueRepeat = res.getIntArray(R.array.repeat_value);

        initSpinner(R.array.delta_text, mSpinnerDelta);
        initSpinner(R.array.size_text, mSpinnerSize);
        initSpinner(R.array.repeat_text, mSpinnerRepeat);

        mSpinnerDelta.setSelection(3);  // supposed to select 1000ms
        mSpinnerSize.setSelection(19);  // supposed to select 20bytes
        mSpinnerRepeat.setSelection(0); // supposed to select Unlimited
    }

    private void initSpinner(int textArrayId, Spinner spinner) {
        ArrayAdapter<CharSequence> adapter;
        adapter = ArrayAdapter.createFromResource(
                this, textArrayId, android.R.layout.simple_spinner_item);
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spinner.setAdapter(adapter);
    }

    private void addTab(TabHost host, String tag, CharSequence text, int viewResource) {
        View indicator = getLayoutInflater().inflate(R.layout.tab_indicator, null);
        TextView tv = (TextView)indicator.findViewById(R.id.indicator_text);
        tv.setText(text);

        TabHost.TabSpec spec = host.newTabSpec(tag);
        spec.setIndicator(indicator);
        spec.setContent(viewResource);
        host.addTab(spec);
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu,
                                        View v,
                                        ContextMenuInfo info) {
        super.onCreateContextMenu(menu, v, info);
        if (v == mMsg) {
            menu.setHeaderTitle("Message Area");
            menu.add(0, MENU_CLEAR, Menu.NONE, "Clear");
        }
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == MENU_CLEAR) {
            mLogBuf.clear();
            mMsg.setText("");
            mMsg.scrollTo(0, 0);
        }
        return true;
    }

    public void onClickSend(View v) {
        CharSequence cs = mInput.getText();
        msgShow("send", cs);
        write(cs);
    }

    public void onClickStartTimer(View v) {
        showDialog(TIMER_DIALOG);
        startTimer();
    }

    public void onClickCompare(View v) {
        Intent i = new Intent(this, ActivityFileChooser.class);
        i.putExtra(Bluebit.CHOOSE_PATH, Bluebit.DATA_DIR);
        startActivityForResult(i, COMPARE_FILE);
    }

    public void onClickChoose(View v) {
        Intent i = new Intent(this, ActivityFileChooser.class);
        i.putExtra(Bluebit.CHOOSE_PATH, Bluebit.DATA_DIR);
        startActivityForResult(i, CHOOSE_FILE);
    }

    public void onClickType(View v) {
        onSetType(mToggleResponse.isChecked());
    }

    public void onClickRcv(View v) {
        toggleReceive();
    }

    private void onSetType(boolean withResponse) {
        Log.d("set write with response:" + withResponse);
    }

    private void toggleReceive() {

        if (mRcvIndicator.isChecked()) {
            disableNotification();
            closeStream();
        } else {
            enableNotification();
            openStream(Bluebit.DEFAULT_LOG);
        }
    }

    private void enableNotification() {
        // XXX: we did not app this request to queue, so the system call might not work correctly
        // if we send request to fast
        boolean set = mService.setCharacteristicNotification(mTransTx, true);
        Log.d("set notification:" + set);
        GattDescriptor dsc = mTransTx.getDescriptor(Bluebit.DES_CLIENT_CHR_CONFIG);
        dsc.setValue(dsc.getConstantBytes(GattDescriptor.ENABLE_NOTIFICATION_VALUE));
        boolean success = mService.writeDescriptor(dsc);
        Log.d("writing enable descriptor:" + success);
    }

    private void disableNotification() {
        // XXX: we did not app this request to queue, so the system call might not work correctly
        // if we send request to fast
        boolean set = mService.setCharacteristicNotification(mTransTx, false);
        Log.d("set notification:" + set);
        GattDescriptor dsc = mTransTx.getDescriptor(Bluebit.DES_CLIENT_CHR_CONFIG);
        dsc.setValue(dsc.getConstantBytes(GattDescriptor.DISABLE_NOTIFICATION_VALUE));
        boolean success = mService.writeDescriptor(dsc);
        Log.d("writing disable descriptor:" + success);
    }

    private void openStream(String path) {
        try {
            File file = new File(path);
            if (!file.exists()) {
                Log.w("Target file does not exist, create: " + path);
                File parent = file.getParentFile();
                Log.w("make dirs:" + parent.getPath());
                parent.mkdirs();
                file.createNewFile();
            }

            mStream = new FileOutputStream(file, false);
        } catch (IOException e) {
            msgShow("open stream fail", e.toString());
            e.printStackTrace();
        }
    }

    private void closeStream() {
        try {
            if (mStream != null) {
                mStream.flush();
                mStream.close();
            }
        } catch (IOException e) {
            msgShow("close stream fail", e.toString());
            e.printStackTrace();
        }

        mStream = null;
    }

    private void writeToStream(byte[] data) {
        if (mStream != null) {
            try {
                mStream.write(data, 0, data.length);
                mStream.flush();
            } catch (IOException e) {
                msgShow("write fail", e.toString());
                e.printStackTrace();
            }
        }
    }

    private void compareFile(String pathA, String pathB) {
        try {
            String md5A = Util.getMD5FromBytes(Util.readBytesFromFile(pathA));
            String md5B = Util.getMD5FromBytes(Util.readBytesFromFile(pathB));
            msgShow(pathA, md5A);
            msgShow(pathB, md5B);
            if (md5A.equals(md5B)) {
                msgShow("compare", "Match");
            } else {
                msgShow("compare", "Not Match");
            }
        } catch (IOException e) {
            msgShow("comapre fail", e.toString());
            e.printStackTrace();
        }
    }

    /**
     * Received data from remote when enabling Echo.
     *
     * Display the data and transfer back to device.
     */
    private void onReceived(byte[] data) {
        StringBuffer sb = new StringBuffer();
        if (data == null) {
            sb.append("Received empty data");
        } else {
            String recv = new String(data);
            sb.append("recv:");
            sb.append(recv);
            writeToStream(data);

            if (mToggleEcho.isChecked()) {
                write(data);
                sb.append("echo:");
                sb.append(recv);
            }
        }
        Bundle msg = new Bundle();
        msg.putCharSequence(INFO_CONTENT, sb);

        updateView(APPEND_MESSAGE, msg);
    }

    @Override
    protected void onActivityResult(int request, int result, Intent data) {
        if (request == CHOOSE_FILE) {
            if (result == Activity.RESULT_OK) {
                Uri uri = data.getData();
                String filePath = uri.getPath();
                Log.d("chosen file:" + filePath);
                try {
                    mStartTime = Calendar.getInstance();
                    write(Util.readBytesFromFile(filePath));
                    msgShow("send", filePath);
                } catch (IOException e) {
                    e.printStackTrace();
                    Log.d("IO Exception");
                }
            }
        } else if (request == COMPARE_FILE) {
            if (data != null) {
                Uri uri = data.getData();
                String filePath = uri.getPath();
                compareFile(filePath, Bluebit.DEFAULT_LOG);
            }
        }
    }

    private void msgShow(CharSequence prefix, CharSequence cs) {
        StringBuffer sb = new StringBuffer();
        sb.append(prefix);
        sb.append(": ");
        sb.append(cs);
        Log.d(sb.toString());
        Bundle msg = new Bundle();
        msg.putCharSequence(INFO_CONTENT, sb.toString());
        updateView(APPEND_MESSAGE, msg);
    }

    /**
     * Write string to remote device.
     */
    private void write(CharSequence cs) {
        byte[] bytes = cs.toString().getBytes();
        write(bytes);
    }

    /**
     * Write data to remote device.
     */
    private void write(byte[] bytes) {
        ByteBuffer buf = ByteBuffer.allocate(bytes.length);
        buf.put(bytes);
        buf.position(0);
        while(buf.remaining() != 0) {
            int size = (buf.remaining() > PAYLOAD_MAX) ? PAYLOAD_MAX: buf.remaining();
            byte[] dst = new byte[size];
            buf.get(dst, 0, size);
            GattTransaction t = new GattTransaction(mTransRx, dst);
            mQueue.add(t);
        }
    }

    @Override
    protected Dialog onCreateDialog(int id, Bundle args) {
        /*FIXME: this function is deprecated. */
        if (id == CONNECTION_DIALOG) {
            mConnectionDialog = new ProgressDialog(this);
            mConnectionDialog.setMessage(this.getString(R.string.connecting));
            mConnectionDialog.setCancelable(true);
            return mConnectionDialog;
        } else if (id == TIMER_DIALOG) {
            mTimerDialog = new ProgressDialog(this);
            mTimerDialog.setMessage("Timer is running");
            mTimerDialog.setOnCancelListener(new Dialog.OnCancelListener() {
                public void onCancel(DialogInterface dialog) {
                    Log.d("some one canceled me");
                    stopTimer();
                }
            });
            return mTimerDialog;
        }
        return null;
    }

    private void onTimerSend(int count, int size) {
        /* max is 20 */
        String out = String.format("%020d", count);
        if (out.length() > size) {
            // if too long
            out = out.substring(out.length() - size);
        }
        msgShow("send", out);
        write(out);
    }

    private boolean mRunning;

    private void startTimer() {

        final int delta  = mValueDelta[mSpinnerDelta.getSelectedItemPosition()];
        final int size   = mValueSize[mSpinnerSize.getSelectedItemPosition()];
        final int repeat = mValueRepeat[mSpinnerRepeat.getSelectedItemPosition()];
        mRunning = true;
        Thread runner = new Thread() {
            public void run() {
                int counter = 0;
                try {
                    while(mRunning) {
                        if (repeat != 0 && repeat == counter) {
                            stopTimer();
                        } else {
                            onTimerSend(counter, size);
                            sleep(delta);
                            counter++;
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                updateView(DISMISS_TIMER_DIALOG, null);
            }
        };
        runner.start();
    }

    private void stopTimer() {
        mRunning = false;
    }

    /**
     * Add message to UI.
     */
    private void appendMsg(CharSequence msg) {
        StringBuffer sb = new StringBuffer();
        sb.append(msg);
        sb.append("\n");
        mLogBuf.add(sb);
        // we don't want to display too many lines
        if (mLogBuf.size() > MAX_LINES) {
            mLogBuf.remove(0);
        }

        StringBuffer text = new StringBuffer();
        for (int i = 0; i < mLogBuf.size(); i++) {
            text.append(mLogBuf.get(i));
        }
        mMsg.setText(text);
    }

    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() {
        Log.d("transparent activity disconnected, closing");
        stopTimer();
        mStartTime = null;
        mQueue.clear();
        this.setResult(Bluebit.RESULT_REMOTE_DISCONNECT);
        this.finish();
    }

    private void onDiscovered() {
        updateView(DISMISS_CONNECTION_DIALOG, null);

        GattService proprietary = mService.getService(mDevice, Bluebit.SERVICE_ISSC_PROPRIETARY);
        mTransTx = proprietary.getCharacteristic(Bluebit.CHR_ISSC_TRANS_TX);
        mTransRx = proprietary.getCharacteristic(Bluebit.CHR_ISSC_TRANS_RX);
        Log.d(String.format("found Tx:%b, Rx:%b", mTransTx != null, mTransRx != null));
    }

    @Override
    public void onTransact(GattTransaction t) {
        t.chr.setValue(t.value);
        if (t.isWrite) {
            int type = mToggleResponse.isChecked() ?
                GattCharacteristic.WRITE_TYPE_DEFAULT:
                GattCharacteristic.WRITE_TYPE_NO_RESPONSE;
            t.chr.setWriteType(type);
            mService.writeCharacteristic(t.chr);
        } else {
            mService.readCharacteristic(t.chr);
        }
    }

    public void updateView(int tag, Bundle info) {
        if (info == null) {
            info = new Bundle();
        }

        // remove previous log since the latest log
        // already contains needed information.
        mViewHandler.removeMessages(tag);

        Message msg = mViewHandler.obtainMessage(tag);
        msg.what = tag;
        msg.setData(info);
        mViewHandler.sendMessage(msg);
    }

    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 == SHOW_CONNECTION_DIALOG) {
                showDialog(CONNECTION_DIALOG);
            } else if (tag == DISMISS_CONNECTION_DIALOG) {
                if (mConnectionDialog != null && mConnectionDialog.isShowing()) {
                    dismissDialog(CONNECTION_DIALOG);
                }
            } else if (tag == DISMISS_TIMER_DIALOG) {
                if (mTimerDialog != null && mTimerDialog.isShowing()) {
                    dismissDialog(TIMER_DIALOG);
                }
            } else if (tag == CONSUME_TRANSACTION) {
                // mQueue itself will consume next transaction
                //mQueue.process();
            } else if (tag == APPEND_MESSAGE) {
                CharSequence content = bundle.getCharSequence(INFO_CONTENT);
                if (content != null) {
                    appendMsg(content);

                    /*fot automaticaly scrolling to end*/
                    final int amount = mMsg.getLayout().getLineTop(mMsg.getLineCount())
                        - mMsg.getHeight();
                    if (amount > 0) {
                        mMsg.scrollTo(0, amount);
                    }
                }
            } else if (tag == RCV_STATE) {
                mRcvIndicator.setChecked(bundle.getBoolean(RCV_ENABLED, false));
            }
        }
    }

    class GattListener extends Gatt.ListenerHelper {

        GattListener() {
            super("ActivityTransparent");
        }

        @Override
        public void onConnectionStateChange(Gatt gatt, int status, int newState) {
            if (!mDevice.getAddress().equals(gatt.getDevice().getAddress())) {
                // not the device I care about
                return;
            }

            if (newState == BluetoothProfile.STATE_CONNECTED) {
                onConnected();
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                onDisconnected();
            }
        }

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

        @Override
        public void onCharacteristicRead(Gatt gatt, GattCharacteristic charac, int status) {
            Log.d("read char, uuid=" + charac.getUuid().toString());
            byte[] value = charac.getValue();
            Log.d("get value, byte length:" + value.length);
            for (int i = 0; i < value.length; i++) {
                Log.d("[" + i + "]" + Byte.toString(value[i]));
            }
            mQueue.onConsumed();
        }

        @Override
        public void onCharacteristicChanged(Gatt gatt, GattCharacteristic chrc) {
            Log.d("on chr changed" );
            if (chrc.getUuid().equals(Bluebit.CHR_ISSC_TRANS_TX)) {
                onReceived(chrc.getValue());
            }
        }

        @Override
        public void onCharacteristicWrite(Gatt gatt, GattCharacteristic charac, int status) {
            if (status == Gatt.GATT_SUCCESS) {
                mSuccess +=charac.getValue().length;
            } else {
                mFail += charac.getValue().length;
            }

            String s = String.format("%d bytes, success= %d, fail= %d, pending= %d",
                    charac.getValue().length,
                    mSuccess,
                    mFail,
                    mQueue.size());
            msgShow("wrote", s);
            mQueue.onConsumed();
            if (mQueue.size() == 0 && mStartTime != null) {
                long elapse =  Calendar.getInstance().getTimeInMillis()
                    - mStartTime.getTimeInMillis();
                msgShow("time", "spent " + (elapse / 1000) + " seconds");
                mStartTime = null;
            }
            updateView(CONSUME_TRANSACTION, null);
        }

        @Override
        public void onDescriptorWrite(Gatt gatt, GattDescriptor dsc, int status) {
            if (status == Gatt.GATT_SUCCESS) {
                byte[] value = dsc.getValue();
                if (Arrays.equals(value, dsc.getConstantBytes(GattDescriptor.ENABLE_NOTIFICATION_VALUE))) {
                    Bundle state = new Bundle();
                    state.putBoolean(RCV_ENABLED, true);
                    updateView(RCV_STATE, state);
                } else if (Arrays.equals(value, dsc.getConstantBytes(GattDescriptor.DISABLE_NOTIFICATION_VALUE))) {
                    Bundle state = new Bundle();
                    state.putBoolean(RCV_ENABLED, false);
                    updateView(RCV_STATE, state);
                }
            }
        }
    }

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

            int conn = mService.getConnectionState(mDevice);
            if (conn == BluetoothProfile.STATE_DISCONNECTED) {
                onDisconnected();
            } else {
                Log.d("already connected");
                onConnected();
            }
        }

        @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