Dismiss the dialog in onDestroy method in PermissionActivity

- Dismiss the dialogs to avoid the window is leaked
  The leaked window may cause "Software rendering doesn't
  support hardware bitmaps" issue
- Cancel the AsyncTask of the positive action in onDestroy
- Move the handling imageview visibility to onCreate to avoid
  updating the component in background thread
- Set cancelable false to the progress dialog
- Add some null checks and empty checks

Bug: 173972399
Test: manual test with MediaProviderDialogsTool
      Switch to dark theme
Change-Id: I89d5ad21c81f999abf77397ec65b45dc8be717e0
Merged-In: I89d5ad21c81f999abf77397ec65b45dc8be717e0
diff --git a/src/com/android/providers/media/PermissionActivity.java b/src/com/android/providers/media/PermissionActivity.java
index 0d721f9..95b5561 100644
--- a/src/com/android/providers/media/PermissionActivity.java
+++ b/src/com/android/providers/media/PermissionActivity.java
@@ -101,6 +101,8 @@
     private String volumeName;
     private ApplicationInfo appInfo;
 
+    private AlertDialog actionDialog;
+    private AsyncTask<Void, Void, Void> positiveActionTask;
     private ProgressDialog progressDialog;
     private TextView titleView;
 
@@ -124,6 +126,8 @@
     private static final int ORDER_AUDIO = 3;
     private static final int ORDER_GENERIC = 4;
 
+    private static final int MAX_THUMBS = 3;
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -151,6 +155,7 @@
         }
 
         progressDialog = new ProgressDialog(this);
+        progressDialog.setCancelable(false);
 
         // Favorite-related requests are automatically granted for now; we still
         // make developers go through this no-op dialog flow to preserve our
@@ -162,9 +167,9 @@
                 return;
             }
         }
-
         // Kick off async loading of description to show in dialog
         final View bodyView = getLayoutInflater().inflate(R.layout.permission_body, null, false);
+        handleImageViewVisibility(bodyView, uris);
         new DescriptionTask(bodyView).execute(uris);
 
         final CharSequence message = resolveMessageText();
@@ -181,16 +186,38 @@
         builder.setCancelable(false);
         builder.setView(bodyView);
 
-        final AlertDialog dialog = builder.show();
-        final WindowManager.LayoutParams params = dialog.getWindow().getAttributes();
+        actionDialog = builder.show();
+        final WindowManager.LayoutParams params = actionDialog.getWindow().getAttributes();
         params.width = getResources().getDimensionPixelSize(R.dimen.permission_dialog_width);
-        dialog.getWindow().setAttributes(params);
+        actionDialog.getWindow().setAttributes(params);
 
         // Hunt around to find the title of our newly created dialog so we can
         // adjust accessibility focus once descriptions have been loaded
-        titleView = (TextView) findViewByPredicate(dialog.getWindow().getDecorView(), (view) -> {
-            return (view instanceof TextView) && view.isImportantForAccessibility();
-        });
+        titleView = (TextView) findViewByPredicate(actionDialog.getWindow().getDecorView(),
+                (view) -> {
+                    return (view instanceof TextView) && view.isImportantForAccessibility();
+                });
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        // Cancel and interrupt the AsyncTask of the positive action. This avoids
+        // calling the old activity during "onPostExecute", but the AsyncTask could
+        // still finish its background task. For now we are ok with:
+        // 1. the task potentially runs again after the configuration is changed
+        // 2. the task completed successfully, but the activity doesn't return
+        // the response.
+        if (positiveActionTask != null) {
+            positiveActionTask.cancel(true /* mayInterruptIfRunning */);
+        }
+        // Dismiss the dialogs to avoid the window is leaked
+        if (actionDialog != null) {
+            actionDialog.dismiss();
+        }
+        if (progressDialog != null) {
+            progressDialog.dismiss();
+        }
     }
 
     private void onPositiveAction(@Nullable DialogInterface dialog, int which) {
@@ -202,7 +229,7 @@
 
         progressDialog.show();
         final long startTime = System.currentTimeMillis();
-        new AsyncTask<Void, Void, Void>() {
+        positiveActionTask = new AsyncTask<Void, Void, Void>() {
             @Override
             protected Void doInBackground(Void... params) {
                 Log.d(TAG, "User allowed grant for " + uris);
@@ -245,6 +272,7 @@
                 } catch (Exception e) {
                     Log.w(TAG, e);
                 }
+
                 return null;
             }
 
@@ -297,6 +325,36 @@
         return keyCode == KeyEvent.KEYCODE_BACK;
     }
 
+    private void handleImageViewVisibility(View bodyView, List<Uri> uris) {
+        if (uris.isEmpty()) {
+            return;
+        }
+        if (uris.size() == 1) {
+            // Set visible to the thumb_full to avoid the size
+            // changed of the dialog in full decoding.
+            final ImageView thumbFull = bodyView.requireViewById(R.id.thumb_full);
+            thumbFull.setVisibility(View.VISIBLE);
+        } else {
+            // If the size equals 2, we will remove thumb1 later.
+            // Set visible to the thumb2 and thumb3 first to avoid
+            // the size changed of the dialog.
+            ImageView thumb = bodyView.requireViewById(R.id.thumb2);
+            thumb.setVisibility(View.VISIBLE);
+            thumb = bodyView.requireViewById(R.id.thumb3);
+            thumb.setVisibility(View.VISIBLE);
+            // If the count of thumbs equals to MAX_THUMBS, set visible to thumb1.
+            if (uris.size() == MAX_THUMBS) {
+                thumb = bodyView.requireViewById(R.id.thumb1);
+                thumb.setVisibility(View.VISIBLE);
+            } else if (uris.size() > MAX_THUMBS) {
+                // If the count is larger than MAX_THUMBS, set visible to
+                // thumb_more_container.
+                final View container = bodyView.requireViewById(R.id.thumb_more_container);
+                container.setVisibility(View.VISIBLE);
+            }
+        }
+    }
+
     /**
      * Resolve a label that represents the app denoted by given {@link ApplicationInfo}.
      */
@@ -444,8 +502,6 @@
      * displayed in the body of the dialog.
      */
     private class DescriptionTask extends AsyncTask<List<Uri>, Void, List<Description>> {
-        private static final int MAX_THUMBS = 3;
-
         private View bodyView;
         private Resources res;
 
@@ -470,29 +526,7 @@
 
             // If we're only asking for single item, load the full image
             if (uris.size() == 1) {
-                // Set visible to the thumb_full to avoid the size
-                // changed of the dialog in full decoding.
-                final ImageView thumbFull = bodyView.requireViewById(R.id.thumb_full);
-                thumbFull.setVisibility(View.VISIBLE);
                 loadFlags |= Description.LOAD_FULL;
-            } else {
-                // If the size equals 2, we will remove thumb1 later.
-                // Set visible to the thumb2 and thumb3 first to avoid
-                // the size changed of the dialog.
-                ImageView thumb = bodyView.requireViewById(R.id.thumb2);
-                thumb.setVisibility(View.VISIBLE);
-                thumb = bodyView.requireViewById(R.id.thumb3);
-                thumb.setVisibility(View.VISIBLE);
-                // If the count of thumbs equals to MAX_THUMBS, set visible to thumb1.
-                if (uris.size() == MAX_THUMBS) {
-                    thumb = bodyView.requireViewById(R.id.thumb1);
-                    thumb.setVisibility(View.VISIBLE);
-                } else if (uris.size() > MAX_THUMBS) {
-                    // If the count is larger than MAX_THUMBS, set visible to
-                    // thumb_more_container.
-                    final View container = bodyView.requireViewById(R.id.thumb_more_container);
-                    container.setVisibility(View.VISIBLE);
-                }
             }
 
             // Sort the uris in DATA_GENERIC case (Image, Video, Audio, Others)
@@ -631,6 +665,9 @@
         private void bindAsText(@NonNull List<Description> results) {
             final List<CharSequence> list = new ArrayList<>();
             for (int i = 0; i < results.size(); i++) {
+                if (TextUtils.isEmpty(results.get(i).contentDescription)) {
+                    continue;
+                }
                 list.add(results.get(i).contentDescription);
 
                 if (list.size() >= MAX_THUMBS && results.size() > list.size()) {
@@ -641,10 +678,11 @@
                     break;
                 }
             }
-
-            final TextView text = bodyView.requireViewById(R.id.list);
-            text.setText(TextUtils.join("\n", list));
-            text.setVisibility(View.VISIBLE);
+            if (!list.isEmpty()) {
+                final TextView text = bodyView.requireViewById(R.id.list);
+                text.setText(TextUtils.join("\n", list));
+                text.setVisibility(View.VISIBLE);
+            }
         }
     }
 
@@ -696,7 +734,9 @@
                 Log.w(TAG, e);
                 if (thumbnail == null && full == null) {
                     final String mimeType = resolver.getType(uri);
-                    mimeIcon = resolver.getTypeInfo(mimeType).getIcon();
+                    if (mimeType != null) {
+                        mimeIcon = resolver.getTypeInfo(mimeType).getIcon();
+                    }
                 }
             }
         }