Google Glass GDK:如何与Android设备通信
我正在寻找一种在我的Android设备和不依赖云API的Google Glass之间发送数据的方法。 这是支持吗? 我在“我的玻璃”应用程序中看到蓝牙连接,这让我觉得可以完成。 有没有一个示例源代码显示如何完成? 或者我必须反编译MyGlass应用程序来弄清楚?
有没有一个这样的数据传输的首选方法? 理想情况下,我想在两个方向上传输数据。
好的,对于请求者….
编辑:下面的代码仍然有效,但我已经把它放到一个git回购为有兴趣的人…
https://github.com/NathanielWaggoner/GoogleGlassBlutooth
这是我的蓝牙主机/客户端代码。 这不是完美的 – 你需要一些耐心,重新连接等有一些错误,但它确实有效。 我一直在使用这个function发送数据到手持式计算机上的Glass,并使用它来驱动UI更新(发布活动卡片,更新活动卡片等)。
主办:
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.UUID; import android.app.Activity; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothServerSocket; import android.bluetooth.BluetoothSocket; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.Log; import android.view.Menu; import android.widget.TextView; public class BluetoothHost extends Activity { public static String msgToSend=""; public static final int STATE_CONNECTION_STARTED = 0; public static final int STATE_CONNECTION_LOST = 1; public static final int READY_TO_CONN = 2; public static final String DEVICE_NAME = "device_name"; public static final String TOAST = "toast"; // our last connection ConnectedThread mConnectedThread;// = new ConnectedThread(socket); // track our connections ArrayList<ConnectedThread> mConnThreads; // bt adapter for all your bt needs (where we get all our bluetooth powers) BluetoothAdapter myBt; // list of sockets we have running (for multiple connections) ArrayList<BluetoothSocket> mSockets = new ArrayList<BluetoothSocket>(); // list of addresses for devices we've connected to ArrayList<String> mDeviceAddresses = new ArrayList<String>(); // just a name, nothing more... String NAME="G6BITCHES"; // We can handle up to 7 connections... or something... UUID[] uuids = new UUID[2]; // some uuid's we like to use.. String uuid1 = "05f2934c-1e81-4554-bb08-44aa761afbfb"; String uuid2 = "c2911cd0-5c3c-11e3-949a-0800200c9a66"; // just a tag.. String TAG = "G6 Bluetooth Host Activity"; // constant we define and pass to startActForResult (must be >0), that the system passes back to you in your onActivityResult() // implementation as the requestCode parameter. int REQUEST_ENABLE_BT = 1; AcceptThread accThread; TextView connectedDevices; Handler handle; BroadcastReceiver receiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // the activity for this is pretty stripped, just a basic selection ui.... setContentView(R.layout.activity_main); uuids[0] = UUID.fromString(uuid1); uuids[1] = UUID.fromString(uuid2); connectedDevices = (TextView) findViewById(R.id.connected_devices_values); handle = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { case STATE_CONNECTION_STARTED: connectedDevices.setText(msg.getData().getString("NAMES")); break; case STATE_CONNECTION_LOST: connectedDevices.setText(""); startListening(); break; case READY_TO_CONN: startListening(); default: break; } } }; // .... myBt = BluetoothAdapter.getDefaultAdapter(); // run the "go get em" thread.. accThread = new AcceptThread(); accThread.start(); } public void startListening() { if(accThread!=null) { accThread.cancel(); }else if (mConnectedThread!= null) { mConnectedThread.cancel(); } else { accThread = new AcceptThread(); accThread.start(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } private class AcceptThread extends Thread { private BluetoothServerSocket mmServerSocket; BluetoothServerSocket tmp; public AcceptThread() { BluetoothServerSocket tmp = null; try { tmp = myBt.listenUsingInsecureRfcommWithServiceRecord(NAME, uuids[0]); } catch (IOException e) { } mmServerSocket = tmp; } public void run() { Log.e(TAG,"Running?"); BluetoothSocket socket = null; // Keep listening until exception occurs or a socket is returned while (true) { try { socket = mmServerSocket.accept(); } catch (IOException e) { e.printStackTrace(); break; } // If a connection was accepted if (socket != null) { try { mmServerSocket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } // Do work to manage the connection (in a separate thread) manageConnectedSocket(socket); break; } } } /** Will cancel the listening socket, and cause the thread to finish */ public void cancel() { try { mmServerSocket.close(); Message msg = handle.obtainMessage(READY_TO_CONN); handle.sendMessage(msg); } catch (IOException e) { } } } private void manageConnectedSocket(BluetoothSocket socket) { // start our connection thread mConnectedThread = new ConnectedThread(socket); mConnectedThread.start(); // Send the name of the connected device back to the UI Activity // so the HH can show you it's working and stuff... String devs=""; for(BluetoothSocket sock: mSockets) { devs+=sock.getRemoteDevice().getName()+"\n"; } // pass it to the UI.... Message msg = handle.obtainMessage(STATE_CONNECTION_STARTED); Bundle bundle = new Bundle(); bundle.putString("NAMES", devs); msg.setData(bundle); handle.sendMessage(msg); } private class ConnectedThread extends Thread { private final BluetoothSocket mmSocket; private final InputStream mmInStream; private final OutputStream mmOutStream; public ConnectedThread(BluetoothSocket socket) { Log.d(TAG, "create ConnectedThread"); mmSocket = socket; InputStream tmpIn = null; OutputStream tmpOut = null; // Get the BluetoothSocket input and output streams try { tmpIn = socket.getInputStream(); tmpOut = socket.getOutputStream(); } catch (IOException e) { Log.e(TAG, "temp sockets not created", e); } mmInStream = tmpIn; mmOutStream = tmpOut; } public void run() { Log.i(TAG, "BEGIN mConnectedThread"); byte[] buffer = new byte[1024]; int bytes; // Keep listening to the InputStream while connected while (true) { try { //byte[] blah = ("System Time:" +System.currentTimeMillis()).getBytes(); if(!msgToSend.equals("")) { Log.e(TAG,"writing!"); write(msgToSend.getBytes()); setMsg(""); } Thread.sleep(1000); } catch (Exception e) { Log.e(TAG, "disconnected", e); connectionLost(); } } } public void connectionLost() { Message msg = handle.obtainMessage(STATE_CONNECTION_LOST); handle.sendMessage(msg); } /** * Write to the connected OutStream. * @param buffer The bytes to write */ public void write(byte[] buffer) { try { mmOutStream.write(buffer); } catch (IOException e) { Log.e(TAG, "Exception during write", e); connectionLost(); } } public void cancel() { try { mmSocket.close(); Message msg = handle.obtainMessage(READY_TO_CONN); handle.sendMessage(msg); } catch (IOException e) { Log.e(TAG, "close() of connect socket failed", e); } } } public static synchronized void setMsg(String newMsg) { msgToSend = newMsg; } public static class HostBroadRec extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Bundle b= intent.getExtras(); String vals =""; for(String key: b.keySet()) { vals+=key+"&"+b.getString(key)+"Z"; } BluetoothHost.setMsg(vals); } } }
客户:
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Set; import java.util.UUID; import android.app.Activity; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothSocket; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.text.format.DateFormat; import android.util.Log; import android.view.Menu; import android.view.View; import android.view.WindowManager; import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.ArrayAdapter; import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; public class BluetoothClient extends Activity { public static final int READY_TO_CONN =0; public static final int CANCEL_CONN =1; public static final int MESSAGE_READ =2; // holds the bluetooth names/ids that we're associated with. ArrayAdapter<String> btArray; // bt adapter for all your bt needs BluetoothAdapter myBt; String NAME="G6BITCHES"; String TAG = "G6 Bluetooth Slave Activity"; UUID[] uuids = new UUID[2]; // some uuid's we like to use.. String uuid1 = "05f2934c-1e81-4554-bb08-44aa761afbfb"; String uuid2 = "c2911cd0-5c3c-11e3-949a-0800200c9a66"; // DateFormat df = new DateFormat("ddyyyy") ConnectThread mConnThread; Spinner devices; Handler handle; // constant we define and pass to startActForResult (must be >0), that the system passes back to you in your onActivityResult() // implementation as the requestCode parameter. int REQUEST_ENABLE_BT = 1; // bc for discovery mode for BT... private final BroadcastReceiver mReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); // When discovery finds a device if (BluetoothDevice.ACTION_FOUND.equals(action)) { // Get the BluetoothDevice object from the Intent BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); // Add the name and address to an array adapter to show in a ListView if(device!= null) { if(device.getName().contains("Nexus")) { } else { btArray.add(device.getName() + "\n" + device.getAddress()); } } update(); } } }; Context ctx; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // publishCards(this); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); ctx = this; handle = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { case READY_TO_CONN: mConnThread=null; update(); break; case CANCEL_CONN: break; case MESSAGE_READ: byte[] readBuf = (byte[]) msg.obj; // construct a string from the valid bytes in the buffer String readMessage = new String(readBuf, 0, msg.arg1); Log.e(TAG,"received: "+readMessage); if (readMessage.length() > 0) { // do soemthing... } // updateCards(ctx, readMessage); // update() // mConversationArrayAdapter.add(mConnectedDeviceName+": " + readMessage); break; default: break; } } }; btArray = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, android.R.id.text1); btArray.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); uuids[0] = UUID.fromString(uuid1); uuids[1] = UUID.fromString(uuid2); // spinner for displaying available devices for pairing devices = (Spinner) findViewById(R.id.devices_spinner); devices.setAdapter(btArray); // use the same UUID across an installation // should allow clients to find us repeatedly myBt = BluetoothAdapter.getDefaultAdapter(); if (myBt == null) { Toast.makeText(this, "Device Does not Support Bluetooth", Toast.LENGTH_LONG).show(); } else if (!myBt.isEnabled()) { // we need to wait until bt is enabled before set up, so that's done either in the following else, or // in the onActivityResult for our code ... Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); } else { detectAndSetUp(); } setContentView(R.layout.bluetooth_activity_layout); } @Override public void onDestroy() { unregisterReceiver(mReceiver); super.onDestroy(); } @Override protected void onActivityResult (int requestCode, int resultCode, Intent data){ if(requestCode == REQUEST_ENABLE_BT) { if (resultCode != RESULT_OK) { Toast.makeText(this, "Failed to enable Bluetooth", Toast.LENGTH_LONG).show(); } else { Toast.makeText(this, "Bluetooth Enabled", Toast.LENGTH_LONG).show(); detectAndSetUp(); } } } private void detectAndSetUp() { IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy Set<BluetoothDevice> pairedDevices = myBt.getBondedDevices(); // If there are paired devices if (pairedDevices.size() > 0) { // Loop through paired devices for (BluetoothDevice device : pairedDevices) { if(device.getName().contains("Nexus")) { } else { btArray.add(device.getName() + "\n" + device.getAddress()); } // Add the name and address to an array adapter to show in a ListView // btArray.add(device.getName() + "\n" + device.getAddress()); // update(); } } myBt.startDiscovery(); } public void update() { devices = (Spinner) findViewById(R.id.devices_spinner); devices.setAdapter(btArray); devices.setOnItemSelectedListener(new OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> arg0, View arg1, int position, long id) { if(mConnThread!=null) { Log.e(TAG,"Canceling old connection, and starting new one."); mConnThread.cancel(); } else { Log.e(TAG,"got a thing..."); String str = ((TextView)arg1).getText().toString(); Log.e(TAG,"tots: "+str); String[] vals = str.split("\n"); Log.e(TAG,"mac: "+vals[1]); BluetoothDevice dev = myBt.getRemoteDevice(vals[1]); mConnThread = new ConnectThread(dev); mConnThread.run(); } } @Override public void onNothingSelected(AdapterView<?> arg0) { // TODO Auto-generated method stub } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.bluetooth, menu); return true; } private class ConnectThread extends Thread { private final BluetoothSocket mmSocket; private final BluetoothDevice mmDevice; public ConnectThread(BluetoothDevice device) { Log.e(TAG,"ConnectThread start...."); // Use a temporary object that is later assigned to mmSocket, // because mmSocket is final BluetoothSocket tmp = null; mmDevice = device; // Get a BluetoothSocket to connect with the given BluetoothDevice try { // this seems to work on the note3... // you can remove the Insecure if you want to... tmp = device.createInsecureRfcommSocketToServiceRecord(uuids[0]); // Method m; // this is an approach I've seen others use, it wasn't nescesary for me, // but your results may vary... // m = device.getClass().getMethod("createInsecureRfcommSocket", new Class[] {int.class}); // tmp = (BluetoothSocket) m.invoke(device, 1); // } catch (NoSuchMethodException e1) { // // TODO Auto-generated catch block // e1.printStackTrace(); // } catch (IllegalArgumentException e2) { // // TODO Auto-generated catch block // e2.printStackTrace(); // } catch (IllegalAccessException e3) { // // TODO Auto-generated catch block // e3.printStackTrace(); // } catch (InvocationTargetException e4) { // // TODO Auto-generated catch block // e4.printStackTrace(); // } // if(tmp.isConnected()) { // break // } } catch (Exception e) { Log.e(TAG,"Danger Will Robinson"); e.printStackTrace(); } mmSocket = tmp; } public void run() { // Cancel discovery because it will slow down the connection myBt.cancelDiscovery(); Log.e(TAG,"stopping discovery"); try { // Connect the device through the socket. This will block // until it succeeds or throws an exception Log.e(TAG,"connecting!"); mmSocket.connect(); } catch (IOException connectException) { Log.e(TAG,"failed to connect"); // Unable to connect; close the socket and get out try { Log.e(TAG,"close-ah-da-socket"); mmSocket.close(); } catch (IOException closeException) { Log.e(TAG,"failed to close hte socket"); } Log.e(TAG,"returning.."); return; } Log.e(TAG,"we can now manage our connection!"); // Do work to manage the connection (in a separate thread) manageConnectedSocket(mmSocket); } /** Will cancel an in-progress connection, and close the socket */ public void cancel() { try { mmSocket.close(); Message msg = handle.obtainMessage(READY_TO_CONN); handle.sendMessage(msg); } catch (IOException e) { } } } public void manageConnectedSocket(BluetoothSocket mmSocket) { ConnectedThread t = new ConnectedThread(mmSocket); t.start(); // manage your socket... I'll probably do a lot of the boiler plate here later } private class ConnectedThread extends Thread { private final BluetoothSocket mmSocket; private final InputStream mmInStream; private final OutputStream mmOutStream; public ConnectedThread(BluetoothSocket socket) { Log.d(TAG, "create ConnectedThread"); mmSocket = socket; InputStream tmpIn = null; OutputStream tmpOut = null; // Get the BluetoothSocket input and output streams try { tmpIn = socket.getInputStream(); tmpOut = socket.getOutputStream(); } catch (IOException e) { Log.e(TAG, "temp sockets not created", e); } mmInStream = tmpIn; mmOutStream = tmpOut; } public void run() { Log.i(TAG, "BEGIN mConnectedThread"); byte[] buffer = new byte[1024]; int bytes; // Keep listening to the InputStream while connected while (true) { try { // byte[] blah = ("System Time:" +System.currentTimeMillis()).getBytes(); // write(blah); // Thread.sleep(1000); // Read from the InputStream bytes = mmInStream.read(buffer); // Send the obtained bytes to the UI Activity handle.obtainMessage(MESSAGE_READ, bytes, -1, buffer) .sendToTarget(); // .sendToTarget(); } catch (Exception e) { Log.e(TAG, "disconnected", e); connectionLost(); // break; } } } public void connectionLost() { Message msg = handle.obtainMessage(CANCEL_CONN); // Bundle bundle = new Bundle(); // bundle.putString("NAMES", devs); // msg.setData(bundle); handle.sendMessage(msg); } /** * Write to the connected OutStream. * @param buffer The bytes to write */ public void write(byte[] buffer) { try { mmOutStream.write(buffer); // Share the sent message back to the UI Activity // mHandler.obtainMessage(BluetoothChat.MESSAGE_WRITE, -1, -1, buffer) // .sendToTarget(); } catch (IOException e) { Log.e(TAG, "Exception during write", e); } } public void cancel() { try { mmSocket.close(); } catch (IOException e) { Log.e(TAG, "close() of connect socket failed", e); } } } }
主持人清单:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="transapps.android_bluetooth_host" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="17" /> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:name="transapps.android_bluetooth_host.BluetoothHost" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <receiver android:name=".BluetoothHost$HostBroadRec" > <intent-filter> <action android:name="transapps.g6.new.alert" /> </intent-filter> </receiver> </application> </manifest>
客户清单:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="transapps.android_blutooth" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="15" /> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:name="transapps.android_blutooth.BluetoothClient" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
我将把UI作为练习留给读者。
没有首选的方法,但如果你正在寻找无线的话,一个蓝牙3.0 RFCOMM确实可以工作。
如果您提供关于您想要解决的问题的更多细节,我将能够提供更具体的答案。
遵循这个解决scheme时我注意到了几件事情(真棒!
1)当我的手机和Google Glass尚未通过MyGlass应用程序配对时,我只能创build蓝牙连接 – 如果您在build立连接时遇到问题,请尝试忘记配对。
2)Glass API不支持通过意图使用命令来控制蓝牙连接
Intent discoveryIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); discoveryIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, DISCOVER_DURATION); startActivityForResult(discoveryIntent, REQUEST_BLU);
为了使玻璃耳机可以发现(而不是它连接的电话),我需要遵循类似于这个问题提供的user4934624和shantanu在这个问题提供的解决scheme。 我调用了一个隐藏的方法直接访问蓝牙function。 警告:隐藏的方法似乎已经存在多年,但不能保证它将继续在未来的API中。
// this method allows us to make the device discoverable without alerting the users // NOTE!!!! This uses a hidden method, so it may be removed from the API in the future public void makeDiscoverable (){ Class <?> baClass = BluetoothAdapter.class; Method[] methods = baClass.getDeclaredMethods(); // we want to use method setScanMode(int mode, int duration) // there are 2 setScanModes // so select the first setScanMode you see // test to see which method is the one we want //for (int i = 0; i<50; i++) {Log.d(Integer.toString(i), methods[i].getName());} //I had trouble calling the first setScanMode, so I called the second. // I need to pass in a discoverable time, but it stays discoverable indefinitely // Thus you must turn off the setScanMode as soon as the connection is established // I should probably write in some other security stuff to turn it off if connection fails // maybe a timer running on a different thread? Method mSetScanMode = methods[38]; try { mSetScanMode.invoke(myBt, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE,300); //mSetScanMode.invoke(myBt, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE); } catch (Exception e) { Log.e("discoverable", e.getMessage()); } } // this method allows us to make the device not discoverable without alerting the user // NOTE!!!! This uses a hidden method, so it may be removed from the API in the future public void makeNotDiscoverable (){ // see notes for makeDiscoverable Class <?> baClass = BluetoothAdapter.class; Method [] methods = baClass.getDeclaredMethods(); Method mSetScanMode = methods[38]; try { mSetScanMode.invoke(myBt, BluetoothAdapter.SCAN_MODE_CONNECTABLE,300); } catch (Exception e) { Log.e("discoverable", e.getMessage()); } }
请注意,我首先运行一个testing,输出我镜像的类中的所有方法的名称; 这让我缩小了我想要使用哪种方法的search范围。
Android的JoeGlass应用声称是MyGlass的替代品。 它会直接与你的玻璃谈话,使用蓝牙连接。 我还没有尝试过,但它是开源(github),所以如果它的工作,你是好的。
我的书开始Google Glass Development有一个完整的章节,完整的示例代码使用蓝牙和套接字在Glass和Android(或iOS)设备之间传输数据。
这本书可在亚马逊: http : //www.amazon.com/Beginning-Google-Glass-Development-Jeff/dp/1430267887
源代码可在http://www.apress.com/downloadable/download/sample/sample_id/1562/下载。;