blob: cd9b9a5bac818a489c666130f5889b16f2caac48 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.permissioncontroller.incident;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface.OnDismissListener;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.IncidentManager;
import android.provider.Settings;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.BulletSpan;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.permissioncontroller.R;
import java.util.ArrayList;
/**
* Confirmation dialog for approving an incident or bug report for sharing off the device.
*/
public class ConfirmationActivity extends Activity implements OnClickListener, OnDismissListener {
private static final String TAG = "ConfirmationActivity";
/**
* Currently displaying activity.
*/
private static ConfirmationActivity sCurrentActivity;
/**
* Currently displaying uri.
*/
private static Uri sCurrentUri;
/**
* If this activity is running in the current process, call finish() on it.
*/
public static void finishCurrent() {
if (sCurrentActivity != null) {
sCurrentActivity.finish();
}
}
/**
* If the activity is in the resumed state, then record the Uri for the current
* one, so PendingList can skip re-showing the same one.
*/
public static Uri getCurrentUri() {
return sCurrentUri;
}
/**
* Create the activity.
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Formatting formatting = new Formatting(this);
final Uri uri = getIntent().getData();
Log.d(TAG, "uri=" + uri);
if (uri == null) {
Log.w(TAG, "No uri in intent: " + getIntent());
finish();
return;
}
final IncidentManager.PendingReport pending = new IncidentManager.PendingReport(uri);
final String appLabel = formatting.getAppLabel(pending.getRequestingPackage());
final Resources res = getResources();
ReportDetails details;
try {
details = ReportDetails.parseIncidentReport(this, uri);
} catch (ReportDetails.ParseException ex) {
Log.w("Rejecting report because it couldn't be parsed", ex);
// If there was an error in the input we will just summarily reject the upload,
// since we can't get proper approval. (Zero-length images or reasons means that
// we will proceed with the imageless consent dialog).
final IncidentManager incidentManager = getSystemService(IncidentManager.class);
incidentManager.denyReport(getIntent().getData());
// Show a message to the user saying... nevermind.
new AlertDialog.Builder(this)
.setTitle(R.string.incident_report_dialog_title)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
})
.setMessage(getString(R.string.incident_report_error_dialog_text, appLabel))
.setOnDismissListener(this)
.show();
return;
}
final View content = getLayoutInflater().inflate(R.layout.incident_confirmation,
null);
final ArrayList<String> reasons = details.getReasons();
final int reasonsSize = reasons.size();
if (reasonsSize > 0) {
content.findViewById(R.id.reasonIntro).setVisibility(View.VISIBLE);
final TextView reasonTextView = (TextView) content.findViewById(R.id.reasons);
reasonTextView.setVisibility(View.VISIBLE);
final int bulletSize =
(int) (res.getDimension(R.dimen.incident_reason_bullet_size) + 0.5f);
final int bulletIndent =
(int) (res.getDimension(R.dimen.incident_reason_bullet_indent) + 0.5f);
final int bulletColor =
getColor(R.color.incident_reason_bullet_color);
final StringBuilder text = new StringBuilder();
for (int i = 0; i < reasonsSize; i++) {
text.append(reasons.get(i));
if (i != reasonsSize - 1) {
text.append("\n");
}
}
final SpannableString spannable = new SpannableString(text.toString());
int spanStart = 0;
for (int i = 0; i < reasonsSize; i++) {
final int length = reasons.get(i).length();
spannable.setSpan(new BulletSpan(bulletIndent, bulletColor, bulletSize),
spanStart, spanStart + length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
spanStart += length + 1;
}
reasonTextView.setText(spannable);
}
final String message = getString(R.string.incident_report_dialog_text,
appLabel,
formatting.getDate(pending.getTimestamp()),
formatting.getTime(pending.getTimestamp()),
appLabel);
((TextView) content.findViewById(R.id.message)).setText(message);
final ArrayList<Drawable> images = details.getImages();
final int imagesSize = images.size();
if (imagesSize > 0) {
content.findViewById(R.id.imageScrollView).setVisibility(View.VISIBLE);
final LinearLayout imageList = (LinearLayout) content.findViewById(R.id.imageList);
final int width = res.getDimensionPixelSize(R.dimen.incident_image_width);
final int height = res.getDimensionPixelSize(R.dimen.incident_image_height);
for (int i = 0; i < imagesSize; i++) {
final Drawable drawable = images.get(i);
final ImageView imageView = new ImageView(this);
imageView.setImageDrawable(images.get(i));
imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
imageList.addView(imageView, new LinearLayout.LayoutParams(width, height));
}
}
final AlertDialog dialog = new AlertDialog.Builder(this)
.setTitle(R.string.incident_report_dialog_title)
.setPositiveButton(R.string.incident_report_dialog_allow_label, this)
.setNegativeButton(R.string.incident_report_dialog_deny_label, this)
.setOnDismissListener(this)
.setView(content)
.create();
if (Settings.canDrawOverlays(this)) {
final Window w = dialog.getWindow();
w.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
}
dialog.show();
}
/**
* Activity lifecycle callback. Now visible.
*/
@Override
protected void onStart() {
super.onStart();
sCurrentActivity = this;
sCurrentUri = getIntent().getData();
}
/**
* Activity lifecycle callback. Now not visible.
*/
@Override
protected void onStop() {
super.onStop();
sCurrentActivity = null;
sCurrentUri = null;
}
/**
* Dialog canceled.
*/
@Override
public void onDismiss(DialogInterface dialog) {
finish();
}
/**
* Explicit button click.
*/
@Override
public void onClick(DialogInterface dialog, int which) {
final IncidentManager incidentManager = getSystemService(IncidentManager.class);
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
incidentManager.approveReport(getIntent().getData());
PendingList.getInstance().updateState(this, 0);
break;
case DialogInterface.BUTTON_NEGATIVE:
incidentManager.denyReport(getIntent().getData());
PendingList.getInstance().updateState(this, 0);
break;
}
finish();
}
}