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.tv.feedbackconsent;
18 
19 import static com.android.tv.feedbackconsent.TvFeedbackConstants.BUGREPORT_CONSENT;
20 import static com.android.tv.feedbackconsent.TvFeedbackConstants.BUGREPORT_REQUESTED;
21 import static com.android.tv.feedbackconsent.TvFeedbackConstants.CANCEL_REQUEST;
22 import static com.android.tv.feedbackconsent.TvFeedbackConstants.CONSENT_RECEIVER;
23 import static com.android.tv.feedbackconsent.TvFeedbackConstants.RESULT_CODE_OK;
24 import static com.android.tv.feedbackconsent.TvFeedbackConstants.SYSTEM_LOGS_CONSENT;
25 import static com.android.tv.feedbackconsent.TvFeedbackConstants.SYSTEM_LOGS_KEY;
26 import static com.android.tv.feedbackconsent.TvFeedbackConstants.SYSTEM_LOGS_REQUESTED;
27 
28 import android.os.Bundle;
29 import android.os.ResultReceiver;
30 import android.util.Log;
31 import android.app.Activity;
32 import android.content.Intent;
33 import android.view.View;
34 import android.widget.Switch;
35 import android.widget.TextView;
36 import android.widget.Toast;
37 
38 import java.time.LocalDateTime;
39 import java.time.ZoneId;
40 import java.time.format.DateTimeFormatter;
41 import java.util.ArrayList;
42 import java.util.Collections;
43 import java.util.List;
44 import java.util.Locale;
45 
46 public class TvFeedbackConsentActivity extends Activity implements
47         TvFeedbackConsentDataCollector.TvFeedbackConsentDataCollectorCallback {
48 
49     private static final String TAG = TvFeedbackConsentActivity.class.getSimpleName();
50 
51     private static final String DATE_PATTERN = "MM/dd/yyyy, hh:mm a";
52     private static ResultReceiver resultReceiver;
53     private boolean systemLogRequested;
54     private boolean bugreportRequested;
55     private boolean sendLogs;
56     private boolean sendBugreport;
57     private boolean cancelRequest;
58 
59     private static final List<String> systemLogsLoadingText = Collections.singletonList(
60             "Loading, please wait.....");
61     TvFeedbackConsentInformationDialog viewLogsDialog;
62     private TvFeedbackConsentDataCollector tvFeedbackDataCollector;
63 
64     @Override
onCreate(Bundle savedInstanceState)65     public void onCreate(Bundle savedInstanceState) {
66         super.onCreate(savedInstanceState);
67         Intent intent = getIntent();
68         systemLogRequested = intent.getBooleanExtra(SYSTEM_LOGS_REQUESTED, false);
69         bugreportRequested = intent.getBooleanExtra(BUGREPORT_REQUESTED, false);
70 
71         if (!systemLogRequested && !bugreportRequested) {
72             Log.e(TAG, "Consent screen requested without requesting any data.");
73             this.onStop();
74         }
75         setContentView(R.layout.tv_feedback_consent);
76         onViewCreated();
77     }
78 
79     @Override
onResume()80     public void onResume() {
81         super.onResume();
82         Intent intent = getIntent();
83         resultReceiver = intent.getParcelableExtra(CONSENT_RECEIVER, ResultReceiver.class);
84         tvFeedbackDataCollector = new TvFeedbackConsentDataCollector(this);
85         tvFeedbackDataCollector.collectSystemLogs(/* numLines= */ 10000);
86     }
87 
onViewCreated()88     private void onViewCreated() {
89         View sendFeedbackButton = requireViewById(R.id.send_feedback_button);
90         sendFeedbackButton.setOnClickListener(this::onSendFeedbackButtonClicked);
91         sendFeedbackButton.requestFocus();
92 
93         View cancelFeedbackButton = requireViewById(R.id.cancel_feedback_button);
94         cancelFeedbackButton.setOnClickListener(this::onCancelFeedbackButtonClicked);
95 
96 
97         if (systemLogRequested) {
98             View systemLogsRow = requireViewById(R.id.system_logs_row);
99             systemLogsRow.setVisibility(View.VISIBLE);
100             View systemLogsSwitch = requireViewById(R.id.system_logs_switch);
101             systemLogsSwitch.setOnFocusChangeListener(
102                     (v, focused) -> systemLogsRow.setSelected(focused));
103             prepareSystemLogsDialog();
104         }
105 
106         if (bugreportRequested) {
107             View bugreportRow = requireViewById(R.id.bugreport_row);
108             bugreportRow.setVisibility(View.VISIBLE);
109 
110             String dateTime = LocalDateTime.now(ZoneId.systemDefault()).format(
111                 DateTimeFormatter.ofPattern(DATE_PATTERN, Locale.US));
112             String formattedBugreportLegalText = getString(
113                 R.string.feedback_bugreport_legal_display_text, dateTime);
114             TextView bugreportLegalTextView = requireViewById(R.id.bugreport_legal_text);
115             bugreportLegalTextView.setText(formattedBugreportLegalText);
116 
117             View bugreportSwitch = requireViewById(R.id.bugreport_switch);
118             bugreportSwitch.setOnFocusChangeListener(
119                 (v, focused) -> bugreportRow.setSelected(focused));
120         }
121     }
122 
prepareSystemLogsDialog()123     private void prepareSystemLogsDialog() {
124         viewLogsDialog = new TvFeedbackConsentInformationDialog(
125                 TvFeedbackConsentActivity.this,
126                 R.style.ViewLogsDialogTheme,
127                 R.layout.view_system_logs_dialog,
128                 systemLogsLoadingText);
129         viewLogsDialog.create();
130 
131         View viewLogsButton = requireViewById(R.id.view_logs_button);
132         viewLogsButton.setOnClickListener((v) -> viewLogsDialog.show());
133         viewLogsButton.setVisibility(View.VISIBLE);
134 
135     }
136 
137     @Override
onSystemLogsReady()138     public void onSystemLogsReady() {
139         if (!systemLogRequested || tvFeedbackDataCollector == null || viewLogsDialog == null) {
140             return;
141         }
142         List<String> systemLogs = tvFeedbackDataCollector.getSystemLogs();
143         viewLogsDialog.updateRecyclerView(systemLogs);
144     }
145 
onSendFeedbackButtonClicked(View view)146     private void onSendFeedbackButtonClicked(View view) {
147         sendLogs = ((Switch) requireViewById(R.id.system_logs_switch)).isChecked();
148         sendBugreport = ((Switch) requireViewById(R.id.bugreport_switch)).isChecked();
149 
150         Toast.makeText(view.getContext(),
151                 view.getContext().getString(R.string.feedback_submitted_notification),
152                 Toast.LENGTH_SHORT)
153             .show();
154         finish();
155     }
156 
onCancelFeedbackButtonClicked(View view)157     private void onCancelFeedbackButtonClicked(View view) {
158         sendLogs = false;
159         sendBugreport = false;
160         cancelRequest = true;
161 
162         Toast.makeText(view.getContext(),
163                 view.getContext().getString(R.string.feedback_cancelled_notification),
164                 Toast.LENGTH_SHORT)
165             .show();
166         finish();
167     }
168 
sendResult()169     private void sendResult() {
170         if (resultReceiver == null) {
171             Log.w(TAG, "Activity intent does not contain a result receiver");
172             return;
173         }
174         Bundle bundle = new Bundle();
175         bundle.putSerializable(SYSTEM_LOGS_CONSENT, sendLogs);
176         bundle.putSerializable(BUGREPORT_CONSENT, sendBugreport);
177         bundle.putSerializable(CANCEL_REQUEST, cancelRequest);
178         if (systemLogRequested && sendLogs) {
179             bundle.putStringArrayList(SYSTEM_LOGS_KEY,
180                     new ArrayList<>(tvFeedbackDataCollector.getSystemLogs()));
181         }
182 
183         try {
184             resultReceiver.send(RESULT_CODE_OK, bundle);
185         } catch (Exception e) {
186             Log.e(TAG, "Exception in sending result: ", e);
187         }
188     }
189 
190     @Override
onStop()191     public void onStop() {
192         sendResult();
193         super.onStop();
194     }
195 }