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 }