Android Wear + Wearable ChannelApi + How to open connection to cloud node (WIFI)? - android

The connection via bluetooth using the new Channel API is working like it should, but if I now switch to the cloud node (WIFI mode) the channel can't be opened. The result is always null and running into a timeout.
ChannelApi.OpenChannelResult result = Wearable.ChannelApi.openChannel(googleApiClient, "cloud", Constants.COPY_FILE_VIA_CHANNEL_PATH).await();
Is this the right way to establish a WIFI connection between nodes and is there somewhere an example how to implement this?
update:
on watch:
(afterwards send nodeID to phone via message. I used this approach after also seeing the problems from this thread Android Wear + Wearable ChannelApi openChannel not opening with remote node?)
on watch: (Send retrieved localNodeID to phone)
NodeApi.GetLocalNodeResult localNodeResult = Wearable.NodeApi.getLocalNode(googleApiClient).await();
on phone:
ChannelApi.OpenChannelResult result = Wearable.ChannelApi.openChannel(googleApiClient, localNodeId, Constants.COPY_FILE_VIA_CHANNEL_PATH).await();
Channel.GetOutputStreamResult getOutputStreamResult = channel.getOutputStream(googleApiClient).await();
OutputStream outputStream = getOutputStreamResult.getOutputStream();
outputStream.write(byteChunk);
outputStream.close();
on watch:
#Override
public void onChannelOpened(Channel channel) {
if (channel.getPath()
.equals(Constants.COPY_FILE_VIA_CHANNEL_PATH)) {
new SaveDataFromChannelTask(channel).execute();
}
}
protected class SaveDataFromChannelTask extends AsyncTask<Void, Void, Void> {
private final Channel channel;
public SaveDataFromChannelTask(Channel channel) {
this.channel = channel;
}
#Override
protected Void doInBackground(Void... params) {
Channel.GetInputStreamResult getInputStreamResult = channel.getInputStream(googleApiClient)
.await();
InputStream inputStream = getInputStreamResult.getInputStream();
saveFileToDisk(inputStream);
return null;
}
}
This is currently the implementation inside the "Wear Media" app, which is using the Channel API. Inside the settings the chunksize can be modified to any byte value between 1000 and 9999999, but during transfer and activated bluetooth and WIFI always the bluetooth connection is used.

"cloud" is not a valid node id - you need to use a node id of a device such as a phone or Android Wear device. The devices will automatically switch from bluetooth to wifi based on the underlying connection - there is nothing you need to do. You can use the CapabilityApi, similar to the sending messages training, to correctly determine the node id to open a channel with.

Related

OpenTok based application using both multiparty and broadcasting feature

The application that i m developing is mixture of both multiparty and broadcasting. There will be a single particular admin/host who's stream is subscribed by all the subscribers(Broadcasting). Both the host/publisher as well as all the subscribers will be able to see all the participants in the session(sort of multiparty). On event from the host/publisher such as button click etc, the publisher will now stop publishing his/her stream and all the subscriber will view the stream from a particular subscriber in the session that the host selects. Since my application uses android SDK and does not have moderator token. How can i succeed creating the application i desire.(
(note: I have already completed Basic audio,video chat )
Can you provide me with necessary methods and sample code to pull it off.
I used the following codes:
private HashMap<String, Stream> mStringStreams = new HashMap<String, Stream>();
#Override
public void onStreamReceived(Session session, final Stream stream) {
Log.d(LOG_TAG, "onStreamReceived: New Stream Received " + stream.getStreamId() + " in session: " + session.getSessionId()+" Stream name: "+stream.getName());
if(stream.getName().equalsIgnoreCase("Main_stream"))
{
mSubscriber = new Subscriber.Builder(SuscriberActivity.this, stream).build();
mSession.subscribe(mSubscriber);
mSubscriberViewContainer.addView(mSubscriber.getView());
}
if (mSubscribers.size() + 1 > MAX_NUM_SUBSCRIBERS ) {
Toast.makeText(this, "New subscriber ignored. MAX_NUM_SUBSCRIBERS limit reached.", Toast.LENGTH_LONG).show();
return;
}
if(stream.getName().compareTo("Main_stream")!=0)
{
final Subscriber subscriber = new Subscriber.Builder(SuscriberActivity.this, stream).build();
mStringStreams.put(stream.getStreamId().toString(), stream);
mSession.subscribe(subscriber);
mSubscribers.add(subscriber);
mSubscriberStreams.put(stream,subscriber);
int position = mSubscribers.size() - 1;
final int id = getResources().getIdentifier("subscriberview" + (new Integer(position)).toString(), "id", SuscriberActivity.this.getPackageName());
FrameLayout subscriberViewContainer = (FrameLayout) findViewById(id);
subscriber.setStyle(BaseVideoRenderer.STYLE_VIDEO_SCALE, BaseVideoRenderer.STYLE_VIDEO_FILL);
subscriberViewContainer.addView(subscriber.getView());
}
#Override
public void onSignalReceived(Session session, String s, String s1, Connection connection) {
stream = mSubscriberStreams.get(s1).getStream();
Stream stream= mStringStreams.get(s1);
Subscriber mSubscriber1 = new Subscriber.Builder(SuscriberActivity.this, stream).build();
mSubscriber1.setStyle(BaseVideoRenderer.STYLE_VIDEO_SCALE, BaseVideoRenderer.STYLE_VIDEO_FILL);
mSubscriberViewContainer.removeView(mPublisher.getView());
mSubscriberViewContainer.addView(mSubscriber1.getView().getRootView());
}
}
If I understand correctly, you're trying to enable participants to subscribe to a different stream when the publisher(main host) clicks on a button. To accomplish this, you can use the OpenTok Signaling API to send a signal with the streamId as the data to all participants so they can subscribe to that specific user.

socket.io + android - disconnect socket and reconnect with different url

What I have:
I have Android application with socket.io implementation. Servers run on node.js. Servers IPs are hardcoded in the app. Clients communicate with servers successfully - they send and receive messages.
IO.socket version I am using in my client: ('io.socket:socket.io-client:1.0.0')
What I need to get: My problem is I have to change server IP which the client is connected to, after client receives determined message.
Simplified client socket behaviour is more less like this:
socket.on("msg", new Emitter.Listener() {
#Override
public void call(Object... args) {
try {
switch (msg){
case "1":
keepOnListeningFirstServer();
break;
case "2":
connectToAnotherServer();
break;
...
What I managed to achieve is that after specific server command (msg = 2) client connects to the second server, but client remains connected to the first server (client is still listening to commands from the first server). Actually what happens I think is that client creates second socket, but not properly closing the first socket.
In this situation, client still receives messages form first server but it sends responses to the new second server. Quite weird, but that was the furthest I managed to get with this here.
What I have tried to disconnect client and start new listener was calling on client:
socket.disconnect();
socket.close();
socket.off();
and then creating new socket and connecting it:
socket = IOSocket.getInstance(socketIP).getSocket();
socket.connect();
So my questions in general are:
Why isn't my client starting listening to new server messages even though this client is sending responses to this new server?
Maybe there is a way to switch/update socket listener without closing it?
What is a proper way of closing/disconnecting/destroying this first socket?
Maybe somebody can give me a hint of better method to change io.socket server IP that client is listening to? (Please note that this application is Android native)
Any help or advice will be very appreciated, so thanks in advance!
You should close the socket and shutdown the channel before the disconnection really completes.
This should be your case:
//create the initial socket
Socket socket = IO.socket("your_url");
//define a constant for your channel
public static final String ON_MESSAGE_RECEIVED = "msg";
//define the event listener
private Emitter.Listener onMessageReceived = new Emitter.Listener() {
#Override
public void call(Object... args) {
//your logic for the event here
}
});
//attach your channels to the socket
socket.on(ON_MESSAGE_RECEIVED, onMessageReceived);
socket.connect();
//when you want to disconnect the socket remember to shutdown all your channels
socket.disconnect();
socket.off(ON_MESSAGE_RECEIVED, onMessageReceived);
//Then create a new socket with a new url and register again for the same event
IO.Options opts = new IO.Options();
opts.forceNew = true;
opts.reconnection = false;
socket = IO.socket("new_url", opts);
socket.on(ON_MESSAGE_RECEIVED, onMessageReceived);
socket.connect();

android : Get or create unique id for each device

I am developing an application using api 14 (android 4.0).
in manifest:
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="14" />
I want to get an unique id from each device (or create one) that could be the same even after reboot the device. But it is important that the id be different even for 2 same devices. How can i do that?
You can use device's IMEI number as unique Id.
You want to call android.telephony.TelephonyManager.getDeviceId().
This will return whatever string uniquely identifies the device (IMEI on GSM, MEID for CDMA).
You'll need the following permission in your AndroidManifest.xml:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
You can generate different device token using GCM....
And this device token will remain same even if you will uninstall and again installed the application or after factory setting.. you have to follow some steps......
Create a new project at Google Developers Console .
At this step, for simplicity, you just need to take note of 2 values: Project Number, which will be used as SENDER_ID in the client project; and API server key (created at Credentials), which will be used as API_KEY in the server project.
Create a new simple Android project for server side (with basic source code as my answer in the following links).
Create a new simple Android project for client side (with basic source code as my answer in the following links, I customized from the original source at Google Cloud Messaging - GitHub).
Run the client app, you will get the registration token (means that your device has successfully registered). Then, paste (hard-code) this token at CLIENT_REGISTRATION_TOKEN variable in server app (or write code to send this token to server app).
You can read more at the following questions, one of them you have read before with one of your previous questions:
How to implement a GCM Hello World for Android using Android Studio
Adding Google Cloud Messagin (GCM) for Android - Registration process
Try this one String android_id = Settings.Secure.getString(getApplicationContext().getContentResolver(),
Settings.Secure.ANDROID_ID);
Here android_id is the unique Id for each device.
Try this code
String UniqueDeviceId = AndroidDeviceIdentifier.getUniqueDeviceIdentifier(context);
Add this class too.
final class AndroidDeviceIdentifier {
private AndroidDeviceIdentifier() {
// hidden constructor of singleton
}
/**
* Returns a stable identifier for the current device.
*
* #param ctx The application's Context
* #return The unique device identifier
* #throws IllegalStateException If the device's identifier could not be determined
*/
public static String getUniqueDeviceIdentifier(#NonNull final Context ctx) throws IllegalStateException {
try {
return getDeviceUUID(ctx);
} catch (UnsupportedEncodingException | NoSuchAlgorithmException e) {
throw new IllegalStateException("Could not determine device identifier", e);
}
}
private static String getDeviceUUID(Context ctx) throws UnsupportedEncodingException, NoSuchAlgorithmException {
byte[] hash = makeHash(getMac(ctx), getSerialNumber(ctx));
return createUUIDFromHash(hash);
}
private static String createUUIDFromHash(byte[] hash) {
return UUID.nameUUIDFromBytes(hash).toString().toLowerCase(); // Server side wants lower cased UUIDs
}
private static byte[] makeHash(final String mac, final String serialNumber) throws UnsupportedEncodingException, NoSuchAlgorithmException {
MessageDigest sha;
sha = MessageDigest.getInstance("SHA-256");
sha.reset();
sha.update(mac.getBytes("UTF-8"));
sha.update(serialNumber.getBytes("UTF-8"));
return sha.digest();
}
private static String getSerialNumber(Context context) {
String serialNumber = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
if (serialNumber == null) {
serialNumber = "0000000000000000";
}
return serialNumber;
}
private static String getMac(Context context) {
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
String mac = wifiManager.getConnectionInfo().getMacAddress();
if (mac == null) {
mac = "000000000000";
}
return mac;
}
You will get a unique device id. Ping me if u have any questions
Try out this code below ,this device id is constant and even if you uninstall the app and reinstall this id remains constant and you can use it to retrieve the user data from database as well.
final String android_id = Settings.Secure.getString(getApplicationContext().getContentResolver(),
Settings.Secure.ANDROID_ID);
Just paste this code in your main activity or any function and can store the ID generated in the shared Preference for later use.

PNS error when sending push notification to registered Android device

I am setting up a service to send push notifications to our apps using an Azure Notification Hub. Registering the android device works fine (shows up correctly in VS 2015 Azure tools) but when I try and send a test post through the Azure portal or the VS 2015 Test Send tool I get the error "The Push Notification System handle for the registration is invalid" and the registration is deleted. I understand that when the notification hub gets an error it deletes the registration so that part makes sense. I can't find any info on why the PNS would be invalid. My registration code is:
public class PushRegObject
{
public string Platform { get; set; }
public string Handle { get; set; }
public string[] Tags { get; set; }
}
public async static Task<ObjectResultObject<string>> RegisterDevice(PushRegObject reg)
{
string newRegistrationID = null;
var hub = NotificationHubClient.CreateClientFromConnectionString(_connectionstring_, _name_);
if (reg.Handle != null)
{
// Get the Registration ID
var registrations = await hub.GetRegistrationsByChannelAsync(reg.Handle, 100);
foreach(var registration in registrations)
{
if (newRegistrationID == null)
newRegistrationID = registration.RegistrationId;
else
await hub.DeleteRegistrationAsync(registration);
}
if (newRegistrationID == null)
newRegistrationID = await hub.CreateRegistrationIdAsync();
// Register the Device
RegistrationDescription regObj = null;
switch (reg.Platform)
{
case "android":
var regDesc = await hub.CreateGcmNativeRegistrationAsync(newRegistrationID);
return new ObjectResultObject<string>(true, null) { Object = regDesc.GcmRegistrationId };
case "ios":
regObj = new AppleRegistrationDescription(reg.Handle);
break;
default:
return new ObjectResultObject<string>(false, "Unknown device type");
}
}
return new ObjectResultObject<string>(true, null) { Object = newRegistrationID };
}
Any ideas?
Update
With the help of Dmitry in narrowing down the problem, I found the solution which was to use the full token return from GCM in the CreateGcmNativeRegistrationAsync call.
var regDesc = await hub.CreateGcmNativeRegistrationAsync(reg.Handle);
Jason, my name is Dmitry and I work in Notification Hubs team. Below are a few steps that will help us to troubleshoot this issue.
Use NotificationHubClient.GetAllRegistrationsAsync(100) to get all your registrations from the notification hub, find the registration from your test android device there and compare the GcmRegistrationId of this registration with the actual registration id which device recieves from GCM.
If the registration ids on the previous step are equal, then make sure that you uploaded the correct GCM API Key to the notification hub.
If GCM credentials in the notification hub are correct, then try to send a notification to your device using some third-party tools, for example cURL and command line as Google suggests here - https://developers.google.com/web/fundamentals/getting-started/push-notifications/step-07?hl=en. Make sure that you use the same API Key, that was configured in the notification hub, and registration id, that was uploaded into the notification hub.
If you were able to successfully send a notification to your device using a third-party tool, then please send namespace and notification hub name to nhubsupport#microsoft.com and I'll take a look.

Wi-Fi Direct Android

I want to transfer files between 2 devices via Wi-Fi Direct.
I wanted to do the same thing as in WifiDirectDemo, but I can't transfer data from the group owner to the other device, so I tried this: each time when one of the devices clicks connect, the other device is set as the group owner, so on each connection the device who asks for connection is always the client and can send data.
The problem with this is that Android always remembers the first group created and therefore its group owner. In other words, what I did only works the first time unless I go to settings and forget the group created by the first connection.
I know that by using the disconnect button, the Wi-Fi group is removed, but the Android system puts it in remembered groups and uses its setting (group owner negotiation) when a new connection is to be made.
The second thing I tried was to create a ServerSocket on each device (on another port), so this way both the group owner and the other device would be clients and servers at the same time. I don't know if the group owner can be set as a client, but I cant create a ServerSocket on both devices. Here is my code:
<pre>
#Override
public void onConnectionInfoAvailable(final WifiP2pInfo info) {
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
this.info = info;
this.getView().setVisibility(View.VISIBLE);
// The owner IP is now known.
TextView view = (TextView) mContentView.findViewById(R.id.group_owner);
view.setText( getResources().getString(R.string.group_owner_text)
+ ((info.isGroupOwner == true) ? getResources().getString(R.string.yes)
: getResources().getString(R.string.no)));
// InetAddress from WifiP2pInfo struct.
view = (TextView) mContentView.findViewById(R.id.device_info);
view.setText("Group Owner IP - " + info.groupOwnerAddress.getHostAddress());
// After the group negotiation, we assign the group owner as the file
// server. The file server is single threaded, single connection server
// socket.
if (info.groupFormed && info.isGroupOwner) {
new FileServerAsyncTask(getActivity(), mContentView.findViewById(R.id.status_text),8988)
.execute();
mContentView.findViewById(R.id.btn_start_client).setVisibility(View.VISIBLE);
Log.d(WiFiDirectActivity.TAG, "serveur8988cree");
} else if (info.groupFormed) {
// The other device acts as the client. In this case, we enable the
// Get file button.
// In this case we create a server socket on another port
new FileServerAsyncTask(getActivity(), mContentView.findViewById(R.id.status_text),8987)
.execute();
mContentView.findViewById(R.id.btn_start_client).setVisibility(View.VISIBLE);
Log.d(WiFiDirectActivity.TAG, "serveur8987cree");
((TextView) mContentView.findViewById(R.id.status_text)).setText(getResources()
.getString(R.string.client_text));
}
</pre>
Thanks for help.
You can delete all groups through reflection but, it's bit of a hack and class members might change later
private void deletePersistentInfo() {
try {
Class persistentInterface = null;
//Iterate and get class PersistentGroupInfoListener
for (Class<?> classR : WifiP2pManager.class.getDeclaredClasses()) {
if (classR.getName().contains("PersistentGroupInfoListener")) {
persistentInterface = classR;
break;
}
}
final Method deletePersistentGroupMethod = WifiP2pManager.class.getDeclaredMethod("deletePersistentGroup", new Class[]{Channel.class, int.class, ActionListener.class});
//anonymous class to implement PersistentGroupInfoListener which has a method, onPersistentGroupInfoAvailable
Object persitentInterfaceObject =
java.lang.reflect.Proxy.newProxyInstance(persistentInterface.getClassLoader(),
new java.lang.Class[]{persistentInterface},
new java.lang.reflect.InvocationHandler() {
#Override
public Object invoke(Object proxy, java.lang.reflect.Method method, Object[] args) throws java.lang.Throwable {
String method_name = method.getName();
if (method_name.equals("onPersistentGroupInfoAvailable")) {
Class wifiP2pGroupListClass = Class.forName("android.net.wifi.p2p.WifiP2pGroupList");
Object wifiP2pGroupListObject = wifiP2pGroupListClass.cast(args[0]);
Collection<WifiP2pGroup> wifiP2pGroupList = (Collection<WifiP2pGroup>) wifiP2pGroupListClass.getMethod("getGroupList", null).invoke(wifiP2pGroupListObject, null);
for (WifiP2pGroup group : wifiP2pGroupList) {
deletePersistentGroupMethod.invoke(wifiP2pManager, channel, (Integer) WifiP2pGroup.class.getMethod("getNetworkId").invoke(group, null), new ActionListener() {
#Override
public void onSuccess() {
//All groups deleted
}
#Override
public void onFailure(int i) {
}
});
}
}
return null;
}
});
Method requestPersistentGroupMethod =
WifiP2pManager.class.getDeclaredMethod("requestPersistentGroupInfo", new Class[]{Channel.class, persistentInterface});
requestPersistentGroupMethod.invoke(wifiP2pManager, channel, persitentInterfaceObject);
} catch (Exception ex) {
ex.printStackTrace();
}
}
To send data you need to know the IP address (not the device address) of the receiver. For the P2P client, the IP address of group_owner is available in the WifiP2pInfo variable, so it can use this to send data to the group owner. If the group owner knows the IP address of the P2P client to which it wants to send data, then it can also send files. This can be achieved in two ways.
Group owner assigns the IP addresses to the clients and stores the information about it.
Every newly added client sends its IP address to the group owner at the time of joining the group.

Categories

Resources