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