1 /* 2 * Copyright (C) 2022 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 package com.android.adservices.ui.settings.viewmodels; 17 18 import android.app.Application; 19 import android.os.Build; 20 import android.util.Pair; 21 22 import androidx.annotation.NonNull; 23 import androidx.annotation.RequiresApi; 24 import androidx.lifecycle.AndroidViewModel; 25 import androidx.lifecycle.LiveData; 26 import androidx.lifecycle.MutableLiveData; 27 28 import com.android.adservices.service.FlagsFactory; 29 import com.android.adservices.service.consent.AdServicesApiConsent; 30 import com.android.adservices.service.consent.AdServicesApiType; 31 import com.android.adservices.service.consent.App; 32 import com.android.adservices.service.consent.ConsentManager; 33 import com.android.adservices.ui.settings.fragments.AdServicesSettingsAppsFragment; 34 import com.android.internal.annotations.VisibleForTesting; 35 import com.android.settingslib.widget.MainSwitchBar; 36 37 import com.google.common.collect.ImmutableList; 38 39 import java.io.IOException; 40 41 /** 42 * View model for the apps view and blocked apps view of the AdServices Settings App. This view 43 * model is responsible for serving apps to the apps view and blocked apps view, and interacting 44 * with the {@link ConsentManager} that persists and changes the apps data in a storage. 45 */ 46 @RequiresApi(Build.VERSION_CODES.S) 47 public class AppsViewModel extends AndroidViewModel { 48 49 private final MutableLiveData<Pair<AppsViewModelUiEvent, App>> mEventTrigger = 50 new MutableLiveData<>(); 51 private final MutableLiveData<ImmutableList<App>> mApps; 52 private final MutableLiveData<ImmutableList<App>> mBlockedApps; 53 private final ConsentManager mConsentManager; 54 private final MutableLiveData<Boolean> mAppsConsent; 55 56 /** UI event triggered by view model */ 57 public enum AppsViewModelUiEvent { 58 SWITCH_ON_APPS, 59 SWITCH_OFF_APPS, 60 BLOCK_APP, 61 RESET_APPS, 62 DISPLAY_BLOCKED_APPS_FRAGMENT, 63 } 64 AppsViewModel(@onNull Application application)65 public AppsViewModel(@NonNull Application application) { 66 super(application); 67 68 mConsentManager = ConsentManager.getInstance(); 69 mApps = new MutableLiveData<>(getAppsFromConsentManager()); 70 mBlockedApps = new MutableLiveData<>(getBlockedAppsFromConsentManager()); 71 mAppsConsent = 72 FlagsFactory.getFlags().getGaUxFeatureEnabled() 73 ? new MutableLiveData<>(getAppsConsentFromConsentManager()) 74 : null; 75 } 76 77 @VisibleForTesting AppsViewModel(@onNull Application application, ConsentManager consentManager)78 public AppsViewModel(@NonNull Application application, ConsentManager consentManager) { 79 super(application); 80 81 mConsentManager = consentManager; 82 mApps = new MutableLiveData<>(getAppsFromConsentManager()); 83 mBlockedApps = new MutableLiveData<>(getBlockedAppsFromConsentManager()); 84 mAppsConsent = new MutableLiveData<>(true); 85 } 86 87 /** 88 * Provides the apps displayed in {@link AdServicesSettingsAppsFragment}. 89 * 90 * @return A list of {@link App}s that represents apps that use FLEDGE. 91 */ getApps()92 public LiveData<ImmutableList<App>> getApps() { 93 return mApps; 94 } 95 96 /** 97 * Provides the blocked apps list. 98 * 99 * @return a list of apps that represents the user's blocked interests. 100 */ getBlockedApps()101 public LiveData<ImmutableList<App>> getBlockedApps() { 102 return mBlockedApps; 103 } 104 105 /** 106 * Revoke the consent for the specified app (i.e. block the app). 107 * 108 * @param app the app to be blocked. 109 */ revokeAppConsent(App app)110 public void revokeAppConsent(App app) throws IOException { 111 mConsentManager.revokeConsentForApp(app); 112 refresh(); 113 } 114 115 /** 116 * Reads all the data from {@link ConsentManager}. 117 * 118 * <p>TODO(b/238387560): To be moved to private when is fixed. 119 */ refresh()120 public void refresh() { 121 mApps.postValue(getAppsFromConsentManager()); 122 mBlockedApps.postValue(getBlockedAppsFromConsentManager()); 123 } 124 125 /** Reset all information related to apps but blocked apps. */ resetApps()126 public void resetApps() throws IOException { 127 mConsentManager.resetApps(); 128 mApps.postValue(getAppsFromConsentManager()); 129 mConsentManager.setPaDataReset(true); 130 } 131 132 /** Returns an observable but immutable event enum representing an view action on UI. */ getUiEvents()133 public LiveData<Pair<AppsViewModelUiEvent, App>> getUiEvents() { 134 return mEventTrigger; 135 } 136 137 /** 138 * Sets the UI Event as handled so the action will not be handled again if activity is 139 * recreated. 140 */ uiEventHandled()141 public void uiEventHandled() { 142 mEventTrigger.postValue(new Pair<>(null, null)); 143 } 144 145 /** 146 * Triggers the block of the specified app in the list of apps in {@link 147 * AdServicesSettingsAppsFragment}. 148 * 149 * @param app the app to be blocked. 150 */ revokeAppConsentButtonClickHandler(App app)151 public void revokeAppConsentButtonClickHandler(App app) { 152 mEventTrigger.postValue(new Pair<>(AppsViewModelUiEvent.BLOCK_APP, app)); 153 } 154 155 /** Triggers a reset of all apps related data. */ resetAppsButtonClickHandler()156 public void resetAppsButtonClickHandler() { 157 mEventTrigger.postValue(new Pair<>(AppsViewModelUiEvent.RESET_APPS, null)); 158 } 159 160 /** Triggers {@link AdServicesSettingsAppsFragment}. */ blockedAppsFragmentButtonClickHandler()161 public void blockedAppsFragmentButtonClickHandler() { 162 mEventTrigger.postValue( 163 new Pair<>(AppsViewModelUiEvent.DISPLAY_BLOCKED_APPS_FRAGMENT, null)); 164 } 165 166 // --------------------------------------------------------------------------------------------- 167 // Private Methods 168 // --------------------------------------------------------------------------------------------- 169 getAppsFromConsentManager()170 private ImmutableList<App> getAppsFromConsentManager() { 171 return mConsentManager.getKnownAppsWithConsent(); 172 } 173 getBlockedAppsFromConsentManager()174 private ImmutableList<App> getBlockedAppsFromConsentManager() { 175 return mConsentManager.getAppsWithRevokedConsent(); 176 } 177 178 /** 179 * Provides {@link AdServicesApiConsent} displayed in {@link AdServicesSettingsAppsFragment} as 180 * a Switch value. 181 * 182 * @return mAppsConsent indicates if user has consented to Apps Api usage. 183 */ getAppsConsent()184 public MutableLiveData<Boolean> getAppsConsent() { 185 return mAppsConsent; 186 } 187 188 /** 189 * Sets the user consent for PP APIs. 190 * 191 * @param newAppsConsentValue the new value that user consent should be set to for Apps PP APIs. 192 */ setAppsConsent(Boolean newAppsConsentValue)193 public void setAppsConsent(Boolean newAppsConsentValue) { 194 if (newAppsConsentValue) { 195 mConsentManager.enable(getApplication(), AdServicesApiType.FLEDGE); 196 } else { 197 mConsentManager.disable(getApplication(), AdServicesApiType.FLEDGE); 198 } 199 mAppsConsent.postValue(getAppsConsentFromConsentManager()); 200 if (FlagsFactory.getFlags().getRecordManualInteractionEnabled()) { 201 ConsentManager.getInstance() 202 .recordUserManualInteractionWithConsent( 203 ConsentManager.MANUAL_INTERACTIONS_RECORDED); 204 } 205 } 206 /** 207 * Triggers opt out process for Privacy Sandbox. Also reverts the switch state, since 208 * confirmation dialog will handle switch change. 209 */ consentSwitchClickHandler(MainSwitchBar appsSwitchBar)210 public void consentSwitchClickHandler(MainSwitchBar appsSwitchBar) { 211 if (appsSwitchBar.isChecked()) { 212 appsSwitchBar.setChecked(false); 213 mEventTrigger.postValue(new Pair<>(AppsViewModelUiEvent.SWITCH_ON_APPS, null)); 214 } else { 215 appsSwitchBar.setChecked(true); 216 mEventTrigger.postValue(new Pair<>(AppsViewModelUiEvent.SWITCH_OFF_APPS, null)); 217 } 218 } 219 getAppsConsentFromConsentManager()220 private boolean getAppsConsentFromConsentManager() { 221 return mConsentManager.getConsent(AdServicesApiType.FLEDGE).isGiven(); 222 } 223 } 224