1 /* 2 * Copyright (C) 2023 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 package com.android.dream.lowlight 17 18 import android.Manifest 19 import android.annotation.IntDef 20 import android.annotation.RequiresPermission 21 import android.app.DreamManager 22 import android.content.ComponentName 23 import android.util.Log 24 import com.android.dream.lowlight.dagger.LowLightDreamModule 25 import com.android.dream.lowlight.dagger.qualifiers.Application 26 import kotlinx.coroutines.CancellationException 27 import kotlinx.coroutines.CoroutineScope 28 import kotlinx.coroutines.Job 29 import kotlinx.coroutines.TimeoutCancellationException 30 import kotlinx.coroutines.launch 31 import javax.inject.Inject 32 import javax.inject.Named 33 import kotlin.time.DurationUnit 34 import kotlin.time.toDuration 35 36 /** 37 * Maintains the ambient light mode of the environment the device is in, and sets a low light dream 38 * component, if present, as the system dream when the ambient light mode is low light. 39 * 40 * @hide 41 */ 42 class LowLightDreamManager @Inject constructor( 43 @Application private val coroutineScope: CoroutineScope, 44 private val dreamManager: DreamManager, 45 private val lowLightTransitionCoordinator: LowLightTransitionCoordinator, 46 @param:Named(LowLightDreamModule.LOW_LIGHT_DREAM_COMPONENT) 47 private val lowLightDreamComponent: ComponentName?, 48 @param:Named(LowLightDreamModule.LOW_LIGHT_TRANSITION_TIMEOUT_MS) 49 private val lowLightTransitionTimeoutMs: Long 50 ) { 51 /** 52 * @hide 53 */ 54 @Retention(AnnotationRetention.SOURCE) 55 @IntDef( 56 prefix = ["AMBIENT_LIGHT_MODE_"], 57 value = [ 58 AMBIENT_LIGHT_MODE_UNKNOWN, 59 AMBIENT_LIGHT_MODE_REGULAR, 60 AMBIENT_LIGHT_MODE_LOW_LIGHT 61 ] 62 ) 63 annotation class AmbientLightMode 64 65 private var mTransitionJob: Job? = null 66 private var mAmbientLightMode = AMBIENT_LIGHT_MODE_UNKNOWN 67 private val mLowLightTransitionTimeout = 68 lowLightTransitionTimeoutMs.toDuration(DurationUnit.MILLISECONDS) 69 70 /** 71 * Sets the current ambient light mode. 72 * 73 * @hide 74 */ 75 @RequiresPermission(Manifest.permission.WRITE_DREAM_STATE) setAmbientLightModenull76 fun setAmbientLightMode(@AmbientLightMode ambientLightMode: Int) { 77 if (lowLightDreamComponent == null) { 78 if (DEBUG) { 79 Log.d( 80 TAG, 81 "ignore ambient light mode change because low light dream component is empty" 82 ) 83 } 84 return 85 } 86 if (mAmbientLightMode == ambientLightMode) { 87 return 88 } 89 if (DEBUG) { 90 Log.d( 91 TAG, "ambient light mode changed from $mAmbientLightMode to $ambientLightMode" 92 ) 93 } 94 mAmbientLightMode = ambientLightMode 95 val shouldEnterLowLight = mAmbientLightMode == AMBIENT_LIGHT_MODE_LOW_LIGHT 96 97 // Cancel any previous transitions 98 mTransitionJob?.cancel() 99 mTransitionJob = coroutineScope.launch { 100 try { 101 lowLightTransitionCoordinator.waitForLowLightTransitionAnimation( 102 timeout = mLowLightTransitionTimeout, 103 entering = shouldEnterLowLight 104 ) 105 } catch (ex: TimeoutCancellationException) { 106 Log.e(TAG, "timed out while waiting for low light animation", ex) 107 } catch (ex: CancellationException) { 108 Log.w(TAG, "low light transition animation cancelled") 109 // Catch the cancellation so that we still set the system dream component if the 110 // animation is cancelled, such as by a user tapping to wake as the transition to 111 // low light happens. 112 } 113 dreamManager.setSystemDreamComponent( 114 if (shouldEnterLowLight) lowLightDreamComponent else null 115 ) 116 } 117 } 118 119 companion object { 120 private const val TAG = "LowLightDreamManager" 121 private val DEBUG = Log.isLoggable(TAG, Log.DEBUG) 122 123 /** 124 * Constant for ambient light mode being unknown. 125 * 126 * @hide 127 */ 128 const val AMBIENT_LIGHT_MODE_UNKNOWN = 0 129 130 /** 131 * Constant for ambient light mode being regular / bright. 132 * 133 * @hide 134 */ 135 const val AMBIENT_LIGHT_MODE_REGULAR = 1 136 137 /** 138 * Constant for ambient light mode being low light / dim. 139 * 140 * @hide 141 */ 142 const val AMBIENT_LIGHT_MODE_LOW_LIGHT = 2 143 } 144 } 145