diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 34e03af4c6663..c235c82de7201 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -218,6 +218,7 @@ public class ChooserActivity extends ResolverActivity implements public static final int SELECTION_TYPE_STANDARD = 3; public static final int SELECTION_TYPE_COPY = 4; public static final int SELECTION_TYPE_NEARBY = 5; + public static final int SELECTION_TYPE_EDIT = 6; private static final int SCROLL_STATUS_IDLE = 0; private static final int SCROLL_STATUS_SCROLLING_VERTICAL = 1; @@ -1196,6 +1197,37 @@ public class ChooserActivity extends ResolverActivity implements return ComponentName.unflattenFromString(nearbyComponent); } + @VisibleForTesting + protected @Nullable ComponentName getEditSharingComponent() { + String editorPackage = getApplicationContext().getString(R.string.config_systemImageEditor); + if (editorPackage == null || TextUtils.isEmpty(editorPackage)) { + return null; + } + return ComponentName.unflattenFromString(editorPackage); + } + + @VisibleForTesting + protected TargetInfo getEditSharingTarget(Intent originalIntent) { + final ComponentName cn = getEditSharingComponent(); + + final Intent resolveIntent = new Intent(originalIntent); + resolveIntent.setComponent(cn); + resolveIntent.setAction(Intent.ACTION_EDIT); + final ResolveInfo ri = getPackageManager().resolveActivity( + resolveIntent, PackageManager.GET_META_DATA); + if (ri == null || ri.activityInfo == null) { + Log.e(TAG, "Device-specified image edit component (" + cn + + ") not available"); + return null; + } + + final DisplayResolveInfo dri = new DisplayResolveInfo( + originalIntent, ri, getString(R.string.screenshot_edit), "", resolveIntent, null); + dri.setDisplayIcon(getDrawable(R.drawable.ic_menu_edit)); + return dri; + } + + @VisibleForTesting protected TargetInfo getNearbySharingTarget(Intent originalIntent) { final ComponentName cn = getNearbySharingComponent(); @@ -1282,6 +1314,27 @@ public class ChooserActivity extends ResolverActivity implements return b; } + private @Nullable Button createEditButton(Intent originalIntent) { + final TargetInfo ti = getEditSharingTarget(originalIntent); + if (ti == null) return null; + + final Button b = createActionButton( + ti.getDisplayIcon(this), + ti.getDisplayLabel(), + (View unused) -> { + // Log share completion via edit + getChooserActivityLogger().logShareTargetSelected( + SELECTION_TYPE_EDIT, + "", + -1); + safelyStartActivity(ti); + finish(); + } + ); + b.setId(R.id.chooser_edit_button); + return b; + } + private void addActionButton(ViewGroup parent, Button b) { if (b == null) return; final ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams( @@ -1378,6 +1431,7 @@ public class ChooserActivity extends ResolverActivity implements (ViewGroup) contentPreviewLayout.findViewById(R.id.chooser_action_row); //TODO: addActionButton(actionRow, createCopyButton()); addActionButton(actionRow, createNearbyButton(targetIntent)); + addActionButton(actionRow, createEditButton(targetIntent)); mPreviewCoord = new ContentPreviewCoordinator(contentPreviewLayout, false); diff --git a/core/java/com/android/internal/app/ChooserActivityLogger.java b/core/java/com/android/internal/app/ChooserActivityLogger.java index 426859e1d5278..c0cc483648fa9 100644 --- a/core/java/com/android/internal/app/ChooserActivityLogger.java +++ b/core/java/com/android/internal/app/ChooserActivityLogger.java @@ -118,7 +118,9 @@ public interface ChooserActivityLogger { @UiEvent(doc = "User selected the copy target.") SHARESHEET_COPY_TARGET_SELECTED(235), @UiEvent(doc = "User selected the nearby target.") - SHARESHEET_NEARBY_TARGET_SELECTED(626); + SHARESHEET_NEARBY_TARGET_SELECTED(626), + @UiEvent(doc = "User selected the edit target.") + SHARESHEET_EDIT_TARGET_SELECTED(627); private final int mId; SharesheetTargetSelectedEvent(int id) { @@ -140,6 +142,8 @@ public interface ChooserActivityLogger { return SHARESHEET_COPY_TARGET_SELECTED; case ChooserActivity.SELECTION_TYPE_NEARBY: return SHARESHEET_NEARBY_TARGET_SELECTED; + case ChooserActivity.SELECTION_TYPE_EDIT: + return SHARESHEET_EDIT_TARGET_SELECTED; default: return INVALID; } diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index d8da60019130d..1845faacc891e 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4605,4 +4605,9 @@ happens that doesn't result in bringing the home task to the front. This is currently only used on TV. --> false + + + diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml index a12d2a9514609..a4c7293e48b6b 100644 --- a/core/res/res/values/ids.xml +++ b/core/res/res/values/ids.xml @@ -206,6 +206,9 @@ + + + diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 236f7cb45a814..c72a0cd63b36d 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2045,6 +2045,7 @@ + @@ -3895,6 +3896,7 @@ + diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java index 787879a6a144c..d9012f649dd36 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java @@ -638,6 +638,59 @@ public class ChooserActivityTest { } + + @Test + public void testEditImageLogs() throws Exception { + Intent sendIntent = createSendImageIntent( + Uri.parse("android.resource://com.android.frameworks.coretests/" + + com.android.frameworks.coretests.R.drawable.test320x240)); + + sOverrides.previewThumbnail = createBitmap(); + sOverrides.isImageType = true; + + List resolvedComponentInfos = createResolvedComponentsForTest(2); + + when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent( + Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); + + final ChooserWrapperActivity activity = mActivityRule + .launchActivity(Intent.createChooser(sendIntent, null)); + waitForIdle(); + + onView(withId(R.id.chooser_edit_button)).check(matches(isDisplayed())); + onView(withId(R.id.chooser_edit_button)).perform(click()); + + ChooserActivityLoggerFake logger = + (ChooserActivityLoggerFake) activity.getChooserActivityLogger(); + // first one should be SHARESHEET_TRIGGERED uievent + assertThat(logger.get(0).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED)); + assertThat(logger.get(0).event.getId(), + is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_TRIGGERED.getId())); + // second one should be SHARESHEET_STARTED event + assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED)); + assertThat(logger.get(1).intent, is(Intent.ACTION_SEND)); + assertThat(logger.get(1).mimeType, is("image/png")); + assertThat(logger.get(1).packageName, is("com.android.frameworks.coretests")); + assertThat(logger.get(1).appProvidedApp, is(0)); + assertThat(logger.get(1).appProvidedDirect, is(0)); + assertThat(logger.get(1).isWorkprofile, is(false)); + assertThat(logger.get(1).previewType, is(1)); + // third one should be SHARESHEET_APP_LOAD_COMPLETE uievent + assertThat(logger.get(2).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED)); + assertThat(logger.get(2).event.getId(), + is(ChooserActivityLogger + .SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE.getId())); + // fourth and fifth are just artifacts of test set-up + // sixth one should be ranking atom with SHARESHEET_EDIT_TARGET_SELECTED event + assertThat(logger.get(5).atomId, is(FrameworkStatsLog.RANKING_SELECTED)); + assertThat(logger.get(5).targetType, + is(ChooserActivityLogger + .SharesheetTargetSelectedEvent.SHARESHEET_EDIT_TARGET_SELECTED.getId())); + } + + @Test public void oneVisibleImagePreview() throws InterruptedException { Uri uri = Uri.parse("android.resource://com.android.frameworks.coretests/" @@ -2202,6 +2255,19 @@ public class ChooserActivityTest { return sendIntent; } + private Intent createSendImageIntent(Uri imageThumbnail) { + Intent sendIntent = new Intent(); + sendIntent.setAction(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_STREAM, imageThumbnail); + sendIntent.setType("image/png"); + if (imageThumbnail != null) { + ClipData.Item clipItem = new ClipData.Item(imageThumbnail); + sendIntent.setClipData(new ClipData("Clip Label", new String[]{"image/png"}, clipItem)); + } + + return sendIntent; + } + private Intent createSendTextIntentWithPreview(String title, Uri imageThumbnail) { Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND);