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 
17 package com.google.android.torus.core.app
18 
19 import android.app.KeyguardManager
20 import android.content.BroadcastReceiver
21 import android.content.Context
22 import android.content.Intent
23 import android.content.IntentFilter
24 
25 /**
26  * Listens to keyguard lock state changes.
27  *
28  * @constructor Creates a new [KeyguardLockController].
29  * @param lockStateListener a listener that we receive Keyguard Lock state changes.
30  */
31 class KeyguardLockController(
32     private val context: Context,
33     private val lockStateListener: LockStateListener? = null
34 ) {
35     @Volatile
36     var locked: Boolean = false
37         private set
38 
39     private val userPresentReceiver = object : BroadcastReceiver() {
onReceivenull40         override fun onReceive(context: Context, intent: Intent) {
41             if (intent.action == Intent.ACTION_USER_PRESENT) onChange(false)
42         }
43     }
44 
45     private val userPresentIntentFilter: IntentFilter = IntentFilter(Intent.ACTION_USER_PRESENT)
46     private val keyguardManager =
47         context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager?
48     private var isRegistered: Boolean = false
49 
50     init {
<lambda>null51         keyguardManager?.let { locked = it.isKeyguardLocked }
52     }
53 
54     /**
55      * Starts listening for [Intent.ACTION_USER_PRESENT] state changes. This should be used
56      * together with [KeyguardLockController.updateLockState] to detect lock state changes. Using a
57      * broadcast listener is not ideal, but there isn't an alternative event to detect lock state
58      * changes.
59      */
startnull60     fun start() {
61         context.registerReceiver(userPresentReceiver, userPresentIntentFilter)
62         isRegistered = true
63     }
64 
65     /**
66      * Stops listening for [Intent.ACTION_USER_PRESENT] state changes. This should be used
67      * together with [KeyguardLockController.updateLockState] to detect lock state changes. Using a
68      * broadcast listener is not ideal, but there isn't an alternative event to detect lock state
69      * changes.
70      */
stopnull71     fun stop() {
72         if (isRegistered) {
73             context.unregisterReceiver(userPresentReceiver)
74             isRegistered = false
75         }
76     }
77 
78     /**
79      * Reads the [KeyguardManager.isKeyguardLocked] new value to know the current Lock state.
80      * This function should be also called on Screen state changes (i.e. [Intent.ACTION_SCREEN_ON],
81      * [Intent.ACTION_SCREEN_OFF]). This function can be used also to do polling of the lock state.
82      */
<lambda>null83     fun updateLockState() = keyguardManager?.let { onChange(it.isKeyguardLocked) }
84 
onChangenull85     private fun onChange(locked: Boolean) {
86         if (this.locked != locked) {
87             this.locked = locked
88             lockStateListener?.onLockStateChanged(locked)
89         }
90     }
91 
92     /** Interface to listen to Keyguard lock changes. */
93     interface LockStateListener {
94         /**
95          * Called when the Keyguard lock state has changed.
96          *
97          * @param locked true if the keyguard is currently locked.
98          */
onLockStateChangednull99         fun onLockStateChanged(locked: Boolean)
100     }
101 }
102