summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java93
-rw-r--r--core/res/res/drawable/chooser_content_preview_rounded.xml31
-rw-r--r--core/res/res/drawable/ic_content_copy_gm2.xml25
-rw-r--r--core/res/res/layout/chooser_grid.xml157
-rw-r--r--core/res/res/values/dimens.xml7
-rw-r--r--core/res/res/values/strings.xml3
-rw-r--r--core/res/res/values/symbols.xml7
-rw-r--r--core/tests/coretests/AndroidManifest.xml1
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java29
9 files changed, 262 insertions, 91 deletions
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 46f42f7864c5..cfe293925058 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -25,6 +25,7 @@ import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
import android.app.prediction.AppTargetId;
import android.content.ClipData;
+import android.content.ClipboardManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -80,11 +81,13 @@ import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
+import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.Space;
import android.widget.TextView;
+import android.widget.Toast;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -350,6 +353,50 @@ public class ChooserActivity extends ResolverActivity {
super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents,
null, false);
+ Button copyButton = findViewById(R.id.copy_button);
+ copyButton.setOnClickListener(view -> {
+ Intent targetIntent = getTargetIntent();
+ if (targetIntent == null) {
+ finish();
+ } else {
+ final String action = targetIntent.getAction();
+
+ ClipData clipData = null;
+ if (Intent.ACTION_SEND.equals(action)) {
+ String extraText = targetIntent.getStringExtra(Intent.EXTRA_TEXT);
+ Uri extraStream = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM);
+
+ if (extraText != null) {
+ clipData = ClipData.newPlainText(null, extraText);
+ } else if (extraStream != null) {
+ clipData = ClipData.newUri(getContentResolver(), null, extraStream);
+ } else {
+ Log.w(TAG, "No data available to copy to clipboard");
+ return;
+ }
+ } else if (Intent.ACTION_SEND_MULTIPLE.equals(action)) {
+ final ArrayList<Uri> streams = targetIntent.getParcelableArrayListExtra(
+ Intent.EXTRA_STREAM);
+ clipData = ClipData.newUri(getContentResolver(), null, streams.get(0));
+ for (int i = 1; i < streams.size(); i++) {
+ clipData.addItem(getContentResolver(), new ClipData.Item(streams.get(i)));
+ }
+ } else {
+ // expected to only be visible with ACTION_SEND or ACTION_SEND_MULTIPLE
+ // so warn about unexpected action
+ Log.w(TAG, "Action (" + action + ") not supported for copying to clipboard");
+ return;
+ }
+
+ ClipboardManager clipboardManager = (ClipboardManager) getSystemService(
+ Context.CLIPBOARD_SERVICE);
+ clipboardManager.setPrimaryClip(clipData);
+ Toast.makeText(getApplicationContext(), R.string.copied, Toast.LENGTH_SHORT).show();
+
+ finish();
+ }
+ });
+
MetricsLogger.action(this, MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN);
mChooserShownTime = System.currentTimeMillis();
@@ -414,39 +461,39 @@ public class ChooserActivity extends ResolverActivity {
private void showDefaultContentPreview(final ViewGroup parentLayout,
final Intent targetIntent) {
CharSequence sharingText = targetIntent.getCharSequenceExtra(Intent.EXTRA_TEXT);
- TextView previewTextView = findViewById(R.id.content_preview_text);
if (sharingText == null) {
- previewTextView.setVisibility(View.GONE);
+ findViewById(R.id.content_preview_text_layout).setVisibility(View.GONE);
} else {
- previewTextView.setText(sharingText);
+ TextView textView = findViewById(R.id.content_preview_text);
+ textView.setText(sharingText);
}
String previewTitle = targetIntent.getStringExtra(Intent.EXTRA_TITLE);
- TextView previewTitleView = findViewById(R.id.content_preview_title);
- if (previewTitle == null) {
- previewTitleView.setVisibility(View.GONE);
+ if (previewTitle == null || previewTitle.trim().isEmpty()) {
+ findViewById(R.id.content_preview_title_layout).setVisibility(View.GONE);
} else {
+ TextView previewTitleView = findViewById(R.id.content_preview_title);
previewTitleView.setText(previewTitle);
- }
- ClipData previewData = targetIntent.getClipData();
- Uri previewThumbnail = null;
- if (previewData != null) {
- if (previewData.getItemCount() > 0) {
- ClipData.Item previewDataItem = previewData.getItemAt(0);
- previewThumbnail = previewDataItem.getUri();
+ ClipData previewData = targetIntent.getClipData();
+ Uri previewThumbnail = null;
+ if (previewData != null) {
+ if (previewData.getItemCount() > 0) {
+ ClipData.Item previewDataItem = previewData.getItemAt(0);
+ previewThumbnail = previewDataItem.getUri();
+ }
}
- }
- ImageView previewThumbnailView = findViewById(R.id.content_preview_thumbnail);
- if (previewThumbnail == null) {
- previewThumbnailView.setVisibility(View.GONE);
- } else {
- Bitmap bmp = loadThumbnail(previewThumbnail, new Size(200, 200));
- if (bmp == null) {
+ ImageView previewThumbnailView = findViewById(R.id.content_preview_thumbnail);
+ if (previewThumbnail == null) {
previewThumbnailView.setVisibility(View.GONE);
} else {
- previewThumbnailView.setImageBitmap(bmp);
+ Bitmap bmp = loadThumbnail(previewThumbnail, new Size(100, 100));
+ if (bmp == null) {
+ previewThumbnailView.setVisibility(View.GONE);
+ } else {
+ previewThumbnailView.setImageBitmap(bmp);
+ }
}
}
}
@@ -2020,8 +2067,8 @@ public class ChooserActivity extends ResolverActivity {
private void updatePath(int width, int height) {
mPath.reset();
- int imageWidth = width - getPaddingLeft() - getPaddingRight();
- int imageHeight = height - getPaddingTop() - getPaddingBottom();
+ int imageWidth = width - getPaddingRight();
+ int imageHeight = height - getPaddingBottom();
mPath.addRoundRect(getPaddingLeft(), getPaddingTop(), imageWidth, imageHeight, mRadius,
mRadius, Path.Direction.CW);
}
diff --git a/core/res/res/drawable/chooser_content_preview_rounded.xml b/core/res/res/drawable/chooser_content_preview_rounded.xml
new file mode 100644
index 000000000000..7d8573801e68
--- /dev/null
+++ b/core/res/res/drawable/chooser_content_preview_rounded.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+
+ <stroke
+ android:width="1dp"
+ android:color="#ccc" />
+
+ <corners android:radius="@dimen/chooser_corner_radius" />
+
+ <padding
+ android:left="16dp"
+ android:top="12dp"
+ android:right="16dp"
+ android:bottom="12dp"/>
+</shape>
diff --git a/core/res/res/drawable/ic_content_copy_gm2.xml b/core/res/res/drawable/ic_content_copy_gm2.xml
new file mode 100644
index 000000000000..940da946cbb8
--- /dev/null
+++ b/core/res/res/drawable/ic_content_copy_gm2.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#F999"
+ android:pathData="M18,21L4,21L4,7L2,7v14c0,1.1 0.9,2 2,2h14v-2zM21,17L21,3c0,-1.1 -0.9,-2 -2,-2L8,1c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2zM19,17L8,17L8,3h11v14z"/>
+</vector>
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index c42f43ac0df5..a24f920de732 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -17,19 +17,19 @@
*/
-->
<com.android.internal.widget.ResolverDrawerLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:maxWidth="@dimen/resolver_max_width"
- android:maxCollapsedHeight="288dp"
- android:maxCollapsedHeightSmall="56dp"
- android:id="@id/contentPanel">
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:maxWidth="@dimen/resolver_max_width"
+ android:maxCollapsedHeight="288dp"
+ android:maxCollapsedHeightSmall="56dp"
+ android:id="@id/contentPanel">
<RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alwaysShow="true"
- android:background="?attr/colorBackgroundFloating" >
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alwaysShow="true"
+ android:background="?attr/colorBackgroundFloating">
<TextView android:id="@+id/profile_button"
android:layout_width="wrap_content"
android:layout_height="48dp"
@@ -51,73 +51,98 @@
android:textAppearance="?attr/textAppearanceMedium"
android:textSize="20sp"
android:gravity="center"
- android:paddingEnd="?attr/dialogPreferredPadding"
- android:paddingTop="12dp"
- android:paddingBottom="6dp"
+ android:paddingTop="18dp"
+ android:paddingBottom="18dp"
+ android:paddingLeft="24dp"
+ android:paddingRight="24dp"
android:layout_below="@id/profile_button"
android:layout_centerHorizontal="true"/>
</RelativeLayout>
- <RelativeLayout
+ <LinearLayout
android:id="@+id/content_preview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingBottom="@dimen/chooser_view_spacing"
android:background="?attr/colorBackgroundFloating">
-
- <view class="com.android.internal.app.ChooserActivity$RoundedRectImageView"
- android:id="@+id/content_preview_thumbnail"
- android:layout_alignParentTop="true"
- android:layout_width="100dp"
+
+ <LinearLayout
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
- android:gravity="center"
- android:adjustViewBounds="true"
- android:maxWidth="90dp"
- android:maxHeight="90dp"
- android:scaleType="fitCenter"
- android:padding="5dp"/>
+ android:orientation="horizontal"
+ android:paddingLeft="@dimen/chooser_edge_margin_normal"
+ android:paddingRight="@dimen/chooser_edge_margin_normal"
+ android:layout_marginBottom="@dimen/chooser_view_spacing"
+ android:id="@+id/content_preview_text_layout">
+ <TextView
+ android:id="@+id/content_preview_text"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:ellipsize="end"
+ android:gravity="start|top"
+ android:paddingRight="24dp"
+ android:maxLines="2"/>
+ <Button
+ android:id="@+id/copy_button"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:gravity="center"
+ android:layout_gravity="center_vertical"
+ android:background="@drawable/ic_content_copy_gm2"/>
+ </LinearLayout>
- <TextView
- android:id="@+id/content_preview_title"
- android:layout_alignParentTop="true"
- android:layout_toEndOf="@id/content_preview_thumbnail"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="start|top"
- android:textAppearance="?attr/textAppearanceMedium"
- android:maxLines="2"
- android:ellipsize="end"
- android:paddingStart="15dp"
- android:paddingEnd="15dp"
- android:paddingTop="10dp" />
+ <!-- Required sub-layout so we can get the nice rounded corners-->
+ <!-- around this section -->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginLeft="@dimen/chooser_edge_margin_thin"
+ android:layout_marginRight="@dimen/chooser_edge_margin_thin"
+ android:minHeight="80dp"
+ android:background="@drawable/chooser_content_preview_rounded"
+ android:id="@+id/content_preview_title_layout">
- <TextView
- android:id="@+id/content_preview_text"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/content_preview_title"
- android:layout_toEndOf="@id/content_preview_thumbnail"
- android:gravity="start|top"
- android:maxLines="2"
- android:ellipsize="end"
- android:paddingStart="15dp"
- android:paddingEnd="15dp"
- android:paddingTop="10dp"
- android:paddingBottom="5dp"/>
- </RelativeLayout>
+ <view class="com.android.internal.app.ChooserActivity$RoundedRectImageView"
+ android:id="@+id/content_preview_thumbnail"
+ android:layout_width="80dp"
+ android:layout_height="80dp"
+ android:layout_marginRight="12dp"
+ android:adjustViewBounds="true"
+ android:layout_gravity="center_vertical"
+ android:gravity="center"
+ android:maxWidth="70dp"
+ android:maxHeight="70dp"
+ android:padding="5dp"
+ android:scaleType="centerCrop"/>
+
+ <TextView
+ android:id="@+id/content_preview_title"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:textAppearance="?attr/textAppearanceMedium"/>
+ </LinearLayout>
+ </LinearLayout>
<ListView
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:id="@+id/resolver_list"
- android:clipToPadding="false"
- android:scrollbarStyle="outsideOverlay"
- android:background="?attr/colorBackgroundFloating"
- android:elevation="8dp"
- android:listSelector="@color/transparent"
- android:divider="@null"
- android:scrollIndicators="top"
- android:nestedScrollingEnabled="true" />
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/resolver_list"
+ android:clipToPadding="false"
+ android:scrollbarStyle="outsideOverlay"
+ android:background="?attr/colorBackgroundFloating"
+ android:elevation="8dp"
+ android:listSelector="@color/transparent"
+ android:divider="@null"
+ android:scrollIndicators="top"
+ android:nestedScrollingEnabled="true"/>
<TextView android:id="@+id/empty"
android:layout_width="match_parent"
@@ -127,6 +152,6 @@
android:text="@string/noApplications"
android:padding="32dp"
android:gravity="center"
- android:visibility="gone" />
+ android:visibility="gone"/>
</com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index baf758752abe..38367fbf3f31 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -715,6 +715,9 @@
<!-- Line spacing modifier for the message field of the harmful app dialog -->
<item name="harmful_app_message_line_spacing_modifier" type="dimen">1.22</item>
- <!-- chooser corner radius -->
- <dimen name="chooser_corner_radius">4dp</dimen>
+ <!-- chooser (sharesheet) spacing -->
+ <dimen name="chooser_corner_radius">8dp</dimen>
+ <dimen name="chooser_view_spacing">18dp</dimen>
+ <dimen name="chooser_edge_margin_thin">16dp</dimen>
+ <dimen name="chooser_edge_margin_normal">24dp</dimen>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 04df97c455a8..fadb28f05ef7 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2645,6 +2645,9 @@
<!-- Displayed to the user to confirm that they have copied text from a web page to the clipboard. -->
<string name="text_copied">Text copied to clipboard.</string>
+ <!-- Displayed to the user to confirm that they have copied text/images to the clipboard [CHAR LIMIT=NONE] -->
+ <string name="copied">Copied</string>
+
<!-- Menu item displayed at the end of a menu to allow users to see another page worth of menu items. This is shown on any app's menu as long as the app has too many items in the menu.-->
<string name="more_item_label">More</string>
<!-- Prepended to the shortcut for a menu item to indicate that the user should hold the MENU button together with the shortcut to invoke the item. For example, if the shortcut to open a new tab in browser is MENU and B together, then this would be prepended to the letter "B" -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 52400de3fe2b..0a104a400c4c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -53,7 +53,10 @@
<java-symbol type="id" name="content_preview" />
<java-symbol type="id" name="content_preview_thumbnail" />
<java-symbol type="id" name="content_preview_text" />
+ <java-symbol type="id" name="content_preview_text_layout" />
<java-symbol type="id" name="content_preview_title" />
+ <java-symbol type="id" name="content_preview_title_layout" />
+ <java-symbol type="id" name="copy_button" />
<java-symbol type="id" name="current_scene" />
<java-symbol type="id" name="scene_layoutid_cache" />
<java-symbol type="id" name="customPanel" />
@@ -545,6 +548,7 @@
<java-symbol type="string" name="activity_resolver_work_profiles_support" />
<java-symbol type="string" name="app_running_notification_title" />
<java-symbol type="string" name="app_running_notification_text" />
+ <java-symbol type="string" name="copied" />
<java-symbol type="string" name="delete" />
<java-symbol type="string" name="deleteText" />
<java-symbol type="string" name="grant_permissions_header_text" />
@@ -2711,6 +2715,9 @@
<java-symbol type="id" name="month_view" />
<java-symbol type="integer" name="config_zen_repeat_callers_threshold" />
<java-symbol type="dimen" name="chooser_corner_radius" />
+ <java-symbol type="dimen" name="chooser_view_spacing" />
+ <java-symbol type="dimen" name="chooser_edge_margin_thin" />
+ <java-symbol type="dimen" name="chooser_edge_margin_normal" />
<java-symbol type="layout" name="chooser_grid" />
<java-symbol type="layout" name="resolve_grid_item" />
<java-symbol type="id" name="day_picker_view_pager" />
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 268bb81e8736..1764249de662 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -59,6 +59,7 @@
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<uses-permission android:name="android.permission.READ_DREAM_STATE" />
<uses-permission android:name="android.permission.WRITE_DREAM_STATE" />
+ <uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" />
<uses-permission android:name="android.permission.READ_LOGS"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_SMS"/>
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 21fcae780b80..6b9e69cdb853 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -34,6 +34,9 @@ import static org.mockito.Mockito.when;
import android.app.usage.UsageStatsManager;
import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.ClipboardManager;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
@@ -392,6 +395,32 @@ public class ChooserActivityTest {
assertThat(chosen[0], is(toChoose));
}
+ @Test
+ public void copyTextToClipboard() throws Exception {
+ Intent sendIntent = createSendTextIntent();
+ List<ResolvedComponentInfo> resolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(1);
+
+ 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.copy_button)).perform(click());
+
+ ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(
+ Context.CLIPBOARD_SERVICE);
+ ClipData clipData = clipboard.getPrimaryClip();
+ assertThat("testing intent sending", is(clipData.getItemAt(0).getText()));
+
+ ClipDescription clipDescription = clipData.getDescription();
+ assertThat("text/plain", is(clipDescription.getMimeType(0)));
+ }
+
private Intent createSendTextIntent() {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);