无法使用 Google fit Api 看到 TicWatch pro 3 传感器

问题描述

我试图使用 Google 健身 api 访问 TicWatch Pro 3 上的传感器,以查看是否可以检索 spo2 数据,因为不可能通过 android api 获取它。当我尝试获取所有可用传感器的列表时,我只得到了智能手机的传感器,我错过了一些东西。这是我的代码`在此处输入代码

package com.google.android.gms.fit.samples.basicsensorsapikotlin

import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Color
import android.net.Uri
import android.os.Bundle
import android.provider.Settings
import android.view.Menu
import android.view.MenuItem
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.widget.TextViewCompat
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.fit.samples.common.logger.Log
import com.google.android.gms.fit.samples.common.logger.LogView
import com.google.android.gms.fit.samples.common.logger.LogWrapper
import com.google.android.gms.fit.samples.common.logger.MessageOnlyLogFilter
import com.google.android.gms.fitness.fitness
import com.google.android.gms.fitness.fitnessOptions
import com.google.android.gms.fitness.data.DataSource
import com.google.android.gms.fitness.data.DataType
import com.google.android.gms.fitness.request.DataSourcesRequest
import com.google.android.gms.fitness.request.OnDataPointListener
import com.google.android.gms.fitness.request.SensorRequest
import com.google.android.material.snackbar.Snackbar
import java.util.concurrent.TimeUnit

const val TAG = "BasicSensorsApi"

/**
 * This enum is used to define actions that can be performed after a successful sign in to Fit.
 * One of these values is passed to the Fit sign-in,and returned in a successful callback,allowing
 * subsequent execution of the desired action.
 */
enum class FitactionRequestCode {
  FIND_DATA_SOURCES
}

/**
 * This sample demonstrates how to use the Sensors API of the Google Fit platform to find available
 * data sources and to register/unregister listeners to those sources. It also demonstrates how to
 * authenticate a user with Google Play Services.
 */
class MainActivity: AppCompatActivity() {
  private val fitnessOptions = fitnessOptions.builder().addDataType(DataType.TYPE_LOCATION_SAMPLE).build()

  private val runningQOrLater =
    android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q

  // [START dataPointListener_variable_reference]
  // Need to hold a reference to this listener,as it's passed into the "unregister"
  // method in order to stop all sensors from sending data to this listener.
  private
  var dataPointListener: OnDataPointListener ? = null

  // [END dataPointListener_variable_reference]

  // [START auth_oncreate_setup]
  override fun onCreate(savedInstanceState: Bundle ? ) {
    super.onCreate(savedInstanceState)
    // Put application specific code here.
    setContentView(R.layout.activity_main)
    // This method sets up our custom logger,which will print all log messages to the device
    // screen,as well as to adb logcat.
    initializeLogging()
    // When permissions are revoked the app is restarted so onCreate is sufficient to check for
    // permissions core to the Activity's functionality.
    checkPermissionsAndRun(FitactionRequestCode.FIND_DATA_SOURCES)
  }

  private fun checkPermissionsAndRun(fitactionRequestCode: FitactionRequestCode) {
    if (permissionApproved()) {
      fitSignIn(fitactionRequestCode)
    } else {
      requestRuntimePermissions(fitactionRequestCode)
    }
  }

  /**
   * Checks that the user is signed in,and if so,executes the specified function. If the user is
   * not signed in,initiates the sign in flow,specifying the post-sign in function to execute.
   *
   * @param requestCode The request code corresponding to the action to perform after sign in.
   */
  private fun fitSignIn(requestCode: FitactionRequestCode) {
    if (oAuthPermissionsApproved()) {
      performActionForRequestCode(requestCode)
    } else {
      requestCode.let {
        GoogleSignIn.requestPermissions(
          this,it.ordinal,getGoogleAccount(),fitnessOptions)
      }
    }
  }

  private fun oAuthPermissionsApproved() = GoogleSignIn.hasPermissions(getGoogleAccount(),fitnessOptions)

  /**
   * Gets a Google account for use in creating the fitness client. This is achieved by either
   * using the last signed-in account,or if necessary,prompting the user to sign in.
   * `getAccountForExtension` is recommended over `getLastSignedInAccount` as the latter can
   * return `null` if there has been no sign in before.
   */
  private fun getGoogleAccount() = GoogleSignIn.getAccountForExtension(this,fitnessOptions)

  /**
   * Handles the callback from the OAuth sign in flow,executing the post sign in function
   */
  override fun onActivityResult(requestCode: Int,resultCode: Int,data: Intent ? ) {
    super.onActivityResult(requestCode,resultCode,data)

    when(resultCode) {
      RESULT_OK - > {
        val postSignInAction = FitactionRequestCode.values()[requestCode]
        postSignInAction.let {
          performActionForRequestCode(postSignInAction)
        }
      }
      else - > oAuthErrorMsg(requestCode,resultCode)
    }
  }

  /**
   * Runs the desired method,based on the specified request code. The request code is typically
   * passed to the Fit sign-in flow,and returned with the success callback. This allows the
   * caller to specify which method,post-sign-in,should be called.
   *
   * @param requestCode The code corresponding to the action to perform.
   */
  private fun performActionForRequestCode(requestCode: FitactionRequestCode) = when(requestCode) {
    FitactionRequestCode.FIND_DATA_SOURCES - > findfitnessDataSources()
  }

  private fun oAuthErrorMsg(requestCode: Int,resultCode: Int) {
    val message = ""
    "
    There was an error signing into Fit.Check the troubleshooting section of the README
    for potential issues.
    Request code was: $requestCode
    Result code was: $resultCode ""
    ".trimIndent()
    Log.e(TAG,message)
  }
  // [END auth_oncreate_setup]

  /** Finds available data sources and attempts to register on a specific [DataType].  */
  private fun findfitnessDataSources() { // [START find_data_sources]
    // Note: fitness.SensorsApi.findDataSources() requires the ACCESS_FINE_LOCATION permission.
    fitness.getSensorsClient(this,getGoogleAccount())
      .findDataSources(
        DataSourcesRequest.Builder()
        .setDataTypes(DataType.TYPE_LOCATION_SAMPLE,DataType.TYPE_SPEED,DataType.TYPE_WORKOUT_EXERCISE,DataType.TYPE_HEART_RATE_BPM)
        .build())
      .addOnSuccessListener {
        dataSources - >
          dataSources.forEach {
            Log.i(TAG,"\nData source found: ${it.streamIdentifier}")
            Log.i(TAG,"Data Source type: ${it.dataType.name}")

            /*if (it.dataType == DataType.TYPE_LOCATION_SAMPLE) {
                Log.i(TAG,"Data source for LOCATION_SAMPLE found!")

            }*/
          }
      }
      .addOnFailureListener {
        e - > Log.e(TAG,"Failed",e)
      }
    // [END find_data_sources]
  }

  /**
   * Registers a listener with the Sensors API for the provided [DataSource] and [DataType] combo.
   */
  private fun registerfitnessDataListener(dataSource: DataSource,dataType: DataType) {
    // [START register_data_listener]
    dataPointListener = OnDataPointListener {
      dataPoint - >
        for (field in dataPoint.dataType.fields) {
          val value = dataPoint.getValue(field)
          Log.i(TAG,"Detected DataPoint field: ${field.name}")
          Log.i(TAG,"Detected DataPoint value: $value")
        }
    }
    fitness.getSensorsClient(this,getGoogleAccount())
      .add(
        SensorRequest.Builder()
        .setDataSource(dataSource) // Optional but recommended for custom data sets.
        .setDataType(dataType) // Can't be omitted.
        .setSamplingRate(10,TimeUnit.SECONDS)
        .build(),dataPointListener)
      .addOnCompleteListener {
        task - >
          if (task.isSuccessful) {
            Log.i(TAG,"Listener registered!")
          } else {
            Log.e(TAG,"Listener not registered.",task.exception)
          }
      }
    // [END register_data_listener]
  }

  /** Unregisters the listener with the Sensors API.  */
  private fun unregisterfitnessDataListener() {
    if (dataPointListener == null) {
      // This code only activates one listener at a time.  If there's no listener,there's
      // nothing to unregister.
      return
    }
    // [START unregister_data_listener]
    // Waiting isn't actually necessary as the unregister call will complete regardless,// even if called from within onStop,but a callback can still be added in order to
    // inspect the results.
    fitness.getSensorsClient(this,getGoogleAccount())
      .remove(dataPointListener)
      .addOnCompleteListener {
        task - >
          if (task.isSuccessful && task.result!!) {
            Log.i(TAG,"Listener was removed!")
          } else {
            Log.i(TAG,"Listener was not removed.")
          }
      }
    // [END unregister_data_listener]
  }

  override fun onCreateOptionsMenu(menu: Menu): Boolean {
    // Inflate the menu; this adds items to the action bar if it is present.
    menuInflater.inflate(R.menu.main,menu)
    return true
  }

  override fun onoptionsItemSelected(item: MenuItem): Boolean {
    val id = item.itemId
    if (id == R.id.action_unregister_listener) {
      unregisterfitnessDataListener()
      return true
    }
    return super.onoptionsItemSelected(item)
  }

  /** Initializes a custom log class that outputs both to in-app targets and logcat.  */
  private fun initializeLogging() { // Wraps Android's native log framework.
    val logWrapper = LogWrapper()
    // Using Log,front-end to the logging chain,emulates android.util.log method signatures.
    Log.setLogNode(logWrapper)
    // Filter strips out everything except the message text.
    val msgFilter = MessageOnlyLogFilter()
    logWrapper.next = msgFilter
    // On screen logging via a customized TextView.
    val logView = findViewById < View > (R.id.sample_logview) as LogView
    TextViewCompat.setTextAppearance(logView,R.style.Log)
    logView.setBackgroundColor(Color.WHITE)
    msgFilter.next = logView
    Log.i(TAG,"Ready")
  }

  private fun permissionApproved(): Boolean {
    val approved =
      if (runningQOrLater) {
        PackageManager.PERMISSION_GRANTED == ActivityCompat.checkSelfPermission(
          this,Manifest.permission.ACCESS_FINE_LOCATION)
      } else {
        true
      }
    return approved
  }

  private fun requestRuntimePermissions(requestCode: FitactionRequestCode) {
    val shouldProvideRationale =
      ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.ACCESS_FINE_LOCATION)

    // Provide an additional rationale to the user. This would happen if the user denied the
    // request prevIoUsly,but didn't check the "Don't ask again" checkBox.
    requestCode.let {
      if (shouldProvideRationale) {
        Log.i(TAG,"displaying permission rationale to provide additional context.")
        Snackbar.make(
            findViewById(R.id.main_activity_view),R.string.permission_rationale,Snackbar.LENGTH_INDEFINITE)
          .setAction(R.string.ok) {
            // Request permission
            ActivityCompat.requestPermissions(this,arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),requestCode.ordinal)
          }
          .show()
      } else {
        Log.i(TAG,"Requesting permission")
        // Request permission. It's possible this can be auto answered if device policy
        // sets the permission in a given state or the user denied the permission
        // prevIoUsly and checked "Never ask again".
        ActivityCompat.requestPermissions(this,requestCode.ordinal)
      }
    }
  }

  override fun onRequestPermissionsResult(requestCode: Int,permissions: Array < String >,grantResults: IntArray) {
    when {
      grantResults.isEmpty() - > {
        // If user interaction was interrupted,the permission request
        // is cancelled and you receive empty arrays.
        Log.i(TAG,"User interaction was cancelled.")
      }
      grantResults[0] == PackageManager.PERMISSION_GRANTED - > {
        // Permission was granted.
        val fitactionRequestCode = FitactionRequestCode.values()[requestCode]
        fitactionRequestCode.let {
          fitSignIn(fitactionRequestCode)
        }
      }
      else - > {
        // Permission denied.

        // In this Activity we've chosen to notify the user that they
        // have rejected a core permission for the app since it makes the Activity useless.
        // We're communicating this message in a Snackbar since this is a sample app,but
        // core permissions would typically be best requested during a welcome-screen flow.

        // Additionally,it is important to remember that a permission might have been
        // rejected without asking the user for permission (device policy or "Never ask
        // again" prompts). Therefore,a user interface affordance is typically implemented
        // when permissions are denied. Otherwise,your app Could appear unresponsive to
        // touches or interactions which have required permissions.

        Snackbar.make(
          findViewById(R.id.main_activity_view),R.string.permission_denied_explanation,Snackbar.LENGTH_INDEFINITE)
        .setAction(R.string.settings) {
          // Build intent that displays the App settings screen.
          val intent = Intent()
          intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
          val uri = Uri.fromParts("package",packageName,null)
          intent.data = uri
          intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
          startActivity(intent)
        }
        .show()
      }
    }
  }
}

移动应用的清单

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.google.android.gms.fit.samples.basicsensorsapikotlin" >
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.BODY_SENSORS" />
    <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    <application
        android:fullBackupContent="false"
        android:allowBackup="false"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

Wear 应用的清单

<?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.example.wear3">

    <uses-permission android:name="android.permission.WAKE_LOCK" />

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.BODY_SENSORS" />
    <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.BLUetoOTH" />
    <uses-permission android:name="android.permission.BLUetoOTH_ADMIN" />


    <uses-feature android:name="android.hardware.type.watch" />

    <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/Theme.BasicSensorsApiKotlin">
        <uses-library
            android:name="com.google.android.wearable"
            android:required="true" />

        <!--
               Set to true if your app is Standalone,that is,it does not require the handheld
               app to run.
        -->
        <Meta-data
            android:name="com.google.android.wearable.standalone"
            android:value="false" />

        <activity
            android:name=".MainActivity"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

感谢您的帮助,谢谢

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)