diff --git a/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog.png b/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog.png
new file mode 100644
index 0000000000..bd3d20143b
Binary files /dev/null and b/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog.png differ
diff --git a/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SendShareDialogTest_showDialog.png b/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SendShareDialogTest_showDialog.png
new file mode 100644
index 0000000000..ee28ab2ac6
Binary files /dev/null and b/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SendShareDialogTest_showDialog.png differ
diff --git a/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles.png b/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles.png
index 56d59ec8b0..0e493a97c0 100644
Binary files a/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles.png and b/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles.png differ
diff --git a/src/androidTest/java/com/owncloud/android/ui/dialog/SendFilesDialogTest.kt b/src/androidTest/java/com/owncloud/android/ui/dialog/SendFilesDialogTest.kt
new file mode 100644
index 0000000000..dc3bb17efa
--- /dev/null
+++ b/src/androidTest/java/com/owncloud/android/ui/dialog/SendFilesDialogTest.kt
@@ -0,0 +1,64 @@
+/*
+ *
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2020 Tobias Kaminsky
+ * Copyright (C) 2020 Nextcloud GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package com.owncloud.android.ui.dialog
+
+import androidx.fragment.app.FragmentManager
+import androidx.test.espresso.intent.rule.IntentsTestRule
+import androidx.test.platform.app.InstrumentationRegistry
+import com.nextcloud.client.TestActivity
+import com.owncloud.android.AbstractIT
+import com.owncloud.android.datamodel.OCFile
+import com.owncloud.android.utils.ScreenshotTest
+import org.junit.Rule
+import org.junit.Test
+
+class SendFilesDialogTest : AbstractIT() {
+ @get:Rule
+ val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false)
+
+ @Test
+ @ScreenshotTest
+ fun showDialog() {
+ val activity = testActivityRule.launchActivity(null)
+
+ val fm: FragmentManager = activity.supportFragmentManager
+ val ft = fm.beginTransaction()
+ ft.addToBackStack(null)
+
+ val files = setOf(
+ OCFile("/1.jpg").apply {
+ mimeType = "image/jpg"
+ },
+ OCFile("/2.jpg").apply {
+ mimeType = "image/jpg"
+ }
+ )
+
+ val sut = SendFilesDialog.newInstance(files)
+ sut.show(ft, "TAG_SEND_SHARE_DIALOG")
+
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+ shortSleep()
+
+ sut.requireDialog().window?.decorView.let { screenshot(it) }
+ }
+}
diff --git a/src/androidTest/java/com/owncloud/android/ui/dialog/SendShareDialogTest.kt b/src/androidTest/java/com/owncloud/android/ui/dialog/SendShareDialogTest.kt
new file mode 100644
index 0000000000..b6a03b1e7b
--- /dev/null
+++ b/src/androidTest/java/com/owncloud/android/ui/dialog/SendShareDialogTest.kt
@@ -0,0 +1,60 @@
+/*
+ *
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2020 Tobias Kaminsky
+ * Copyright (C) 2020 Nextcloud GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package com.owncloud.android.ui.dialog
+
+import androidx.fragment.app.FragmentManager
+import androidx.test.espresso.intent.rule.IntentsTestRule
+import androidx.test.platform.app.InstrumentationRegistry
+import com.nextcloud.client.TestActivity
+import com.owncloud.android.AbstractIT
+import com.owncloud.android.datamodel.OCFile
+import com.owncloud.android.lib.resources.status.OCCapability
+import com.owncloud.android.utils.ScreenshotTest
+import org.junit.Rule
+import org.junit.Test
+
+class SendShareDialogTest : AbstractIT() {
+ @get:Rule
+ val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false)
+
+ @Test
+ @ScreenshotTest
+ fun showDialog() {
+ val activity = testActivityRule.launchActivity(null)
+
+ val fm: FragmentManager = activity.supportFragmentManager
+ val ft = fm.beginTransaction()
+ ft.addToBackStack(null)
+
+ val file = OCFile("/1.jpg").apply {
+ mimeType = "image/jpg"
+ }
+
+ val sut = SendShareDialog.newInstance(file, false, OCCapability())
+ sut.show(ft, "TAG_SEND_SHARE_DIALOG")
+
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+ shortSleep()
+
+ sut.requireDialog().window?.decorView.let { screenshot(it) }
+ }
+}
diff --git a/src/main/java/com/owncloud/android/files/FileMenuFilter.java b/src/main/java/com/owncloud/android/files/FileMenuFilter.java
index 8e06f2a9e3..576c4996a6 100644
--- a/src/main/java/com/owncloud/android/files/FileMenuFilter.java
+++ b/src/main/java/com/owncloud/android/files/FileMenuFilter.java
@@ -58,6 +58,7 @@ import androidx.annotation.Nullable;
public class FileMenuFilter {
private static final int SINGLE_SELECT_ITEMS = 1;
+ public static final String SEND_OFF = "off";
private int numberOfAllFiles;
private Collection files;
@@ -203,6 +204,7 @@ public class FileMenuFilter {
filterCancelSync(toShow, toHide, synchronizing);
filterSync(toShow, toHide, synchronizing);
filterShareFile(toShow, toHide, capability);
+ filterSendFiles(toShow, toHide);
filterDetails(toShow, toHide);
filterFavorite(toShow, toHide, synchronizing);
filterUnfavorite(toShow, toHide, synchronizing);
@@ -222,6 +224,15 @@ public class FileMenuFilter {
}
}
+ private void filterSendFiles(List toShow, List toHide) {
+ if (containsEncryptedFile() || isSingleSelection() || overflowMenu || !anyFileDown() ||
+ SEND_OFF.equalsIgnoreCase(context.getString(R.string.send_files_to_other_apps))) {
+ toHide.add(R.id.action_send_file);
+ } else {
+ toShow.add(R.id.action_send_file);
+ }
+ }
+
private void filterDetails(Collection toShow, Collection toHide) {
if (isSingleSelection()) {
toShow.add(R.id.action_see_details);
diff --git a/src/main/java/com/owncloud/android/ui/dialog/SendFilesDialog.java b/src/main/java/com/owncloud/android/ui/dialog/SendFilesDialog.java
new file mode 100644
index 0000000000..a90ab02dd8
--- /dev/null
+++ b/src/main/java/com/owncloud/android/ui/dialog/SendFilesDialog.java
@@ -0,0 +1,160 @@
+package com.owncloud.android.ui.dialog;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
+import com.owncloud.android.R;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.ui.adapter.SendButtonAdapter;
+import com.owncloud.android.ui.components.SendButtonData;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+/*
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2020 Tobias Kaminsky
+ * Copyright (C) 2020 Nextcloud GmbH.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+public class SendFilesDialog extends BottomSheetDialogFragment {
+
+ private static final String KEY_OCFILES = "KEY_OCFILES";
+
+ private OCFile[] files;
+
+ public static SendFilesDialog newInstance(Set files) {
+
+ SendFilesDialog dialogFragment = new SendFilesDialog();
+
+ Bundle args = new Bundle();
+ args.putParcelableArray(KEY_OCFILES, files.toArray(new OCFile[0]));
+ dialogFragment.setArguments(args);
+
+ return dialogFragment;
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ // keep the state of the fragment on configuration changes
+ setRetainInstance(true);
+
+ files = (OCFile[]) requireArguments().getParcelableArray(KEY_OCFILES);
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater,
+ @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+
+ View view = inflater.inflate(R.layout.send_files_fragment, container, false);
+
+ // populate send apps
+ Intent sendIntent = createSendIntent();
+
+ List sendButtonDataList = setupSendButtonData(sendIntent);
+
+ SendButtonAdapter.ClickListener clickListener = setupSendButtonClickListener(sendIntent);
+
+ RecyclerView sendButtonsView = view.findViewById(R.id.send_button_recycler_view);
+ sendButtonsView.setHasFixedSize(true);
+ sendButtonsView.setLayoutManager(new GridLayoutManager(getActivity(), 4));
+ sendButtonsView.setAdapter(new SendButtonAdapter(sendButtonDataList, clickListener));
+
+ return view;
+ }
+
+ @NonNull
+ private SendButtonAdapter.ClickListener setupSendButtonClickListener(Intent sendIntent) {
+ return sendButtonDataData -> {
+ String packageName = sendButtonDataData.getPackageName();
+ String activityName = sendButtonDataData.getActivityName();
+
+ sendIntent.setComponent(new ComponentName(packageName, activityName));
+ requireActivity().startActivity(Intent.createChooser(sendIntent, getString(R.string.send)));
+
+ dismiss();
+ };
+ }
+
+ @NonNull
+ private List setupSendButtonData(Intent sendIntent) {
+ Drawable icon;
+ SendButtonData sendButtonData;
+ CharSequence label;
+ List matches = requireActivity().getPackageManager().queryIntentActivities(sendIntent, 0);
+ List sendButtonDataList = new ArrayList<>(matches.size());
+ for (ResolveInfo match : matches) {
+ icon = match.loadIcon(requireActivity().getPackageManager());
+ label = match.loadLabel(requireActivity().getPackageManager());
+ sendButtonData = new SendButtonData(icon, label,
+ match.activityInfo.packageName,
+ match.activityInfo.name);
+
+ sendButtonDataList.add(sendButtonData);
+ }
+ return sendButtonDataList;
+ }
+
+ @NonNull
+ private Intent createSendIntent() {
+ Intent sendIntent = new Intent(Intent.ACTION_SEND_MULTIPLE);
+ sendIntent.setType(getUniqueMimetype());
+ sendIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, getExposedFileUris());
+ sendIntent.putExtra(Intent.ACTION_SEND, true);
+ return sendIntent;
+ }
+
+ @Nullable
+ private String getUniqueMimetype() {
+ String mimetype = files[0].getMimeType();
+
+ for (OCFile file : files) {
+ if (!mimetype.equals(file.getMimeType())) {
+ return null;
+ }
+ }
+
+ return mimetype;
+ }
+
+ private ArrayList getExposedFileUris() {
+ ArrayList uris = new ArrayList<>();
+
+ for (OCFile file : files) {
+ uris.add(file.getExposedFileUri(requireContext()));
+ }
+
+ return uris;
+ }
+}
diff --git a/src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.java b/src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.java
index 3fd2d0fbd6..8d45db171a 100644
--- a/src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.java
+++ b/src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.java
@@ -241,11 +241,15 @@ public class SendShareDialog extends BottomSheetDialogFragment {
@NonNull
private List setupSendButtonData(Intent sendIntent) {
- List sendButtonDataList = new ArrayList<>();
- for (ResolveInfo match : getActivity().getPackageManager().queryIntentActivities(sendIntent, 0)) {
- Drawable icon = match.loadIcon(getActivity().getPackageManager());
- CharSequence label = match.loadLabel(getActivity().getPackageManager());
- SendButtonData sendButtonData = new SendButtonData(icon, label,
+ Drawable icon;
+ SendButtonData sendButtonData;
+ CharSequence label;
+ List matches = requireActivity().getPackageManager().queryIntentActivities(sendIntent, 0);
+ List sendButtonDataList = new ArrayList<>(matches.size());
+ for (ResolveInfo match : matches) {
+ icon = match.loadIcon(requireActivity().getPackageManager());
+ label = match.loadLabel(requireActivity().getPackageManager());
+ sendButtonData = new SendButtonData(icon, label,
match.activityInfo.packageName,
match.activityInfo.name);
diff --git a/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java b/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java
index b5c4656a1d..99a04fbd48 100644
--- a/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java
+++ b/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java
@@ -1197,6 +1197,9 @@ public class OCFileListFragment extends ExtendedListFragment implements
selectAllFiles(false);
return true;
}
+ case R.id.action_send_file:
+ mContainerActivity.getFileOperationsHelper().sendFiles(checkedFiles);
+ return true;
default:
return false;
}
diff --git a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java
index e81843e81c..11178e080d 100755
--- a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java
+++ b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java
@@ -73,6 +73,7 @@ import com.owncloud.android.ui.activity.FileDisplayActivity;
import com.owncloud.android.ui.activity.RichDocumentsEditorWebView;
import com.owncloud.android.ui.activity.ShareActivity;
import com.owncloud.android.ui.activity.TextEditorWebView;
+import com.owncloud.android.ui.dialog.SendFilesDialog;
import com.owncloud.android.ui.dialog.SendShareDialog;
import com.owncloud.android.ui.events.EncryptionEvent;
import com.owncloud.android.ui.events.FavoriteEvent;
@@ -97,6 +98,7 @@ import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Locale;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -703,6 +705,16 @@ public class FileOperationsHelper {
mSendShareDialog.show(ft, "TAG_SEND_SHARE_DIALOG");
}
+ public void sendFiles(Set files) {
+ // Show dialog
+ FragmentManager fm = fileActivity.getSupportFragmentManager();
+ FragmentTransaction ft = fm.beginTransaction();
+ ft.addToBackStack(null);
+
+ SendFilesDialog sendFilesDialog = SendFilesDialog.newInstance(files);
+ sendFilesDialog.show(ft, "TAG_SEND_SHARE_DIALOG");
+ }
+
public void sendShareFile(OCFile file) {
sendShareFile(file, !file.canReshare());
}
diff --git a/src/main/res/layout/send_files_fragment.xml b/src/main/res/layout/send_files_fragment.xml
new file mode 100644
index 0000000000..07b1d96acc
--- /dev/null
+++ b/src/main/res/layout/send_files_fragment.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
diff --git a/src/main/res/menu/item_file.xml b/src/main/res/menu/item_file.xml
index 26a5e3ee76..02955f6cf1 100644
--- a/src/main/res/menu/item_file.xml
+++ b/src/main/res/menu/item_file.xml
@@ -75,7 +75,7 @@
android:title="@string/stream"
app:showAsAction="never"
android:showAsAction="never"
- android:orderInCategory="1"/>
+ android:orderInCategory="1" />
+
+