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.android.providers.media.photopicker.viewmodel; 18 19 import android.annotation.UserIdInt; 20 import android.content.Context; 21 import android.os.UserHandle; 22 import android.util.Log; 23 24 import androidx.annotation.NonNull; 25 import androidx.annotation.Nullable; 26 import androidx.annotation.UiThread; 27 import androidx.lifecycle.LiveData; 28 import androidx.lifecycle.MutableLiveData; 29 30 import com.android.providers.media.photopicker.data.UserIdManager; 31 import com.android.providers.media.util.ForegroundThread; 32 import com.android.providers.media.util.PerUser; 33 34 class BannerManager { 35 private static final String TAG = "BannerManager"; 36 37 private final UserIdManager mUserIdManager; 38 39 // Authority of the current CloudMediaProvider of the current user 40 private final MutableLiveData<String> mCloudMediaProviderAuthority = new MutableLiveData<>(); 41 // Label of the current CloudMediaProvider of the current user 42 private final MutableLiveData<String> mCloudMediaProviderLabel = new MutableLiveData<>(); 43 // Account name of the current CloudMediaProvider of the current user 44 private final MutableLiveData<String> mCloudMediaAccountName = new MutableLiveData<>(); 45 46 // Boolean Choose App Banner visibility 47 private final MutableLiveData<Boolean> mShowChooseAppBanner = new MutableLiveData<>(false); 48 // Boolean Cloud Media Available Banner visibility 49 private final MutableLiveData<Boolean> mShowCloudMediaAvailableBanner = 50 new MutableLiveData<>(false); 51 // Boolean 'Account Updated' banner visibility 52 private final MutableLiveData<Boolean> mShowAccountUpdatedBanner = new MutableLiveData<>(false); 53 // Boolean 'Choose Account' banner visibility 54 private final MutableLiveData<Boolean> mShowChooseAccountBanner = new MutableLiveData<>(false); 55 56 // The banner controllers per user 57 private final PerUser<BannerController> mBannerControllers; 58 BannerManager(@onNull Context context, @NonNull UserIdManager userIdManager)59 BannerManager(@NonNull Context context, @NonNull UserIdManager userIdManager) { 60 mUserIdManager = userIdManager; 61 mBannerControllers = new PerUser<BannerController>() { 62 @NonNull 63 @Override 64 protected BannerController create(@UserIdInt int userId) { 65 return new BannerController(context, UserHandle.of(userId)); 66 } 67 }; 68 maybeInitialiseAndSetBannersForCurrentUser(); 69 } 70 getCurrentUserProfileId()71 @UserIdInt int getCurrentUserProfileId() { 72 return mUserIdManager.getCurrentUserProfileId().getIdentifier(); 73 } 74 getBannerControllersPerUser()75 PerUser<BannerController> getBannerControllersPerUser() { 76 return mBannerControllers; 77 } 78 79 /** 80 * @return a {@link LiveData} that holds the value (once it's fetched) of the 81 * {@link android.content.ContentProvider#mAuthority authority} of the current 82 * {@link android.provider.CloudMediaProvider}. 83 */ 84 @NonNull getCloudMediaProviderAuthorityLiveData()85 MutableLiveData<String> getCloudMediaProviderAuthorityLiveData() { 86 return mCloudMediaProviderAuthority; 87 } 88 89 /** 90 * @return a {@link LiveData} that holds the value (once it's fetched) of the label 91 * of the current {@link android.provider.CloudMediaProvider}. 92 */ 93 @NonNull getCloudMediaProviderAppTitleLiveData()94 MutableLiveData<String> getCloudMediaProviderAppTitleLiveData() { 95 return mCloudMediaProviderLabel; 96 } 97 98 /** 99 * @return a {@link LiveData} that holds the value (once it's fetched) of the account name 100 * of the current {@link android.provider.CloudMediaProvider}. 101 */ 102 @NonNull getCloudMediaAccountNameLiveData()103 MutableLiveData<String> getCloudMediaAccountNameLiveData() { 104 return mCloudMediaAccountName; 105 } 106 107 /** 108 * @return the {@link LiveData} of the 'Choose App' banner visibility. 109 */ 110 @NonNull shouldShowChooseAppBannerLiveData()111 MutableLiveData<Boolean> shouldShowChooseAppBannerLiveData() { 112 return mShowChooseAppBanner; 113 } 114 115 /** 116 * @return the {@link LiveData} of the 'Cloud Media Available' banner visibility. 117 */ 118 @NonNull shouldShowCloudMediaAvailableBannerLiveData()119 MutableLiveData<Boolean> shouldShowCloudMediaAvailableBannerLiveData() { 120 return mShowCloudMediaAvailableBanner; 121 } 122 123 /** 124 * @return the {@link LiveData} of the 'Account Updated' banner visibility. 125 */ 126 @NonNull shouldShowAccountUpdatedBannerLiveData()127 MutableLiveData<Boolean> shouldShowAccountUpdatedBannerLiveData() { 128 return mShowAccountUpdatedBanner; 129 } 130 131 /** 132 * @return the {@link LiveData} of the 'Choose Account' banner visibility. 133 */ 134 @NonNull shouldShowChooseAccountBannerLiveData()135 MutableLiveData<Boolean> shouldShowChooseAccountBannerLiveData() { 136 return mShowChooseAccountBanner; 137 } 138 139 /** 140 * Dismiss (hide) the 'Choose App' banner for the current user. 141 */ 142 @UiThread onUserDismissedChooseAppBanner()143 void onUserDismissedChooseAppBanner() { 144 if (Boolean.FALSE.equals(mShowChooseAppBanner.getValue())) { 145 Log.d(TAG, "Choose App banner visibility live data value is false on dismiss"); 146 } else { 147 mShowChooseAppBanner.setValue(false); 148 } 149 150 final BannerController bannerController = getCurrentBannerController(); 151 if (bannerController != null) { 152 bannerController.onUserDismissedChooseAppBanner(); 153 } 154 } 155 156 /** 157 * Dismiss (hide) the 'Cloud Media Available' banner for the current user. 158 */ 159 @UiThread onUserDismissedCloudMediaAvailableBanner()160 void onUserDismissedCloudMediaAvailableBanner() { 161 if (Boolean.FALSE.equals(mShowCloudMediaAvailableBanner.getValue())) { 162 Log.d(TAG, "Cloud Media Available banner visibility live data value is false on " 163 + "dismiss"); 164 } else { 165 mShowCloudMediaAvailableBanner.setValue(false); 166 } 167 168 final BannerController bannerController = getCurrentBannerController(); 169 if (bannerController != null) { 170 bannerController.onUserDismissedCloudMediaAvailableBanner(); 171 } 172 } 173 174 /** 175 * Dismiss (hide) the 'Account Updated' banner for the current user. 176 */ 177 @UiThread onUserDismissedAccountUpdatedBanner()178 void onUserDismissedAccountUpdatedBanner() { 179 if (Boolean.FALSE.equals(mShowAccountUpdatedBanner.getValue())) { 180 Log.d(TAG, "Account Updated banner visibility live data value is false on dismiss"); 181 } else { 182 mShowAccountUpdatedBanner.setValue(false); 183 } 184 185 final BannerController bannerController = getCurrentBannerController(); 186 if (bannerController != null) { 187 bannerController.onUserDismissedAccountUpdatedBanner(); 188 } 189 } 190 191 /** 192 * Dismiss (hide) the 'Choose Account' banner for the current user. 193 */ 194 @UiThread onUserDismissedChooseAccountBanner()195 void onUserDismissedChooseAccountBanner() { 196 if (Boolean.FALSE.equals(mShowChooseAccountBanner.getValue())) { 197 Log.d(TAG, "Choose Account banner visibility live data value is false on dismiss"); 198 } else { 199 mShowChooseAccountBanner.setValue(false); 200 } 201 202 final BannerController bannerController = getCurrentBannerController(); 203 if (bannerController != null) { 204 bannerController.onUserDismissedChooseAccountBanner(); 205 } 206 } 207 208 @Nullable getCurrentBannerController()209 private BannerController getCurrentBannerController() { 210 final int currentUserProfileId = getCurrentUserProfileId(); 211 return mBannerControllers.get(currentUserProfileId); 212 } 213 214 /** 215 * Resets the banner controller per user. 216 * 217 * Note - Since {@link BannerController#reset()} cannot be called in the Main thread, using 218 * {@link ForegroundThread} here. 219 */ maybeResetAllBannerData()220 void maybeResetAllBannerData() { 221 for (int arrayIndex = 0, numControllers = mBannerControllers.size(); 222 arrayIndex < numControllers; arrayIndex++) { 223 final BannerController bannerController = mBannerControllers.valueAt(arrayIndex); 224 ForegroundThread.getExecutor().execute(bannerController::reset); 225 } 226 } 227 228 /** 229 * Update the banner {@link LiveData} values. 230 * 231 * 1. {@link #hideAllBanners()} in the Main thread to ensure consistency with the media items 232 * displayed for the period when the items and categories have been updated but the 233 * {@link BannerController} construction or {@link BannerController#reset()} is still in 234 * progress. 235 * 236 * 2. Initialise and set the banner data for the current user 237 * {@link #maybeInitialiseAndSetBannersForCurrentUser()}. 238 */ 239 @UiThread maybeUpdateBannerLiveDatas()240 void maybeUpdateBannerLiveDatas() { 241 // Hide all banners in the Main thread to ensure consistency with the media items 242 hideAllBanners(); 243 244 // Initialise and set the banner data for the current user 245 maybeInitialiseAndSetBannersForCurrentUser(); 246 } 247 248 /** 249 * Hide all banners in the Main thread. 250 * 251 * Set all banner {@link LiveData} values to {@code false}. 252 */ 253 @UiThread hideAllBanners()254 private void hideAllBanners() { 255 mShowChooseAppBanner.setValue(false); 256 mShowCloudMediaAvailableBanner.setValue(false); 257 mShowAccountUpdatedBanner.setValue(false); 258 mShowChooseAccountBanner.setValue(false); 259 } 260 261 262 /** 263 * Initialise and set the banner data for the current user. 264 * 265 * No-op by default, overridden for cloud. 266 */ maybeInitialiseAndSetBannersForCurrentUser()267 void maybeInitialiseAndSetBannersForCurrentUser() { 268 // No-op, may be overridden 269 } 270 271 static class CloudBannerManager extends BannerManager { CloudBannerManager(@onNull Context context, @NonNull UserIdManager userIdManager)272 CloudBannerManager(@NonNull Context context, @NonNull UserIdManager userIdManager) { 273 super(context, userIdManager); 274 } 275 276 /** 277 * Initialise and set the banner data for the current user. 278 * 279 * 1. Get or create the {@link BannerController} for 280 * {@link UserIdManager#getCurrentUserProfileId()} using {@link PerUser#forUser(int)}. 281 * Since, the {@link BannerController} construction cannot be done in the Main thread, 282 * using {@link ForegroundThread} here. 283 * 284 * 2. Post the updated {@link BannerController} {@link LiveData} values. 285 */ 286 @Override maybeInitialiseAndSetBannersForCurrentUser()287 void maybeInitialiseAndSetBannersForCurrentUser() { 288 final int currentUserProfileId = getCurrentUserProfileId(); 289 ForegroundThread.getExecutor().execute(() -> { 290 // Get (iff exists) or create the banner controller for the current user 291 final BannerController bannerController = 292 getBannerControllersPerUser().forUser(currentUserProfileId); 293 // Post the banner related live data values from this current user banner controller 294 getCloudMediaProviderAuthorityLiveData() 295 .postValue(bannerController.getCloudMediaProviderAuthority()); 296 getCloudMediaProviderAppTitleLiveData() 297 .postValue(bannerController.getCloudMediaProviderLabel()); 298 getCloudMediaAccountNameLiveData() 299 .postValue(bannerController.getCloudMediaProviderAccountName()); 300 shouldShowChooseAppBannerLiveData() 301 .postValue(bannerController.shouldShowChooseAppBanner()); 302 shouldShowCloudMediaAvailableBannerLiveData() 303 .postValue(bannerController.shouldShowCloudMediaAvailableBanner()); 304 shouldShowAccountUpdatedBannerLiveData() 305 .postValue(bannerController.shouldShowAccountUpdatedBanner()); 306 shouldShowChooseAccountBannerLiveData() 307 .postValue(bannerController.shouldShowChooseAccountBanner()); 308 }); 309 } 310 } 311 } 312