Google Nearby - Connections API - Discovery not working - android

so recently my friends and i thought about creating an app that allows somewhat of realtime communication between the connected devices, without the need of a webserver. More explicitily, it should be an app, where a single device (master/host) create a game/session and multiple devices (slaves/clients) can join. after establishing all necessary connections (4-5 clients), the host should be able to broadcast data to the clients. Hence i researched a bit and if i understand it correctly the best guess for android are either the WiFi direct oder the google nearby connections api.
Q1. Is this the most simple approach to the desired goal ? or is this already too deep?
So i played a bit around with the connections api, i made a simple application and just used the code from the Nearby Documentation. Since im new to Kotlin, it could also be a rather simple mistake, however after a 2 hours, i swapped back to java with the same error. when the clients try to discover the host, they triggered their OnFailureListener. I tried to search for a solution online (including SO), but i could not find any useful information.
Im testing the application on a HTC ONE M8 and a Samsung Galaxy S7. To Ensure the Nearby Connection API features should work I also downloaded 2 example apps and those worked just fine. I tried how these handled the usage of the api but could not find the important part.
Q2. Where do i use the API wrong ? Or is it really just a error in the coding ?
MainActivity.kt
private const val TAG = android.R.attr.packageNames.toString() + "/Filter"
class MainActivity : AppCompatActivity() {
private lateinit var connectionClient : ConnectionsClient
private val payloadCallback = object : PayloadCallback() {
override fun onPayloadReceived(p0: String, p1: Payload) {
Toast.makeText(applicationContext, "Payload Received", Toast.LENGTH_SHORT).show()
}
override fun onPayloadTransferUpdate(p0: String, p1: PayloadTransferUpdate) {
Toast.makeText(applicationContext, "Payload Transfer Update", Toast.LENGTH_SHORT).show()
}
}
private val connPoint = object : ConnectionLifecycleCallback() {
override fun onConnectionInitiated(p0: String, p1: ConnectionInfo) {
connectionClient.acceptConnection(p0, payloadCallback)
Log.i(TAG, "OnConnectionInitiated")
}
override fun onConnectionResult(p0: String, p1: ConnectionResolution) {
when(p1.status.statusCode){
ConnectionsStatusCodes.STATUS_OK -> Log.i(TAG, "ConnectionsStatusCodes STATUS_OK")
ConnectionsStatusCodes.STATUS_CONNECTION_REJECTED -> Log.i(TAG, "ConnectionsStatusCodes STATUS_CONNECTION_REJECTED")
ConnectionsStatusCodes.STATUS_ERROR -> Log.i(TAG, "ConnectionsStatusCodes STATUS_ERROR")
else -> Log.i(TAG, "ConnectionsStatusCodes STATUS_UNKNOWN")
}
}
override fun onDisconnected(p0: String) {
Log.i(TAG, "onDisconnected $p0")
}
}
private val endPoint = object : EndpointDiscoveryCallback() {
override fun onEndpointFound(p0: String, p1: DiscoveredEndpointInfo) {
Log.i(TAG, "onEndpointFound ID: $p0 Name: ${p1.endpointName} ")
connectionClient.requestConnection(p1.endpointName, p0, connPoint)
.addOnSuccessListener {
Log.i(TAG, "OnSuccessListener requestConnection")
}
.addOnFailureListener {
Log.i(TAG, "OnFailureListener requestConnection")
}
}
override fun onEndpointLost(p0: String) {
Log.i(TAG, "$p0 disconnected")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
connectionClient = Nearby.getConnectionsClient(this.applicationContext)
//Toast.makeText(applicationContext, connectionClient.instanceId, Toast.LENGTH_SHORT).show()
setButtonOnClick()
}
override fun onStop() {
connectionClient.stopAllEndpoints()
connectionClient.stopAdvertising()
connectionClient.stopDiscovery()
super.onStop()
}
private fun setButtonOnClick(){
val create = findViewById<Button>(R.id.create_btn)
val join = findViewById<Button>(R.id.join_btn)
create.setOnClickListener{ _ -> CreateGroup()}
join.setOnClickListener{ _ -> JoinGroup()}
Log.i(TAG, "On Click Listener set")
}
private fun CreateGroup(){
Log.i(TAG, "Starting Advertising")
connectionClient
.startAdvertising(android.os.Build.MODEL,
packageName.toString(),
connPoint,
AdvertisingOptions.Builder().apply{
setStrategy(Strategy.P2P_CLUSTER)
}.build())
.addOnSuccessListener {
OnSuccessListener<Void> {
Log.i(TAG, "OnSuccessListener CreateGroup() was triggered")
}
}
.addOnFailureListener {
OnFailureListener {
Log.i(TAG, "OnFailureListener CreateGroup() was triggered")
}
}
}
private fun JoinGroup(){
Log.i(TAG, "Starting Discovering")
connectionClient.startDiscovery(packageName.toString(),
endPoint,
DiscoveryOptions.Builder().apply{
setStrategy(Strategy.P2P_CLUSTER)
}.build())
.addOnSuccessListener {
OnSuccessListener<Void> {
Log.i(TAG, "OnSuccessListener JoinGroup() was triggered")
}
}
.addOnFailureListener {
OnFailureListener {
Log.i(TAG, "OnSuccessListener JoinGroup() was triggered")
}
}
}
}
Android Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.testapplication">
<!-- Required for Nearby Connections -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Optional: only required for FILE payloads -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

Okay, after figuring out how to correctly setup the Listeners using Kotlin i found out that i got the exception of a missing permission, 01-20 21:11:14.269 1058-1058/com.example.testapplication I/16843649/Filter: 8034: MISSING_PERMISSION_ACCESS_COARSE_LOCATION, which i thought was strange since its in the manifest. However i went to the normal app settings and turned on the permissions manually, and it works now.

Related

WearableListenerService onCreate and onDataChanged not getting called

I'm trying to send data using the DataClient from a phone to a watch.
Things I looked out for:
same package name
no build flavors on both modules
added service to the wear modules manifest
same path prefix
same signing config
I tried this sample project and copied parts over to my project. I just can't find any issues with it.
The sample project ran fine on my hardware, interestingly enough it wasn't working in the emulator. Therefore I tested my app also only with my hardware. (Pixel 6 Pro & Pixel Watch)
The sending data part seems to be working, as it behaves the same way as the sample project does.
How I send data from the phone:
class WearDataManager(val context: Context) {
private val dataClient by lazy { Wearable.getDataClient(context) }
companion object {
private const val CLIENTS_PATH = "/clients"
private const val CLIENT_LIST_KEY = "clientlist"
}
fun sendClientList(clientList: MutableList<String>) {
GlobalScope.launch {
try {
val request = PutDataMapRequest.create(CLIENTS_PATH).apply {
dataMap.putStringArray(CLIENT_LIST_KEY, arrayOf("clientList, test"))
}
.asPutDataRequest()
.setUrgent()
val result = dataClient.putDataItem(request).await()
Log.d("TAG", "DataItem saved: $result")
} catch (cancellationException: CancellationException) {
throw cancellationException
} catch (exception: Exception) {
Log.d("TAG", "Saving DataItem failed: $exception")
}
}
}
}
This is how I'm receiving data on the watch:
class WearableListenerService: WearableListenerService() {
companion object {
const val CLIENTS_PATH = "/clients"
}
override fun onCreate() {
super.onCreate()
Log.d("testing", "STARTED SERVICE")
}
override fun onDataChanged(dataEvents: DataEventBuffer) {
super.onDataChanged(dataEvents)
Log.d("testing", "RECEIVED $dataEvents")
}
}
Surprisingly "STARTED SERVICE" does not appear in the log when I start the app on the watch. For my understanding that means that the system isn't aware of the listeners existance and didn't register it. So something must be wrong with the manifest below.
This is the service inside the manifest on the watch:
<service android:name=".wear.communication.WearableListenerService"
android:exported="true">
<intent-filter>
<action android:name="com.google.android.gms.wearable.DATA_CHANGED" />
<data
android:host="*"
android:pathPrefix="/clients"
android:scheme="wear" />
</intent-filter>
</service>
What am I missing here?
Turns out the sending part was the culprit after all. Be careful what scope you use or if you even want to use one at all. This function is being called inside of a worker in my code so it isn't an issue.
I completely modified the demo project above and with the help of this I found out why it wasn't working.
This is the working solution:
fun sendClientList(clientList: MutableList<String>) {
val request = PutDataMapRequest.create(CLIENTS_PATH).apply {
dataMap.putStringArray(CLIENT_LIST_KEY, arrayOf(clientList.joinToString()))
}
.asPutDataRequest()
.setUrgent()
val result = dataClient.putDataItem(request)
Log.d("TAG", "DataItem saved: $result")
}

Android 11 - CallRedirectionService not getting triggered

We need to invoke a service when an outgoing call is placed so that we can use the target number to show certain additional information. As per Android documentation, CallRedirectionService should be used. However, after declaring a custom service as depicted in documentation, we find that the custom service is not getting triggered. Please let us know what we are doing wrong. Appreciate your help.
I referred to this link as well but not clear on the answer. There is a mention to role acquisition but I did not find that in Android documentation. Please direct me to the relevant page if available.
CallRedirectionService Implementation not working
Manifest.xml
<service android:name="<mypackage>.CustomCallService"
android:permission="android.permission.BIND_CALL_REDIRECTION_SERVICE">
<intent-filter>
<action android:name="android.telecom.CallRedirectionService"/>
</intent-filter>
</service>
Custom service code
#Override
public void onPlaceCall(#NonNull Uri handle, #NonNull PhoneAccountHandle initialPhoneAccount, boolean allowInteractiveResponse) {
System.out.println("Outgoing:" + initialPhoneAccount + ":" + handle); //Call does not reach here
placeCallUnmodified();
}
In Kotlin:
Your implementation of the CallRedirectionService seems correct. I understand that the only step missing is the role request and acquisition.
You can prompt the user to give you the CallRedirectionService role by using the RoleManager class.
In this example below, we are requesting this role as soon as the MainActivity is created:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (!isRedirection())
roleAcquire(RoleManager.ROLE_CALL_REDIRECTION)
}
}
The following functions shall be used:
private fun isRedirection(): Boolean {
return isRoleHeldByApp(RoleManager.ROLE_CALL_REDIRECTION)
}
private fun isRoleHeldByApp(roleName: String): Boolean {
val roleManager: RoleManager? = getSystemService(RoleManager::class.java)
return roleManager!!.isRoleHeld(roleName)
}
private fun roleAcquire(roleName: String) {
val roleManager: RoleManager?
if (roleAvailable(roleName)) {
roleManager = getSystemService(RoleManager::class.java)
val intent = roleManager.createRequestRoleIntent(roleName)
startActivityForResult(intent, 1)
} else {
Toast.makeText(
this,
"Redirection call with role in not available",
Toast.LENGTH_SHORT
).show()
}
}
private fun roleAvailable(roleName: String): Boolean {
val roleManager: RoleManager? = getSystemService(RoleManager::class.java)
return roleManager!!.isRoleAvailable(roleName)
}

Android - invalidate Fingerprint authentication when user minimises app but not when when moving across activities

I have an app in which fingerprint lock is used to protect the app from being used without first authenticating with the fingerprint.
The issue is that the verification only happens at first launch of the app; so after first launch, no more verification until when the app is being launched again,
But I want the current verification to be valid when moving across different activities in the app, but it should be invalidated if the user minimises the app or when the phone screen goes off, so that the user would be asked to verify fingerprint again after minimizing and resuming back into the app.
I have tried using a base activity and overriding the onPause and onResume, but the methods get called even when moving across activities, which is not what I want.
You can likely use the system broadcast intents ACTION_SCREEN_ON, ACTION_SCREEN_OFF, and/or ACTION_USER_PRESENT to achieve your goals. Check https://developer.android.com/reference/android/content/Intent for more on these.
To do so, register as a broadcast receiver to receive these notifications in your main activity onCreate, un-register in onDestroy. You can use these notifications to set flags indicating whether authentication is needed.
Example code:
#Override
protected void onCreate(Bundle savedInstanceState)
{
.
.
.
// register to receive appropriate events
IntentFilter intentUserPresent = new IntentFilter(Intent.ACTION_USER_PRESENT);
intentUserPresent.addAction(Intent.ACTION_USER_PRESENT);
registerReceiver(userActionReceiver, intentUserPresent);
.
.
.
}
// create a BroadcastReceiver to receive the notifications
private BroadcastReceiver userActionReceiver = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
Log.d("MyLog", String.format("MainActivity received broadcaast %s", intent.toString()));
if (intent.getAction() == Intent.ACTION_USER_PRESENT)
{
// set flag indicating re-authentication is needed
}
}
};
#Override
protected void onDestroy()
{
// make sure to un-register the receiver!
unregisterReceiver(userActionReceiver);
super.onDestroy();
}
This won't detect a user "minimizing the app", but its unclear why you would consider that re-authentication would be needed in this scenario. Lifecycle events onStart/onStop can detect background/foreground of an activity.
Solved this few weeks ago while working on a completely different project from the one I was working on when I asked the question.
I am glad to share my workaround.
The app is locked after three seconds of minimizing the app or the screen going off; as long as the fingerprint switch is enabled in the app's preferences setting.
First, I created a BaseActivity class from which all other project activities inherit from except the Fingerprint activity class which inherits directly from AppCompatActivity()
package com.domainName.appName.ui.activities
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
open class BaseActivity : AppCompatActivity() {
private var securitySet=false
private var backPressed=false
private var goingToFingerprint=false
companion object{
private var handler:Handler?=null
}
override fun
onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
prefSaver.save(prefFileName, "launched_new_activity",sValue = true,saveImmediately=true)
if(handler==null){
handler= Handler(mainLooper)
}
handler?.removeCallbacksAndMessages(null)
}
override fun onDestroy() {
super.onDestroy()
handler?.removeCallbacksAndMessages(null)
}
override fun onStart() {
handler?.removeCallbacksAndMessages(null)
goingToFingerprint=false
securitySet=prefChecker.check(prefFileName,"fingerprint_security_switch",false)
super.onStart()
}
override fun onPause() {
super.onPause()
prefSaver.save(prefFileName,"launched_new_activity",sValue = false,saveImmediately=true)
if(securitySet && !backPressed){
if(!goingToFingerprint){postLock()}
}
}
override fun onResume() {
super.onResume()
backPressed=false
if(prefChecker.check(prefFileName,"app_locked",false)&&securitySet){
prefSaver.save(prefFileName,"launched_new_activity",sValue = true,saveImmediately=true)
goingToFingerprint=true
startActivity(Intent(this,FingerprintActivity::class.java))
}
}
override fun onBackPressed() {
backPressed=true
super.onBackPressed()
}
private fun postLock(){
handler?.removeCallbacksAndMessages(null)
handler?.postDelayed({
if(!prefChecker.check(prefFileName,"launched_new_activity",false)) {
prefSaver.save(prefFileName,"app_locked",sValue = true,saveImmediately=true)
}
},3000)
}
}
I created an InitialActivity which has noHistory mode set in the Android manifest and acts as the app launcher activity.
The InitialActivity inherits from BaseActivity as well and simply calls startActivity() in its onCreate method for the MainActivity activity.
The InitialActivity overrides onPause this way :
override fun onPause() {
if(prefChecker.check(prefFileName,"fingerprint_security_switch",false)) {
prefSaver.save(prefFileName,"app_locked",sValue = true,true)
/*setting this so app is locked on First Launch if the fingerprint switch is on.
The fingerprint switch is turned on and off from the SettingsActivity */
}
super.onPause()
}
Then FingerprintActivity is implemented this way:
package com.domainName.appName.ui.activities
import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.Gravity
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt
import androidx.biometric.BiometricPrompt.PromptInfo
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
class FingerprintActivity : AppCompatActivity() {
private lateinit var executor: Executor
var biometricPrompt: BiometricPrompt? = null
var promptInfo: PromptInfo? = null
var notifIntentLabel: String? = null
var finishImmediately=false
override fun
onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState);setContentView(R.layout.fingerprint_main)
executor = ContextCompat.getMainExecutor(this)
finishImmediately = intent.getBooleanExtra("finishImmediately",false)
if(finishImmediately)finish()
biometricPrompt = BiometricPrompt(this,
executor, object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
biometricPrompt?.cancelAuthentication()
val intent=Intent(this#FingerprintActivity,FingerprintActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.putExtra("finishImmediately",true)
startActivity(intent)
}
override fun onAuthenticationSucceeded(
result: BiometricPrompt.AuthenticationResult
) {
super.onAuthenticationSucceeded(result)
prefSaver.save(prefFileName,"app_locked",sValue = false,true)
finish()
}
})
promptInfo = PromptInfo.Builder()
.setTitle("AppName Biometric Login")
.setSubtitle("\t\tLog in using your fingerprint")
.setNegativeButtonText("Cancel")
.build()
val biometricManager = BiometricManager.from(this)
when (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)) {
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> showToast("No fingerprint hardware detected on this device.")
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> showToast("Fingerprint hardware features are currently unavailable on this device.")
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> showToast("There is no any fingerprint credential associated with your account.\nPlease go to your phone settings to enrol fingerprints.")
BiometricManager.BIOMETRIC_SUCCESS -> authenticateUser()
BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED -> showToast("Fingerprint feature on device requires updating and is therefore currently unavailable.")
BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED -> showToast("Fingerprint features not supported on device")
BiometricManager.BIOMETRIC_STATUS_UNKNOWN -> showToast("Fingerprint features status unknown")
}
}
private fun showToast(txt_to_show: String) {
val t = Toast.makeText(this, txt_to_show, Toast.LENGTH_SHORT)
t.setGravity(Gravity.CENTER or Gravity.CENTER_HORIZONTAL, 0, 0)
t.show()
}
private fun authenticateUser() {
if(!finishImmediately){
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.USE_FINGERPRINT
) == PackageManager.PERMISSION_GRANTED
) {
promptInfo?.let{ biometricPrompt?.authenticate(it)}
} else if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.USE_BIOMETRIC
) == PackageManager.PERMISSION_GRANTED
) {
promptInfo?.let{ biometricPrompt?.authenticate(it)}
} else {
showToast("Please grant the AppName app the necessary permissions to use fingerprint hardware.")
}
}
}
}
prefSaver and prefChecker are basically the class. The names were used for descriptive purpose. The class was implemented this way :
package com.domainName.appName.utils
import android.annotation.SuppressLint
import android.content.Context;
import android.content.SharedPreferences;
class PrefCheckerOrSaver(private val context:Context?){
fun <T>check(sFile: String,keyS: String,defaultS: T):T{
if(context==null)return defaultS
val sPref:SharedPreferences= context.getSharedPreferences(sFile,Context.MODE_PRIVATE)
return when(defaultS){
is String->sPref.getString(keyS,defaultS) as T
is Int-> sPref.getInt(keyS,defaultS) as T
is Long-> sPref.getLong(keyS,defaultS) as T
is Boolean-> sPref.getBoolean(keyS,defaultS) as T
else-> defaultS
}
}
#SuppressLint("ApplySharedPref")
fun save(sFile:String, sKey:String, sValue:Any, saveImmediately:Boolean=false){
if(context==null)return
val sharedPreferences:SharedPreferences = context.getSharedPreferences(sFile, Context.MODE_PRIVATE);
val editor:SharedPreferences.Editor = sharedPreferences.edit();
when(sValue) {
is String-> editor.putString(sKey, sValue)
is Int->editor.putInt(sKey, sValue)
is Long-> editor.putLong(sKey, sValue)
is Boolean->editor.putBoolean(sKey, sValue)
}
if(saveImmediately){editor.commit()}
else{ editor.apply()}
}
}
Permission in app manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
package="com.domainName.appName">
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<application
android:name=".MainApplication"
android:allowBackup="false"
android:icon="${appIcon}"
android:label="#string/app_name"
android:roundIcon="${appIconRound}"
android:resizeableActivity="true"
android:supportsRtl="false"
android:theme="#style/ThemeAppMain"
tools:targetApi="n"
>
<activity
android:name=".ui.activities.InitialActivity"
android:label="#string/app_name"
android:exported="true"
android:noHistory="true"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".ui.activities.BaseActivity" />
<activity android:name=".ui.activities.MainActivity" android:launchMode="singleTask"/>
<activity android:name=".ui.activities.SettingsActivity" />
<activity android:name=".ui.activities.FingerprintActivity"
android:launchMode="singleTask"
android:noHistory="true"
/>
</application>
</manifest>

Sending message from wearable to phone: onMessageRecieved not called

So I have a connected Wear Emulator and a android phone to test.
Sending data maps from phone to wearable works fine.
Now I want to send a message from the wearable to the phone.
I've tried this with AsyncTask and without it. Messages are being successfully sent in both cases, but the message never reaches my phone.
My application IDs are the same.
Here's my code:
//Wear
//Try with Async
inner class requestTokenTask : AsyncTask<Void?, Void?, Void?>() {
override fun doInBackground(vararg params: Void?): Void? {
Wearable.getMessageClient(this).sendMessage(_connectedNode.toString(), "/requesttoken", null)
return null
}
override fun onPostExecute(aVoid: Void?) {
super.onPostExecute(aVoid)
Log.d(TAG, "Message sent: $aVoid")
}
}
//Try without async
fun requestToken() {
if(_connectedNode?.id != null){
val sendTask: Task<*> = Wearable.getMessageClient(this).sendMessage(
_connectedNode!!.id!!,
"/requesttoken",
null
).apply {
addOnSuccessListener {
Log.d(TAG, "Message sent: $it")
}
addOnFailureListener {
Log.d(TAG, "Message NOT sent, error: $it")
}
}
}
}
Handheld code:
public override fun onResume() {
super.onResume()
Wearable.getDataClient(this).addListener(this)
Wearable.getMessageClient(this).addListener(this)
Wearable.getCapabilityClient(this)
.addListener(this, Uri.parse("wear://"), CapabilityClient.FILTER_REACHABLE)
}
override fun onMessageReceived(messageEvent: MessageEvent) {
Log.d(TAG, "onMessageReceived()")
//Receive the message from wear
if (messageEvent.path.equals("/requesttoken")) {
//Do stuff
}
}
Manifest part:
<activity
android:name=".wear.WearableActivity"
android:theme="#style/Theme.Transparent">
<intent-filter>
<action
android:name="com.google.android.gms.wearable.BIND_LISTENER"/>
<!-- listeners receive events that match the action and data filters -->
<action android:name="com.google.android.gms.wearable.DATA_CHANGED" />
<data android:scheme="wear" android:host="*" android:pathPrefix="/token"/>
</intent-filter>
</activity>
UPDATE: Just found out that "BIND_LISTENER" is deprecated, tried again with removing it and adding "MESSAGE_RECIEVED" instead, but it's still not working.
I tried another way with Broadcast Recievers with this tutorial and the communication now works in both ways

Auto Read OTP from SMS

I am trying to Read Sms by using this method. But my application is not reading Message.
The Code i have tried yet.
Permission :
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.SEND_SMS" />
Activity (Main Code) :
class OtpActivity : AppCompatActivity(), View.OnClickListener {
private var smsVerifyCatcher: SmsVerifyCatcher? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_otp)
smsVerifyCatcher = SmsVerifyCatcher(this, OnSmsCatchListener { message ->
val code = parseCode(message)//Parse verification code
Log.e("Code", code)
//then you can send verification code to server
})
smsVerifyCatcher!!.setPhoneNumberFilter("0902249") // I passed 10 digit number here
smsVerifyCatcher!!.setFilter("Ashish") // For extra i added Filter for name
}
private fun parseCode(message: String): String {
val p = Pattern.compile("\\b\\d{4}\\b")
val m = p.matcher(message)
var code = ""
while (m.find()) {
code = m.group(0)
}
return code
}
override fun onStart() {
super.onStart()
smsVerifyCatcher!!.onStart()
}
override fun onStop() {
super.onStop()
smsVerifyCatcher!!.onStop()
}
}
It's not a good idea because of this Reminder SMS/Call Log Policy Changes.
The recomended way is using SMS Retriever API from Google Play Services. See the Automatic SMS Verification with the SMS Retriever API.
Notice though that your server needs to send the messages following a few rules (message starts with "<#>", includes the OTP plus additional information and ends up with a hash identifying your app).

Categories

Resources