diff options
| author | 2019-05-23 07:21:36 -0400 | |
|---|---|---|
| committer | 2019-05-28 12:54:25 -0400 | |
| commit | e7cacab797108cd75e8fd16ac59d9d48e6f2d200 (patch) | |
| tree | bbb059dfe8873f9fb7a527019c2412aa60949bbd | |
| parent | 358c2b33f361bfc8350cc8697608bea352c10e9f (diff) | |
Sharesheet - Load images off main thread
To prevent main thread from being blocked and potential ANRs, load
images on AsyncTasks. If no images are successfully loaded within a
timeout period, hide the content preview area. Optimize scrolling by
reusing the views in their entirety and not reloading images. Also
allow for more time for direct share loading
Bug: 132698784
Test: Manual
Change-Id: Ia73e9b6912bf36a1c6c9660423d6f0602e832187
| -rw-r--r-- | core/java/com/android/internal/app/ChooserActivity.java | 230 | ||||
| -rw-r--r-- | core/res/res/layout/chooser_grid_preview_file.xml | 3 | ||||
| -rw-r--r-- | core/res/res/layout/chooser_grid_preview_image.xml | 1 |
3 files changed, 179 insertions, 55 deletions
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index a88c51a28229..fca97fee603e 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -185,7 +185,7 @@ public class ChooserActivity extends ResolverActivity { private static final int SHARE_TARGET_QUERY_PACKAGE_LIMIT = 20; private static final int QUERY_TARGET_SERVICE_LIMIT = 5; - private static final int WATCHDOG_TIMEOUT_MILLIS = 3000; + private static final int WATCHDOG_TIMEOUT_MILLIS = 5000; private static final int DEFAULT_SALT_EXPIRATION_DAYS = 7; private int mMaxHashSaltDays = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI, @@ -252,6 +252,123 @@ public class ChooserActivity extends ResolverActivity { // Sorted list of DisplayResolveInfos for the alphabetical app section. private List<ResolverActivity.DisplayResolveInfo> mSortedList = new ArrayList<>(); + private ContentPreviewCoordinator mPreviewCoord; + + private class ContentPreviewCoordinator { + private static final int IMAGE_LOAD_TIMEOUT_MILLIS = 300; + private static final int IMAGE_FADE_IN_MILLIS = 150; + private static final int IMAGE_LOAD_TIMEOUT = 1; + private static final int IMAGE_LOAD_INTO_VIEW = 2; + + private final View mParentView; + private boolean mHideParentOnFail; + private boolean mAtLeastOneLoaded = false; + + class LoadUriTask { + public final Uri mUri; + public final int mImageResourceId; + public final int mExtraCount; + public final Bitmap mBmp; + + LoadUriTask(int imageResourceId, Uri uri, int extraCount, Bitmap bmp) { + this.mImageResourceId = imageResourceId; + this.mUri = uri; + this.mExtraCount = extraCount; + this.mBmp = bmp; + } + } + + // If at least one image loads within the timeout period, allow other + // loads to continue. Otherwise terminate and optionally hide + // the parent area + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case IMAGE_LOAD_TIMEOUT: + maybeHideContentPreview(); + break; + + case IMAGE_LOAD_INTO_VIEW: + if (isFinishing()) break; + + LoadUriTask task = (LoadUriTask) msg.obj; + RoundedRectImageView imageView = mParentView.findViewById( + task.mImageResourceId); + if (task.mBmp == null) { + imageView.setVisibility(View.GONE); + maybeHideContentPreview(); + return; + } + + mAtLeastOneLoaded = true; + imageView.setVisibility(View.VISIBLE); + imageView.setAlpha(0.0f); + imageView.setImageBitmap(task.mBmp); + + ValueAnimator fadeAnim = ObjectAnimator.ofFloat(imageView, "alpha", 0.0f, + 1.0f); + fadeAnim.setInterpolator(new DecelerateInterpolator(1.0f)); + fadeAnim.setDuration(IMAGE_FADE_IN_MILLIS); + fadeAnim.start(); + + if (task.mExtraCount > 0) { + imageView.setExtraImageCount(task.mExtraCount); + } + } + } + }; + + ContentPreviewCoordinator(View parentView, boolean hideParentOnFail) { + super(); + + this.mParentView = parentView; + this.mHideParentOnFail = hideParentOnFail; + } + + private void loadUriIntoView(final int imageResourceId, final Uri uri, + final int extraImages) { + mHandler.sendEmptyMessageDelayed(IMAGE_LOAD_TIMEOUT, IMAGE_LOAD_TIMEOUT_MILLIS); + + AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> { + final Bitmap bmp = loadThumbnail(uri, new Size(200, 200)); + final Message msg = Message.obtain(); + msg.what = IMAGE_LOAD_INTO_VIEW; + msg.obj = new LoadUriTask(imageResourceId, uri, extraImages, bmp); + mHandler.sendMessage(msg); + }); + } + + private void cancelLoads() { + mHandler.removeMessages(IMAGE_LOAD_INTO_VIEW); + mHandler.removeMessages(IMAGE_LOAD_TIMEOUT); + } + + private void maybeHideContentPreview() { + if (!mAtLeastOneLoaded && mHideParentOnFail) { + Log.i(TAG, "Hiding image preview area. Timed out waiting for preview to load" + + " within " + IMAGE_LOAD_TIMEOUT_MILLIS + "ms."); + collapseParentView(); + if (mChooserRowAdapter != null) { + mChooserRowAdapter.hideContentPreview(); + } + mHideParentOnFail = false; + } + } + + private void collapseParentView() { + // This will effectively hide the content preview row by forcing the height + // to zero. It is faster than forcing a relayout of the listview + final View v = mParentView; + int widthSpec = MeasureSpec.makeMeasureSpec(v.getWidth(), MeasureSpec.EXACTLY); + int heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY); + v.measure(widthSpec, heightSpec); + v.getLayoutParams().height = 0; + v.layout(v.getLeft(), v.getTop(), v.getRight(), v.getTop()); + v.invalidate(); + } + } + private final Handler mChooserHandler = new Handler() { @Override public void handleMessage(Message msg) { @@ -621,14 +738,15 @@ public class ChooserActivity extends ResolverActivity { private ViewGroup displayContentPreview(@ContentPreviewType int previewType, Intent targetIntent, LayoutInflater layoutInflater, ViewGroup convertView, ViewGroup parent) { + if (convertView != null) return convertView; + switch (previewType) { case CONTENT_PREVIEW_TEXT: - return displayTextContentPreview(targetIntent, layoutInflater, convertView, parent); + return displayTextContentPreview(targetIntent, layoutInflater, parent); case CONTENT_PREVIEW_IMAGE: - return displayImageContentPreview(targetIntent, layoutInflater, convertView, - parent); + return displayImageContentPreview(targetIntent, layoutInflater, parent); case CONTENT_PREVIEW_FILE: - return displayFileContentPreview(targetIntent, layoutInflater, convertView, parent); + return displayFileContentPreview(targetIntent, layoutInflater, parent); default: Log.e(TAG, "Unexpected content preview type: " + previewType); } @@ -637,10 +755,9 @@ public class ChooserActivity extends ResolverActivity { } private ViewGroup displayTextContentPreview(Intent targetIntent, LayoutInflater layoutInflater, - ViewGroup convertView, ViewGroup parent) { - ViewGroup contentPreviewLayout = - convertView != null ? convertView : (ViewGroup) layoutInflater.inflate( - R.layout.chooser_grid_preview_text, parent, false); + ViewGroup parent) { + ViewGroup contentPreviewLayout = (ViewGroup) layoutInflater.inflate( + R.layout.chooser_grid_preview_text, parent, false); contentPreviewLayout.findViewById(R.id.copy_button).setOnClickListener( this::onCopyButtonClicked); @@ -677,12 +794,8 @@ public class ChooserActivity extends ResolverActivity { if (previewThumbnail == null) { previewThumbnailView.setVisibility(View.GONE); } else { - Bitmap bmp = loadThumbnail(previewThumbnail, new Size(100, 100)); - if (bmp == null) { - previewThumbnailView.setVisibility(View.GONE); - } else { - previewThumbnailView.setImageBitmap(bmp); - } + mPreviewCoord = new ContentPreviewCoordinator(contentPreviewLayout, false); + mPreviewCoord.loadUriIntoView(R.id.content_preview_thumbnail, previewThumbnail, 0); } } @@ -690,15 +803,15 @@ public class ChooserActivity extends ResolverActivity { } private ViewGroup displayImageContentPreview(Intent targetIntent, LayoutInflater layoutInflater, - ViewGroup convertView, ViewGroup parent) { - ViewGroup contentPreviewLayout = - convertView != null ? convertView : (ViewGroup) layoutInflater.inflate( - R.layout.chooser_grid_preview_image, parent, false); + ViewGroup parent) { + ViewGroup contentPreviewLayout = (ViewGroup) layoutInflater.inflate( + R.layout.chooser_grid_preview_image, parent, false); + mPreviewCoord = new ContentPreviewCoordinator(contentPreviewLayout, true); String action = targetIntent.getAction(); if (Intent.ACTION_SEND.equals(action)) { Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM); - loadUriIntoView(R.id.content_preview_image_1_large, uri, contentPreviewLayout); + mPreviewCoord.loadUriIntoView(R.id.content_preview_image_1_large, uri, 0); } else { ContentResolver resolver = getContentResolver(); @@ -717,21 +830,16 @@ public class ChooserActivity extends ResolverActivity { return contentPreviewLayout; } - loadUriIntoView(R.id.content_preview_image_1_large, imageUris.get(0), - contentPreviewLayout); + mPreviewCoord.loadUriIntoView(R.id.content_preview_image_1_large, imageUris.get(0), 0); if (imageUris.size() == 2) { - loadUriIntoView(R.id.content_preview_image_2_large, imageUris.get(1), - contentPreviewLayout); + mPreviewCoord.loadUriIntoView(R.id.content_preview_image_2_large, + imageUris.get(1), 0); } else if (imageUris.size() > 2) { - loadUriIntoView(R.id.content_preview_image_2_small, imageUris.get(1), - contentPreviewLayout); - RoundedRectImageView imageView = loadUriIntoView( - R.id.content_preview_image_3_small, imageUris.get(2), contentPreviewLayout); - - if (imageUris.size() > 3) { - imageView.setExtraImageCount(imageUris.size() - 3); - } + mPreviewCoord.loadUriIntoView(R.id.content_preview_image_2_small, + imageUris.get(1), 0); + mPreviewCoord.loadUriIntoView(R.id.content_preview_image_3_small, + imageUris.get(2), imageUris.size() - 3); } } @@ -803,11 +911,10 @@ public class ChooserActivity extends ResolverActivity { } private ViewGroup displayFileContentPreview(Intent targetIntent, LayoutInflater layoutInflater, - ViewGroup convertView, ViewGroup parent) { + ViewGroup parent) { - ViewGroup contentPreviewLayout = - convertView != null ? convertView : (ViewGroup) layoutInflater.inflate( - R.layout.chooser_grid_preview_file, parent, false); + ViewGroup contentPreviewLayout = (ViewGroup) layoutInflater.inflate( + R.layout.chooser_grid_preview_file, parent, false); // TODO(b/120417119): Disable file copy until after moving to sysui, // due to permissions issues @@ -839,6 +946,10 @@ public class ChooserActivity extends ResolverActivity { R.id.content_preview_filename); fileNameView.setText(fileName); + View thumbnailView = contentPreviewLayout.findViewById( + R.id.content_preview_file_thumbnail); + thumbnailView.setVisibility(View.GONE); + ImageView fileIconView = contentPreviewLayout.findViewById( R.id.content_preview_file_icon); fileIconView.setVisibility(View.VISIBLE); @@ -849,32 +960,25 @@ public class ChooserActivity extends ResolverActivity { return contentPreviewLayout; } - private void loadFileUriIntoView(Uri uri, View parent) { + private void loadFileUriIntoView(final Uri uri, final View parent) { FileInfo fileInfo = extractFileInfo(uri, getContentResolver()); TextView fileNameView = parent.findViewById(R.id.content_preview_filename); fileNameView.setText(fileInfo.name); if (fileInfo.hasThumbnail) { - loadUriIntoView(R.id.content_preview_file_thumbnail, uri, parent); + mPreviewCoord = new ContentPreviewCoordinator(parent, false); + mPreviewCoord.loadUriIntoView(R.id.content_preview_file_thumbnail, uri, 0); } else { + View thumbnailView = parent.findViewById(R.id.content_preview_file_thumbnail); + thumbnailView.setVisibility(View.GONE); + ImageView fileIconView = parent.findViewById(R.id.content_preview_file_icon); fileIconView.setVisibility(View.VISIBLE); fileIconView.setImageResource(R.drawable.chooser_file_generic); } } - private RoundedRectImageView loadUriIntoView(int imageResourceId, Uri uri, View parent) { - RoundedRectImageView imageView = parent.findViewById(imageResourceId); - Bitmap bmp = loadThumbnail(uri, new Size(200, 200)); - if (bmp != null) { - imageView.setVisibility(View.VISIBLE); - imageView.setImageBitmap(bmp); - } - - return imageView; - } - @VisibleForTesting protected boolean isImageType(String mimeType) { return mimeType != null && mimeType.startsWith("image/"); @@ -944,6 +1048,9 @@ public class ChooserActivity extends ResolverActivity { mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_RESULT); mChooserHandler.removeMessages(SHORTCUT_MANAGER_SHARE_TARGET_RESULT); mChooserHandler.removeMessages(SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED); + + if (mPreviewCoord != null) mPreviewCoord.cancelLoads(); + if (mAppPredictor != null) { mAppPredictor.unregisterPredictionUpdates(mAppPredictorCallback); mAppPredictor.destroy(); @@ -2036,9 +2143,12 @@ public class ChooserActivity extends ResolverActivity { } int availableWidth = right - left - v.getPaddingLeft() - v.getPaddingRight(); - if (mChooserRowAdapter.calculateChooserTargetWidth(availableWidth) + if (mChooserRowAdapter.consumeLayoutRequest() + || mChooserRowAdapter.calculateChooserTargetWidth(availableWidth) || mAdapterView.getAdapter() == null) { - mAdapterView.setAdapter(mChooserRowAdapter); + if (mAdapterView.getAdapter() == null) { + mAdapterView.setAdapter(mChooserRowAdapter); + } getMainThreadHandler().post(() -> { if (mResolverDrawerLayout == null || mChooserRowAdapter == null) { @@ -2589,6 +2699,9 @@ public class ChooserActivity extends ResolverActivity { private int mChooserTargetWidth = 0; private boolean mShowAzLabelIfPoss; + private boolean mHideContentPreview = false; + private boolean mLayoutRequested = false; + private static final int VIEW_TYPE_DIRECT_SHARE = 0; private static final int VIEW_TYPE_NORMAL = 1; private static final int VIEW_TYPE_CONTENT_PREVIEW = 2; @@ -2651,6 +2764,18 @@ public class ChooserActivity extends ResolverActivity { return maxTargets; } + public void hideContentPreview() { + mHideContentPreview = true; + mLayoutRequested = true; + notifyDataSetChanged(); + } + + public boolean consumeLayoutRequest() { + boolean oldValue = mLayoutRequested; + mLayoutRequested = false; + return oldValue; + } + @Override public boolean areAllItemsEnabled() { return false; @@ -2684,7 +2809,8 @@ public class ChooserActivity extends ResolverActivity { return 0; } - if (mChooserListAdapter == null || mChooserListAdapter.getCount() == 0) { + if (mHideContentPreview || mChooserListAdapter == null + || mChooserListAdapter.getCount() == 0) { return 0; } diff --git a/core/res/res/layout/chooser_grid_preview_file.xml b/core/res/res/layout/chooser_grid_preview_file.xml index 27c6041ba257..f7d60c91052d 100644 --- a/core/res/res/layout/chooser_grid_preview_file.xml +++ b/core/res/res/layout/chooser_grid_preview_file.xml @@ -44,8 +44,7 @@ android:adjustViewBounds="true" android:layout_gravity="center_vertical" android:gravity="center" - android:scaleType="centerCrop" - android:visibility="gone"/> + android:scaleType="centerCrop"/> <ImageView android:id="@+id/content_preview_file_icon" android:layout_width="36dp" diff --git a/core/res/res/layout/chooser_grid_preview_image.xml b/core/res/res/layout/chooser_grid_preview_image.xml index ad31e0d0c194..79a0de4b271f 100644 --- a/core/res/res/layout/chooser_grid_preview_image.xml +++ b/core/res/res/layout/chooser_grid_preview_image.xml @@ -33,7 +33,6 @@ <view class="com.android.internal.app.ChooserActivity$RoundedRectImageView" android:id="@+id/content_preview_image_1_large" - android:visibility="gone" android:layout_width="120dp" android:layout_height="140dp" android:layout_alignParentTop="true" |