1 /*
2  * Copyright (C) 2019 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.car.bugreport;
17 
18 import android.view.LayoutInflater;
19 import android.view.View;
20 import android.view.ViewGroup;
21 import android.widget.Button;
22 import android.widget.TextView;
23 
24 import androidx.recyclerview.widget.RecyclerView;
25 
26 import java.util.ArrayList;
27 import java.util.List;
28 
29 /**
30  * Shows bugreport title, status, status message and user action buttons. "Upload to Google" button
31  * is enabled when the status is {@link Status#STATUS_PENDING_USER_ACTION}, "Move to USB" button is
32  * enabled only when status is  {@link Status#STATUS_PENDING_USER_ACTION} and USB device is plugged
33  * in.
34  */
35 public class BugInfoAdapter extends RecyclerView.Adapter<BugInfoAdapter.BugInfoViewHolder> {
36     static final int BUTTON_TYPE_UPLOAD = 0;
37     static final int BUTTON_TYPE_MOVE = 1;
38     static final int BUTTON_TYPE_ADD_AUDIO = 2;
39 
40     /** If bugreport TTL points go below this number, show a notice message. */
41     private static final int MIN_TTL_POINTS_TO_SHOW_NOTICE = 10;
42 
43     /** Provides a handler for click events. */
44     interface ItemClickedListener {
45         /**
46          * Handles click events differently depending on provided buttonType and
47          * uses additional information provided in metaBugReport.
48          *
49          * @param buttonType    One of {@link #BUTTON_TYPE_UPLOAD}, {@link #BUTTON_TYPE_MOVE} or
50          *                      {@link #BUTTON_TYPE_ADD_AUDIO}.
51          * @param metaBugReport Selected bugreport.
52          * @param holder        ViewHolder of the clicked item.
53          */
onItemClicked(int buttonType, MetaBugReport metaBugReport, BugInfoViewHolder holder)54         void onItemClicked(int buttonType, MetaBugReport metaBugReport, BugInfoViewHolder holder);
55     }
56 
57     /**
58      * Reference to each bug report info views.
59      */
60     static class BugInfoViewHolder extends RecyclerView.ViewHolder {
61         /** Title view */
62         TextView mTitleView;
63 
64         /** Status View */
65         TextView mStatusView;
66 
67         /** Message View */
68         TextView mMessageView;
69 
70         /** Expiration Notice View */
71         TextView mExpirationNoticeView;
72 
73         /** Move Button */
74         Button mMoveButton;
75 
76         /** Upload Button */
77         Button mUploadButton;
78 
79         /** Add Audio Button */
80         Button mAddAudioButton;
81 
BugInfoViewHolder(View v)82         BugInfoViewHolder(View v) {
83             super(v);
84             mTitleView = itemView.findViewById(R.id.bug_info_row_title);
85             mStatusView = itemView.findViewById(R.id.bug_info_row_status);
86             mMessageView = itemView.findViewById(R.id.bug_info_row_status_message);
87             mExpirationNoticeView = itemView.findViewById(R.id.bug_info_row_expiration_notice);
88             mMoveButton = itemView.findViewById(R.id.bug_info_move_button);
89             mUploadButton = itemView.findViewById(R.id.bug_info_upload_button);
90             mAddAudioButton = itemView.findViewById(R.id.bug_info_add_audio_button);
91         }
92     }
93 
94     private List<MetaBugReport> mDataset;
95     private final ItemClickedListener mItemClickedListener;
96     private final Config mConfig;
97 
BugInfoAdapter(ItemClickedListener itemClickedListener, Config config)98     BugInfoAdapter(ItemClickedListener itemClickedListener, Config config) {
99         mItemClickedListener = itemClickedListener;
100         mDataset = new ArrayList<>();
101         mConfig = config;
102         // Allow RecyclerView to efficiently update UI; getItemId() is implemented below.
103         setHasStableIds(true);
104     }
105 
106     @Override
onCreateViewHolder(ViewGroup parent, int viewType)107     public BugInfoViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
108         // create a new view
109         View v = LayoutInflater.from(parent.getContext())
110                 .inflate(R.layout.bug_info_view, parent, false);
111         return new BugInfoViewHolder(v);
112     }
113 
114     @Override
onBindViewHolder(BugInfoViewHolder holder, int position)115     public void onBindViewHolder(BugInfoViewHolder holder, int position) {
116         MetaBugReport bugReport = mDataset.get(position);
117         holder.mTitleView.setText(bugReport.getTitle());
118         holder.mStatusView.setText(Status.toString(bugReport.getStatus()));
119 
120         showOrHideMessageView(bugReport, holder);
121         showOrHideExpirationNotice(bugReport, holder);
122         showOrHideUploadButton(bugReport, holder);
123         showOrHideMoveButton(bugReport, holder);
124         showOrHideAddAudioButton(bugReport, holder);
125     }
126 
pendingUserAction(MetaBugReport bugReport)127     private boolean pendingUserAction(MetaBugReport bugReport) {
128         int bugReportStatus = bugReport.getStatus();
129         return bugReportStatus == Status.STATUS_PENDING_USER_ACTION.getValue()
130                 || bugReportStatus == Status.STATUS_MOVE_FAILED.getValue()
131                 || bugReportStatus == Status.STATUS_UPLOAD_FAILED.getValue();
132     }
133 
pendingAudioRecording(MetaBugReport bugReport)134     private boolean pendingAudioRecording(MetaBugReport bugReport) {
135         return bugReport.getStatus() == Status.STATUS_AUDIO_PENDING.getValue();
136     }
137 
showOrHideMessageView(MetaBugReport bugReport, BugInfoViewHolder holder)138     private void showOrHideMessageView(MetaBugReport bugReport, BugInfoViewHolder holder) {
139         holder.mMessageView.setText(bugReport.getStatusMessage());
140         if (bugReport.getStatusMessage().isEmpty()) {
141             holder.mMessageView.setVisibility(View.GONE);
142         } else {
143             holder.mMessageView.setVisibility(View.VISIBLE);
144         }
145     }
146 
showOrHideExpirationNotice(MetaBugReport bugReport, BugInfoViewHolder holder)147     private void showOrHideExpirationNotice(MetaBugReport bugReport, BugInfoViewHolder holder) {
148         if (bugReport.getTtlPoints() <= MIN_TTL_POINTS_TO_SHOW_NOTICE
149                 && BugStorageUtils.canBugReportBeExpired(bugReport.getStatus())) {
150             holder.mExpirationNoticeView.setVisibility(View.VISIBLE);
151         } else {
152             holder.mExpirationNoticeView.setVisibility(View.GONE);
153         }
154     }
155 
showOrHideUploadButton(MetaBugReport bugReport, BugInfoViewHolder holder)156     private void showOrHideUploadButton(MetaBugReport bugReport, BugInfoViewHolder holder) {
157         // Enable the upload button only for userdebug/eng builds.
158         if (pendingUserAction(bugReport) && Config.isDebuggable()) {
159             holder.mUploadButton.setText(R.string.bugreport_upload_gcs_button_text);
160             holder.mUploadButton.setEnabled(true);
161             holder.mUploadButton.setVisibility(View.VISIBLE);
162             holder.mUploadButton.setOnClickListener(
163                     view -> mItemClickedListener.onItemClicked(BUTTON_TYPE_UPLOAD, bugReport,
164                             holder));
165         } else {
166             holder.mUploadButton.setVisibility(View.GONE);
167             holder.mUploadButton.setEnabled(false);
168         }
169     }
170 
showOrHideMoveButton(MetaBugReport bugReport, BugInfoViewHolder holder)171     private void showOrHideMoveButton(MetaBugReport bugReport, BugInfoViewHolder holder) {
172         if (pendingUserAction(bugReport)) {
173             holder.mMoveButton.setEnabled(true);
174             holder.mMoveButton.setVisibility(View.VISIBLE);
175             holder.mMoveButton.setOnClickListener(
176                     view -> mItemClickedListener.onItemClicked(BUTTON_TYPE_MOVE, bugReport,
177                             holder));
178         } else {
179             holder.mMoveButton.setEnabled(false);
180             holder.mMoveButton.setVisibility(View.GONE);
181         }
182     }
183 
showOrHideAddAudioButton(MetaBugReport bugReport, BugInfoViewHolder holder)184     private void showOrHideAddAudioButton(MetaBugReport bugReport, BugInfoViewHolder holder) {
185         boolean showButton = true;
186 
187         if (pendingAudioRecording(bugReport)) {
188             if (mConfig.isAutoUpload()) {
189                 holder.mAddAudioButton.setText(
190                         R.string.bugreport_add_audio_upload_button_text);
191             } else {
192                 holder.mAddAudioButton.setText(R.string.bugreport_add_audio_button_text);
193             }
194         } else if (pendingUserAction(bugReport)) {
195             holder.mAddAudioButton.setText(R.string.bugreport_replace_audio_button_text);
196         } else {
197             showButton = false;
198         }
199 
200         if (showButton) {
201             holder.mAddAudioButton.setEnabled(true);
202             holder.mAddAudioButton.setVisibility(View.VISIBLE);
203             holder.mAddAudioButton.setOnClickListener(view ->
204                     mItemClickedListener.onItemClicked(BUTTON_TYPE_ADD_AUDIO, bugReport, holder));
205         } else {
206             holder.mAddAudioButton.setEnabled(false);
207             holder.mAddAudioButton.setVisibility(View.GONE);
208         }
209     }
210 
211     /** Sets dataSet; it copies the list, because it modifies it in this adapter. */
setDataset(List<MetaBugReport> bugReports)212     void setDataset(List<MetaBugReport> bugReports) {
213         mDataset = new ArrayList<>(bugReports);
214         notifyDataSetChanged();
215     }
216 
217     /** Update a bug report in the data set. */
updateBugReportInDataSet(MetaBugReport bugReport, int position)218     void updateBugReportInDataSet(MetaBugReport bugReport, int position) {
219         if (position != RecyclerView.NO_POSITION) {
220             mDataset.set(position, bugReport);
221             notifyItemChanged(position);
222         }
223     }
224 
225     @Override
getItemId(int position)226     public long getItemId(int position) {
227         return mDataset.get(position).getId();
228     }
229 
230     @Override
getItemCount()231     public int getItemCount() {
232         return mDataset.size();
233     }
234 }
235