• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.data.topics.Topic;
29 import com.android.adservices.service.FlagsFactory;
30 import com.android.adservices.service.consent.AdServicesApiConsent;
31 import com.android.adservices.service.consent.AdServicesApiType;
32 import com.android.adservices.service.consent.ConsentManager;
33 import com.android.adservices.ui.settings.fragments.AdServicesSettingsTopicsFragment;
34 import com.android.settingslib.widget.MainSwitchBar;
35 
36 import com.google.common.annotations.VisibleForTesting;
37 import com.google.common.collect.ImmutableList;
38 
39 /**
40  * View model for the topics view and blocked topics view of the AdServices Settings App. This view
41  * model is responsible for serving topics to the topics view and blocked topics view, and
42  * interacting with the {@link ConsentManager} that persists and changes the topics data in a
43  * storage.
44  */
45 // TODO(b/269798827): Enable for R.
46 @RequiresApi(Build.VERSION_CODES.S)
47 public class TopicsViewModel extends AndroidViewModel {
48 
49     private final MutableLiveData<Pair<TopicsViewModelUiEvent, Topic>> mEventTrigger =
50             new MutableLiveData<>();
51     private final MutableLiveData<ImmutableList<Topic>> mTopics;
52     private final MutableLiveData<ImmutableList<Topic>> mBlockedTopics;
53     private final ConsentManager mConsentManager;
54     private final MutableLiveData<Boolean> mTopicsConsent;
55 
56     /** UI event triggered by view model */
57     public enum TopicsViewModelUiEvent {
58         SWITCH_ON_TOPICS,
59         SWITCH_OFF_TOPICS,
60         BLOCK_TOPIC,
61         RESET_TOPICS,
62         DISPLAY_BLOCKED_TOPICS_FRAGMENT,
63     }
64 
TopicsViewModel(@onNull Application application)65     public TopicsViewModel(@NonNull Application application) {
66         super(application);
67         mConsentManager = ConsentManager.getInstance(application);
68         mTopics = new MutableLiveData<>(getTopicsFromConsentManager());
69         mBlockedTopics = new MutableLiveData<>(getBlockedTopicsFromConsentManager());
70         mTopicsConsent =
71                 FlagsFactory.getFlags().getGaUxFeatureEnabled()
72                         ? new MutableLiveData<>(getTopicsConsentFromConsentManager())
73                         : null;
74     }
75 
76     @VisibleForTesting
TopicsViewModel( @onNull Application application, ConsentManager consentManager, Boolean topicsConsent)77     public TopicsViewModel(
78             @NonNull Application application,
79             ConsentManager consentManager,
80             Boolean topicsConsent) {
81         super(application);
82         mConsentManager = consentManager;
83         mTopics = new MutableLiveData<>(getTopicsFromConsentManager());
84         mBlockedTopics = new MutableLiveData<>(getBlockedTopicsFromConsentManager());
85         mTopicsConsent = new MutableLiveData<>(topicsConsent);
86     }
87 
88     /**
89      * Provides the topics displayed in {@link AdServicesSettingsTopicsFragment}.
90      *
91      * @return A list of {@link Topic}s that represents the user's interests.
92      */
getTopics()93     public LiveData<ImmutableList<Topic>> getTopics() {
94         return mTopics;
95     }
96 
97     /**
98      * Provides the blocked topics list.
99      *
100      * @return a list of topics that represents the user's blocked interests.
101      */
getBlockedTopics()102     public LiveData<ImmutableList<Topic>> getBlockedTopics() {
103         return mBlockedTopics;
104     }
105 
106     /**
107      * Revoke the consent for the specified topic (i.e. block the topic).
108      *
109      * @param topic the topic to be blocked.
110      */
revokeTopicConsent(Topic topic)111     public void revokeTopicConsent(Topic topic) {
112         mConsentManager.revokeConsentForTopic(topic);
113         refresh();
114     }
115 
116     /**
117      * Reads all the data from {@link ConsentManager}.
118      *
119      * <p>TODO(b/238387560): To be moved to private when is fixed.
120      */
refresh()121     public void refresh() {
122         mTopics.postValue(getTopicsFromConsentManager());
123         mBlockedTopics.postValue(getBlockedTopicsFromConsentManager());
124     }
125 
126     /** Reset all information related to topics but blocked topics. */
resetTopics()127     public void resetTopics() {
128         mConsentManager.resetTopics();
129         mTopics.postValue(getTopicsFromConsentManager());
130     }
131 
132     /** Returns an observable but immutable event enum representing an view action on UI. */
getUiEvents()133     public LiveData<Pair<TopicsViewModelUiEvent, Topic>> 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 topic in the list of topics in {@link
147      * AdServicesSettingsTopicsFragment}.
148      *
149      * @param topic the topic to be blocked.
150      */
revokeTopicConsentButtonClickHandler(Topic topic)151     public void revokeTopicConsentButtonClickHandler(Topic topic) {
152         mEventTrigger.postValue(new Pair<>(TopicsViewModelUiEvent.BLOCK_TOPIC, topic));
153     }
154 
155     /** Triggers a reset of all topics related data. */
resetTopicsButtonClickHandler()156     public void resetTopicsButtonClickHandler() {
157         mEventTrigger.postValue(new Pair<>(TopicsViewModelUiEvent.RESET_TOPICS, null));
158     }
159 
160     /** Triggers {@link AdServicesSettingsTopicsFragment}. */
blockedTopicsFragmentButtonClickHandler()161     public void blockedTopicsFragmentButtonClickHandler() {
162         mEventTrigger.postValue(
163                 new Pair<>(TopicsViewModelUiEvent.DISPLAY_BLOCKED_TOPICS_FRAGMENT, null));
164     }
165 
166     // ---------------------------------------------------------------------------------------------
167     // Private Methods
168     // ---------------------------------------------------------------------------------------------
169 
getTopicsFromConsentManager()170     private ImmutableList<Topic> getTopicsFromConsentManager() {
171         return mConsentManager.getKnownTopicsWithConsent();
172     }
173 
getBlockedTopicsFromConsentManager()174     private ImmutableList<Topic> getBlockedTopicsFromConsentManager() {
175         return mConsentManager.getTopicsWithRevokedConsent();
176     }
177 
178     /**
179      * Provides {@link AdServicesApiConsent} displayed in {@link AdServicesSettingsTopicsFragment}
180      * as a Switch value.
181      *
182      * @return mTopicsConsent indicates if user has consented to Topics Api usage.
183      */
getTopicsConsent()184     public MutableLiveData<Boolean> getTopicsConsent() {
185         return mTopicsConsent;
186     }
187 
188     /**
189      * Sets the user consent for PP APIs.
190      *
191      * @param newTopicsConsentValue the new value that user consent should be set to for Topics PP
192      *     APIs.
193      */
setTopicsConsent(Boolean newTopicsConsentValue)194     public void setTopicsConsent(Boolean newTopicsConsentValue) {
195         if (newTopicsConsentValue) {
196             mConsentManager.enable(getApplication(), AdServicesApiType.TOPICS);
197         } else {
198             mConsentManager.disable(getApplication(), AdServicesApiType.TOPICS);
199         }
200         mTopicsConsent.postValue(getTopicsConsentFromConsentManager());
201         if (FlagsFactory.getFlags().getRecordManualInteractionEnabled()) {
202             ConsentManager.getInstance(getApplication())
203                     .recordUserManualInteractionWithConsent(
204                             ConsentManager.MANUAL_INTERACTIONS_RECORDED);
205         }
206     }
207     /**
208      * Triggers opt out process for Privacy Sandbox. Also reverts the switch state, since
209      * confirmation dialog will handle switch change.
210      */
consentSwitchClickHandler(MainSwitchBar topicsSwitchBar)211     public void consentSwitchClickHandler(MainSwitchBar topicsSwitchBar) {
212         if (topicsSwitchBar.isChecked()) {
213             topicsSwitchBar.setChecked(false);
214             mEventTrigger.postValue(new Pair<>(TopicsViewModelUiEvent.SWITCH_ON_TOPICS, null));
215         } else {
216             topicsSwitchBar.setChecked(true);
217             mEventTrigger.postValue(new Pair<>(TopicsViewModelUiEvent.SWITCH_OFF_TOPICS, null));
218         }
219     }
220 
getTopicsConsentFromConsentManager()221     private boolean getTopicsConsentFromConsentManager() {
222         return mConsentManager.getConsent(AdServicesApiType.TOPICS).isGiven();
223     }
224 }
225