我的目标:我想用一个小的 Android 应用程序从 Google FIT API 中检索我的计步器数据。因此我尝试了教程代码:
- 我实现了使用 Google FIT 所必需的登录代码(https://developers.google.com/identity/sign-in/android/sign-in)
- 然后我为历史计步器数据实现了 Google Fit 请求(https://developers.google.com/fit/android/history)
- 我为我的应用授予了对 FIT API 的访问权限。
我的问题:代码似乎工作正常。调用了successListener,但没有检索到数据。我究竟做错了什么?
我的代码(对不起,它是从教程中复制的,没有格式化,不正确):
class GoogleFitStepCounterActivity : AppCompatActivity() {
val GOOGLE_FIT_PERMISSIONS_REQUEST_CODE = 123456
private val RC_SIGN_IN = 1
private var mGoogleSignInClient: GoogleSignInClient? = null
// Configure sign-in to request the user's ID, email address, and basic
// profile. ID and basic profile are included in DEFAULT_SIGN_IN.
var gso: GoogleSignInOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.build()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val signInButton = findViewById<SignInButton>(R.id.sign_in_button)
signInButton.setSize(SignInButton.SIZE_STANDARD)
signInButton.setOnClickListener {
signIn()
}
// Build a GoogleSignInClient with the options specified by gso.
mGoogleSignInClient = GoogleSignIn.getClient(this, gso);
}
override fun onStart() {
super.onStart()
val account = GoogleSignIn.getLastSignedInAccount(this)
updateUI(account)
}
private fun updateUI(account: GoogleSignInAccount?) {
if (account != null)
{
this.requestGoogleFITData()
}
}
private fun signIn() {
val signInIntent = mGoogleSignInClient!!.signInIntent
startActivityForResult(signInIntent, RC_SIGN_IN)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
super.onActivityResult(requestCode, resultCode, data)
// Result returned from launching the Intent from GoogleSignInClient.getSignInIntent(...);
// Result returned from launching the Intent from GoogleSignInClient.getSignInIntent(...);
if (requestCode === RC_SIGN_IN) {
// The Task returned from this call is always completed, no need to attach
// a listener.
val task: Task<GoogleSignInAccount> =
GoogleSignIn.getSignedInAccountFromIntent(data)
handleSignInResult(task)
}
}
private fun handleSignInResult(completedTask: Task<GoogleSignInAccount>) {
try {
val account = completedTask.getResult(ApiException::class.java)
// Signed in successfully, show authenticated UI.
updateUI(account)
} catch (e: ApiException) {
// The ApiException status code indicates the detailed failure reason.
// Please refer to the GoogleSignInStatusCodes class reference for more information.
Log.w(TAG, "signInResult:failed code=" + e.statusCode)
updateUI(null)
}
}
fun requestGoogleFITData()
{
var fitnessOptions = FitnessOptions.builder()
.addDataType(DataType.TYPE_STEP_COUNT_DELTA, FitnessOptions.ACCESS_READ)
.addDataType(DataType.AGGREGATE_STEP_COUNT_DELTA, FitnessOptions.ACCESS_READ)
.build()
val account = GoogleSignIn.getAccountForExtension(this, fitnessOptions)
if (!GoogleSignIn.hasPermissions(account, fitnessOptions)) {
GoogleSignIn.requestPermissions(
this, // your activity
GOOGLE_FIT_PERMISSIONS_REQUEST_CODE, // e.g. 1
account,
fitnessOptions)
} else {
accessGoogleFit()
}
}
private fun accessGoogleFit() {
val end = LocalDateTime.now()
val start = end.minusYears(1)
val endSeconds = end.atZone(ZoneId.systemDefault()).toEpochSecond()
val startSeconds = start.atZone(ZoneId.systemDefault()).toEpochSecond()
val readRequest = DataReadRequest.Builder()
.aggregate(DataType.AGGREGATE_STEP_COUNT_DELTA)
.setTimeRange(startSeconds, endSeconds, TimeUnit.SECONDS)
.bucketByTime(1, TimeUnit.DAYS)
.build()
val fitnessOptions = FitnessOptions.builder()
.addDataType(DataType.TYPE_STEP_COUNT_DELTA, FitnessOptions.ACCESS_READ)
.addDataType(DataType.AGGREGATE_STEP_COUNT_DELTA, FitnessOptions.ACCESS_READ)
.build()
val account = GoogleSignIn.getAccountForExtension(this, fitnessOptions)
Fitness.getHistoryClient(this, account)
.readData(readRequest)
.addOnSuccessListener({ response ->
// Use response data here
//Log.i(TAG, "OnSuccess()")
// The aggregate query puts datasets into buckets, so flatten into a
// single list of datasets
for (dataSet in response.buckets.flatMap { it.dataSets }) {
dumpDataSet(dataSet)
}
})
.addOnFailureListener(
{
e -> Log.d(TAG, "OnFailure()", e)
})
}
fun dumpDataSet(dataSet: DataSet) {
Log.i(TAG, "Data returned for Data type: ${dataSet.dataType.name}")
for (dp in dataSet.dataPoints) {
Log.i(TAG,"Data point:")
Log.i(TAG,"\tType: ${dp.dataType.name}")
Log.i(TAG,"\tStart: ${dp.getStartTimeString()}")
Log.i(TAG,"\tEnd: ${dp.getEndTimeString()}")
for (field in dp.dataType.fields) {
Log.i(TAG,"\tField: ${field.name.toString()} Value: ${dp.getValue(field)}")
}
}
}
fun DataPoint.getStartTimeString() = Instant.ofEpochSecond(this.getStartTime(TimeUnit.SECONDS))
.atZone(ZoneId.systemDefault())
.toLocalDateTime().toString()
fun DataPoint.getEndTimeString() = Instant.ofEpochSecond(this.getEndTime(TimeUnit.SECONDS))
.atZone(ZoneId.systemDefault())
.toLocalDateTime().toString()
}