1 /* 2 * Copyright 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 17 package androidx.camera.core.internal 18 19 import androidx.annotation.GuardedBy 20 import androidx.camera.core.ImageCapture.ScreenFlash 21 import androidx.camera.core.ImageCapture.ScreenFlashListener 22 import androidx.camera.core.Logger 23 24 /** 25 * Wrapper class around [ScreenFlash] to save the [ScreenFlashListener] passed to app. 26 * 27 * This allows us to clean up properly in case a capture is cancelled earlier (e.g. ImageCapture is 28 * unbound after [apply] is invoked but [clear] is not). 29 */ 30 internal class ScreenFlashWrapper private constructor(private val screenFlash: ScreenFlash?) : 31 ScreenFlash { 32 private val lock = Object() 33 34 @GuardedBy("lock") private var isClearScreenFlashPending: Boolean = false 35 @GuardedBy("lock") private var pendingListener: ScreenFlashListener? = null 36 37 companion object { 38 private const val TAG = "ScreenFlashWrapper" 39 fromnull40 @JvmStatic fun from(screenFlash: ScreenFlash?) = ScreenFlashWrapper(screenFlash) 41 } 42 43 override fun apply(expirationTimeMillis: Long, screenFlashListener: ScreenFlashListener) { 44 synchronized(lock) { 45 isClearScreenFlashPending = true 46 pendingListener = screenFlashListener 47 } 48 49 screenFlash?.apply(expirationTimeMillis) { 50 synchronized(lock) { 51 if (pendingListener == null) { 52 Logger.w(TAG, "apply: pendingListener is null!") 53 } 54 completePendingScreenFlashListener() 55 } 56 } 57 ?: run { 58 Logger.e(TAG, "apply: screenFlash is null!") 59 // Complete immediately in case this error case is invoked by some bug 60 completePendingScreenFlashListener() 61 } 62 } 63 clearnull64 override fun clear() { 65 completePendingScreenFlashClear() 66 } 67 68 /** Gets the base [ScreenFlash] where the interface methods are delegated to. */ getBaseScreenFlashnull69 fun getBaseScreenFlash(): ScreenFlash? = screenFlash 70 71 /** Completes the pending [ScreenFlashListener], if any. */ 72 private fun completePendingScreenFlashListener() { 73 synchronized(lock) { 74 pendingListener?.onCompleted() 75 pendingListener = null 76 } 77 } 78 79 /** Completes pending [ScreenFlash.clear] invocation, if any. */ completePendingScreenFlashClearnull80 private fun completePendingScreenFlashClear() { 81 synchronized(lock) { 82 if (isClearScreenFlashPending) { 83 screenFlash?.clear() 84 ?: run { 85 Logger.e(TAG, "completePendingScreenFlashClear: screenFlash is null!") 86 } 87 } else { 88 Logger.w(TAG, "completePendingScreenFlashClear: none pending!") 89 } 90 isClearScreenFlashPending = false 91 } 92 } 93 94 /** Completes all pending operations. */ completePendingTasksnull95 fun completePendingTasks() { 96 completePendingScreenFlashListener() 97 completePendingScreenFlashClear() 98 } 99 } 100