1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.unfold 18 19 import android.util.Log 20 import com.android.systemui.unfold.FoldStateLoggingProvider.FoldStateLoggingListener 21 import com.android.systemui.unfold.FoldStateLoggingProvider.LoggedFoldedStates 22 import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED 23 import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN 24 import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN 25 import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING 26 import com.android.systemui.unfold.updates.FOLD_UPDATE_START_OPENING 27 import com.android.systemui.unfold.updates.FoldStateProvider 28 import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate 29 import com.android.systemui.util.time.SystemClock 30 31 /** 32 * Reports device fold states for logging purposes. 33 * 34 * Wraps the state provided by [FoldStateProvider] to output only [FULLY_OPENED], [FULLY_CLOSED] and 35 * [HALF_OPENED] for logging purposes. 36 * 37 * Note that [HALF_OPENED] state is only emitted after the device angle is stable for some timeout. 38 * Check [FoldStateProvider] impl for it. 39 * 40 * This doesn't log the following transitions: 41 * - [HALF_OPENED] -> [FULLY_OPENED]: not interesting, as there is no transition going on 42 * - [HALF_OPENED] -> [HALF_OPENED]: not meaningful. 43 */ 44 class FoldStateLoggingProviderImpl( 45 private val foldStateProvider: FoldStateProvider, 46 private val clock: SystemClock 47 ) : FoldStateLoggingProvider, FoldStateProvider.FoldUpdatesListener { 48 49 private val outputListeners: MutableList<FoldStateLoggingListener> = mutableListOf() 50 51 @LoggedFoldedStates private var lastState: Int? = null 52 private var actionStartMillis: Long? = null 53 initnull54 override fun init() { 55 foldStateProvider.addCallback(this) 56 foldStateProvider.start() 57 } 58 uninitnull59 override fun uninit() { 60 foldStateProvider.removeCallback(this) 61 foldStateProvider.stop() 62 } 63 onFoldUpdatenull64 override fun onFoldUpdate(@FoldUpdate update: Int) { 65 val now = clock.elapsedRealtime() 66 when (update) { 67 FOLD_UPDATE_START_OPENING -> { 68 lastState = FULLY_CLOSED 69 actionStartMillis = now 70 } 71 FOLD_UPDATE_START_CLOSING -> actionStartMillis = now 72 FOLD_UPDATE_FINISH_HALF_OPEN -> dispatchState(HALF_OPENED) 73 FOLD_UPDATE_FINISH_FULL_OPEN -> dispatchState(FULLY_OPENED) 74 FOLD_UPDATE_FINISH_CLOSED -> dispatchState(FULLY_CLOSED) 75 } 76 } 77 onUnfoldedScreenAvailablenull78 override fun onUnfoldedScreenAvailable() { 79 Log.d(TAG, "Unfolded screen available") 80 } 81 dispatchStatenull82 private fun dispatchState(@LoggedFoldedStates current: Int) { 83 val now = clock.elapsedRealtime() 84 val previous = lastState 85 val lastActionStart = actionStartMillis 86 87 if (previous != null && previous != current && lastActionStart != null) { 88 val time = now - lastActionStart 89 val foldStateChange = FoldStateChange(previous, current, time) 90 outputListeners.forEach { it.onFoldUpdate(foldStateChange) } 91 if (DEBUG) { 92 Log.d(TAG, "From $previous to $current in $time") 93 } 94 } 95 96 actionStartMillis = null 97 lastState = current 98 } 99 addCallbacknull100 override fun addCallback(listener: FoldStateLoggingListener) { 101 outputListeners.add(listener) 102 } 103 removeCallbacknull104 override fun removeCallback(listener: FoldStateLoggingListener) { 105 outputListeners.remove(listener) 106 } 107 } 108 109 private const val DEBUG = false 110 private const val TAG = "FoldStateLoggingProviderImpl" 111