Merge "Update demo app for content insertion to save & display images" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
7bbcbefdda
@@ -8,6 +8,9 @@ android_app {
|
|||||||
static_libs: [
|
static_libs: [
|
||||||
"guava",
|
"guava",
|
||||||
"jsr305",
|
"jsr305",
|
||||||
|
"androidx.appcompat_appcompat",
|
||||||
|
"androidx.recyclerview_recyclerview",
|
||||||
|
"com.google.android.material_material",
|
||||||
],
|
],
|
||||||
sdk_version: "current",
|
sdk_version: "current",
|
||||||
dex_preopt: {
|
dex_preopt: {
|
||||||
|
|||||||
@@ -15,21 +15,29 @@
|
|||||||
~ limitations under the License.
|
~ limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!-- Declare the contents of this Android application. The namespace
|
|
||||||
attribute brings in the Android platform namespace, and the package
|
|
||||||
supplies a unique name for the application. When writing your
|
|
||||||
own application, the package name must be changed from "com.example.*"
|
|
||||||
to come from a domain that you own or have control over. -->
|
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
package="com.example.android.receivecontent">
|
package="com.example.android.receivecontent">
|
||||||
|
|
||||||
<application android:label="@string/app_name">
|
<application
|
||||||
<activity android:name=".ReceiveContentDemoActivity" android:exported="true">
|
android:label="@string/app_name"
|
||||||
|
android:theme="@style/AppTheme">
|
||||||
|
<activity
|
||||||
|
android:name="com.example.android.receivecontent.ReceiveContentDemoActivity"
|
||||||
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
android:authorities="com.example.android.receivecontent.fileprovider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/file_paths" />
|
||||||
|
</provider>
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2020 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">
|
||||||
|
<solid android:color="@color/white"/>
|
||||||
|
<stroke android:width="1dp" android:color="@color/gray" />
|
||||||
|
<corners android:radius="10dp"/>
|
||||||
|
<padding
|
||||||
|
android:bottom="1dp"
|
||||||
|
android:left="1dp"
|
||||||
|
android:right="1dp"
|
||||||
|
android:top="1dp"/>
|
||||||
|
</shape>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2020 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">
|
||||||
|
<corners android:radius="5dp" />
|
||||||
|
</shape>
|
||||||
70
samples/ReceiveContentDemo/res/layout/activity_main.xml
Normal file
70
samples/ReceiveContentDemo/res/layout/activity_main.xml
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2020 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:id="@+id/layout_root"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:id="@+id/appbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/app_toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"/>
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_margin="18dp"
|
||||||
|
android:padding="2dp"
|
||||||
|
android:background="@drawable/container_background">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/attachments"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/attachments_recycler_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginHorizontal="6dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layoutManager="LinearLayoutManager" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/text_input"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="8dp"
|
||||||
|
android:layout_marginVertical="2dp"
|
||||||
|
android:gravity="bottom"
|
||||||
|
android:text="@string/text_input_default_text" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
26
samples/ReceiveContentDemo/res/layout/attachment.xml
Normal file
26
samples/ReceiveContentDemo/res/layout/attachment.xml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2020 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/attachment_thumbnail"
|
||||||
|
android:layout_width="72dp"
|
||||||
|
android:layout_height="72dp"
|
||||||
|
android:layout_marginHorizontal="8dp"
|
||||||
|
android:layout_marginVertical="16dp"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:background="@drawable/thumbnail_background"/>
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
~ Copyright (C) 2020 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.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:id="@+id/container"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/edittext_no_callback"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="top"
|
|
||||||
android:hint="@string/hint_edittext_no_callback"/>
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/edittext_images"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="top"
|
|
||||||
android:hint="@string/hint_edittext_images"/>
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/edittext_all_types"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="top"
|
|
||||||
android:hint="@string/hint_edittext_all_types"/>
|
|
||||||
</LinearLayout>
|
|
||||||
22
samples/ReceiveContentDemo/res/menu/app_menu.xml
Normal file
22
samples/ReceiveContentDemo/res/menu/app_menu.xml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2020 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_clear_attachments"
|
||||||
|
android:title="@string/app_menu_item_clear_attachments" />
|
||||||
|
</menu>
|
||||||
21
samples/ReceiveContentDemo/res/values/colors.xml
Normal file
21
samples/ReceiveContentDemo/res/values/colors.xml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2020 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<resources>
|
||||||
|
<color name="gray">#5f6368</color>
|
||||||
|
<color name="white">#ffffff</color>
|
||||||
|
</resources>
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
~ you may not use this file except in compliance with the License.
|
~ you may not use this file except in compliance with the License.
|
||||||
~ You may obtain a copy of the License at
|
~ You may obtain a copy of the License at
|
||||||
~
|
~
|
||||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
~
|
~
|
||||||
~ Unless required by applicable law or agreed to in writing, software
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -16,12 +16,8 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Receive Content Demo</string>
|
<string name="app_name">Android: Receive Content Demo</string>
|
||||||
<string name="hint_edittext_no_callback">Default EditText</string>
|
<string name="app_menu_item_clear_attachments">Clear attachments</string>
|
||||||
<string name="hint_edittext_images">
|
<string name="text_input_default_text">Try inserting text, images and other content
|
||||||
EditText with a listener that handles images
|
using copy/paste or drag-and-drop</string>
|
||||||
</string>
|
|
||||||
<string name="hint_edittext_all_types">
|
|
||||||
EditText with a listener that handles all content
|
|
||||||
</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
21
samples/ReceiveContentDemo/res/values/styles.xml
Normal file
21
samples/ReceiveContentDemo/res/values/styles.xml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2020 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<resources>
|
||||||
|
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
20
samples/ReceiveContentDemo/res/xml/file_paths.xml
Normal file
20
samples/ReceiveContentDemo/res/xml/file_paths.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2020 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<files-path name="my_attachments" path="attachments/"/>
|
||||||
|
</paths>
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 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.example.android.receivecontent;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.widget.AppCompatImageView;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
final class AttachmentsRecyclerViewAdapter extends
|
||||||
|
RecyclerView.Adapter<AttachmentsRecyclerViewAdapter.MyViewHolder> {
|
||||||
|
|
||||||
|
static final class MyViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
public AppCompatImageView mAttachmentThumbnailView;
|
||||||
|
|
||||||
|
MyViewHolder(AppCompatImageView attachmentThumbnailView) {
|
||||||
|
super(attachmentThumbnailView);
|
||||||
|
mAttachmentThumbnailView = attachmentThumbnailView;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final List<Uri> mAttachments;
|
||||||
|
|
||||||
|
AttachmentsRecyclerViewAdapter(List<Uri> attachments) {
|
||||||
|
mAttachments = new ArrayList<>(attachments);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAttachment(Uri uri) {
|
||||||
|
mAttachments.add(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearAttachments() {
|
||||||
|
mAttachments.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return mAttachments.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
|
AppCompatImageView view = (AppCompatImageView) LayoutInflater.from(parent.getContext())
|
||||||
|
.inflate(R.layout.attachment, parent, false);
|
||||||
|
return new MyViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
|
||||||
|
Uri uri = mAttachments.get(position);
|
||||||
|
holder.mAttachmentThumbnailView.setImageURI(uri);
|
||||||
|
holder.mAttachmentThumbnailView.setClipToOutline(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 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.example.android.receivecontent;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.webkit.MimeTypeMap;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.content.FileProvider;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.io.ByteStreams;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores attachments as files in the app's private storage directory (see
|
||||||
|
* {@link Context#getDataDir()}, {@link Context#getFilesDir()}, etc).
|
||||||
|
*/
|
||||||
|
final class AttachmentsRepo {
|
||||||
|
|
||||||
|
// This matches the name declared in AndroidManifest.xml
|
||||||
|
private static final String FILE_PROVIDER_AUTHORITY =
|
||||||
|
"com.example.android.receivecontent.fileprovider";
|
||||||
|
|
||||||
|
private final Context mContext;
|
||||||
|
private final File mAttachmentsDir;
|
||||||
|
|
||||||
|
AttachmentsRepo(@NonNull Context context) {
|
||||||
|
mContext = context;
|
||||||
|
mAttachmentsDir = new File(mContext.getFilesDir(), "attachments");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the content at the given URI and writes it to private storage. Then returns a content
|
||||||
|
* URI referencing the newly written file.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public Uri write(@NonNull Uri uri) {
|
||||||
|
ContentResolver contentResolver = mContext.getContentResolver();
|
||||||
|
String mimeType = contentResolver.getType(uri);
|
||||||
|
String ext = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
|
||||||
|
try (InputStream is = contentResolver.openInputStream(uri)) {
|
||||||
|
if (is == null) {
|
||||||
|
throw new IllegalArgumentException(String.valueOf(uri));
|
||||||
|
}
|
||||||
|
mAttachmentsDir.mkdirs();
|
||||||
|
String fileName = "a-" + UUID.randomUUID().toString() + "." + ext;
|
||||||
|
File newAttachment = new File(mAttachmentsDir, fileName);
|
||||||
|
try (OutputStream os = new FileOutputStream(newAttachment);) {
|
||||||
|
ByteStreams.copy(is, os);
|
||||||
|
}
|
||||||
|
Log.i(Logcat.TAG,
|
||||||
|
"Wrote file [" + fileName + "]: " + newAttachment.length() + " bytes");
|
||||||
|
return getUriForFile(newAttachment);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteAll() {
|
||||||
|
File[] files = mAttachmentsDir.listFiles();
|
||||||
|
if (files == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (File file : files) {
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public ImmutableList<Uri> getAllUris() {
|
||||||
|
File[] files = mAttachmentsDir.listFiles();
|
||||||
|
if (files == null || files.length == 0) {
|
||||||
|
return ImmutableList.of();
|
||||||
|
}
|
||||||
|
ImmutableList.Builder<Uri> uris = ImmutableList.builderWithExpectedSize(files.length);
|
||||||
|
for (File file : files) {
|
||||||
|
uris.add(getUriForFile(file));
|
||||||
|
}
|
||||||
|
return uris.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private Uri getUriForFile(@NonNull File file) {
|
||||||
|
return FileProvider.getUriForFile(mContext, FILE_PROVIDER_AUTHORITY, file);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 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.example.android.receivecontent;
|
||||||
|
|
||||||
|
final class Logcat {
|
||||||
|
private Logcat() {}
|
||||||
|
|
||||||
|
public static final String TAG = "ReceiveContentDemo";
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020 The Android Open Source Project
|
* Copyright 2021 The Android Open Source Project
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -16,13 +16,39 @@
|
|||||||
|
|
||||||
package com.example.android.receivecontent;
|
package com.example.android.receivecontent;
|
||||||
|
|
||||||
import java.util.concurrent.ExecutorService;
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
|
||||||
|
import com.google.common.util.concurrent.MoreExecutors;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
public class MyExecutors {
|
final class MyExecutors {
|
||||||
private static final ExecutorService mBg = Executors.newSingleThreadExecutor();
|
private MyExecutors() {}
|
||||||
|
|
||||||
public static ExecutorService getBg() {
|
private static final ListeningScheduledExecutorService BG =
|
||||||
return mBg;
|
MoreExecutors.listeningDecorator(Executors.newSingleThreadScheduledExecutor());
|
||||||
|
|
||||||
|
private static final Handler MAIN_HANDLER = new Handler(Looper.getMainLooper());
|
||||||
|
private static final Executor MAIN_EXECUTOR =
|
||||||
|
runnable -> {
|
||||||
|
if (!MAIN_HANDLER.post(runnable)) {
|
||||||
|
Log.e(Logcat.TAG, "Failed to post runnable on main thread");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static ListeningScheduledExecutorService bg() {
|
||||||
|
return BG;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Executor main() {
|
||||||
|
return MAIN_EXECUTOR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,88 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2020 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.example.android.receivecontent;
|
|
||||||
|
|
||||||
import static com.example.android.receivecontent.ReceiveContentDemoActivity.LOG_TAG;
|
|
||||||
import static com.example.android.receivecontent.Utils.matchesAny;
|
|
||||||
import static com.example.android.receivecontent.Utils.showMessage;
|
|
||||||
|
|
||||||
import android.content.ClipData;
|
|
||||||
import android.content.ClipDescription;
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.ContentInfo;
|
|
||||||
import android.view.OnReceiveContentListener;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sample implementation that:
|
|
||||||
* <ul>
|
|
||||||
* <li>Accepts images and mp4 videos.
|
|
||||||
* <li>Rejects other content URIs.
|
|
||||||
* <li>Coerces all other content to lower-case, plain text and delegates its insertion to the
|
|
||||||
* platform.
|
|
||||||
* </ul>
|
|
||||||
*/
|
|
||||||
public class MyListenerAllContent implements OnReceiveContentListener {
|
|
||||||
static final String[] SUPPORTED_MIME_TYPES = new String[]{"image/*", "video/mp4"};
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ContentInfo onReceiveContent(View view, ContentInfo payload) {
|
|
||||||
ClipData clip = payload.getClip();
|
|
||||||
ClipDescription description = clip.getDescription();
|
|
||||||
ArrayList<ClipData.Item> remainingItems = new ArrayList<>();
|
|
||||||
for (int i = 0; i < clip.getItemCount(); i++) {
|
|
||||||
ClipData.Item item = clip.getItemAt(i);
|
|
||||||
Uri uri = item.getUri();
|
|
||||||
if (uri != null) {
|
|
||||||
receive(view, uri);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
CharSequence text = item.coerceToText(view.getContext());
|
|
||||||
text = text.toString().toLowerCase();
|
|
||||||
remainingItems.add(new ClipData.Item(
|
|
||||||
text, item.getHtmlText(), item.getIntent(), item.getUri()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!remainingItems.isEmpty()) {
|
|
||||||
Log.i(LOG_TAG, "Delegating " + remainingItems.size() + " item(s) to platform");
|
|
||||||
ClipData newClip = new ClipData(description, remainingItems.get(0));
|
|
||||||
for (int i = 1; i < remainingItems.size(); i++) {
|
|
||||||
newClip.addItem(remainingItems.get(i));
|
|
||||||
}
|
|
||||||
return new ContentInfo.Builder(payload).setClip(newClip).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void receive(View view, Uri contentUri) {
|
|
||||||
final String viewClassName = view.getClass().getSimpleName();
|
|
||||||
MyExecutors.getBg().submit(() -> {
|
|
||||||
ContentResolver contentResolver = view.getContext().getContentResolver();
|
|
||||||
String mimeType = contentResolver.getType(contentUri);
|
|
||||||
if (!matchesAny(mimeType, SUPPORTED_MIME_TYPES)) {
|
|
||||||
showMessage(view, "Content of type " + mimeType + " is not supported");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
showMessage(view, viewClassName + ": Received " + mimeType + ": " + contentUri);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2020 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.example.android.receivecontent;
|
|
||||||
|
|
||||||
import static com.example.android.receivecontent.Utils.matchesAny;
|
|
||||||
import static com.example.android.receivecontent.Utils.showMessage;
|
|
||||||
|
|
||||||
import android.content.ClipData;
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.util.Pair;
|
|
||||||
import android.view.ContentInfo;
|
|
||||||
import android.view.OnReceiveContentListener;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sample implementation that accepts images, rejects other URIs, and delegates handling for all
|
|
||||||
* non-URI content to the platform.
|
|
||||||
*/
|
|
||||||
public class MyListenerImages implements OnReceiveContentListener {
|
|
||||||
static final String[] SUPPORTED_MIME_TYPES = new String[]{"image/*"};
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ContentInfo onReceiveContent(View view, ContentInfo payload) {
|
|
||||||
Pair<ContentInfo, ContentInfo> split = Utils.partition(payload,
|
|
||||||
item -> item.getUri() != null);
|
|
||||||
ContentInfo uriContent = split.first;
|
|
||||||
ContentInfo remaining = split.second;
|
|
||||||
if (uriContent != null) {
|
|
||||||
ClipData clip = uriContent.getClip();
|
|
||||||
for (int i = 0; i < clip.getItemCount(); i++) {
|
|
||||||
receive(view, clip.getItemAt(i).getUri());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return remaining;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void receive(View view, Uri contentUri) {
|
|
||||||
MyExecutors.getBg().submit(() -> {
|
|
||||||
ContentResolver contentResolver = view.getContext().getContentResolver();
|
|
||||||
String mimeType = contentResolver.getType(contentUri);
|
|
||||||
if (!matchesAny(mimeType, SUPPORTED_MIME_TYPES)) {
|
|
||||||
showMessage(view, "Content of type " + mimeType + " is not supported");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
showMessage(view, "Received " + mimeType + ": " + contentUri);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 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.example.android.receivecontent;
|
||||||
|
|
||||||
|
import android.content.ClipData;
|
||||||
|
import android.content.ClipDescription;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.AssetFileDescriptor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.util.Pair;
|
||||||
|
import android.view.ContentInfo;
|
||||||
|
import android.view.OnReceiveContentListener;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
|
import com.google.common.util.concurrent.Futures;
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sample {@link OnReceiveContentListener} implementation that accepts all URIs, and delegates
|
||||||
|
* handling for all other content to the platform.
|
||||||
|
*/
|
||||||
|
final class MyReceiver implements OnReceiveContentListener {
|
||||||
|
public static final String[] SUPPORTED_MIME_TYPES = new String[]{"image/*"};
|
||||||
|
|
||||||
|
private final AttachmentsRepo mAttachmentsRepo;
|
||||||
|
private final AttachmentsRecyclerViewAdapter mAttachmentsRecyclerViewAdapter;
|
||||||
|
|
||||||
|
MyReceiver(@NonNull AttachmentsRepo attachmentsRepo,
|
||||||
|
@NonNull AttachmentsRecyclerViewAdapter attachmentsRecyclerViewAdapter) {
|
||||||
|
mAttachmentsRepo = attachmentsRepo;
|
||||||
|
mAttachmentsRecyclerViewAdapter = attachmentsRecyclerViewAdapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public ContentInfo onReceiveContent(@NonNull View view,
|
||||||
|
@NonNull ContentInfo contentInfo) {
|
||||||
|
// Split the incoming content into two groups: content URIs and everything else.
|
||||||
|
// This way we can implement custom handling for URIs and delegate the rest.
|
||||||
|
Pair<ContentInfo, ContentInfo> split = Utils.partition(contentInfo,
|
||||||
|
item -> item.getUri() != null);
|
||||||
|
ContentInfo uriContent = split.first;
|
||||||
|
ContentInfo remaining = split.second;
|
||||||
|
if (uriContent != null) {
|
||||||
|
ContentResolver contentResolver = view.getContext().getContentResolver();
|
||||||
|
ClipData clip = uriContent.getClip();
|
||||||
|
for (int i = 0; i < clip.getItemCount(); i++) {
|
||||||
|
Uri uri = clip.getItemAt(i).getUri();
|
||||||
|
String mimeType = contentResolver.getType(uri);
|
||||||
|
receive(view, uri, mimeType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Return anything that we didn't handle ourselves. This preserves the default platform
|
||||||
|
// behavior for text and anything else for which we are not implementing custom handling.
|
||||||
|
return remaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles incoming content URIs. If the content is an image, stores it as an attachment in the
|
||||||
|
* app's private storage. If the content is any other type, simply shows a toast with the type
|
||||||
|
* of the content and its size in bytes.
|
||||||
|
*/
|
||||||
|
private void receive(@NonNull View view, @NonNull Uri uri, @NonNull String mimeType) {
|
||||||
|
Log.i(Logcat.TAG, "Receiving " + mimeType + ": " + uri);
|
||||||
|
if (ClipDescription.compareMimeTypes(mimeType, "image/*")) {
|
||||||
|
createAttachment(uri, mimeType);
|
||||||
|
} else {
|
||||||
|
showMessage(view, uri, mimeType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the image at the given URI and writes it to private storage. Then shows the image in
|
||||||
|
* the UI by passing the URI pointing to the locally stored copy to the recycler view adapter.
|
||||||
|
*/
|
||||||
|
private void createAttachment(@NonNull Uri uri, @NonNull String mimeType) {
|
||||||
|
ListenableFuture<Uri> addAttachmentFuture = MyExecutors.bg().submit(() ->
|
||||||
|
mAttachmentsRepo.write(uri)
|
||||||
|
);
|
||||||
|
Futures.addCallback(addAttachmentFuture, new FutureCallback<Uri>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Uri result) {
|
||||||
|
mAttachmentsRecyclerViewAdapter.addAttachment(result);
|
||||||
|
mAttachmentsRecyclerViewAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Throwable t) {
|
||||||
|
Log.e(Logcat.TAG,
|
||||||
|
"Error receiving content: uri=" + uri + ", mimeType" + mimeType, t);
|
||||||
|
}
|
||||||
|
}, MyExecutors.main());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the size of the given content URI and shows a toast with the type of the content and
|
||||||
|
* its size in bytes.
|
||||||
|
*/
|
||||||
|
private void showMessage(@NonNull View view, @NonNull Uri uri, @NonNull String mimeType) {
|
||||||
|
Context applicationContext = view.getContext().getApplicationContext();
|
||||||
|
MyExecutors.bg().execute(() -> {
|
||||||
|
ContentResolver contentResolver = applicationContext.getContentResolver();
|
||||||
|
long lengthBytes;
|
||||||
|
try {
|
||||||
|
AssetFileDescriptor fd = contentResolver.openAssetFileDescriptor(uri, "r");
|
||||||
|
lengthBytes = fd.getLength();
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
Log.e(Logcat.TAG, "Error opening content URI: " + uri, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String msg = "Received " + mimeType + " (" + lengthBytes + " bytes): " + uri;
|
||||||
|
Log.i(Logcat.TAG, msg);
|
||||||
|
MyExecutors.main().execute(() -> {
|
||||||
|
Toast.makeText(applicationContext, msg, Toast.LENGTH_LONG).show();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020 The Android Open Source Project
|
* Copyright 2021 The Android Open Source Project
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -16,32 +16,87 @@
|
|||||||
|
|
||||||
package com.example.android.receivecontent;
|
package com.example.android.receivecontent;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.View;
|
import android.util.Log;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
public class ReceiveContentDemoActivity extends Activity {
|
import androidx.annotation.NonNull;
|
||||||
public static final String LOG_TAG = "ReceiveContentDemo";
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
|
import com.google.common.util.concurrent.Futures;
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
|
||||||
|
/** Main activity for the demo. */
|
||||||
|
public class ReceiveContentDemoActivity extends AppCompatActivity {
|
||||||
|
private AttachmentsRepo mAttachmentsRepo;
|
||||||
|
private AttachmentsRecyclerViewAdapter mAttachmentsRecyclerViewAdapter;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.demo);
|
setContentView(R.layout.activity_main);
|
||||||
|
|
||||||
EditText editTextImagesOnly = findViewById(R.id.edittext_images);
|
// Setup the app toolbar.
|
||||||
editTextImagesOnly.setOnReceiveContentListener(
|
Toolbar toolbar = findViewById(R.id.app_toolbar);
|
||||||
MyListenerImages.SUPPORTED_MIME_TYPES,
|
setSupportActionBar(toolbar);
|
||||||
new MyListenerImages());
|
|
||||||
|
|
||||||
EditText editTextAllTypes = findViewById(R.id.edittext_all_types);
|
// Setup the repository and recycler view for attachments.
|
||||||
editTextAllTypes.setOnReceiveContentListener(
|
mAttachmentsRepo = new AttachmentsRepo(this);
|
||||||
MyListenerAllContent.SUPPORTED_MIME_TYPES,
|
ImmutableList<Uri> attachments = mAttachmentsRepo.getAllUris();
|
||||||
new MyListenerAllContent());
|
RecyclerView attachmentsRecyclerView = findViewById(R.id.attachments_recycler_view);
|
||||||
|
attachmentsRecyclerView.setHasFixedSize(true);
|
||||||
|
mAttachmentsRecyclerViewAdapter = new AttachmentsRecyclerViewAdapter(attachments);
|
||||||
|
attachmentsRecyclerView.setAdapter(mAttachmentsRecyclerViewAdapter);
|
||||||
|
|
||||||
View container = findViewById(R.id.container);
|
// Setup the listener for receiving content.
|
||||||
container.setOnReceiveContentListener(
|
MyReceiver receiver = new MyReceiver(mAttachmentsRepo, mAttachmentsRecyclerViewAdapter);
|
||||||
MyListenerAllContent.SUPPORTED_MIME_TYPES,
|
LinearLayout container = findViewById(R.id.container);
|
||||||
new MyListenerAllContent());
|
container.setOnReceiveContentListener(MyReceiver.SUPPORTED_MIME_TYPES, receiver);
|
||||||
|
EditText textInput = findViewById(R.id.text_input);
|
||||||
|
textInput.setOnReceiveContentListener(MyReceiver.SUPPORTED_MIME_TYPES, receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(@NonNull Menu menu) {
|
||||||
|
MenuInflater inflater = getMenuInflater();
|
||||||
|
inflater.inflate(R.menu.app_menu, menu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||||
|
if (item.getItemId() == R.id.action_clear_attachments) {
|
||||||
|
deleteAllAttachments();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteAllAttachments() {
|
||||||
|
ListenableFuture<Void> deleteAllFuture = MyExecutors.bg().submit(() -> {
|
||||||
|
mAttachmentsRepo.deleteAll();
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
Futures.addCallback(deleteAllFuture, new FutureCallback<Void>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(@Nullable Void result) {
|
||||||
|
mAttachmentsRecyclerViewAdapter.clearAttachments();
|
||||||
|
mAttachmentsRecyclerViewAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Throwable t) {
|
||||||
|
Log.e(Logcat.TAG, "Error deleting attachments", t);
|
||||||
|
}
|
||||||
|
}, MyExecutors.main());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,15 +16,10 @@
|
|||||||
|
|
||||||
package com.example.android.receivecontent;
|
package com.example.android.receivecontent;
|
||||||
|
|
||||||
import static com.example.android.receivecontent.ReceiveContentDemoActivity.LOG_TAG;
|
|
||||||
|
|
||||||
import android.content.ClipData;
|
import android.content.ClipData;
|
||||||
import android.content.ClipDescription;
|
import android.content.ClipDescription;
|
||||||
import android.util.Log;
|
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import android.view.ContentInfo;
|
import android.view.ContentInfo;
|
||||||
import android.view.View;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -33,24 +28,8 @@ import java.util.function.Predicate;
|
|||||||
final class Utils {
|
final class Utils {
|
||||||
private Utils() {}
|
private Utils() {}
|
||||||
|
|
||||||
public static boolean matchesAny(String mimeType, String[] targetMimeTypes) {
|
|
||||||
for (String targetMimeType : targetMimeTypes) {
|
|
||||||
if (ClipDescription.compareMimeTypes(mimeType, targetMimeType)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void showMessage(View view, String msg) {
|
|
||||||
Log.i(LOG_TAG, msg);
|
|
||||||
view.getHandler().post(() ->
|
|
||||||
Toast.makeText(view.getContext(), msg, Toast.LENGTH_LONG).show()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If you use the support library, use {@code androidx.core.view.ContentInfoCompat.partition()}.
|
* If you use Jetpack, use {@code androidx.core.view.ContentInfoCompat.partition()}.
|
||||||
*/
|
*/
|
||||||
public static Pair<ContentInfo, ContentInfo> partition(ContentInfo payload,
|
public static Pair<ContentInfo, ContentInfo> partition(ContentInfo payload,
|
||||||
Predicate<ClipData.Item> itemPredicate) {
|
Predicate<ClipData.Item> itemPredicate) {
|
||||||
|
|||||||
Reference in New Issue
Block a user