ServiceBackedExerciseClient.kt
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.health.services.client.impl
import android.content.Context
import androidx.core.content.ContextCompat
import androidx.health.services.client.ExerciseClient
import androidx.health.services.client.ExerciseUpdateListener
import androidx.health.services.client.data.ExerciseCapabilities
import androidx.health.services.client.data.ExerciseConfig
import androidx.health.services.client.data.ExerciseGoal
import androidx.health.services.client.data.ExerciseInfo
import androidx.health.services.client.impl.ExerciseIpcClient.Companion.getServiceInterface
import androidx.health.services.client.impl.internal.ExerciseInfoCallback
import androidx.health.services.client.impl.internal.HsConnectionManager
import androidx.health.services.client.impl.internal.StatusCallback
import androidx.health.services.client.impl.ipc.ServiceOperation
import androidx.health.services.client.impl.ipc.internal.ConnectionManager
import androidx.health.services.client.impl.request.AutoPauseAndResumeConfigRequest
import androidx.health.services.client.impl.request.CapabilitiesRequest
import androidx.health.services.client.impl.request.ExerciseGoalRequest
import androidx.health.services.client.impl.request.StartExerciseRequest
import androidx.health.services.client.impl.response.ExerciseCapabilitiesResponse
import com.google.common.util.concurrent.Futures
import com.google.common.util.concurrent.ListenableFuture
import java.util.concurrent.Executor
/**
* [ExerciseClient] implementation that is backed by Health Services.
*
* @hide
*/
internal class ServiceBackedExerciseClient
private constructor(private val context: Context, connectionManager: ConnectionManager) :
ExerciseClient {
private val ipcClient: ExerciseIpcClient = ExerciseIpcClient(connectionManager)
override fun startExercise(configuration: ExerciseConfig): ListenableFuture<Void> {
val serviceOperation =
ServiceOperation<Void> { binder, resultFuture ->
getServiceInterface(binder)
.startExercise(
StartExerciseRequest(context.packageName, configuration),
StatusCallback(resultFuture)
)
}
return ipcClient.execute(serviceOperation)
}
override fun pauseExercise(): ListenableFuture<Void> {
val serviceOperation =
ServiceOperation<Void> { binder, resultFuture ->
getServiceInterface(binder)
.pauseExercise(context.packageName, StatusCallback(resultFuture))
}
return ipcClient.execute(serviceOperation)
}
override fun resumeExercise(): ListenableFuture<Void> {
val serviceOperation =
ServiceOperation<Void> { binder, resultFuture ->
getServiceInterface(binder)
.resumeExercise(context.packageName, StatusCallback(resultFuture))
}
return ipcClient.execute(serviceOperation)
}
override fun endExercise(): ListenableFuture<Void> {
val serviceOperation =
ServiceOperation<Void> { binder, resultFuture ->
getServiceInterface(binder)
.endExercise(context.packageName, StatusCallback(resultFuture))
}
return ipcClient.execute(serviceOperation)
}
override fun markLap(): ListenableFuture<Void> {
val serviceOperation =
ServiceOperation<Void> { binder, resultFuture ->
getServiceInterface(binder)
.markLap(context.packageName, StatusCallback(resultFuture))
}
return ipcClient.execute(serviceOperation)
}
override val currentExerciseInfo: ListenableFuture<ExerciseInfo>
get() {
val serviceOperation =
ServiceOperation<ExerciseInfo> { binder, resultFuture ->
getServiceInterface(binder)
.getCurrentExerciseInfo(
context.packageName,
ExerciseInfoCallback(resultFuture)
)
}
return ipcClient.execute(serviceOperation)
}
override fun setUpdateListener(listener: ExerciseUpdateListener): ListenableFuture<Void> {
return setUpdateListener(listener, ContextCompat.getMainExecutor(context))
}
override fun setUpdateListener(
listener: ExerciseUpdateListener,
executor: Executor
): ListenableFuture<Void> {
val listenerStub =
ExerciseUpdateListenerStub.ExerciseUpdateListenerCache.INSTANCE.getOrCreate(
listener,
executor
)
val serviceOperation =
ServiceOperation<Void> { binder, resultFuture ->
getServiceInterface(binder)
.setUpdateListener(
context.packageName,
listenerStub,
StatusCallback(resultFuture)
)
}
return ipcClient.registerListener(listenerStub.listenerKey, serviceOperation)
}
override fun clearUpdateListener(listener: ExerciseUpdateListener): ListenableFuture<Void> {
val listenerStub =
ExerciseUpdateListenerStub.ExerciseUpdateListenerCache.INSTANCE.remove(listener)
?: return Futures.immediateFailedFuture(
IllegalArgumentException("Given listener was not added.")
)
val serviceOperation =
ServiceOperation<Void> { binder, resultFuture ->
getServiceInterface(binder)
.clearUpdateListener(
context.packageName,
listenerStub,
StatusCallback(resultFuture)
)
}
return ipcClient.unregisterListener(listenerStub.listenerKey, serviceOperation)
}
override fun addGoalToActiveExercise(exerciseGoal: ExerciseGoal): ListenableFuture<Void> {
val serviceOperation =
ServiceOperation<Void> { binder, resultFuture ->
getServiceInterface(binder)
.addGoalToActiveExercise(
ExerciseGoalRequest(context.packageName, exerciseGoal),
StatusCallback(resultFuture)
)
}
return ipcClient.execute(serviceOperation)
}
override fun overrideAutoPauseAndResumeForActiveExercise(
enabled: Boolean
): ListenableFuture<Void> {
val serviceOperation =
ServiceOperation<Void> { binder, resultFuture ->
getServiceInterface(binder)
.overrideAutoPauseAndResumeForActiveExercise(
AutoPauseAndResumeConfigRequest(context.packageName, enabled),
StatusCallback(resultFuture)
)
}
return ipcClient.execute(serviceOperation)
}
override val capabilities: ListenableFuture<ExerciseCapabilities>
get() {
val request = CapabilitiesRequest(context.packageName)
val serviceOperation =
ServiceOperation<ExerciseCapabilitiesResponse> { binder, resultFuture ->
resultFuture.set(getServiceInterface(binder).getCapabilities(request))
}
return Futures.transform(
ipcClient.execute(serviceOperation),
{ response -> response?.exerciseCapabilities },
ContextCompat.getMainExecutor(context)
)
}
internal companion object {
@JvmStatic
fun getClient(context: Context): ServiceBackedExerciseClient {
return ServiceBackedExerciseClient(context, HsConnectionManager.getInstance(context))
}
}
}