如何在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'加了值

<?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>
<?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应用的包名和手表应用的包名不一样。此名称必须相同才能读取信息。

Reference document

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'
        }
    }