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