• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * 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.providers.media.photopicker.v2;
18 
19 import static com.android.providers.media.photopicker.v2.PickerUriResolverV2.ALBUM_PATH_SEGMENT;
20 import static com.android.providers.media.photopicker.v2.PickerUriResolverV2.AVAILABLE_PROVIDERS_PATH_SEGMENT;
21 import static com.android.providers.media.photopicker.v2.PickerUriResolverV2.MEDIA_PATH_SEGMENT;
22 import static com.android.providers.media.photopicker.v2.PickerUriResolverV2.MEDIA_SETS_PATH_SEGMENT;
23 import static com.android.providers.media.photopicker.v2.PickerUriResolverV2.MEDIA_SET_CONTENTS_PATH_SEGMENT;
24 import static com.android.providers.media.photopicker.v2.PickerUriResolverV2.PICKER_INTERNAL_PATH_SEGMENT;
25 import static com.android.providers.media.photopicker.v2.PickerUriResolverV2.PICKER_V2_PATH_SEGMENT;
26 import static com.android.providers.media.photopicker.v2.PickerUriResolverV2.SEARCH_RESULT_MEDIA_PATH_SEGMENT;
27 import static com.android.providers.media.photopicker.v2.PickerUriResolverV2.UPDATE_PATH_SEGMENT;
28 
29 import static java.util.Objects.requireNonNull;
30 
31 import android.annotation.NonNull;
32 import android.content.ContentResolver;
33 import android.content.Context;
34 import android.net.Uri;
35 import android.provider.MediaStore;
36 import android.util.Log;
37 
38 import com.android.providers.media.photopicker.PickerSyncController;
39 
40 public class PickerNotificationSender {
41     private static final String TAG = "PickerNotificationSender";
42 
43     /**
44      * Flag for {@link #notifyChange(Uri, ContentObserver, int)} to indicate that this notification
45      * should not be subject to any delays when dispatching to apps running in the background.
46      * Using this flag may negatively impact system health and performance, and should be used
47      * sparingly.
48      */
49     public static final int NOTIFY_NO_DELAY = 1 << 15;
50 
51     private static final Uri AVAILABLE_PROVIDERS_UPDATE_URI = new Uri.Builder()
52             .scheme(ContentResolver.SCHEME_CONTENT)
53             .authority(MediaStore.AUTHORITY)
54             .appendPath(PICKER_INTERNAL_PATH_SEGMENT)
55             .appendPath(PICKER_V2_PATH_SEGMENT)
56             .appendPath(AVAILABLE_PROVIDERS_PATH_SEGMENT)
57             .appendPath(UPDATE_PATH_SEGMENT)
58             .build();
59 
60     private static final Uri MEDIA_UPDATE_URI = new Uri.Builder()
61             .scheme(ContentResolver.SCHEME_CONTENT)
62             .authority(MediaStore.AUTHORITY)
63             .appendPath(PICKER_INTERNAL_PATH_SEGMENT)
64             .appendPath(PICKER_V2_PATH_SEGMENT)
65             .appendPath(MEDIA_PATH_SEGMENT)
66             .appendPath(UPDATE_PATH_SEGMENT)
67             .build();
68 
69     private static final Uri ALBUM_UPDATE_URI = new Uri.Builder()
70             .scheme(ContentResolver.SCHEME_CONTENT)
71             .authority(MediaStore.AUTHORITY)
72             .appendPath(PICKER_INTERNAL_PATH_SEGMENT)
73             .appendPath(PICKER_V2_PATH_SEGMENT)
74             .appendPath(ALBUM_PATH_SEGMENT)
75             .appendPath(UPDATE_PATH_SEGMENT)
76             .build();
77 
78     private static final Uri SEARCH_RESULTS_UPDATE_URI = new Uri.Builder()
79             .scheme(ContentResolver.SCHEME_CONTENT)
80             .authority(MediaStore.AUTHORITY)
81             .appendPath(PICKER_INTERNAL_PATH_SEGMENT)
82             .appendPath(PICKER_V2_PATH_SEGMENT)
83             .appendPath(SEARCH_RESULT_MEDIA_PATH_SEGMENT)
84             .appendPath(UPDATE_PATH_SEGMENT)
85             .build();
86 
87     private static final Uri MEDIA_SETS_UPDATE_URI = new Uri.Builder()
88             .scheme(ContentResolver.SCHEME_CONTENT)
89             .authority(MediaStore.AUTHORITY)
90             .appendPath(PICKER_INTERNAL_PATH_SEGMENT)
91             .appendPath(PICKER_V2_PATH_SEGMENT)
92             .appendPath(MEDIA_SETS_PATH_SEGMENT)
93             .appendPath(UPDATE_PATH_SEGMENT)
94             .build();
95 
96     private static final Uri MEDIA_SET_CONTENT_UPDATE_URI = new Uri.Builder()
97             .scheme(ContentResolver.SCHEME_CONTENT)
98             .authority(MediaStore.AUTHORITY)
99             .appendPath(PICKER_INTERNAL_PATH_SEGMENT)
100             .appendPath(PICKER_V2_PATH_SEGMENT)
101             .appendPath(MEDIA_SET_CONTENTS_PATH_SEGMENT)
102             .appendPath(UPDATE_PATH_SEGMENT)
103             .build();
104 
105     /**
106      * Send media update notification to the registered {@link android.database.ContentObserver}-s.
107      * @param context The application context.
108      */
notifyAvailableProvidersChange(@onNull Context context)109     public static void notifyAvailableProvidersChange(@NonNull Context context) {
110         Log.d(TAG, "Sending a notification for available providers update");
111         context.getContentResolver().notifyChange(
112                 AVAILABLE_PROVIDERS_UPDATE_URI, /* observer= */ null, NOTIFY_NO_DELAY);
113     }
114 
115     /**
116      * Send media update notification to the registered {@link android.database.ContentObserver}-s.
117      * @param context The application context.
118      */
notifyMediaChange(@onNull Context context)119     public static void notifyMediaChange(@NonNull Context context) {
120         Log.d(TAG, "Sending a notification for media update");
121         context.getContentResolver().notifyChange(MEDIA_UPDATE_URI, /* observer= */ null);
122         notifyMergedAlbumMediaChange(context, PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY);
123     }
124 
125     /**
126      * Send album media update notification to the registered
127      * {@link android.database.ContentObserver}-s.
128      * @param context The application context.
129      * @param albumAuthority authority of the updated album
130      * @param albumId ID of the updated album
131      */
notifyAlbumMediaChange( @onNull Context context, @NonNull String albumAuthority, @NonNull String albumId)132     public static void notifyAlbumMediaChange(
133             @NonNull Context context,
134             @NonNull String albumAuthority,
135             @NonNull String albumId) {
136         Log.d(TAG, "Sending a notification for album media update " + albumId);
137         context.getContentResolver().notifyChange(
138                 getAlbumMediaUpdateUri(albumAuthority, albumId),
139                 /* observer= */ null);
140     }
141 
142     /**
143      * Send album media update notification to the registered
144      * {@link android.database.ContentObserver}-s for all merged album updates.
145      * @param context The application context.
146      * @param localAuthority authority of the local provider.
147      */
notifyMergedAlbumMediaChange( @onNull Context context, @NonNull String localAuthority)148     public static void notifyMergedAlbumMediaChange(
149             @NonNull Context context,
150             @NonNull String localAuthority) {
151         for (String mergedAlbumId: PickerDataLayerV2.MERGED_ALBUMS) {
152             Log.d(TAG, "Sending a notification for merged album media update " + mergedAlbumId);
153 
154             // By default, always keep merged album authority as local.
155             notifyAlbumMediaChange(context, localAuthority, mergedAlbumId);
156         }
157     }
158 
159     /**
160      * Send search results update notification to the registered
161      * {@link android.database.ContentObserver}-s.
162      * @param context The application context.
163      * @param searchRequestId Search request ID corresponding for which the search results
164      *                        have updated.
165      */
notifySearchResultsChange( @onNull Context context, int searchRequestId)166     public static void notifySearchResultsChange(
167             @NonNull Context context,
168             int searchRequestId) {
169         Log.d(TAG, "Sending a notification for search results update " + searchRequestId);
170         context.getContentResolver().notifyChange(
171                 getSearchResultsUpdateUri(searchRequestId),
172                 /* observer= */ null);
173     }
174 
175     /**
176      * Send media sets update notification to the registered
177      * {@link android.database.ContentObserver}-s.
178      * @param context The application context.
179      * @param categoryId Category ID for which the media sets have updated.
180      */
notifyMediaSetsChange( @onNull Context context, @NonNull String categoryId)181     public static void notifyMediaSetsChange(
182             @NonNull Context context,
183             @NonNull String categoryId) {
184         requireNonNull(context);
185         requireNonNull(categoryId);
186         Log.d(TAG, "Sending notification for media sets update for the "
187                 + "given categoryId " + categoryId);
188         context.getContentResolver().notifyChange(
189                 getMediaSetsUpdateUri(categoryId), /* observer */ null
190         );
191     }
192 
getMediaSetsUpdateUri(@onNull String categoryId)193     private static Uri getMediaSetsUpdateUri(@NonNull String categoryId) {
194         return MEDIA_SETS_UPDATE_URI
195                 .buildUpon()
196                 .appendPath(categoryId)
197                 .build();
198     }
199 
200     /**
201      * Send media set content update notification to the registered
202      * {@link android.database.ContentObserver}-s.
203      * @param context The application context.
204      * @param mediaSetId MediaSet ID for which the media content has updated.
205      */
notifyMediaSetContentChange( @onNull Context context, @NonNull String mediaSetId)206     public static void notifyMediaSetContentChange(
207             @NonNull Context context, @NonNull String mediaSetId) {
208         requireNonNull(context);
209         requireNonNull(mediaSetId);
210         Log.d(TAG, "Sending notification for media set content update for the "
211                 + "given mediaSet " + mediaSetId);
212         context.getContentResolver().notifyChange(
213                 getMediaSetContentUpdateUri(mediaSetId), /* observer */ null
214         );
215     }
216 
getAlbumMediaUpdateUri( @onNull String albumAuthority, @NonNull String albumId)217     private static Uri getAlbumMediaUpdateUri(
218             @NonNull String albumAuthority,
219             @NonNull String albumId) {
220         return ALBUM_UPDATE_URI
221                 .buildUpon()
222                 .appendPath(requireNonNull(albumAuthority))
223                 .appendPath(requireNonNull(albumId))
224                 .build();
225     }
226 
getSearchResultsUpdateUri(int searchRequestId)227     private static Uri getSearchResultsUpdateUri(int searchRequestId) {
228         return SEARCH_RESULTS_UPDATE_URI
229                 .buildUpon()
230                 .appendPath(Integer.toString(searchRequestId))
231                 .build();
232     }
233 
getMediaSetContentUpdateUri(@onNull String mediaSetId)234     private static Uri getMediaSetContentUpdateUri(@NonNull String mediaSetId) {
235         return MEDIA_SET_CONTENT_UPDATE_URI
236                 .buildUpon()
237                 .appendPath(mediaSetId)
238                 .build();
239     }
240 }
241