• 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.u18notifications;
17 
18 import static com.android.adservices.ui.notifications.ConsentNotificationActivity.NotificationFragmentEnum.LANDING_PAGE_DISMISSED;
19 import static com.android.adservices.ui.notifications.ConsentNotificationActivity.NotificationFragmentEnum.LANDING_PAGE_DISPLAYED;
20 import static com.android.adservices.ui.notifications.ConsentNotificationActivity.NotificationFragmentEnum.LANDING_PAGE_GOT_IT_CLICKED;
21 import static com.android.adservices.ui.notifications.ConsentNotificationActivity.NotificationFragmentEnum.LANDING_PAGE_MORE_BUTTON_CLICKED;
22 import static com.android.adservices.ui.notifications.ConsentNotificationActivity.NotificationFragmentEnum.LANDING_PAGE_SCROLLED;
23 import static com.android.adservices.ui.notifications.ConsentNotificationActivity.NotificationFragmentEnum.LANDING_PAGE_SCROLLED_TO_BOTTOM;
24 import static com.android.adservices.ui.notifications.ConsentNotificationActivity.NotificationFragmentEnum.LANDING_PAGE_SETTINGS_BUTTON_CLICKED;
25 import static com.android.adservices.ui.settings.activities.AdServicesSettingsMainActivity.FROM_NOTIFICATION_KEY;
26 
27 import android.content.Context;
28 import android.content.Intent;
29 import android.os.Build;
30 import android.os.Bundle;
31 import android.text.method.LinkMovementMethod;
32 import android.view.LayoutInflater;
33 import android.view.View;
34 import android.view.View.OnScrollChangeListener;
35 import android.view.ViewGroup;
36 import android.widget.Button;
37 import android.widget.ScrollView;
38 import android.widget.TextView;
39 
40 import androidx.annotation.NonNull;
41 import androidx.annotation.Nullable;
42 import androidx.annotation.RequiresApi;
43 import androidx.fragment.app.Fragment;
44 
45 import com.android.adservices.api.R;
46 import com.android.adservices.service.FlagsFactory;
47 import com.android.adservices.service.consent.ConsentManager;
48 import com.android.adservices.ui.notifications.ConsentNotificationActivity;
49 import com.android.adservices.ui.settings.activities.AdServicesSettingsMainActivity;
50 
51 /** Fragment for the Notification landing page for U18 users. */
52 @RequiresApi(Build.VERSION_CODES.S)
53 public class ConsentNotificationU18Fragment extends Fragment {
54     public static final String IS_TOPICS_INFO_VIEW_EXPANDED_KEY = "is_topics_info_view_expanded";
55 
56     private @Nullable ScrollToBottomController mScrollToBottomController;
57 
58     @Override
onCreateView( @onNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)59     public View onCreateView(
60             @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
61         return setupActivity(inflater, container);
62     }
63 
64     @Override
onViewCreated(@onNull View view, Bundle savedInstanceState)65     public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
66         setupListeners(savedInstanceState);
67         ConsentNotificationActivity.handleAction(LANDING_PAGE_DISPLAYED);
68     }
69 
70     @Override
onSaveInstanceState(@onNull Bundle savedInstanceState)71     public void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
72         super.onSaveInstanceState(savedInstanceState);
73 
74         ConsentNotificationActivity.handleAction(LANDING_PAGE_DISMISSED);
75         if (mScrollToBottomController != null) {
76             mScrollToBottomController.saveInstanceState(savedInstanceState);
77         }
78     }
79 
setupActivity(LayoutInflater inflater, ViewGroup container)80     private View setupActivity(LayoutInflater inflater, ViewGroup container) {
81         return inflater.inflate(R.layout.consent_notification_u18_fragment, container, false);
82     }
83 
setupListeners(Bundle savedInstanceState)84     private void setupListeners(Bundle savedInstanceState) {
85         // set up privacy policy link movement
86         ((TextView) requireActivity().findViewById(R.id.learn_more_from_privacy_policy))
87                 .setMovementMethod(LinkMovementMethod.getInstance());
88 
89         // set up left control button and right control button
90         Button leftControlButton = requireActivity().findViewById(R.id.leftControlButton);
91         leftControlButton.setOnClickListener(
92                 view -> {
93                     ConsentNotificationActivity.handleAction(LANDING_PAGE_SETTINGS_BUTTON_CLICKED);
94 
95                     // go to settings activity
96                     Intent intent =
97                             new Intent(requireActivity(), AdServicesSettingsMainActivity.class);
98                     intent.putExtra(FROM_NOTIFICATION_KEY, true);
99                     startActivity(intent);
100                     requireActivity().finish();
101                 });
102 
103         Button rightControlButton = requireActivity().findViewById(R.id.rightControlButton);
104         ScrollView scrollView = requireView().findViewById(R.id.notification_fragment_scrollview);
105 
106         mScrollToBottomController =
107                 new ScrollToBottomController(
108                         scrollView, leftControlButton, rightControlButton, savedInstanceState);
109         mScrollToBottomController.bind();
110         // check whether it can scroll vertically and update buttons after layout can be measured
111         scrollView.post(() -> mScrollToBottomController.updateButtonsIfHasScrolledToBottom());
112     }
113 
114     /**
115      * Allows the positive, acceptance button to scroll the view.
116      *
117      * <p>When the positive button first appears it will show the text "More". When the user taps
118      * the button, the view will scroll to the bottom. Once the view has scrolled to the bottom, the
119      * button text will be replaced with the acceptance text. Once the text has changed, the button
120      * will trigger the positive action no matter where the view is scrolled.
121      */
122     private class ScrollToBottomController implements OnScrollChangeListener {
123         private static final String STATE_HAS_SCROLLED_TO_BOTTOM = "has_scrolled_to_bottom";
124         private static final int SCROLL_DIRECTION_DOWN = 1;
125         private static final double SCROLL_MULTIPLIER = 0.8;
126 
127         private final ScrollView mScrollContainer;
128         private final Button mLeftControlButton;
129         private final Button mRightControlButton;
130 
131         private boolean mHasScrolledToBottom;
132 
ScrollToBottomController( ScrollView scrollContainer, Button leftControlButton, Button rightControlButton, @Nullable Bundle savedInstanceState)133         ScrollToBottomController(
134                 ScrollView scrollContainer,
135                 Button leftControlButton,
136                 Button rightControlButton,
137                 @Nullable Bundle savedInstanceState) {
138             this.mScrollContainer = scrollContainer;
139             this.mLeftControlButton = leftControlButton;
140             this.mRightControlButton = rightControlButton;
141             mHasScrolledToBottom =
142                     savedInstanceState != null
143                             && savedInstanceState.containsKey(STATE_HAS_SCROLLED_TO_BOTTOM)
144                             && savedInstanceState.getBoolean(STATE_HAS_SCROLLED_TO_BOTTOM);
145         }
146 
bind()147         public void bind() {
148             mScrollContainer.setOnScrollChangeListener(this);
149             mRightControlButton.setOnClickListener(this::onMoreOrAcceptClicked);
150             updateControlButtons();
151         }
152 
saveInstanceState(Bundle bundle)153         public void saveInstanceState(Bundle bundle) {
154             if (mHasScrolledToBottom) {
155                 bundle.putBoolean(STATE_HAS_SCROLLED_TO_BOTTOM, true);
156             }
157         }
158 
updateControlButtons()159         private void updateControlButtons() {
160             if (mHasScrolledToBottom) {
161                 mLeftControlButton.setVisibility(View.VISIBLE);
162                 mRightControlButton.setText(R.string.notificationUI_right_control_button_text);
163             } else {
164                 mLeftControlButton.setVisibility(View.INVISIBLE);
165                 mRightControlButton.setText(R.string.notificationUI_more_button_text);
166             }
167         }
168 
onMoreOrAcceptClicked(View view)169         private void onMoreOrAcceptClicked(View view) {
170             Context context = getContext();
171             if (context == null) {
172                 return;
173             }
174 
175             if (mHasScrolledToBottom) {
176 
177                 ConsentNotificationActivity.handleAction(LANDING_PAGE_GOT_IT_CLICKED);
178 
179                 if (FlagsFactory.getFlags().getRecordManualInteractionEnabled()) {
180                     ConsentManager.getInstance()
181                             .recordUserManualInteractionWithConsent(
182                                     ConsentManager.MANUAL_INTERACTIONS_RECORDED);
183 
184                     // acknowledge and dismiss
185                     requireActivity().finish();
186                 }
187             } else {
188                 ConsentNotificationActivity.handleAction(LANDING_PAGE_MORE_BUTTON_CLICKED);
189 
190                 mScrollContainer.smoothScrollTo(
191                         0,
192                         mScrollContainer.getScrollY()
193                                 + (int) (mScrollContainer.getHeight() * SCROLL_MULTIPLIER));
194             }
195         }
196 
197         @Override
onScrollChange( View view, int scrollX, int scrollY, int oldScrollX, int oldScrollY)198         public void onScrollChange(
199                 View view, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
200             ConsentNotificationActivity.handleAction(LANDING_PAGE_SCROLLED);
201             updateButtonsIfHasScrolledToBottom();
202         }
203 
updateButtonsIfHasScrolledToBottom()204         void updateButtonsIfHasScrolledToBottom() {
205             if (!mScrollContainer.canScrollVertically(SCROLL_DIRECTION_DOWN)) {
206                 ConsentNotificationActivity.handleAction(LANDING_PAGE_SCROLLED_TO_BOTTOM);
207                 mHasScrolledToBottom = true;
208                 updateControlButtons();
209             }
210         }
211     }
212 }
213