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.delegates;
17 
18 import android.content.Intent;
19 import android.os.Build;
20 import android.text.method.LinkMovementMethod;
21 import android.util.Log;
22 import android.util.Pair;
23 import android.view.View;
24 import android.widget.TextView;
25 
26 import androidx.annotation.RequiresApi;
27 import androidx.lifecycle.Observer;
28 
29 import com.android.adservices.api.R;
30 import com.android.adservices.service.FlagsFactory;
31 import com.android.adservices.service.consent.App;
32 import com.android.adservices.service.stats.UiStatsLogger;
33 import com.android.adservices.ui.settings.DialogFragmentManager;
34 import com.android.adservices.ui.settings.DialogManager;
35 import com.android.adservices.ui.settings.activities.AppsActivity;
36 import com.android.adservices.ui.settings.activities.BlockedAppsActivity;
37 import com.android.adservices.ui.settings.fragments.AdServicesSettingsAppsFragment;
38 import com.android.adservices.ui.settings.viewmodels.AppsViewModel;
39 import com.android.adservices.ui.settings.viewmodels.AppsViewModel.AppsViewModelUiEvent;
40 import com.android.settingslib.widget.MainSwitchBar;
41 
42 import java.io.IOException;
43 
44 /**
45  * Delegate class that helps AdServices Settings fragments to respond to all view model/user events.
46  */
47 @RequiresApi(Build.VERSION_CODES.S)
48 public class AppsActionDelegate {
49     private final AppsActivity mAppsActivity;
50     private final AppsViewModel mAppsViewModel;
51 
AppsActionDelegate(AppsActivity appsActivity, AppsViewModel appsViewModel)52     public AppsActionDelegate(AppsActivity appsActivity, AppsViewModel appsViewModel) {
53         mAppsActivity = appsActivity;
54         mAppsViewModel = appsViewModel;
55         listenToAppsViewModelUiEvents();
56     }
57 
listenToAppsViewModelUiEvents()58     private void listenToAppsViewModelUiEvents() {
59         Observer<Pair<AppsViewModelUiEvent, App>> observer =
60                 eventAppPair -> {
61                     if (eventAppPair == null) {
62                         return;
63                     }
64                     AppsViewModelUiEvent event = eventAppPair.first;
65                     App app = eventAppPair.second;
66                     if (event == null) {
67                         return;
68                     }
69                     try {
70                         switch (event) {
71                             case SWITCH_ON_APPS:
72                                 if (FlagsFactory.getFlags().getToggleSpeedBumpEnabled()) {
73                                     DialogFragmentManager.showOptInAppsDialog(mAppsActivity);
74                                 }
75                                 mAppsViewModel.setAppsConsent(true);
76                                 mAppsViewModel.refresh();
77                                 break;
78                             case SWITCH_OFF_APPS:
79                                 if (FlagsFactory.getFlags().getToggleSpeedBumpEnabled()) {
80                                     DialogFragmentManager.showOptOutAppsDialog(
81                                             mAppsActivity, mAppsViewModel);
82                                 } else {
83                                     mAppsViewModel.setAppsConsent(false);
84                                     mAppsViewModel.refresh();
85                                 }
86 
87                                 break;
88                             case BLOCK_APP:
89                                 UiStatsLogger.logBlockAppSelected();
90                                 if (FlagsFactory.getFlags().getUiDialogsFeatureEnabled()) {
91                                     if (FlagsFactory.getFlags().getUiDialogFragmentEnabled()) {
92                                         DialogFragmentManager.showBlockAppDialog(
93                                                 mAppsActivity, mAppsViewModel, app);
94                                     } else {
95                                         DialogManager.showBlockAppDialog(
96                                                 mAppsActivity, mAppsViewModel, app);
97                                     }
98                                 } else {
99                                     mAppsViewModel.revokeAppConsent(app);
100                                 }
101                                 break;
102                             case RESET_APPS:
103                                 UiStatsLogger.logResetAppSelected();
104                                 if (FlagsFactory.getFlags().getUiDialogsFeatureEnabled()) {
105                                     if (FlagsFactory.getFlags().getUiDialogFragmentEnabled()) {
106                                         DialogFragmentManager.showResetAppDialog(
107                                                 mAppsActivity, mAppsViewModel);
108                                     } else {
109                                         DialogManager.showResetAppDialog(
110                                                 mAppsActivity, mAppsViewModel);
111                                     }
112                                 } else {
113                                     mAppsViewModel.resetApps();
114                                 }
115                                 break;
116                             case DISPLAY_BLOCKED_APPS_FRAGMENT:
117                                 Intent intent =
118                                         new Intent(mAppsActivity, BlockedAppsActivity.class);
119                                 mAppsActivity.startActivity(intent);
120                                 break;
121                             default:
122                                 Log.e("AdservicesUI", "Unknown Action for UI Logging");
123                         }
124                     } catch (IOException e) {
125                         Log.e(
126                                 "AdServicesUI",
127                                 "Error while processing AppsViewModelUiEvent " + event + ":" + e);
128                     } finally {
129                         mAppsViewModel.uiEventHandled();
130                     }
131                 };
132         mAppsViewModel.getUiEvents().observe(mAppsActivity, observer);
133     }
134 
135     /**
136      * Configure all UI elements (except apps list) in {@link AdServicesSettingsAppsFragment} to
137      * handle user actions.
138      */
initAppsFragment(AdServicesSettingsAppsFragment fragment)139     public void initAppsFragment(AdServicesSettingsAppsFragment fragment) {
140         mAppsActivity.setTitle(R.string.settingsUI_apps_ga_title);
141 
142         configureAppsConsentSwitch(fragment);
143 
144         setGaUxAppsViewText();
145         configureBlockedAppsFragmentButton(fragment);
146         configureResetAppsButton(fragment);
147     }
148 
setGaUxAppsViewText()149     private void setGaUxAppsViewText() {
150         ((TextView) mAppsActivity.findViewById(R.id.reset_apps_button_child))
151                 .setText(R.string.settingsUI_reset_apps_ga_title);
152         ((TextView) mAppsActivity.findViewById(R.id.no_apps_state))
153                 .setText(R.string.settingsUI_apps_view_no_apps_ga_text);
154         ((TextView) mAppsActivity.findViewById(R.id.no_apps_state))
155                 .setMovementMethod(LinkMovementMethod.getInstance());
156     }
157 
configureAppsConsentSwitch(AdServicesSettingsAppsFragment fragment)158     private void configureAppsConsentSwitch(AdServicesSettingsAppsFragment fragment) {
159         MainSwitchBar appsSwitchBar = mAppsActivity.findViewById(R.id.apps_switch_bar);
160 
161         mAppsViewModel.getAppsConsent().observe(fragment, appsSwitchBar::setChecked);
162         appsSwitchBar.setOnClickListener(
163                 switchBar -> mAppsViewModel.consentSwitchClickHandler((MainSwitchBar) switchBar));
164     }
165 
configureBlockedAppsFragmentButton(AdServicesSettingsAppsFragment fragment)166     private void configureBlockedAppsFragmentButton(AdServicesSettingsAppsFragment fragment) {
167         View blockedAppsButton = fragment.requireView().findViewById(R.id.blocked_apps_button);
168         View blockedAppsWhenEmptyListButton =
169                 fragment.requireView().findViewById(R.id.blocked_apps_when_empty_state_button);
170 
171         blockedAppsButton.setOnClickListener(
172                 view -> {
173                     mAppsViewModel.blockedAppsFragmentButtonClickHandler();
174                 });
175         blockedAppsWhenEmptyListButton.setOnClickListener(
176                 view -> {
177                     mAppsViewModel.blockedAppsFragmentButtonClickHandler();
178                 });
179     }
180 
configureResetAppsButton(AdServicesSettingsAppsFragment fragment)181     private void configureResetAppsButton(AdServicesSettingsAppsFragment fragment) {
182         View resetAppsButton = fragment.requireView().findViewById(R.id.reset_apps_button);
183 
184         resetAppsButton.setOnClickListener(view -> mAppsViewModel.resetAppsButtonClickHandler());
185     }
186 }
187