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.photopicker.data
18 
19 import android.content.ContentResolver
20 import android.content.UriMatcher
21 import android.database.ContentObserver
22 import android.net.Uri
23 
24 /**
25  * Test implementation of Notification Service. It registers the observers in memory. Test writers
26  * can use [dispatchChangeToObservers] method to notify the registered content observers.
27  */
28 class TestNotificationServiceImpl : NotificationService {
29     companion object {
30         private const val MATCH = 0
31     }
32 
33     private val registeredObservers: MutableMap<Uri, UriObservers> = mutableMapOf()
34 
35     override fun registerContentObserverCallback(
36         contentResolver: ContentResolver,
37         uri: Uri,
38         notifyDescendants: Boolean,
39         observer: ContentObserver
40     ) {
41         val uriObservers: UriObservers = registeredObservers[uri] ?: UriObservers()
42         uriObservers.add(observer, notifyDescendants)
43         registeredObservers.put(uri, uriObservers)
44     }
45 
46     override fun unregisterContentObserverCallback(
47         contentResolver: ContentResolver,
48         observer: ContentObserver
49     ) {
50         registeredObservers.values.forEach {
51             it.remove(observer)
52         }
53     }
54 
55     /**
56      * This method can be used to simulate notifying [ContentObserver]-s registered against a
57      * given Uri.
58      */
59     fun dispatchChangeToObservers(uri: Uri) {
60         val uriBuilder = Uri.parse("content://" + uri.authority).buildUpon()
61         uri.pathSegments.forEach {
62             uriBuilder.appendPath(it)
63             val newUri = uriBuilder.build()
64             val allowExactMatch = newUri.equals(uri)
65             registeredObservers[newUri]
66                 ?.getMatchedObservers(newUri, allowExactMatch)
67                 ?.forEach { contentObserver: ContentObserver ->
68                     contentObserver.dispatchChange(
69                         /* selfChange */ true,
70                         uri,
71                         /* flags */ 0
72                     )
73                 }
74         }
75     }
76 
77     /**
78      * Represents the Content Observers registered against a Uri. A registered content observer can
79      * choose to be notified for descendant Uris.
80      */
81     class UriObservers {
82         private val exactMatchObservers: MutableList<ContentObserver> = mutableListOf()
83         private val descendantMatchObservers: MutableList<ContentObserver> = mutableListOf()
84 
85         fun add(observer: ContentObserver, notifyDescendants: Boolean) {
86             if (notifyDescendants) {
87                 descendantMatchObservers.add(observer)
88             } else {
89                 exactMatchObservers.add(observer)
90             }
91         }
92 
93         fun remove(observer: ContentObserver): Boolean {
94             return exactMatchObservers.remove(observer) ||
95                     descendantMatchObservers.remove(observer)
96         }
97 
98         fun getMatchedObservers(uri: Uri, allowExactMatch: Boolean):
99                 Set<ContentObserver> {
100             val uriMatcher = UriMatcher(UriMatcher.NO_MATCH)
101             uriMatcher.addURI(uri.authority, uri.path, MATCH)
102 
103             val observers: MutableSet<ContentObserver> = mutableSetOf()
104             if (uriMatcher.match(uri) == MATCH) {
105                 if (allowExactMatch) {
106                     observers.addAll(exactMatchObservers)
107                     observers.addAll(descendantMatchObservers)
108                 } else {
109                     observers.addAll(descendantMatchObservers)
110                 }
111             }
112             return observers
113         }
114     }
115 }