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.util.Executors.MAIN_EXECUTOR 24 import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR 25 26 class LockedUserState(private val mContext: Context) : SafeCloseable { 27 val isUserUnlockedAtLauncherStartup: Boolean 28 var isUserUnlocked = false 29 private set(value) { 30 field = value 31 if (value) { 32 notifyUserUnlocked() 33 } 34 } 35 36 private val mUserUnlockedActions: RunnableList = RunnableList() 37 38 @VisibleForTesting 39 val mUserUnlockedReceiver = <lambda>null40 SimpleBroadcastReceiver(UI_HELPER_EXECUTOR) { 41 if (Intent.ACTION_USER_UNLOCKED == it.action) { 42 isUserUnlocked = true 43 } 44 } 45 46 init { 47 // 1) when user reboots devices, launcher process starts at lock screen and both 48 // isUserUnlocked and isUserUnlockedAtLauncherStartup are init as false. After user unlocks 49 // screen, isUserUnlocked will be updated to true via Intent.ACTION_USER_UNLOCKED, 50 // yet isUserUnlockedAtLauncherStartup will remains as false. 51 // 2) when launcher process restarts after user has unlocked screen, both variable are 52 // init as true and will not change. 53 isUserUnlocked = checkIsUserUnlocked() 54 isUserUnlockedAtLauncherStartup = isUserUnlocked 55 if (!isUserUnlocked) { 56 mUserUnlockedReceiver.register( 57 mContext, <lambda>null58 { 59 // If user is unlocked while registering broadcast receiver, we should update 60 // [isUserUnlocked], which will call [notifyUserUnlocked] in setter 61 if (checkIsUserUnlocked()) { 62 MAIN_EXECUTOR.execute { isUserUnlocked = true } 63 } 64 }, 65 Intent.ACTION_USER_UNLOCKED 66 ) 67 } 68 } 69 checkIsUserUnlockednull70 private fun checkIsUserUnlocked() = 71 mContext.getSystemService(UserManager::class.java)!!.isUserUnlocked(Process.myUserHandle()) 72 73 private fun notifyUserUnlocked() { 74 mUserUnlockedActions.executeAllAndDestroy() 75 mUserUnlockedReceiver.unregisterReceiverSafely(mContext) 76 } 77 78 /** Stops the receiver from listening for ACTION_USER_UNLOCK broadcasts. */ closenull79 override fun close() { 80 mUserUnlockedReceiver.unregisterReceiverSafely(mContext) 81 } 82 83 /** 84 * Adds a `Runnable` to be executed when a user is unlocked. If the user is already unlocked, 85 * this runnable will run immediately because RunnableList will already have been destroyed. 86 */ runOnUserUnlockednull87 fun runOnUserUnlocked(action: Runnable) { 88 mUserUnlockedActions.add(action) 89 } 90 91 /** 92 * Removes a previously queued `Runnable` to be run when the user is unlocked. 93 */ removeOnUserUnlockedRunnablenull94 fun removeOnUserUnlockedRunnable(action: Runnable) { 95 mUserUnlockedActions.remove(action) 96 } 97 98 companion object { 99 @VisibleForTesting 100 @JvmField <lambda>null101 val INSTANCE = MainThreadInitializedObject { LockedUserState(it) } 102 getnull103 @JvmStatic fun get(context: Context): LockedUserState = INSTANCE.get(context) 104 } 105 } 106