1 /* <lambda>null2 * Copyright (C) 2024 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.android.wallpaper.picker.broadcast 18 19 import android.content.BroadcastReceiver 20 import android.content.Context 21 import android.content.Intent 22 import android.content.IntentFilter 23 import android.os.Handler 24 import android.os.Looper 25 import com.android.systemui.dagger.qualifiers.Main 26 import com.android.wallpaper.picker.di.modules.ConcurrencyModule.* 27 import java.util.concurrent.Executor 28 import javax.inject.Inject 29 import javax.inject.Singleton 30 import kotlinx.coroutines.channels.awaitClose 31 import kotlinx.coroutines.flow.Flow 32 import kotlinx.coroutines.flow.callbackFlow 33 34 /** 35 * WallpaperPicker master Broadcast Dispatcher. 36 * 37 * This class allows [BroadcastReceiver] to register and centralizes registrations to [Context] from 38 * WallpaperPicker. That way the number of calls to [BroadcastReceiver.onReceive] can be reduced for 39 * a given broadcast. 40 * 41 * Use only for IntentFilters with actions and optionally categories. It does not support schemes, 42 * data types, data authorities or priority different than 0. 43 * 44 * Cannot be used for getting sticky broadcasts (either as return of registering or as re-delivery). 45 */ 46 @Singleton 47 open class BroadcastDispatcher 48 @Inject 49 constructor( 50 private val context: Context, 51 @Main private val mainExecutor: Executor, 52 @BroadcastRunning private val broadcastLooper: Looper, 53 @BroadcastRunning private val broadcastExecutor: Executor, 54 ) { 55 /** 56 * Register a receiver for broadcast with the dispatcher 57 * 58 * @param receiver A receiver to dispatch the [Intent] 59 * @param filter A filter to determine what broadcasts should be dispatched to this receiver. It 60 * will only take into account actions and categories for filtering. It must have at least one 61 * action. 62 * @param executor An executor to dispatch [BroadcastReceiver.onReceive]. 63 * @param flags Flags to use when registering the receiver. [Context.RECEIVER_EXPORTED] by 64 * default. 65 * @param permission to enforce security on who can send broadcasts to the receiver. 66 * @throws IllegalArgumentException if the filter has other constraints that are not actions or 67 * categories or the filter has no actions. 68 */ 69 @JvmOverloads 70 open fun registerReceiver( 71 receiver: BroadcastReceiver, 72 filter: IntentFilter, 73 executor: Executor = mainExecutor, 74 @Context.RegisterReceiverFlags flags: Int = Context.RECEIVER_EXPORTED, 75 permission: String? = null 76 ) { 77 checkFilter(filter) 78 context.registerReceiver(receiver, filter, permission, Handler(broadcastLooper), flags) 79 } 80 81 /** 82 * Returns a [Flow] that, when collected, emits a new value whenever a broadcast matching 83 * [filter] is received. The value will be computed from the intent and the registered receiver 84 * using [map]. 85 * 86 * @see registerReceiver 87 */ 88 @JvmOverloads 89 fun <T> broadcastFlow( 90 filter: IntentFilter, 91 @Context.RegisterReceiverFlags flags: Int = Context.RECEIVER_EXPORTED, 92 permission: String? = null, 93 map: (Intent, BroadcastReceiver) -> T, 94 ): Flow<T> = callbackFlow { 95 val receiver = 96 object : BroadcastReceiver() { 97 override fun onReceive(context: Context, intent: Intent) { 98 trySend(map(intent, this)) 99 } 100 } 101 102 registerReceiver( 103 receiver, 104 filter, 105 broadcastExecutor, 106 flags, 107 permission, 108 ) 109 110 awaitClose { unregisterReceiver(receiver) } 111 } 112 113 /** 114 * Returns a [Flow] that, when collected, emits `Unit` whenever a broadcast matching [filter] is 115 * received. 116 * 117 * @see registerReceiver 118 */ 119 @JvmOverloads 120 fun broadcastFlow( 121 filter: IntentFilter, 122 @Context.RegisterReceiverFlags flags: Int = Context.RECEIVER_EXPORTED, 123 permission: String? = null, 124 ): Flow<Unit> = broadcastFlow(filter, flags, permission) { _, _ -> Unit } 125 126 private fun checkFilter(filter: IntentFilter) { 127 buildString { 128 if (filter.countActions() == 0) { 129 append("Filter must contain at least one action. ") 130 } 131 if (filter.countDataAuthorities() != 0) { 132 append("Filter cannot contain DataAuthorities. ") 133 } 134 if (filter.countDataPaths() != 0) { 135 append("Filter cannot contain DataPaths. ") 136 } 137 if (filter.countDataSchemes() != 0) { 138 append("Filter cannot contain DataSchemes. ") 139 } 140 if (filter.countDataTypes() != 0) { 141 append("Filter cannot contain DataTypes. ") 142 } 143 if (filter.priority != 0) { 144 append("Filter cannot modify priority. ") 145 } 146 } 147 .let { 148 if (it.isNotEmpty()) { 149 throw IllegalArgumentException(it) 150 } 151 } 152 } 153 154 /** 155 * Unregister receiver for the current user. 156 * 157 * @param receiver The receiver to unregister. 158 */ 159 open fun unregisterReceiver(receiver: BroadcastReceiver) { 160 context.unregisterReceiver(receiver) 161 } 162 } 163