如何在android wear app of React-native-android中使用App检测
How to use App detection in android wear app of React-native-android
我用 React-native
开发了一个 Android
应用程序,然后 link 我的移动 phone 应用程序和一个 wear 应用程序。
所以我要在 wear
应用程序中找出我手机 phone 上是否有互锁应用程序。
我参考了应用检测功能document among Android functions.
我根据文档描述给'app/res/values/wear.xml'和'wear/res/values/wear.xml'加了值
- app/res/values/wear.xml
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@array/android_wear_capabilities">
<string-array name="android_wear_capabilities">
<item>sample_app</item>
</string-array>
</resources>
- wear/res/values/wear.xml
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@array/android_wear_capabilities">
<string-array name="android_wear_capabilities">
<item>sample_wear</item>
</string-array>
</resources>
并且我使用手表 app.However 中的 Capability Client 检查了 phone 应用程序是否安装在配对的手机 phone 上,Wear 应用程序没有检测到我的手机 phone应用
wear/MainActivity
package com.soundgym.watchos
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.util.Log
import androidx.core.view.doOnPreDraw
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.view.setPadding
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.lifecycleScope
import androidx.wear.phone.interactions.PhoneTypeHelper
import androidx.wear.remote.interactions.RemoteActivityHelper
import androidx.wear.widget.ConfirmationOverlay
import com.google.android.gms.tasks.Tasks
import com.google.android.gms.wearable.*
import com.soundgym.watchos.databinding.ActivityMainBinding
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.guava.await
import kotlinx.coroutines.launch
import kotlinx.coroutines.tasks.await
import kotlinx.coroutines.withContext
import kotlin.math.roundToInt
import kotlin.math.sqrt
class MainWearActivity : FragmentActivity(), CapabilityClient.OnCapabilityChangedListener {
private lateinit var binding: ActivityMainBinding
private lateinit var capabilityClient: CapabilityClient
private lateinit var nodeClient: NodeClient
private lateinit var remoteActivityHelper: RemoteActivityHelper
private var androidPhoneNodeWithApp: Node? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
capabilityClient = Wearable.getCapabilityClient(this.applicationContext)
nodeClient = Wearable.getNodeClient(this)
remoteActivityHelper = RemoteActivityHelper(this)
binding.informationTextView.text = getString(R.string.message_checking)
binding.remoteOpenButton.setOnClickListener {
openAppInStoreOnPhone()
}
if (resources.configuration.isScreenRound) {
binding.scrollingContentContainer.doOnPreDraw {
// Calculate the padding necessary to make the scrolling content fit in a square inscribed on a round
// screen.
it.setPadding((it.width / 2.0 * (1.0 - 1.0 / sqrt(2.0))).roundToInt())
}
}
}
override fun onPause() {
super.onPause()
Wearable.getCapabilityClient(this).removeListener(this, CAPABILITY_PHONE_APP)
}
override fun onResume() {
super.onResume()
Wearable.getCapabilityClient(this).addListener(this, CAPABILITY_PHONE_APP)
lifecycleScope.launch {
checkIfPhoneHasApp()
}
}
/*
* Updates UI when capabilities change (install/uninstall phone app).
*/
override fun onCapabilityChanged(capabilityInfo: CapabilityInfo) {
Log.d(TAG, "onCapabilityChanged(): $capabilityInfo")
// There should only ever be one phone in a node set (much less w/ the correct capability), so
// I am just grabbing the first one (which should be the only one).
androidPhoneNodeWithApp = capabilityInfo.nodes.firstOrNull()
updateUi()
}
private suspend fun checkIfPhoneHasApp() {
Log.d(TAG, "checkIfPhoneHasApp()")
try {
val capabilityInfo = capabilityClient
.getCapability(CAPABILITY_PHONE_APP, CapabilityClient.FILTER_REACHABLE)
.await()
Log.d(TAG, "Capability request succeeded.${capabilityInfo.nodes.size}")
withContext(Dispatchers.Main) {
// There should only ever be one phone in a node set (much less w/ the correct capability), so
// I am just grabbing the first one (which should be the only one).
androidPhoneNodeWithApp = capabilityInfo.nodes.firstOrNull()
updateUi()
}
} catch (cancellationException: CancellationException) {
// Request was cancelled normally
} catch (throwable: Throwable) {
Log.d(TAG, "Capability request failed to return any results. \n reason : ${throwable.message}")
}
}
private fun updateUi() {
val androidPhoneNodeWithApp = androidPhoneNodeWithApp
if (androidPhoneNodeWithApp != null) {
// TODO: Add your code to communicate with the phone app via
// Wear APIs (MessageClient, DataClient, etc.)
Log.d(TAG, "Installed")
binding.informationTextView.text =
getString(R.string.message_installed, androidPhoneNodeWithApp.displayName)
binding.remoteOpenButton.isInvisible = true
} else {
Log.d(TAG, "Missing")
binding.informationTextView.text = getString(R.string.message_missing)
binding.remoteOpenButton.isVisible = true
}
}
private fun openAppInStoreOnPhone() {
Log.d(TAG, "openAppInStoreOnPhone()")
val intent = when (PhoneTypeHelper.getPhoneDeviceType(applicationContext)) {
PhoneTypeHelper.DEVICE_TYPE_ANDROID -> {
Log.d(TAG, "\tDEVICE_TYPE_ANDROID")
// Create Remote Intent to open Play Store listing of app on remote device.
Intent(Intent.ACTION_VIEW)
.addCategory(Intent.CATEGORY_BROWSABLE)
.setData(Uri.parse(ANDROID_MARKET_APP_URI))
}
PhoneTypeHelper.DEVICE_TYPE_IOS -> {
Log.d(TAG, "\tDEVICE_TYPE_IOS")
// Create Remote Intent to open App Store listing of app on iPhone.
Intent(Intent.ACTION_VIEW)
.addCategory(Intent.CATEGORY_BROWSABLE)
.setData(Uri.parse(APP_STORE_APP_URI))
}
else -> {
Log.d(TAG, "\tDEVICE_TYPE_ERROR_UNKNOWN")
return
}
}
lifecycleScope.launch {
try {
remoteActivityHelper.startRemoteActivity(intent).await()
ConfirmationOverlay().showOn(this@MainWearActivity)
} catch (cancellationException: CancellationException) {
// Request was cancelled normally
throw cancellationException
} catch (throwable: Throwable) {
ConfirmationOverlay()
.setType(ConfirmationOverlay.FAILURE_ANIMATION)
.showOn(this@MainWearActivity)
}
}
}
companion object {
private const val TAG = "MainWearActivity"
// Name of capability listed in Phone app's wear.xml.
// IMPORTANT NOTE: This should be named differently than your Wear app's capability.
private const val CAPABILITY_PHONE_APP = "sample_app"
// Links to install mobile app for both Android (Play Store) and iOS.
// TODO: Replace with your links/packages.
private const val ANDROID_MARKET_APP_URI = "market://details?id=com.sample.app"
// TODO: Replace with your links/packages.
private const val APP_STORE_APP_URI = "https://apps.apple.com/kr/app/com/id11111115"
}
}
为什么我检测不到???
问题的原因是手机phone应用的包名和手表应用的包名不一样。此名称必须相同才能读取信息。
NOTE : In the case of React-native
projects, the same changes should
be made to the wear app
because there is a default sign key for debug
.
wear/build.gradle
signingConfigs {
debug {
storeFile file('../app/debug.keystore')
storePassword 'signingConfigs storePassword of app/build.gradle'
keyAlias 'signingConfigs keyAlias of app/build.gradle'
keyPassword 'signingConfigs keyPassword of app/build.gradle'
}
}
我用 React-native
开发了一个 Android
应用程序,然后 link 我的移动 phone 应用程序和一个 wear 应用程序。
所以我要在 wear
应用程序中找出我手机 phone 上是否有互锁应用程序。
我参考了应用检测功能document among Android functions.
我根据文档描述给'app/res/values/wear.xml'和'wear/res/values/wear.xml'加了值
- app/res/values/wear.xml
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@array/android_wear_capabilities">
<string-array name="android_wear_capabilities">
<item>sample_app</item>
</string-array>
</resources>
- wear/res/values/wear.xml
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@array/android_wear_capabilities">
<string-array name="android_wear_capabilities">
<item>sample_wear</item>
</string-array>
</resources>
并且我使用手表 app.However 中的 Capability Client 检查了 phone 应用程序是否安装在配对的手机 phone 上,Wear 应用程序没有检测到我的手机 phone应用
wear/MainActivity
package com.soundgym.watchos
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.util.Log
import androidx.core.view.doOnPreDraw
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.view.setPadding
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.lifecycleScope
import androidx.wear.phone.interactions.PhoneTypeHelper
import androidx.wear.remote.interactions.RemoteActivityHelper
import androidx.wear.widget.ConfirmationOverlay
import com.google.android.gms.tasks.Tasks
import com.google.android.gms.wearable.*
import com.soundgym.watchos.databinding.ActivityMainBinding
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.guava.await
import kotlinx.coroutines.launch
import kotlinx.coroutines.tasks.await
import kotlinx.coroutines.withContext
import kotlin.math.roundToInt
import kotlin.math.sqrt
class MainWearActivity : FragmentActivity(), CapabilityClient.OnCapabilityChangedListener {
private lateinit var binding: ActivityMainBinding
private lateinit var capabilityClient: CapabilityClient
private lateinit var nodeClient: NodeClient
private lateinit var remoteActivityHelper: RemoteActivityHelper
private var androidPhoneNodeWithApp: Node? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
capabilityClient = Wearable.getCapabilityClient(this.applicationContext)
nodeClient = Wearable.getNodeClient(this)
remoteActivityHelper = RemoteActivityHelper(this)
binding.informationTextView.text = getString(R.string.message_checking)
binding.remoteOpenButton.setOnClickListener {
openAppInStoreOnPhone()
}
if (resources.configuration.isScreenRound) {
binding.scrollingContentContainer.doOnPreDraw {
// Calculate the padding necessary to make the scrolling content fit in a square inscribed on a round
// screen.
it.setPadding((it.width / 2.0 * (1.0 - 1.0 / sqrt(2.0))).roundToInt())
}
}
}
override fun onPause() {
super.onPause()
Wearable.getCapabilityClient(this).removeListener(this, CAPABILITY_PHONE_APP)
}
override fun onResume() {
super.onResume()
Wearable.getCapabilityClient(this).addListener(this, CAPABILITY_PHONE_APP)
lifecycleScope.launch {
checkIfPhoneHasApp()
}
}
/*
* Updates UI when capabilities change (install/uninstall phone app).
*/
override fun onCapabilityChanged(capabilityInfo: CapabilityInfo) {
Log.d(TAG, "onCapabilityChanged(): $capabilityInfo")
// There should only ever be one phone in a node set (much less w/ the correct capability), so
// I am just grabbing the first one (which should be the only one).
androidPhoneNodeWithApp = capabilityInfo.nodes.firstOrNull()
updateUi()
}
private suspend fun checkIfPhoneHasApp() {
Log.d(TAG, "checkIfPhoneHasApp()")
try {
val capabilityInfo = capabilityClient
.getCapability(CAPABILITY_PHONE_APP, CapabilityClient.FILTER_REACHABLE)
.await()
Log.d(TAG, "Capability request succeeded.${capabilityInfo.nodes.size}")
withContext(Dispatchers.Main) {
// There should only ever be one phone in a node set (much less w/ the correct capability), so
// I am just grabbing the first one (which should be the only one).
androidPhoneNodeWithApp = capabilityInfo.nodes.firstOrNull()
updateUi()
}
} catch (cancellationException: CancellationException) {
// Request was cancelled normally
} catch (throwable: Throwable) {
Log.d(TAG, "Capability request failed to return any results. \n reason : ${throwable.message}")
}
}
private fun updateUi() {
val androidPhoneNodeWithApp = androidPhoneNodeWithApp
if (androidPhoneNodeWithApp != null) {
// TODO: Add your code to communicate with the phone app via
// Wear APIs (MessageClient, DataClient, etc.)
Log.d(TAG, "Installed")
binding.informationTextView.text =
getString(R.string.message_installed, androidPhoneNodeWithApp.displayName)
binding.remoteOpenButton.isInvisible = true
} else {
Log.d(TAG, "Missing")
binding.informationTextView.text = getString(R.string.message_missing)
binding.remoteOpenButton.isVisible = true
}
}
private fun openAppInStoreOnPhone() {
Log.d(TAG, "openAppInStoreOnPhone()")
val intent = when (PhoneTypeHelper.getPhoneDeviceType(applicationContext)) {
PhoneTypeHelper.DEVICE_TYPE_ANDROID -> {
Log.d(TAG, "\tDEVICE_TYPE_ANDROID")
// Create Remote Intent to open Play Store listing of app on remote device.
Intent(Intent.ACTION_VIEW)
.addCategory(Intent.CATEGORY_BROWSABLE)
.setData(Uri.parse(ANDROID_MARKET_APP_URI))
}
PhoneTypeHelper.DEVICE_TYPE_IOS -> {
Log.d(TAG, "\tDEVICE_TYPE_IOS")
// Create Remote Intent to open App Store listing of app on iPhone.
Intent(Intent.ACTION_VIEW)
.addCategory(Intent.CATEGORY_BROWSABLE)
.setData(Uri.parse(APP_STORE_APP_URI))
}
else -> {
Log.d(TAG, "\tDEVICE_TYPE_ERROR_UNKNOWN")
return
}
}
lifecycleScope.launch {
try {
remoteActivityHelper.startRemoteActivity(intent).await()
ConfirmationOverlay().showOn(this@MainWearActivity)
} catch (cancellationException: CancellationException) {
// Request was cancelled normally
throw cancellationException
} catch (throwable: Throwable) {
ConfirmationOverlay()
.setType(ConfirmationOverlay.FAILURE_ANIMATION)
.showOn(this@MainWearActivity)
}
}
}
companion object {
private const val TAG = "MainWearActivity"
// Name of capability listed in Phone app's wear.xml.
// IMPORTANT NOTE: This should be named differently than your Wear app's capability.
private const val CAPABILITY_PHONE_APP = "sample_app"
// Links to install mobile app for both Android (Play Store) and iOS.
// TODO: Replace with your links/packages.
private const val ANDROID_MARKET_APP_URI = "market://details?id=com.sample.app"
// TODO: Replace with your links/packages.
private const val APP_STORE_APP_URI = "https://apps.apple.com/kr/app/com/id11111115"
}
}
为什么我检测不到???
问题的原因是手机phone应用的包名和手表应用的包名不一样。此名称必须相同才能读取信息。
NOTE : In the case of
React-native
projects, the same changes should be made to thewear app
because there is a defaultsign key for debug
.
wear/build.gradle
signingConfigs {
debug {
storeFile file('../app/debug.keystore')
storePassword 'signingConfigs storePassword of app/build.gradle'
keyAlias 'signingConfigs keyAlias of app/build.gradle'
keyPassword 'signingConfigs keyPassword of app/build.gradle'
}
}