I am trying to create an Android application that receive some text from other application (using share intent) and send it on a MQTT server.
I'm using Eclipse Paho library and the Android example that publish text from button action and log text from subscribe topic. The example is working fine.
But, when I am trying to add some code to handle share intent, the application crash when I try to publish text.
This is the Java code:
package net.example.testingmqtt;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
import org.eclipse.paho.android.service.MqttAndroidClient;
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
public class MainActivity extends AppCompatActivity {
MqttAndroidClient mqttAndroidClient;
final String serverUri = "tcp://iot.eclipse.org:1883";
String clientId = "AndroidClient";
final String subscriptionTopic = "topic";
final String publishTopic = "pubtopic";
final String logTag = "MqttLog";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
publishMessage("Hello from Android");
}
});
// Setup MQTT
setupMqtt();
// Intent
Intent intent = getIntent();
String action = intent.getAction();
String type = intent.getType();
if(Intent.ACTION_SEND.equals(action) && type != null) {
if("text/plain".equals(type)) {
String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
Log.d(logTag, "Get text/plain intent: " + sharedText);
publishMessage("Oh"); // -- This is the problem!
}
}
}
public void setupMqtt() {
clientId = clientId + System.currentTimeMillis();
mqttAndroidClient = new MqttAndroidClient(getApplicationContext(), serverUri, clientId);
mqttAndroidClient.setCallback(new MqttCallbackExtended() {
#Override
public void connectComplete(boolean reconnect, String serverURI) {
if (reconnect) {
Log.d(logTag, "Reconnected to " + serverURI);
subscribeToTopic();
}
else {
Log.d(logTag, "Connected to " + serverURI);
}
}
#Override
public void connectionLost(Throwable cause) {
Log.d(logTag, "The connection was lost");
}
#Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
String msg = new String(message.getPayload());
Log.d(logTag, "Incoming message: " + msg);
Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
}
#Override
public void deliveryComplete(IMqttDeliveryToken token) {
}
});
MqttConnectOptions mqttConnectOptions = new MqttConnectOptions();
mqttConnectOptions.setAutomaticReconnect(true);
mqttConnectOptions.setCleanSession(false);
Log.d(logTag, "Trying to connect to MQTT server...");
try {
mqttAndroidClient.connect(mqttConnectOptions, null, new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
subscribeToTopic();
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
Log.w(logTag, "Failed to connect to " + serverUri);
}
});
} catch (MqttException ex) {
ex.printStackTrace();
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
public void subscribeToTopic() {
try {
mqttAndroidClient.subscribe(subscriptionTopic, 0, null, new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
Log.d(logTag, "Subscribed");
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
Log.w(logTag, "Failed to subscribe");
}
});
} catch (MqttException ex) {
ex.printStackTrace();
}
}
public void publishMessage(String text) {
try {
MqttMessage message = new MqttMessage();
message.setPayload(text.getBytes());
mqttAndroidClient.publish(publishTopic, message);
Log.d(logTag, "Message published");
} catch (MqttException ex) {
ex.printStackTrace();
}
}
}
The publishMessage("Oh") return an exception:
2019-03-14 22:15:44.579 19507-19507/net.e.testingmqtt E/AndroidRuntime: FATAL EXCEPTION: main
Process: net.example.testingmqtt, PID: 19507
java.lang.NullPointerException: Attempt to invoke virtual method 'org.eclipse.paho.client.mqttv3.IMqttDeliveryToken org.eclipse.paho.android.service.MqttService.publish(java.lang.String, java.lang.String, org.eclipse.paho.client.mqttv3.MqttMessage, java.lang.String, java.lang.String)' on a null object reference
at org.eclipse.paho.android.service.MqttAndroidClient.publish(MqttAndroidClient.java:812)
at org.eclipse.paho.android.service.MqttAndroidClient.publish(MqttAndroidClient.java:668)
at net.example.testingmqtt.MainActivity.publishMessage(MainActivity.java:177)
at net.example.testingmqtt.MainActivity$1.onClick(MainActivity.java:48)
at android.view.View.performClick(View.java:6891)
at android.view.View$PerformClick.run(View.java:26083)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6938)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
I understand that the MQTT connection is not establish when the application is launch (after using share intent). But I don't understand why in that case the connection is not set up by the previous statement (setupMqtt).
Do you have any advice to fix the code?
That is a basic issue - NPE aka NullPointerException :-)
You are trying to call mqttAndroidClient.publish(publishTopic, message) but the mqttAndroidClient is not initialized yet (by you, in your setupMqtt method) and thus null.
You could add an if (mqttAndroidClient != null) condition or just catch and ignore the NPE:
public void publishMessage(String text) {
try {
MqttMessage message = new MqttMessage();
message.setPayload(text.getBytes());
mqttAndroidClient.publish(publishTopic, message);
Log.d(logTag, "Message published");
} catch (NullPointerException | MqttException ex) {
ex.printStackTrace();
}
}
Well, I found a solution (maybe not the best) by using the singleTask mode (new android:launchMode="singleTask" attribute of activity tag in AndroidManifest.xml) and the onNewIntent method:
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.d(logTag, "Get new intent...");
String action = intent.getAction();
String type = intent.getType();
if(Intent.ACTION_SEND.equals(action) && type != null) {
if("text/plain".equals(type)) {
String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
Log.d(logTag, "Get text/plain intent: " + sharedText);
publishMessage("Oh, a new intent!");
}
}
}
No more error when I publish the message when I received intent.
Thanks for the The Cheese Factory Blog that help me understood Android activity launch mode.
Related
Hello I am new to Android Development. I have created an NSD Utility class. Currently its unable to call the method DiscoveryListner. Any help will be great
MainActivity
package com.example.android.implicitintents;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.example.android.implicitintents.Utils.NsdUtils;
import java.net.URL;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "NsdChat";
public static String URL_WEB;
NsdUtils mNsdUtils;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
NsdUtils mNsdUtils = new NsdUtils(this);
mNsdUtils.initializeNsd();
}
public void onClickOpenWebpageButton(View view) {
String urlAsString = "http://192.168.10.1";
openWebPage(urlAsString);
}
private void openWebPage(String url) {
Intent intent = new Intent(this, ConfigActivity.class);
intent.putExtra(URL_WEB, url);
Log.d(TAG, "Button Pressed");
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
}
NSD Utility Class
package com.example.android.implicitintents.Utils;
import android.content.Context;
import android.net.nsd.NsdServiceInfo;
import android.net.nsd.NsdManager;
import android.util.Log;
public class NsdUtils {
Context mContext;
NsdManager mNsdManager;
NsdManager.ResolveListener mResolveListener;
NsdManager.DiscoveryListener mDiscoveryListener;
public static final String SERVICE_TYPE = "http";
public static final String TAG = "NsdHelper";
public String mServiceName = "Plug_Service";
NsdServiceInfo mService;
public NsdUtils(Context context) {
mContext = context;
mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
}
public void initializeNsd() {
initializeResolveListener();
initializeDiscoveryListener();
}
public void initializeDiscoveryListener() {
mDiscoveryListener = new NsdManager.DiscoveryListener() {
#Override
public void onDiscoveryStarted(String regType) {
Log.d(TAG, "Service discovery started");
}
#Override
public void onServiceFound(NsdServiceInfo service) {
Log.d(TAG, "Service discovery success" + service);
if (!service.getServiceType().equals(SERVICE_TYPE)) {
Log.d(TAG, "Unknown Service Type: " + service.getServiceType());
} else if (service.getServiceName().equals(mServiceName)) {
Log.d(TAG, "Same machine: " + mServiceName);
} else if (service.getServiceName().contains(mServiceName)){
mNsdManager.resolveService(service, mResolveListener);
}
}
#Override
public void onServiceLost(NsdServiceInfo service) {
Log.e(TAG, "service lost" + service);
if (mService == service) {
mService = null;
}
}
#Override
public void onDiscoveryStopped(String serviceType) {
Log.i(TAG, "Discovery stopped: " + serviceType);
}
#Override
public void onStartDiscoveryFailed(String serviceType, int errorCode) {
Log.e(TAG, "Discovery failed: Error code:" + errorCode);
mNsdManager.stopServiceDiscovery(this);
}
#Override
public void onStopDiscoveryFailed(String serviceType, int errorCode) {
Log.e(TAG, "Discovery failed: Error code:" + errorCode);
mNsdManager.stopServiceDiscovery(this);
}
};
}
public void initializeResolveListener() {
mResolveListener = new NsdManager.ResolveListener() {
#Override
public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
Log.e(TAG, "Resolve failed" + errorCode);
}
#Override
public void onServiceResolved(NsdServiceInfo serviceInfo) {
Log.e(TAG, "Resolve Succeeded. " + serviceInfo);
if (serviceInfo.getServiceName().equals(mServiceName)) {
Log.d(TAG, "Same IP.");
return;
}
mService = serviceInfo;
}
};
}
public void discoverServices() {
mNsdManager.discoverServices(
SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);
}
}
I tried calling the dicoverService in onclick button event in failed. Do we need to create a Async Task or a new Thread?
first specify the type of TCP network you want to find. Then make the object of the class and call the Intilization and then the discovery function of the NSD.
It should work just fine.
It is not necessary to run this functionality in another thread.
After some investigation current functionality stop crash and start discovery, when change String SERVICE_TYPE = "http"; to public static final String SERVICE_TYPE = "_name._tcp";
You can discover tcp service and then when you will obtain IP just connect to web server via http use those IP adress.
I have been trying to incorporate MQTT into one of my Android applications. I originally had it working within an activity and have since tried to move it to a service to run in the background. Im able to connect and send messages from the service but Im not able to receive messages. My service implements MqttCallback and overrides the messageReceived() function but it never seems to get called. Can someone help me understand why the callback is not firing?
package com.example.test;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttClientPersistence;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.MqttToken;
import org.eclipse.paho.client.mqttv3.MqttTopic;
import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
public class MqttService extends Service implements MqttCallback{
final static String MY_ACTION = "MqttService";
String username;
//MQTT Variables
MqttClient mySubClient;
MqttConnectOptions connOpt;
String BROKER_URL;
String PUB_TOPIC;
String SUB_TOPIC;
String PUB_CLIENT_ID;
String SUB_CLIENT_ID;
Boolean MqttConnState;
Boolean IsRunning;
static final String TOPIC_BASE = MyProperties.TOPIC_BASE;
static final String PI_BROKER_URL = MyProperties.PI_BROKER_URL; //no encryption
static final String PI_SSL_BROKER_URL = MyProperties.PI_SSL_BROKER_URL; //ssl enabled
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("log_mqtt_service", "Service onStartCommand entered");
//Get session id from previous activity
Bundle extras = intent.getExtras();
if (extras != null) {
username = extras.getString("SESSION_ID");
}
//set mqtt vars
BROKER_URL = PI_BROKER_URL;
PUB_CLIENT_ID = "My-Pub-" + username;
SUB_CLIENT_ID = "My-Sub-" + username;
SUB_TOPIC = TOPIC_BASE + username;
PUB_TOPIC = TOPIC_BASE + "test";
IsRunning = false;
MqttThread myThread = new MqttThread();
myThread.start();
return super.onStartCommand(intent, flags, startId);
}
public class MqttThread extends Thread{
#Override
public void run() {
if(IsRunning==false)
{
IsRunning = true;
connMqtt();
}
while(IsRunning){
//keep thread running
Log.i("log_mqtt_thread_send", "thread sending mqtt message");
sendMsg(PUB_TOPIC, "0x0001", "Message from " + PUB_CLIENT_ID);
}
Log.i("log_mqtt_thread", "thread stopped");
}
}
#Override
public void onDestroy()
{
super.onDestroy();
try {
mySubClient.disconnect();
} catch (MqttException e) {
Log.e ("log_mqtt_disconnect",e.toString());
}
}
#Override
public void connectionLost(Throwable cause) {
// TODO Auto-generated method stub
}
#Override
public void messageArrived(String topic, MqttMessage message)
throws Exception {
String msgStr;
msgStr = new String(message.getPayload());
Log.i("log_mqtt_Rx", "-------------------------------------------------");
Log.i("log_mqtt_Rx", "| Topic: " + topic.toString());
Log.i("log_mqtt_Rx", "| Message: " + msgStr);
Log.i("log_mqtt_Rx", "-------------------------------------------------");
Intent intent = new Intent();
intent.setAction(MY_ACTION);
intent.putExtra("RX_MESSAGE", msgStr);
sendBroadcast(intent);
}
#Override
public void deliveryComplete(IMqttDeliveryToken token) {
// TODO Auto-generated method stub
}
// ------------------------------------------------------------------------
// Function to make mqtt connection and subscribe
// ------------------------------------------------------------------------
int connMqtt()
{
int result=0;
MqttClientPersistence persistence = new MqttDefaultFilePersistence(getBaseContext().getApplicationInfo().dataDir);
try{
Log.i("log_mqtt","mqtt start");
try{
// setup MQTT Client
connOpt = new MqttConnectOptions();
connOpt.setCleanSession(true);
connOpt.setKeepAliveInterval(30);
Log.i("log_mqtt","connOpt vars setup");
}catch(Exception e){
result = -1;
Log.e ("log_mqtt",e.toString());
}
// Connect to Broker for Subscriber connection
try {
mySubClient = new MqttClient(BROKER_URL, SUB_CLIENT_ID, persistence);
Log.i("log_mqtt_conn","create mqttClient");
mySubClient.connect(connOpt);
Log.i("log_mqtt_conn","MQTT client connected to " + BROKER_URL);
} catch (MqttException e) {
result = -2;
Log.i ("log_mqtt_conn","BROKER: " + BROKER_URL);
Log.i ("log_mqtt_conn","SUB_CLIENT_ID: " + SUB_CLIENT_ID);
Log.e ("log_mqtt_conn",e.toString());
}
try {
int subQoS = 0;
mySubClient.subscribe(SUB_TOPIC, subQoS);
Log.i("log_mqtt_sub","mqtt client subscribed to \"" + SUB_TOPIC + "\"");
} catch (Exception e) {
result = -3;
Log.e ("log_mqtt_sub",e.toString());
}
}catch(Exception e){
result = -4;
Log.e ("log_mqtt",e.toString());
}
return result;
}
// ------------------------------------------------------------------------
// Function to send mqtt message to a topic
// ------------------------------------------------------------------------
public void sendMsg(String sendTopic, String msgid, String msg)
{
String timeStamp = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(Calendar.getInstance().getTime());
String pubMsg = "{\"msgid\":\"" + msgid + "\",\"time\":\"" + timeStamp + "\", \"message\":\"" + msg + "\"}";
int pubQoS = 0;
MqttMessage message = new MqttMessage(pubMsg.getBytes());
message.setQos(pubQoS);
message.setRetained(false);
//Topic for publisher
MqttTopic pubTopic = mySubClient.getTopic(sendTopic);
// Publish the message
Log.i("log_mqtt_send","Publishing to topic \"" + pubTopic + "\" qos " + pubQoS);
Log.i("log_mqtt_msg", pubMsg);
MqttDeliveryToken token = null;
try {
// Publish message to broker then disconnect
token = pubTopic.publish(message);
// Wait until the message has been delivered to the broker
token.waitForCompletion();
Thread.sleep(1000);
} catch (Exception ex) {
Log.e("log_mqtt_error",ex.toString());
}
}
}
You have to add Callback on your 'mySubClient.setCallBack' then only your callback methods will get called.
I am trying to connect the Bluetooth device (BT headphone) programmatically with my android app. The issue I am facing is in for first couple of attempts it fails to connect the device and says "Pairing Rejected". then after it connects successfully.
Here's my code:
package rockwellcollins.bluetooth_pairing_siu;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
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.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;
public class MainActivity extends AppCompatActivity {
public ServerSocket serverSocket;
public BluetoothA2dp btA2dp;
BluetoothAdapter mBluetoothAdapter;
private BluetoothSocket mSocket = null;
private UUID applicationUUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
public InetAddress serverAddr,serverAddrConnected;
public Socket socket,socketConnected;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mBluetoothAdapter.getProfileProxy(this, new BluetoothProfile.ServiceListener() {
#Override
public void onServiceConnected(int profile, BluetoothProfile proxy) {
Log.i("oncreate","Bluetooth Service connected.\n");
btA2dp = (BluetoothA2dp) proxy;
}
#Override
public void onServiceDisconnected(int profile) {
Log.i("oncreate", "Bluetooth Service disconnected.\n");
}
}, BluetoothProfile.A2DP);
Log.i("oncreate", "going to start thread.\n");
Thread fst = new Thread(new ServerThread());
fst.start();
registerReceiver(mPairReceiver, new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED));
}
public class ServerThread implements Runnable
{
String[] strLine;
#Override
public void run() {
try {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
serverSocket = new ServerSocket(4500);
while(true) {
Log.i("inrun","in run method");
Socket client = serverSocket.accept();
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
String line = in.readLine();
strLine = line.split(";");
Log.i("ServerThread", "Line is: " + strLine[1]);
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(strLine[1]);
if(strLine[0].contains("connect")) {
pairDevice(device);
}
else if(strLine[0].contains("unpair"))
{
Log.i("run","Going to unpair device");
unpairDevice(device, strLine[2]);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void pairDevice(BluetoothDevice device) {
try {
if(device.getBondState() == BluetoothDevice.BOND_NONE) {
Method method = device.getClass().getMethod("createBond", (Class[]) null);
method.invoke(device, (Object[]) null);
Log.d("pairdevice", String.valueOf(device.getBondState()));
}
} catch (Exception e) {
e.printStackTrace();
}
}
public class FinalThread implements Runnable{
String strMsg = "";
public FinalThread(String msg)
{
strMsg = msg;
}
#Override
public void run() {
try {
serverAddr = InetAddress.getByName("10.240.8.23");
socket = new Socket(serverAddr,5500);
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
out.println(strMsg);
out.flush();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void unpairDevice(BluetoothDevice device, String seatInfo) {
try {
if(device.getBondState() == BluetoothDevice.BOND_BONDED) {
Method method = device.getClass().getMethod("removeBond", (Class[]) null);
method.invoke(device, (Object[]) null);
}
Thread sThread = new Thread(new ConnectThread(seatInfo));
sThread.start();
} catch (Exception e) {
e.printStackTrace();
}
}
public class ConnectThread implements Runnable{
String strSeat = "";
public ConnectThread(String seat){
strSeat = seat;
}
#Override
public void run() {
try {
Log.i("ConnectThread","Going to connect");
serverAddr = InetAddress.getByName("10.240.8.23");
socket = new Socket(serverAddr,5500);
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
out.println("startpairconnect;" + strSeat);
out.flush();
socket.close();
Log.i("ConnectThread","Send message back for connect");
} catch (Exception e) {
e.printStackTrace();
}
}
}
private final BroadcastReceiver mPairReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
BluetoothDevice deviceBT = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
final int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR);
final int prevState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, BluetoothDevice.ERROR);
if (state == BluetoothDevice.BOND_BONDED && prevState == BluetoothDevice.BOND_BONDING) {
ShowToast("Paired");
connectA2dp(deviceBT);
} else if (state == BluetoothDevice.BOND_NONE && prevState == BluetoothDevice.BOND_BONDED){
ShowToast("Unpaired");
} else if (state == BluetoothDevice.BOND_NONE && prevState == BluetoothDevice.BOND_BONDING){
ShowToast("Pairing rejected, trying again");
pairDevice(deviceBT);
}
}
}
};
private void connectA2dp(final BluetoothDevice btdevice) {
Method connect;
String strMessage = "";
try {
connect = BluetoothA2dp.class.getDeclaredMethod("connect", BluetoothDevice.class);
//Log.i("connecta2dp","get connect method " + connect);
} catch (NoSuchMethodException ex) {
Log.e("connectA2dp", "Unable to find connect(BluetoothDevice) method in BluetoothA2dp proxy.");
return;
}
connect.setAccessible(true);
try {
//Log.i("connecta2dp","before invoke" );
connect.invoke(btA2dp, btdevice);
if(btdevice.getBondState() == BluetoothDevice.BOND_BONDED)
strMessage = "success;" + btdevice.getAddress();
else
strMessage = "failure";
Thread fThread = new Thread(new FinalThread(strMessage));
fThread.start();
//Log.i("connecta2dp", "after invoke");
} catch (InvocationTargetException ex) {
Log.e("connectA2dp", "Unable to invoke connect(BluetoothDevice) method on proxy. " + ex.toString());
} catch (IllegalAccessException ex) {
Log.e("connectA2dp", "Illegal Access! " + ex.toString());
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
public void ShowToast(String message)
{
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
}
}
Little explanation of the scenario which rejects the pairing for couple of times class will start a thread on it's onCreate method.Now this thread will continuously monitor the port for a incoming message and once it gets the message it'll extract the mac address of the headphones from it. Once it get the mac address it'll try to connect it programmatically where it fails for couple of attempts and then connects it successfully.
Thanks
I am trying to control an arduino micro controller from android phone.
I need help to to continuously receive the output from the temperature sensor and display it on text view.
Here is the code:
package com.war10ck6001gmail.finalproject;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.Settings;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.TextView;
import android.widget.ToggleButton;
public class MainActivity extends ActionBarActivity {
public ToggleButton fan, light, manauto;
public Button inc, dec;
public TextView temp;
public Toolbar mToolbar;
TCPClient mTcpClient = null;
public connectTask conctTask = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mToolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(mToolbar);
getSupportActionBar().setDisplayShowHomeEnabled(true);
// connect to the server
temp = (TextView) findViewById(R.id.temprature);
fan = (ToggleButton) findViewById(R.id.fanbutton);
light = (ToggleButton) findViewById(R.id.lightbutton);
manauto = (ToggleButton) findViewById(R.id.manauto);
inc = (Button) findViewById(R.id.increment);
dec = (Button) findViewById(R.id.decrement);
Automatic(light);
Automatic(fan);
Automatic(inc);
Automatic(dec);
conctTask = new connectTask();
conctTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
mTcpClient = null;
manauto.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
Manual(light);
Manual(fan);
Manual(inc);
Manual(dec);
mTcpClient.sendMessage("Man");
}
else {
Automatic(light);
Automatic(fan);
Automatic(inc);
Automatic(dec);
mTcpClient.sendMessage("Auto");
}
}
});
light.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
mTcpClient.sendMessage("0");
}
else {
mTcpClient.sendMessage("1");
}
}
});
fan.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
mTcpClient.sendMessage("2");
}
else {
mTcpClient.sendMessage("3");
}
}
});
inc.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mTcpClient.sendMessage("4");
}
});
dec.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mTcpClient.sendMessage("5");
}
});
}
public void Automatic(Button btn) {
btn.setEnabled(false);
btn.getBackground().setColorFilter(Color.GRAY, PorterDuff.Mode.MULTIPLY);
}
public void Manual(Button btn) {
btn.setEnabled(true);
btn.getBackground().setColorFilter(null);
}
public class connectTask extends AsyncTask<String, String, TCPClient> {
String finalmessage;
#Override
protected TCPClient doInBackground(final String... message) {
//we create a TCPClient object and
mTcpClient = new TCPClient(new TCPClient.OnMessageReceived() {
#Override
//here the messageReceived method is implemented
public void messageReceived(final String message) {
try {
//this method calls the onProgressUpdate
//publishProgress(message);
if (message != null) {
//finalmessage = message;
runOnUiThread(new Runnable() {
#Override
public void run() {
temp.setText(message);
}
});
System.out.println("Return Message from Socket::::: >>>>> " + message);
}
}
catch (Exception e) {
e.printStackTrace();
}
}
});
mTcpClient.run();
if (mTcpClient != null) {
mTcpClient.sendMessage("Initial Message when connected with Socket Server");
}
/* runOnUiThread(new Runnable() {
#Override
public void run() {
temp.setText(finalmessage);
}
});*/
return null;
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS));
return true;
}
if (id == R.id.action_master) {
return true;
}
if (id == R.id.action_bedroom) {
Intent intent = new Intent(getApplicationContext(), Bedroom.class);
startActivity(intent);
return true;
}
if (id == R.id.action_Cbedroom) {
Intent intent = new Intent(getApplicationContext(), Cbedroom.class);
startActivity(intent);
return true;
}
if (id == R.id.action_diningroom) {
Intent intent = new Intent(getApplicationContext(), Diningroom.class);
startActivity(intent);
return true;
}
return super.onOptionsItemSelected(item);
}
}
here is the class which has the onmessage recieved
public class TCPClient {
public String serverMessage;
/**
* Specify the Server Ip Address here. Whereas our Socket Server is started.
*/
public static final String SERVERIP = "10.5.38.148"; // your computer IP address
public static final int SERVERPORT = 8888;
private OnMessageReceived mMessageListener = null;
private boolean mRun = false;
private PrintWriter out = null;
private BufferedReader in = null;
MainActivity mainActivity = null;
/**
* Constructor of the class. OnMessagedReceived listens for the messages received from server
*/
public TCPClient(final OnMessageReceived listener) {
mMessageListener = listener;
}
/**
* Sends the message entered by client to the server
*
* #param message text entered by client
*/
public void sendMessage(String message) {
if (out != null && !out.checkError()) {
System.out.println("message: " + message);
out.println(message);
out.flush();
}
}
public void stopClient() {
mRun = false;
}
public void run() {
mRun = true;
while (mRun) {
try {
//here you must put your computer's IP address.
InetAddress serverAddr = InetAddress.getByName(SERVERIP);
Log.e("TCP SI Client", "SI: Connecting...");
//create a socket to make the connection with the server
Socket socket = new Socket(serverAddr, SERVERPORT);
try {
//send the message to the server
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
Log.e("TCP SI Client", "SI: Sent.");
Log.e("TCP SI Client", "SI: Done.");
//receive the message which the server sends back
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//in this while the client listens for the messages sent by the server
serverMessage = in.readLine();
if (serverMessage != null && mMessageListener != null) {
//call the method messageReceived from MyActivity class
mMessageListener.messageReceived(serverMessage);
Log.e("RESPONSE FROM SERVER", "S: Received Message: '" + serverMessage + "'");
}
serverMessage = null;
}
catch (Exception e) {
Log.e("TCP SI Error", "SI: Error", e);
e.printStackTrace();
}
finally {
//the socket must be closed. It is not possible to reconnect to this socket
// after it is closed, which means a new socket instance has to be created.
socket.close();
}
}
catch (Exception e) {
Log.e("TCP SI Error", "SI: Error", e);
}
}
}
//Declare the interface. The method messageReceived(String message) will must be implemented in the MyActivity
//class at on asynckTask doInBackground
public interface OnMessageReceived {
public void messageReceived(String message);
}
}
I am looking for and example of casting an image to chromecast in android. Oddly enough it doesn't seem like this is covered in the googlecast sample repositories. Does anyone have a simple implementation of this? I basically would like to click on an image in my app's photo gallery on my android device and have it cast to the screen.
One side question is, does the image need to be at a url? or is it possible to stream the image to the device? I appreciate the help in advance.
I've solved this without the CastCompanionLibrary, but based on google's CastHelloText-android sample. Basically what I did was:
encode an image into a base64 string and send it as a message to a custom receiver
modify the sample's receiver to receive a base64 string and set it as the image source.
upload and register my receiver and have the application use the generated application id
This is the code for the receiver:
<!DOCTYPE html>
<html>
<head>
<style>
img#androidImage {
height:auto;
width:100%;
}
</style>
<title>Cast Hello Text</title>
</head>
<body>
<img id="androidImage" src="" />
<script type="text/javascript" src="//www.gstatic.com/cast/sdk/libs/receiver/2.0.0/cast_receiver.js"></script>
<script type="text/javascript">
window.onload = function() {
cast.receiver.logger.setLevelValue(0);
window.castReceiverManager = cast.receiver.CastReceiverManager.getInstance();
console.log('Starting Receiver Manager');
// handler for the 'ready' event
castReceiverManager.onReady = function(event) {
console.log('Received Ready event: ' + JSON.stringify(event.data));
window.castReceiverManager.setApplicationState("Application status is ready...");
};
// handler for 'senderconnected' event
castReceiverManager.onSenderConnected = function(event) {
console.log('Received Sender Connected event: ' + event.data);
console.log(window.castReceiverManager.getSender(event.data).userAgent);
};
// handler for 'senderdisconnected' event
castReceiverManager.onSenderDisconnected = function(event) {
console.log('Received Sender Disconnected event: ' + event.data);
if (window.castReceiverManager.getSenders().length == 0) {
window.close();
}
};
// handler for 'systemvolumechanged' event
castReceiverManager.onSystemVolumeChanged = function(event) {
console.log('Received System Volume Changed event: ' + event.data['level'] + ' ' +
event.data['muted']);
};
// create a CastMessageBus to handle messages for a custom namespace
window.messageBus =
window.castReceiverManager.getCastMessageBus(
'urn:x-cast:com.google.cast.sample.helloworld');
// handler for the CastMessageBus message event
window.messageBus.onMessage = function(event) {
console.log('Message recieved');
var obj = JSON.parse(event.data)
console.log('Message type: ' + obj.type);
if (obj.type == "text") {
console.log('Skipping message: ' + obj.data);
}
if (obj.type == "image") {
var source = 'data:image/png;base64,'.concat(obj.data)
displayImage(source);
}
// inform all senders on the CastMessageBus of the incoming message event
// sender message listener will be invoked
window.messageBus.send(event.senderId, event.data);
}
// initialize the CastReceiverManager with an application status message
window.castReceiverManager.start({statusText: "Application is starting"});
console.log('Receiver Manager started');
};
function displayImage(source) {
console.log('received image');
document.getElementById("androidImage").src=source;
window.castReceiverManager.setApplicationState('image source changed');
};
</script>
</body>
</html>
Below is the modified MainActivity.java code. Don't forget to modify the app_id in string.xml once your receiver application is registered.
2 notes:
The sent messages are wrapped in a JSON object so I can filter out
the text messages.
The ENCODED_IMAGE_STRING variable isn't defined in this
example, you'll have to find an image and convert it to a base64 string yourself.
MainActivity.java:
package com.example.casthelloworld;
import java.io.IOException;
import java.util.ArrayList;
import android.content.Intent;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.speech.RecognizerIntent;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.MediaRouteActionProvider;
import android.support.v7.media.MediaRouteSelector;
import android.support.v7.media.MediaRouter;
import android.support.v7.media.MediaRouter.RouteInfo;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
import com.google.android.gms.cast.ApplicationMetadata;
import com.google.android.gms.cast.Cast;
import com.google.android.gms.cast.Cast.ApplicationConnectionResult;
import com.google.android.gms.cast.Cast.MessageReceivedCallback;
import com.google.android.gms.cast.CastDevice;
import com.google.android.gms.cast.CastMediaControlIntent;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;
/**
* Main activity to send messages to the receiver.
*/
public class MainActivity extends ActionBarActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private static final int REQUEST_CODE = 1;
private MediaRouter mMediaRouter;
private MediaRouteSelector mMediaRouteSelector;
private MediaRouter.Callback mMediaRouterCallback;
private CastDevice mSelectedDevice;
private GoogleApiClient mApiClient;
private Cast.Listener mCastListener;
private ConnectionCallbacks mConnectionCallbacks;
private ConnectionFailedListener mConnectionFailedListener;
private HelloWorldChannel mHelloWorldChannel;
private boolean mApplicationStarted;
private boolean mWaitingForReconnect;
private String mSessionId;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActionBar actionBar = getSupportActionBar();
actionBar.setBackgroundDrawable(new ColorDrawable(
android.R.color.transparent));
// When the user clicks on the button, use Android voice recognition to
// get text
Button voiceButton = (Button) findViewById(R.id.voiceButton);
voiceButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
startVoiceRecognitionActivity();
}
});
// When the user clicks on the button, use Android voice recognition to
// get text
Button yarrButton = (Button) findViewById(R.id.tmpButton);
yarrButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
castImage();
}
});
// Configure Cast device discovery
mMediaRouter = MediaRouter.getInstance(getApplicationContext());
mMediaRouteSelector = new MediaRouteSelector.Builder()
.addControlCategory(
CastMediaControlIntent.categoryForCast(getResources()
.getString(R.string.app_id))).build();
mMediaRouterCallback = new MyMediaRouterCallback();
}
private void castImage()
{
Log.d(TAG, "castImage()");
String image_string = createJsonMessage(MessageType.image, ENCODED_IMAGE_STRING);
sendMessage(image_string);
}
/**
* Android voice recognition
*/
private void startVoiceRecognitionActivity() {
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
intent.putExtra(RecognizerIntent.EXTRA_PROMPT,
getString(R.string.message_to_cast));
startActivityForResult(intent, REQUEST_CODE);
}
/*
* Handle the voice recognition response
*
* #see android.support.v4.app.FragmentActivity#onActivityResult(int, int,
* android.content.Intent)
*/
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
ArrayList<String> matches = data
.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
if (matches.size() > 0) {
Log.d(TAG, matches.get(0));
String message = createJsonMessage(MessageType.text, matches.get(0));
sendMessage(message);
}
}
super.onActivityResult(requestCode, resultCode, data);
}
#Override
protected void onResume() {
super.onResume();
// Start media router discovery
mMediaRouter.addCallback(mMediaRouteSelector, mMediaRouterCallback,
MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
}
#Override
protected void onPause() {
if (isFinishing()) {
// End media router discovery
mMediaRouter.removeCallback(mMediaRouterCallback);
}
super.onPause();
}
#Override
public void onDestroy() {
teardown();
super.onDestroy();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.main, menu);
MenuItem mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item);
MediaRouteActionProvider mediaRouteActionProvider = (MediaRouteActionProvider) MenuItemCompat
.getActionProvider(mediaRouteMenuItem);
// Set the MediaRouteActionProvider selector for device discovery.
mediaRouteActionProvider.setRouteSelector(mMediaRouteSelector);
return true;
}
/**
* Callback for MediaRouter events
*/
private class MyMediaRouterCallback extends MediaRouter.Callback {
#Override
public void onRouteSelected(MediaRouter router, RouteInfo info) {
Log.d(TAG, "onRouteSelected");
// Handle the user route selection.
mSelectedDevice = CastDevice.getFromBundle(info.getExtras());
launchReceiver();
}
#Override
public void onRouteUnselected(MediaRouter router, RouteInfo info) {
Log.d(TAG, "onRouteUnselected: info=" + info);
teardown();
mSelectedDevice = null;
}
}
/**
* Start the receiver app
*/
private void launchReceiver() {
try {
mCastListener = new Cast.Listener() {
#Override
public void onApplicationDisconnected(int errorCode) {
Log.d(TAG, "application has stopped");
teardown();
}
};
// Connect to Google Play services
mConnectionCallbacks = new ConnectionCallbacks();
mConnectionFailedListener = new ConnectionFailedListener();
Cast.CastOptions.Builder apiOptionsBuilder = Cast.CastOptions
.builder(mSelectedDevice, mCastListener);
mApiClient = new GoogleApiClient.Builder(this)
.addApi(Cast.API, apiOptionsBuilder.build())
.addConnectionCallbacks(mConnectionCallbacks)
.addOnConnectionFailedListener(mConnectionFailedListener)
.build();
mApiClient.connect();
} catch (Exception e) {
Log.e(TAG, "Failed launchReceiver", e);
}
}
/**
* Google Play services callbacks
*/
private class ConnectionCallbacks implements
GoogleApiClient.ConnectionCallbacks {
#Override
public void onConnected(Bundle connectionHint) {
Log.d(TAG, "onConnected");
if (mApiClient == null) {
// We got disconnected while this runnable was pending
// execution.
return;
}
try {
if (mWaitingForReconnect) {
mWaitingForReconnect = false;
// Check if the receiver app is still running
if ((connectionHint != null)
&& connectionHint
.getBoolean(Cast.EXTRA_APP_NO_LONGER_RUNNING)) {
Log.d(TAG, "App is no longer running");
teardown();
} else {
// Re-create the custom message channel
try {
Cast.CastApi.setMessageReceivedCallbacks(
mApiClient,
mHelloWorldChannel.getNamespace(),
mHelloWorldChannel);
} catch (IOException e) {
Log.e(TAG, "Exception while creating channel", e);
}
}
} else {
// Launch the receiver app
Cast.CastApi
.launchApplication(mApiClient,
getString(R.string.app_id), false)
.setResultCallback(
new ResultCallback<Cast.ApplicationConnectionResult>() {
#Override
public void onResult(
ApplicationConnectionResult result) {
Status status = result.getStatus();
Log.d(TAG,
"ApplicationConnectionResultCallback.onResult: statusCode "
+ status.getStatusCode());
if (status.isSuccess()) {
ApplicationMetadata applicationMetadata = result
.getApplicationMetadata();
mSessionId = result
.getSessionId();
String applicationStatus = result
.getApplicationStatus();
boolean wasLaunched = result
.getWasLaunched();
Log.d(TAG,
"application name: "
+ applicationMetadata
.getName()
+ ", status: "
+ applicationStatus
+ ", sessionId: "
+ mSessionId
+ ", wasLaunched: "
+ wasLaunched);
mApplicationStarted = true;
// Create the custom message
// channel
mHelloWorldChannel = new HelloWorldChannel();
try {
Cast.CastApi
.setMessageReceivedCallbacks(
mApiClient,
mHelloWorldChannel
.getNamespace(),
mHelloWorldChannel);
} catch (IOException e) {
Log.e(TAG,
"Exception while creating channel",
e);
}
// set the initial instructions
// on the receiver
String message = createJsonMessage(MessageType.text, getString(R.string.instructions));
sendMessage(message);
} else {
Log.e(TAG,
"application could not launch");
teardown();
}
}
});
}
} catch (Exception e) {
Log.e(TAG, "Failed to launch application", e);
}
}
#Override
public void onConnectionSuspended(int cause) {
Log.d(TAG, "onConnectionSuspended");
mWaitingForReconnect = true;
}
}
/**
* Google Play services callbacks
*/
private class ConnectionFailedListener implements
GoogleApiClient.OnConnectionFailedListener {
#Override
public void onConnectionFailed(ConnectionResult result) {
Log.e(TAG, "onConnectionFailed ");
teardown();
}
}
/**
* Tear down the connection to the receiver
*/
private void teardown() {
Log.d(TAG, "teardown");
if (mApiClient != null) {
if (mApplicationStarted) {
if (mApiClient.isConnected() || mApiClient.isConnecting()) {
try {
Cast.CastApi.stopApplication(mApiClient, mSessionId);
if (mHelloWorldChannel != null) {
Cast.CastApi.removeMessageReceivedCallbacks(
mApiClient,
mHelloWorldChannel.getNamespace());
mHelloWorldChannel = null;
}
} catch (IOException e) {
Log.e(TAG, "Exception while removing channel", e);
}
mApiClient.disconnect();
}
mApplicationStarted = false;
}
mApiClient = null;
}
mSelectedDevice = null;
mWaitingForReconnect = false;
mSessionId = null;
}
/**
* Send a text message to the receiver
*
* #param message
*/
private void sendMessage(String message) {
if (mApiClient != null && mHelloWorldChannel != null) {
try {
Cast.CastApi.sendMessage(mApiClient,
mHelloWorldChannel.getNamespace(), message)
.setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(Status result) {
if (!result.isSuccess()) {
Log.e(TAG, "Sending message failed");
}
}
});
} catch (Exception e) {
Log.e(TAG, "Exception while sending message", e);
}
} else {
Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT)
.show();
}
}
/**
* Custom message channel
*/
class HelloWorldChannel implements MessageReceivedCallback {
/**
* #return custom namespace
*/
public String getNamespace() {
return getString(R.string.namespace);
}
/*
* Receive message from the receiver app
*/
#Override
public void onMessageReceived(CastDevice castDevice, String namespace,
String message) {
Log.d(TAG, "onMessageReceived: " + message);
}
}
enum MessageType {
text,
image,
}
public static Bitmap getBitmapFromView(View view) {
//Define a bitmap with the same size as the view
Bitmap returnedBitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),Bitmap.Config.ARGB_8888);
//Bind a canvas to it
Canvas canvas = new Canvas(returnedBitmap);
//Get the view's background
Drawable bgDrawable =view.getBackground();
if (bgDrawable!=null)
//has background drawable, then draw it on the canvas
bgDrawable.draw(canvas);
else
//does not have background drawable, then draw white background on the canvas
canvas.drawColor(Color.WHITE);
// draw the view on the canvas
view.draw(canvas);
//return the bitmap
return returnedBitmap;
}
private static String createJsonMessage(MessageType type, String message)
{
return String.format("{\"type\":\"%s\", \"data\":\"%s\"}", type.toString(), message);
}
}
Since on Chromecast your application is running inside a web browser, you need to have an <img/> tag show the image. The src attribute of that tag should point to the image that you want to see and it has to be a url, so if your image is residing on your phone's local storage, you need to start a small web server in your mobile application to serve that image and communicate with the receiver what url it should point at (which would be the url at which your server is serving that image). these are all doable and you can use the CastCompanionLibrary, if you want, to communicate with your custom receiver; simply use the DataCastManager class instead of VideoCastManager.
May be my answer will be helpful for other developers, because I also did'nt found good solution and done it by myself.
For showing image via Google Cast on your device screen from your app you can create and start simply web server from your app which will process http requests with selected image name or id in URL.
Example:
public class MyWebServer {
private Activity activity;
private static ServerSocket httpServerSocket;
private static boolean isWebServerSunning;
public static final String drawableDelimiter = "pic-"
public MyWebServer(Activity activity) {
this.activity = activity;
}
public void stopWebServer() {
isWebServerSunning = false;
try {
if (httpServerSocket != null) {
httpServerSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void startWebServer() {
isWebServerSunning = true;
Thread webServerThread = new Thread(() -> {
Socket socket;
HttpResponseThread httpResponseThread;
try {
httpServerSocket = new ServerSocket(5050);
while (isWebServerSunning) {
socket = httpServerSocket.accept();
httpResponseThread = new HttpResponseThread(socket);
httpResponseThread.start();
}
} catch (Exception e) {
e.printStackTrace();
}
});
webServerThread.start();
}
private class HttpResponseThread extends Thread {
Socket clientSocket;
HttpResponseThread(Socket socket) {
this.clientSocket = socket;
}
#Override
public void run() {
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
OutputStream outputStream = clientSocket.getOutputStream();
) {
String input = bufferedReader.readLine();
if (input != null && !input.isEmpty() && input.contains("/") && input.contains(" ")) {
if (input.contains(drawableDelimiter)) {
String imageId = input.substring(input.indexOf("/") + 1, input.lastIndexOf(" ")).trim().split(drawableDelimiter)[1];
Bitmap bitmap = BitmapFactory.decodeResource(activity.getResources(), Integer.parseInt(imageId));
if (bitmap != null) {
ByteArrayOutputStream bitmapBytes = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, bitmapBytes);
outputStream.write("HTTP/1.0 200 OK\r\n".getBytes());
outputStream.write("Server: Apache/0.8.4\r\n".getBytes());
outputStream.write(("Content-Length: " + bitmapBytes.toByteArray().length + "\r\n").getBytes());
outputStream.write("\r\n".getBytes());
outputStream.write(bitmapBytes.toByteArray());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
And just start or stop your web server at when Google Cast will be useable or stopped.
MyWebServer myWebServer = new MyWebServer(this); // pass your activity here
myWebServer.startWebServer();
myWebServer.stopWebServer();