Java tutorial
/* * Copyright 2010-2011, Qualcomm Innovation Center, Inc. * * 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 org.alljoyn.bus.samples.simpleclient; import android.app.ProgressDialog; import android.content.Context; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import org.alljoyn.bus.BusAttachment; import org.alljoyn.bus.BusException; import org.alljoyn.bus.BusListener; import org.alljoyn.bus.Mutable; import org.alljoyn.bus.ProxyBusObject; import org.alljoyn.bus.SessionListener; import org.alljoyn.bus.SessionOpts; import org.alljoyn.bus.Status; import java.util.ArrayList; public class DevicesActivity extends FragmentActivity implements DeviceListFragment.OnDeviceSelectedListener, DeviceDetailsFragment.OnButtonClickedListener { /* Load the native alljoyn_java library. */ static { System.loadLibrary("alljoyn_java"); } private static final int MESSAGE_PING = 1; private static final int MESSAGE_PING_REPLY = 2; private static final int MESSAGE_POST_TOAST = 3; private static final int MESSAGE_START_PROGRESS_DIALOG = 4; private static final int MESSAGE_STOP_PROGRESS_DIALOG = 5; private static final int MESSAGE_ADD_FOUND_DEVICE = 6; private static final int MESSAGE_REMOVE_FOUND_DEVICE = 7; private static final int MESSAGE_UPDATE_DETAILSFRAGMENT_UI = 8; //TODO // should make transport a variable private short DEVICE_TRANSPORT; private static final String SERVICE_NAME = "org.alljoyn.bus.samples.simple"; private static String mDeviceType; private static String mDeviceName; private static final String TAG = "SimpleDevicesClient"; private ArrayAdapter<String> mListViewArrayAdapter; private ListView mListView; private Menu menu; private ArrayList<Service> serviceArrayList = new ArrayList<Service>(); private Bundle mBundle; private Context mContext; // Git is cool // this is definitely cool // so now what? // /* Handler used to make calls to AllJoyn methods. See onCreate(). */ private BusHandler mBusHandler; private ProgressDialog mDialog; //private Fragment mDeviceListFragment; private DeviceListFragment mDeviceListFragment; private DeviceDetailsFragment mDeviceDetailsFragment; // /** // * Called from the AllJoyn Service when it gets a FoundAdvertisedName. We // * know by construction that the advertised name will correspond to an chat // * channel. Note that the channel here is the complete well-known name of // * the bus attachment advertising the channel. In most other places it is // * simply the channel name, which is the final segment of the well-known // * name. // */ // public synchronized void addFoundDevice(Service foundService) { // Log.i(TAG, "addFoundDevice(" + foundService.getWellKnownName() + ")"); // Message msg = mHandler.obtainMessage(MESSAGE_ADD_FOUND_DEVICE); // msg.obj = foundService.getWellKnownName(); // msg.arg1 = foundService.getTransport(); // mHandler.sendMessage(msg); // } /** * Called from the AllJoyn Service when it gets a FoundAdvertisedName. We * know by construction that the advertised name will correspond to an chat * channel. Note that the channel here is the complete well-known name of * the bus attachment advertising the channel. In most other places it is * simply the channel name, which is the final segment of the well-known * name. */ public synchronized void addFoundDevice(Message deviceInfo) { mHandler.sendMessage(deviceInfo); } // // /** // * Called from the AllJoyn Service when it gets a LostAdvertisedName. We // * know by construction that the advertised name will correspond to an chat // * channel. // */ // public synchronized void removeFoundDevice(Service lostService) { // Log.i(TAG, "removeFoundDevice(" + lostService.getWellKnownName() + ")"); // Message msg = mHandler.obtainMessage(MESSAGE_REMOVE_FOUND_DEVICE); // msg.obj = lostService.getWellKnownName(); // msg.arg1 = lostService.getTransport(); // mHandler.sendMessage(msg); // } /** * Called from the AllJoyn Service when it gets a LostAdvertisedName. We * know by construction that the advertised name will correspond to an chat * channel. */ public synchronized void removeFoundDevice(Message deviceInfo) { mHandler.sendMessage(deviceInfo); } private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { int deviceNum; String deviceName; switch (msg.what) { case MESSAGE_PING: String ping = (String) msg.obj; mListViewArrayAdapter.add("Ping: " + ping); break; case MESSAGE_PING_REPLY: String ret = (String) msg.obj; mListViewArrayAdapter.add("Reply: " + ret); break; case MESSAGE_POST_TOAST: Toast.makeText(getApplicationContext(), (String) msg.obj, Toast.LENGTH_LONG).show(); break; case MESSAGE_START_PROGRESS_DIALOG: mDialog = ProgressDialog.show(DevicesActivity.this, "", "Finding Simple Service.\nPlease wait...", true, true); break; case MESSAGE_STOP_PROGRESS_DIALOG: mDialog.dismiss(); break; case MESSAGE_ADD_FOUND_DEVICE: deviceName = msg.obj.toString(); Log.i("Device Name", deviceName); // "com.flybits.devices.coffeemaker.My_Coffee_Maker" deviceName = deviceName.replace(SERVICE_NAME + ".", "").replace("_", " "); // "coffeemaker.My Coffee Maker" mDeviceListFragment.addFoundDevice(deviceName); if (mDialog.isShowing()) { mHandler.sendEmptyMessage(MESSAGE_STOP_PROGRESS_DIALOG); } break; case MESSAGE_REMOVE_FOUND_DEVICE: deviceName = msg.obj.toString(); // "com.flybits.devices.coffeemaker.My_Coffee_Maker" deviceName = deviceName.replace(SERVICE_NAME + ".", "").replace("_", " "); // "coffeemaker.My Coffee Maker" mDeviceListFragment.removeFoundDevice(deviceName); deviceNum = mDeviceListFragment.getDeviceArraySize(); if (deviceNum <= 0 && !mDialog.isShowing()) { mHandler.sendEmptyMessage(MESSAGE_START_PROGRESS_DIALOG); } break; case MESSAGE_UPDATE_DETAILSFRAGMENT_UI: TextView textView = (TextView) findViewById(R.id.deviceNameTextView); textView.setText(mBundle.getString("Name")); break; default: break; } } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_devices); mContext = this; // mEditText = (EditText) findViewById(R.id.EditText); // mEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() { // public boolean onEditorAction(TextView view, int actionId, KeyEvent event) { // if (actionId == EditorInfo.IME_NULL // && event.getAction() == KeyEvent.ACTION_UP) { // /* Call the remote object's Ping method. */ // Message msg = mBusHandler.obtainMessage(BusHandler.PING, // view.getText().toString()); // mBusHandler.sendMessage(msg); // } // return true; // } // }); //Master data list Fragment String fullName = SERVICE_NAME + ".coffeemaker.Beagle_Bone"; String[] tokens = fullName.split("[.]"); for (String token : tokens) { Log.i("token:", token); } mDeviceListFragment = new DeviceListFragment(); FragmentManager fragmentManager = getSupportFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); fragmentTransaction.add(R.id.myFragmentContainer, mDeviceListFragment, "DeviceListFragment"); fragmentTransaction.commit(); /* Make all AllJoyn calls through a separate handler thread to prevent blocking the UI. */ HandlerThread busThread = new HandlerThread("BusHandler"); busThread.start(); mBusHandler = new BusHandler(busThread.getLooper()); /* Connect to an AllJoyn object. */ mBusHandler.sendEmptyMessage(BusHandler.CONNECT); mHandler.sendEmptyMessage(MESSAGE_START_PROGRESS_DIALOG); } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.mainmenu, menu); this.menu = menu; return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle item selection switch (item.getItemId()) { case R.id.quit: finish(); return true; default: return super.onOptionsItemSelected(item); } } @Override protected void onDestroy() { super.onDestroy(); /* Disconnect to prevent resource leaks. */ mBusHandler.sendEmptyMessage(BusHandler.DISCONNECT); } @Override public void onDeviceSelected(String deviceName) { // Store the created fragment into a variable if (mDeviceDetailsFragment == null) { FragmentManager fragmentManager = getSupportFragmentManager(); mDeviceDetailsFragment = (DeviceDetailsFragment) fragmentManager .findFragmentByTag("DeviceDetailsFragment"); } Log.d("Device selected:", deviceName); // coffeemaker.My Coffee Maker //TODO // What happens when 2 services have the same name? Message msg = mBusHandler.obtainMessage(mBusHandler.JOIN_SESSION); msg.arg1 = DEVICE_TRANSPORT; String fullAdvertisedName = SERVICE_NAME + "." + deviceName.replace(" ", "_"); // com.flybits.devices.coffeemaker.My_Coffee_Maker msg.obj = fullAdvertisedName; mBusHandler.sendMessage(msg); } @Override public void onButtonClicked() { mBusHandler.sendEmptyMessage(BusHandler.GET_DEVICE_NAME); } /* This class will handle all AllJoyn calls. See onCreate(). */ class BusHandler extends Handler { /* * Name used as the well-known name and the advertised name of the service this client is * interested in. This name must be a unique name both to the bus and to the network as a * whole. * * The name uses reverse URL style of naming, and matches the name used by the service. */ private static final String SERVICE_NAME = "org.alljoyn.bus.samples.simple"; private static final short CONTACT_PORT = 42; private BusAttachment mBus; private ProxyBusObject mProxyObj; private SimpleInterface mSimpleInterface; private CoffeeMakerInterface mCoffeeMakerInterface; private ThermostatInterface mThermostatInterface; private int mSessionId; private boolean mIsInASession; private boolean mIsConnected; private boolean mIsStoppingDiscovery; /* These are the messages sent to the BusHandler from the UI. */ public static final int CONNECT = 1; public static final int JOIN_SESSION = 2; public static final int DISCONNECT = 3; public static final int PING = 4; public static final int GET_DEVICE_NAME = 5; public BusHandler(Looper looper) { super(looper); mIsInASession = false; mIsConnected = false; mIsStoppingDiscovery = false; } private void interfaceSelector(String deviceType, String WellKnownName, int sessionIdvalue, boolean connect) { if (connect) { if (deviceType.contentEquals("coffeemaker")) { Log.d("interface", "coffeemaker"); mProxyObj = mBus.getProxyBusObject(WellKnownName, "/coffeemaker", sessionIdvalue, new Class<?>[] { CoffeeMakerInterface.class }); /* We make calls to the methods of the AllJoyn object through one of its interfaces. */ mCoffeeMakerInterface = mProxyObj.getInterface(CoffeeMakerInterface.class); } else if (deviceType.contentEquals("thermostat")) { Log.d("interface", "thermostat"); mProxyObj = mBus.getProxyBusObject(WellKnownName, "/thermo", sessionIdvalue, new Class<?>[] { ThermostatInterface.class }); /* We make calls to the methods of the AllJoyn object through one of its interfaces. */ mThermostatInterface = mProxyObj.getInterface(ThermostatInterface.class); } else { Log.d("interface", "simple"); mProxyObj = mBus.getProxyBusObject(WellKnownName, "/simple", sessionIdvalue, new Class<?>[] { SimpleInterface.class }); /* We make calls to the methods of the AllJoyn object through one of its interfaces. */ mSimpleInterface = mProxyObj.getInterface(SimpleInterface.class); } } else { //TODO // make a list of interfaces mProxyObj = null; mCoffeeMakerInterface = null; mThermostatInterface = null; mSimpleInterface = null; } } @Override public void handleMessage(Message msg) { switch (msg.what) { /* Connect to a remote instance of an object implementing the SimpleInterface. */ case CONNECT: { org.alljoyn.bus.alljoyn.DaemonInit.PrepareDaemon(getApplicationContext()); /* * All communication through AllJoyn begins with a BusAttachment. * * A BusAttachment needs a name. The actual name is unimportant except for internal * security. As a default we use the class name as the name. * * By default AllJoyn does not allow communication between devices (i.e. bus to bus * communication). The second argument must be set to Receive to allow communication * between devices. */ mBus = new BusAttachment(getPackageName(), BusAttachment.RemoteMessage.Receive); /* * Create a bus listener class */ mBus.registerBusListener(new BusListener() { @Override public void lostAdvertisedName(String name, short transport, String namePrefix) { logInfo(String.format("MyBusListener.lostAdvertisedName(%s, 0x%04x, %s)", name, transport, namePrefix)); // Service service = new Service(name); // service.setTransport(transport); // service.setWellKnownNamePrefix(namePrefix); // serviceArrayList.remove(service); // // for (Service service1: serviceArrayList) { // Log.d("serviceArrayList",service1.getWellKnownName()); // } Message deviceInfo = obtainMessage(MESSAGE_REMOVE_FOUND_DEVICE); deviceInfo.arg1 = transport; DEVICE_TRANSPORT = transport; deviceInfo.obj = name; removeFoundDevice(deviceInfo); } @Override public void foundAdvertisedName(String name, short transport, String namePrefix) { logInfo(String.format("MyBusListener.foundAdvertisedName(%s, 0x%04x, %s)", name, transport, namePrefix)); // Service service = new Service(name); // service.setTransport(transport); // service.setWellKnownNamePrefix(namePrefix); // serviceArrayList.add(service); // // for (Service service1: serviceArrayList) { // Log.d("serviceArrayList",service1.getWellKnownName()); // } Message deviceInfo = obtainMessage(MESSAGE_ADD_FOUND_DEVICE); deviceInfo.arg1 = transport; deviceInfo.obj = name; DEVICE_TRANSPORT = transport; addFoundDevice(deviceInfo); } }); /* To communicate with AllJoyn objects, we must connect the BusAttachment to the bus. */ Status status = mBus.connect(); logStatus("BusAttachment.connect()", status); if (Status.OK != status) { finish(); return; } /* * Now find an instance of the AllJoyn object we want to call. We start by looking for * a name, then connecting to the device that is advertising that name. * * In this case, we are looking for the well-known SERVICE_NAME. */ status = mBus.findAdvertisedName(SERVICE_NAME); logStatus(String.format("BusAttachement.findAdvertisedName(%s)", SERVICE_NAME), status); if (Status.OK != status) { finish(); return; } break; } case (JOIN_SESSION): { /* * If discovery is currently being stopped don't join to any other sessions. */ if (mIsStoppingDiscovery) { break; } /* * In order to join the session, we need to provide the well-known * contact port. This is pre-arranged between both sides as part * of the definition of the chat service. As a result of joining * the session, we get a session identifier which we must use to * identify the created session communication channel whenever we * talk to the remote side. */ if (mIsConnected) { Status status = mBus.leaveSession(mSessionId); logStatus("BusAttachment.leaveSession()", status); } String[] tokens = ((String) msg.obj).split("[.]"); // msg.obj = "com.flybits.devices.coffeemaker.My_Coffee_Maker mDeviceType = tokens[tokens.length - 2]; // coffeemaker mDeviceName = String.valueOf(msg.obj); // "com.flybits.devices.coffeemaker.My_Coffee_Maker" interfaceSelector(mDeviceType, mDeviceName, mSessionId, false); short contactPort = CONTACT_PORT; SessionOpts sessionOpts = new SessionOpts(); sessionOpts.transports = (short) msg.arg1; Mutable.IntegerValue sessionId = new Mutable.IntegerValue(); Status status = mBus.joinSession(mDeviceName, contactPort, sessionId, sessionOpts, new SessionListener() { @Override public void sessionLost(int sessionId) { mIsConnected = false; logInfo(String.format("MyBusListener.sessionLost(%d)", sessionId)); mHandler.sendEmptyMessage(MESSAGE_START_PROGRESS_DIALOG); } }); logStatus("BusAttachment.joinSession() - sessionId: " + sessionId.value, status); if (status == Status.OK) { /* * To communicate with an AllJoyn object, we create a ProxyBusObject. * A ProxyBusObject is composed of a name, path, sessionID and interfaces. * * This ProxyBusObject is located at the well-known SERVICE_NAME, under path * "/SimpleService", uses sessionID of CONTACT_PORT, and implements the SimpleInterface. */ // Select the appropriate Bus Interface based on the Device Type, interfaceSelector(mDeviceType, mDeviceName, sessionId.value, true); mSessionId = sessionId.value; mIsConnected = true; mHandler.sendEmptyMessage(MESSAGE_STOP_PROGRESS_DIALOG); } break; } /* Release all resources acquired in the connect. */ case DISCONNECT: { mIsStoppingDiscovery = true; if (mIsConnected) { Status status = mBus.leaveSession(mSessionId); logStatus("BusAttachment.leaveSession()", status); } mBus.disconnect(); getLooper().quit(); break; } /* * Call the service's Ping method through the ProxyBusObject. * * This will also print the String that was sent to the service and the String that was * received from the service to the user interface. */ case PING: { try { if (mSimpleInterface != null) { sendUiMessage(MESSAGE_PING, msg.obj); String reply = mSimpleInterface.Ping((String) msg.obj); sendUiMessage(MESSAGE_PING_REPLY, reply); //String deviceName = mSimpleInterface.GetDeviceName(); //Log.i("Device Name", deviceName); } } catch (BusException ex) { logException("SimpleInterface.Ping()", ex); } break; } case GET_DEVICE_NAME: { try { if (mCoffeeMakerInterface != null) { mBundle = new Bundle(); String nameReply = mCoffeeMakerInterface.getDeviceName(); Log.d("handleMessage", nameReply); mBundle.putCharSequence("Name", nameReply); String potReply = mCoffeeMakerInterface.getPotStatus(); Log.d("handleMessage", potReply); String lidReply = mCoffeeMakerInterface.getLidStatus(); Log.d("handleMessage", lidReply); String powerReply = mCoffeeMakerInterface.getPowerStatus(); Log.d("handleMessage", powerReply); String waterReply = mCoffeeMakerInterface.getWaterLevel(); Log.d("handleMessage", waterReply); mHandler.sendEmptyMessage(MESSAGE_UPDATE_DETAILSFRAGMENT_UI); } if (mThermostatInterface != null) { mBundle = new Bundle(); mBundle.putCharSequence("Interface", "Thermostat"); String nameReply = mThermostatInterface.getDeviceName(); Log.d("handleMessage", nameReply); mBundle.putCharSequence("Name", nameReply); String tempReply = mThermostatInterface.getTemperature(); Log.d("handleMessage", tempReply); mBundle.putCharSequence("Temperature", tempReply); mHandler.sendEmptyMessage(MESSAGE_UPDATE_DETAILSFRAGMENT_UI); } if (mSimpleInterface != null) { String reply = mSimpleInterface.getDeviceName(); Log.d("handleMessage", reply); } } catch (BusException ex) { logException("SimpleInterface.GetSDFSDF()", ex); } } default: break; } } /* Helper function to send a message to the UI thread. */ private void sendUiMessage(int what, Object obj) { mHandler.sendMessage(mHandler.obtainMessage(what, obj)); } } private void logStatus(String msg, Status status) { String log = String.format("%s: %s", msg, status); if (status == Status.OK) { Log.i(TAG, log); } else { Message toastMsg = mHandler.obtainMessage(MESSAGE_POST_TOAST, log); mHandler.sendMessage(toastMsg); Log.e(TAG, log); } } private void logException(String msg, BusException ex) { String log = String.format("%s: %s", msg, ex); Message toastMsg = mHandler.obtainMessage(MESSAGE_POST_TOAST, log); mHandler.sendMessage(toastMsg); Log.e(TAG, log, ex); } /* * print the status or result to the Android log. If the result is the expected * result only print it to the log. Otherwise print it to the error log and * Sent a Toast to the users screen. */ private void logInfo(String msg) { Log.i(TAG, msg); } }