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.launcher3.util 17 18 import android.content.Context 19 import android.content.Intent 20 import android.os.Process 21 import android.os.UserManager 22 import androidx.annotation.VisibleForTesting 23 import com.android.launcher3.dagger.ApplicationContext 24 import com.android.launcher3.dagger.LauncherAppComponent 25 import com.android.launcher3.dagger.LauncherAppSingleton 26 import com.android.launcher3.util.Executors.MAIN_EXECUTOR 27 import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR 28 import javax.inject.Inject 29 30 @LauncherAppSingleton 31 class LockedUserState 32 @Inject 33 constructor(@ApplicationContext private val context: Context, lifeCycle: DaggerSingletonTracker) { 34 val isUserUnlockedAtLauncherStartup: Boolean 35 var isUserUnlocked = false 36 private set(value) { 37 field = value 38 if (value) { 39 notifyUserUnlocked() 40 } 41 } 42 43 private val mUserUnlockedActions: RunnableList = RunnableList() 44 45 @VisibleForTesting 46 val userUnlockedReceiver = <lambda>null47 SimpleBroadcastReceiver(context, UI_HELPER_EXECUTOR) { 48 if (Intent.ACTION_USER_UNLOCKED == it.action) { 49 isUserUnlocked = true 50 } 51 } 52 53 init { 54 // 1) when user reboots devices, launcher process starts at lock screen and both 55 // isUserUnlocked and isUserUnlockedAtLauncherStartup are init as false. After user unlocks 56 // screen, isUserUnlocked will be updated to true via Intent.ACTION_USER_UNLOCKED, 57 // yet isUserUnlockedAtLauncherStartup will remains as false. 58 // 2) when launcher process restarts after user has unlocked screen, both variable are 59 // init as true and will not change. 60 isUserUnlocked = checkIsUserUnlocked() 61 isUserUnlockedAtLauncherStartup = isUserUnlocked 62 if (!isUserUnlocked) { 63 userUnlockedReceiver.register( <lambda>null64 { 65 // If user is unlocked while registering broadcast receiver, we should update 66 // [isUserUnlocked], which will call [notifyUserUnlocked] in setter 67 if (checkIsUserUnlocked()) { 68 MAIN_EXECUTOR.execute { isUserUnlocked = true } 69 } 70 }, 71 Intent.ACTION_USER_UNLOCKED, 72 ) 73 } <lambda>null74 lifeCycle.addCloseable { userUnlockedReceiver.unregisterReceiverSafely() } 75 } 76 checkIsUserUnlockednull77 private fun checkIsUserUnlocked() = 78 context.getSystemService(UserManager::class.java)!!.isUserUnlocked(Process.myUserHandle()) 79 80 private fun notifyUserUnlocked() { 81 mUserUnlockedActions.executeAllAndDestroy() 82 userUnlockedReceiver.unregisterReceiverSafely() 83 } 84 85 /** 86 * Adds a `Runnable` to be executed when a user is unlocked. If the user is already unlocked, 87 * this runnable will run immediately because RunnableList will already have been destroyed. 88 */ runOnUserUnlockednull89 fun runOnUserUnlocked(action: Runnable) { 90 mUserUnlockedActions.add(action) 91 } 92 93 /** Removes a previously queued `Runnable` to be run when the user is unlocked. */ removeOnUserUnlockedRunnablenull94 fun removeOnUserUnlockedRunnable(action: Runnable) { 95 mUserUnlockedActions.remove(action) 96 } 97 98 companion object { 99 @VisibleForTesting 100 @JvmField 101 val INSTANCE = DaggerSingletonObject(LauncherAppComponent::getLockedUserState) 102 getnull103 @JvmStatic fun get(context: Context): LockedUserState = INSTANCE.get(context) 104 } 105 } 106