Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3e324443 authored by Matt Pietal's avatar Matt Pietal Committed by Automerger Merge Worker
Browse files

Merge "Device Control metrics" into sc-dev am: e5262784

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13677132

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: Id9eaa0725459c5d273e5e8a9279ec38ad438c045
parents 1c3290d8 e5262784
Loading
Loading
Loading
Loading
+90 −0
Original line number Diff line number Diff line
/*
 * 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 com.android.systemui.controls

import android.service.controls.DeviceTypes.DeviceType

import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
import com.android.systemui.controls.ui.ControlViewHolder

/**
 * Interface for logging UI events related to controls
 */
interface ControlsMetricsLogger {

    /**
     * Assign a new instance id for this controls session, defined as when the controls area is
     * made visible to when it is closed.
     */
    fun assignInstanceId()

    fun touch(cvh: ControlViewHolder, isLocked: Boolean) {
        log(ControlsEvents.CONTROL_TOUCH.id, cvh.deviceType, cvh.uid, isLocked)
    }

    fun drag(cvh: ControlViewHolder, isLocked: Boolean) {
        log(ControlsEvents.CONTROL_DRAG.id, cvh.deviceType, cvh.uid, isLocked)
    }

    fun longPress(cvh: ControlViewHolder, isLocked: Boolean) {
        log(ControlsEvents.CONTROL_LONG_PRESS.id, cvh.deviceType, cvh.uid, isLocked)
    }

    fun refreshBegin(uid: Int, isLocked: Boolean) {
        assignInstanceId()
        log(ControlsEvents.CONTROL_REFRESH_BEGIN.id, 0, uid, isLocked)
    }

    fun refreshEnd(cvh: ControlViewHolder, isLocked: Boolean) {
        log(ControlsEvents.CONTROL_REFRESH_END.id, cvh.deviceType, cvh.uid, isLocked)
    }

    /**
     * Logs a controls-related event
     *
     * @param eventId Main UIEvent to capture
     * @param deviceType One of {@link android.service.controls.DeviceTypes}
     * @param packageName Package name of the service that receives the request
     * @param isLocked Is the device locked at the start of the action?
     */
    fun log(
        eventId: Int,
        @DeviceType deviceType: Int,
        uid: Int,
        isLocked: Boolean
    )

    private enum class ControlsEvents(val metricId: Int) : UiEventLogger.UiEventEnum {
        @UiEvent(doc = "User touched a control")
        CONTROL_TOUCH(714),

        @UiEvent(doc = "User dragged a control")
        CONTROL_DRAG(713),

        @UiEvent(doc = "User long-pressed a control")
        CONTROL_LONG_PRESS(715),

        @UiEvent(doc = "User has opened controls, and a state refresh has begun")
        CONTROL_REFRESH_BEGIN(716),

        @UiEvent(doc = "User has opened controls, and a state refresh has ended")
        CONTROL_REFRESH_END(717);

        override fun getId() = metricId
    }
}
+62 −0
Original line number Diff line number Diff line
/*
 * 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 com.android.systemui.controls

import android.service.controls.DeviceTypes.DeviceType

import com.android.internal.logging.InstanceIdSequence
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.shared.system.SysUiStatsLog

import javax.inject.Inject

/**
 * Implementation for logging UI events related to controls
 */
@SysUISingleton
class ControlsMetricsLoggerImpl @Inject constructor() : ControlsMetricsLogger {

    companion object {
        private const val INSTANCE_ID_MAX = 1 shl 13
    }

    private val instanceIdSequence = InstanceIdSequence(INSTANCE_ID_MAX)
    private var instanceId = 0

    override fun assignInstanceId() {
        instanceId = instanceIdSequence.newInstanceId().id
    }

    /**
     * {@see ControlsMetricsLogger#log}
     */
    override fun log(
        eventId: Int,
        @DeviceType deviceType: Int,
        uid: Int,
        isLocked: Boolean
    ) {
        SysUiStatsLog.write(
            SysUiStatsLog.DEVICE_CONTROL_CHANGED,
            eventId,
            instanceId,
            deviceType,
            uid,
            isLocked
        )
    }
}
+5 −0
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.systemui.controls.dagger

import android.app.Activity
import android.content.pm.PackageManager
import com.android.systemui.controls.ControlsMetricsLogger
import com.android.systemui.controls.ControlsMetricsLoggerImpl
import com.android.systemui.controls.controller.ControlsBindingController
import com.android.systemui.controls.controller.ControlsBindingControllerImpl
import com.android.systemui.controls.controller.ControlsController
@@ -79,6 +81,9 @@ abstract class ControlsModule {
    @Binds
    abstract fun provideUiController(controller: ControlsUiControllerImpl): ControlsUiController

    @Binds
    abstract fun provideMetricsLogger(logger: ControlsMetricsLoggerImpl): ControlsMetricsLogger

    @Binds
    abstract fun provideControlActionCoordinator(
        coordinator: ControlActionCoordinatorImpl
+14 −19
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ package com.android.systemui.controls.ui

import android.annotation.MainThread
import android.app.Dialog
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
@@ -33,6 +32,7 @@ import android.util.Log
import android.view.HapticFeedbackConstants
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.controls.ControlsMetricsLogger
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.globalactions.GlobalActionsComponent
@@ -54,13 +54,15 @@ class ControlActionCoordinatorImpl @Inject constructor(
    private val globalActionsComponent: GlobalActionsComponent,
    private val taskViewFactory: Optional<TaskViewFactory>,
    private val broadcastDispatcher: BroadcastDispatcher,
    private val lazyUiController: Lazy<ControlsUiController>
    private val lazyUiController: Lazy<ControlsUiController>,
    private val controlsMetricsLogger: ControlsMetricsLogger
) : ControlActionCoordinator {
    private var dialog: Dialog? = null
    private val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
    private var pendingAction: Action? = null
    private var actionsInProgress = mutableSetOf<String>()

    private val isLocked: Boolean
        get() = !keyguardStateController.isUnlocked()
    override var activityContext: Context? = null

    companion object {
@@ -73,6 +75,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
    }

    override fun toggle(cvh: ControlViewHolder, templateId: String, isChecked: Boolean) {
        controlsMetricsLogger.touch(cvh, isLocked)
        bouncerOrRun(createAction(cvh.cws.ci.controlId, {
            cvh.layout.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
            cvh.action(BooleanAction(templateId, !isChecked))
@@ -80,6 +83,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
    }

    override fun touch(cvh: ControlViewHolder, templateId: String, control: Control) {
        controlsMetricsLogger.touch(cvh, isLocked)
        val blockable = cvh.usePanel()
        bouncerOrRun(createAction(cvh.cws.ci.controlId, {
            cvh.layout.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
@@ -100,12 +104,14 @@ class ControlActionCoordinatorImpl @Inject constructor(
    }

    override fun setValue(cvh: ControlViewHolder, templateId: String, newValue: Float) {
        controlsMetricsLogger.drag(cvh, isLocked)
        bouncerOrRun(createAction(cvh.cws.ci.controlId, {
            cvh.action(FloatAction(templateId, newValue))
        }, false /* blockable */))
    }

    override fun longPress(cvh: ControlViewHolder) {
        controlsMetricsLogger.longPress(cvh, isLocked)
        bouncerOrRun(createAction(cvh.cws.ci.controlId, {
            // Long press snould only be called when there is valid control state, otherwise ignore
            cvh.cws.control?.let {
@@ -116,7 +122,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
    }

    override fun runPendingAction(controlId: String) {
        if (!keyguardStateController.isUnlocked()) return
        if (isLocked) return
        if (pendingAction?.controlId == controlId) {
            pendingAction?.invoke()
            pendingAction = null
@@ -141,28 +147,17 @@ class ControlActionCoordinatorImpl @Inject constructor(
    @VisibleForTesting
    fun bouncerOrRun(action: Action) {
        if (keyguardStateController.isShowing()) {
            var closeDialog = !keyguardStateController.isUnlocked()
            if (closeDialog) {
            if (isLocked) {
                context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))

                // pending actions will only run after the control state has been refreshed
                pendingAction = action
            }

            val wasLocked = isLocked
            activityStarter.dismissKeyguardThenExecute({
                Log.d(ControlsUiController.TAG, "Device unlocked, invoking controls action")
                if (closeDialog) {
                    activityContext?.let {
                        val i = Intent().apply {
                            component = ComponentName(context, ControlsActivity::class.java)
                            addFlags(
                                Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
                            putExtra(ControlsUiController.BACK_TO_GLOBAL_ACTIONS, false)
                        }
                        it.startActivity(i)
                    } ?: run {
                if (wasLocked && activityContext == null) {
                    globalActionsComponent.handleShowGlobalActionsMenu()
                    }
                } else {
                    action.invoke()
                }
+11 −2
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ import android.widget.TextView
import com.android.internal.graphics.ColorUtils
import com.android.systemui.Interpolators
import com.android.systemui.R
import com.android.systemui.controls.ControlsMetricsLogger
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.util.concurrency.DelayableExecutor
import kotlin.reflect.KClass
@@ -63,7 +64,9 @@ class ControlViewHolder(
    val controlsController: ControlsController,
    val uiExecutor: DelayableExecutor,
    val bgExecutor: DelayableExecutor,
    val controlActionCoordinator: ControlActionCoordinator
    val controlActionCoordinator: ControlActionCoordinator,
    val controlsMetricsLogger: ControlsMetricsLogger,
    val uid: Int
) {

    companion object {
@@ -141,7 +144,7 @@ class ControlViewHolder(
        status.setSelected(true)
    }

    fun bindData(cws: ControlWithState) {
    fun bindData(cws: ControlWithState, isLocked: Boolean) {
        // If an interaction is in progress, the update may visually interfere with the action the
        // action the user wants to make. Don't apply the update, and instead assume a new update
        // will coming from when the user interaction is complete.
@@ -171,10 +174,16 @@ class ControlViewHolder(
            controlActionCoordinator.runPendingAction(cws.ci.controlId)
        }

        val wasLoading = isLoading
        isLoading = false
        behavior = bindBehavior(behavior,
            findBehaviorClass(controlStatus, controlTemplate, deviceType))
        updateContentDescription()

        // Only log one event per control, at the moment we have determined that the control
        // switched from the loading to done state
        val doneLoading = wasLoading && !isLoading
        if (doneLoading) controlsMetricsLogger.refreshEnd(this, isLocked)
    }

    fun actionResponse(@ControlAction.ResponseResult response: Int) {
Loading