mirror of
https://github.com/nextcloud/talk-android
synced 2025-07-11 14:54:09 +01:00
add ability to "share to" files to nextcloud talk app
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This commit is contained in:
parent
74f6f91dc9
commit
36fdd6d82b
@ -63,6 +63,7 @@
|
|||||||
android:name="android.permission.USE_CREDENTIALS"
|
android:name="android.permission.USE_CREDENTIALS"
|
||||||
android:maxSdkVersion="22" />
|
android:maxSdkVersion="22" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||||
@ -96,6 +97,18 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="*/*" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="*/*" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
@ -307,9 +307,7 @@ class MainActivity : BaseActivity(), ActionBarProvider {
|
|||||||
|
|
||||||
override fun onNewIntent(intent: Intent) {
|
override fun onNewIntent(intent: Intent) {
|
||||||
super.onNewIntent(intent)
|
super.onNewIntent(intent)
|
||||||
|
|
||||||
handleActionFromContact(intent)
|
handleActionFromContact(intent)
|
||||||
|
|
||||||
if (intent.hasExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL)) {
|
if (intent.hasExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL)) {
|
||||||
if (intent.getBooleanExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL, false)) {
|
if (intent.getBooleanExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL, false)) {
|
||||||
router!!.pushController(
|
router!!.pushController(
|
||||||
|
@ -26,6 +26,7 @@ import android.app.Activity.RESULT_OK
|
|||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.PorterDuff
|
import android.graphics.PorterDuff
|
||||||
@ -263,6 +264,8 @@ class ChatController(args: Bundle) :
|
|||||||
var pastPreconditionFailed = false
|
var pastPreconditionFailed = false
|
||||||
var futurePreconditionFailed = false
|
var futurePreconditionFailed = false
|
||||||
|
|
||||||
|
val filesToUpload: MutableList<String> = ArrayList()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
|
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
|
||||||
@ -683,27 +686,27 @@ class ChatController(args: Bundle) :
|
|||||||
if (resultCode == RESULT_OK) {
|
if (resultCode == RESULT_OK) {
|
||||||
try {
|
try {
|
||||||
checkNotNull(intent)
|
checkNotNull(intent)
|
||||||
val files: MutableList<String> = ArrayList()
|
filesToUpload.clear()
|
||||||
intent.clipData?.let {
|
intent.clipData?.let {
|
||||||
for (index in 0 until it.itemCount) {
|
for (index in 0 until it.itemCount) {
|
||||||
files.add(it.getItemAt(index).uri.toString())
|
filesToUpload.add(it.getItemAt(index).uri.toString())
|
||||||
}
|
}
|
||||||
} ?: run {
|
} ?: run {
|
||||||
checkNotNull(intent.data)
|
checkNotNull(intent.data)
|
||||||
intent.data.let {
|
intent.data.let {
|
||||||
files.add(intent.data.toString())
|
filesToUpload.add(intent.data.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
require(files.isNotEmpty())
|
require(filesToUpload.isNotEmpty())
|
||||||
|
|
||||||
val filenamesWithLinebreaks = StringBuilder("\n")
|
val filenamesWithLinebreaks = StringBuilder("\n")
|
||||||
|
|
||||||
for (file in files) {
|
for (file in filesToUpload) {
|
||||||
val filename = UriUtils.getFileName(Uri.parse(file), context)
|
val filename = UriUtils.getFileName(Uri.parse(file), context)
|
||||||
filenamesWithLinebreaks.append(filename).append("\n")
|
filenamesWithLinebreaks.append(filename).append("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
val confirmationQuestion = when (files.size) {
|
val confirmationQuestion = when (filesToUpload.size) {
|
||||||
1 -> context?.resources?.getString(R.string.nc_upload_confirm_send_single)?.let {
|
1 -> context?.resources?.getString(R.string.nc_upload_confirm_send_single)?.let {
|
||||||
String.format(it, title)
|
String.format(it, title)
|
||||||
}
|
}
|
||||||
@ -717,11 +720,11 @@ class ChatController(args: Bundle) :
|
|||||||
.setTitle(confirmationQuestion)
|
.setTitle(confirmationQuestion)
|
||||||
.setMessage(filenamesWithLinebreaks.toString())
|
.setMessage(filenamesWithLinebreaks.toString())
|
||||||
.setPositiveButton(R.string.nc_yes) { v ->
|
.setPositiveButton(R.string.nc_yes) { v ->
|
||||||
uploadFiles(files)
|
if (UploadAndShareFilesWorker.isStoragePermissionGranted(context!!)) {
|
||||||
Toast.makeText(
|
uploadFiles(filesToUpload)
|
||||||
context, context?.resources?.getString(R.string.nc_upload_in_progess),
|
} else {
|
||||||
Toast.LENGTH_LONG
|
UploadAndShareFilesWorker.requestStoragePermission(this)
|
||||||
).show()
|
}
|
||||||
}
|
}
|
||||||
.setNegativeButton(R.string.nc_no) {}
|
.setNegativeButton(R.string.nc_no) {}
|
||||||
.show()
|
.show()
|
||||||
@ -738,6 +741,15 @@ class ChatController(args: Bundle) :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||||
|
if (requestCode == UploadAndShareFilesWorker.REQUEST_PERMISSION && grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
Log.d(ConversationsListController.TAG, "upload starting after permissions were granted")
|
||||||
|
uploadFiles(filesToUpload)
|
||||||
|
} else {
|
||||||
|
Toast.makeText(context, context?.getString(R.string.read_storage_no_permission), Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun uploadFiles(files: MutableList<String>) {
|
private fun uploadFiles(files: MutableList<String>) {
|
||||||
try {
|
try {
|
||||||
require(files.isNotEmpty())
|
require(files.isNotEmpty())
|
||||||
@ -750,6 +762,11 @@ class ChatController(args: Bundle) :
|
|||||||
.setInputData(data)
|
.setInputData(data)
|
||||||
.build()
|
.build()
|
||||||
WorkManager.getInstance().enqueue(uploadWorker)
|
WorkManager.getInstance().enqueue(uploadWorker)
|
||||||
|
|
||||||
|
Toast.makeText(
|
||||||
|
context, context?.getString(R.string.nc_upload_in_progess),
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show()
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
Toast.makeText(context, context?.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG).show()
|
Toast.makeText(context, context?.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG).show()
|
||||||
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
|
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
|
||||||
|
@ -26,7 +26,10 @@ import android.animation.AnimatorInflater;
|
|||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.SearchManager;
|
import android.app.SearchManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@ -42,6 +45,7 @@ import android.view.ViewGroup;
|
|||||||
import android.view.inputmethod.EditorInfo;
|
import android.view.inputmethod.EditorInfo;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.bluelinelabs.conductor.RouterTransaction;
|
import com.bluelinelabs.conductor.RouterTransaction;
|
||||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
|
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
|
||||||
@ -54,6 +58,7 @@ import com.facebook.imagepipeline.core.ImagePipeline;
|
|||||||
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;
|
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;
|
||||||
import com.facebook.imagepipeline.image.CloseableImage;
|
import com.facebook.imagepipeline.image.CloseableImage;
|
||||||
import com.facebook.imagepipeline.request.ImageRequest;
|
import com.facebook.imagepipeline.request.ImageRequest;
|
||||||
|
import com.google.android.material.appbar.AppBarLayout;
|
||||||
import com.google.android.material.button.MaterialButton;
|
import com.google.android.material.button.MaterialButton;
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
import com.kennyc.bottomsheet.BottomSheet;
|
import com.kennyc.bottomsheet.BottomSheet;
|
||||||
@ -73,6 +78,7 @@ import com.nextcloud.talk.interfaces.ConversationMenuInterface;
|
|||||||
import com.nextcloud.talk.jobs.AccountRemovalWorker;
|
import com.nextcloud.talk.jobs.AccountRemovalWorker;
|
||||||
import com.nextcloud.talk.jobs.ContactAddressBookWorker;
|
import com.nextcloud.talk.jobs.ContactAddressBookWorker;
|
||||||
import com.nextcloud.talk.jobs.DeleteConversationWorker;
|
import com.nextcloud.talk.jobs.DeleteConversationWorker;
|
||||||
|
import com.nextcloud.talk.jobs.UploadAndShareFilesWorker;
|
||||||
import com.nextcloud.talk.models.database.UserEntity;
|
import com.nextcloud.talk.models.database.UserEntity;
|
||||||
import com.nextcloud.talk.models.json.conversations.Conversation;
|
import com.nextcloud.talk.models.json.conversations.Conversation;
|
||||||
import com.nextcloud.talk.models.json.participants.Participant;
|
import com.nextcloud.talk.models.json.participants.Participant;
|
||||||
@ -81,6 +87,7 @@ import com.nextcloud.talk.utils.ApiUtils;
|
|||||||
import com.nextcloud.talk.utils.ConductorRemapping;
|
import com.nextcloud.talk.utils.ConductorRemapping;
|
||||||
import com.nextcloud.talk.utils.DisplayUtils;
|
import com.nextcloud.talk.utils.DisplayUtils;
|
||||||
import com.nextcloud.talk.utils.KeyboardUtils;
|
import com.nextcloud.talk.utils.KeyboardUtils;
|
||||||
|
import com.nextcloud.talk.utils.UriUtils;
|
||||||
import com.nextcloud.talk.utils.bundle.BundleKeys;
|
import com.nextcloud.talk.utils.bundle.BundleKeys;
|
||||||
import com.nextcloud.talk.utils.database.user.UserUtils;
|
import com.nextcloud.talk.utils.database.user.UserUtils;
|
||||||
import com.nextcloud.talk.utils.preferences.AppPreferences;
|
import com.nextcloud.talk.utils.preferences.AppPreferences;
|
||||||
@ -91,11 +98,13 @@ import org.apache.commons.lang3.builder.CompareToBuilder;
|
|||||||
import org.greenrobot.eventbus.EventBus;
|
import org.greenrobot.eventbus.EventBus;
|
||||||
import org.greenrobot.eventbus.Subscribe;
|
import org.greenrobot.eventbus.Subscribe;
|
||||||
import org.greenrobot.eventbus.ThreadMode;
|
import org.greenrobot.eventbus.ThreadMode;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
@ -128,7 +137,7 @@ public class ConversationsListController extends BaseController implements Searc
|
|||||||
FlexibleAdapter.OnItemClickListener, FlexibleAdapter.OnItemLongClickListener, FastScroller
|
FlexibleAdapter.OnItemClickListener, FlexibleAdapter.OnItemLongClickListener, FastScroller
|
||||||
.OnScrollStateChangeListener, ConversationMenuInterface {
|
.OnScrollStateChangeListener, ConversationMenuInterface {
|
||||||
|
|
||||||
public static final String TAG = "ConversationsListController";
|
public static final String TAG = "ConvListController";
|
||||||
public static final int ID_DELETE_CONVERSATION_DIALOG = 0;
|
public static final int ID_DELETE_CONVERSATION_DIALOG = 0;
|
||||||
private static final String KEY_SEARCH_QUERY = "ContactsController.searchQuery";
|
private static final String KEY_SEARCH_QUERY = "ContactsController.searchQuery";
|
||||||
@Inject
|
@Inject
|
||||||
@ -187,6 +196,12 @@ public class ConversationsListController extends BaseController implements Searc
|
|||||||
|
|
||||||
private Bundle conversationMenuBundle = null;
|
private Bundle conversationMenuBundle = null;
|
||||||
|
|
||||||
|
private boolean showShareToScreen = false;
|
||||||
|
private boolean shareToScreenWasShown = false;
|
||||||
|
|
||||||
|
private ArrayList<String> filesToShare;
|
||||||
|
private Conversation selectedConversation;
|
||||||
|
|
||||||
public ConversationsListController() {
|
public ConversationsListController() {
|
||||||
super();
|
super();
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
@ -262,7 +277,6 @@ public class ConversationsListController extends BaseController implements Searc
|
|||||||
if (!eventBus.isRegistered(this)) {
|
if (!eventBus.isRegistered(this)) {
|
||||||
eventBus.register(this);
|
eventBus.register(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentUser = userUtils.getCurrentUser();
|
currentUser = userUtils.getCurrentUser();
|
||||||
|
|
||||||
if (currentUser != null) {
|
if (currentUser != null) {
|
||||||
@ -322,15 +336,21 @@ public class ConversationsListController extends BaseController implements Searc
|
|||||||
|
|
||||||
searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
|
searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
|
||||||
|
|
||||||
|
showShareToScreen = !shareToScreenWasShown && hasActivityActionSendIntent();
|
||||||
|
|
||||||
|
if (showShareToScreen) {
|
||||||
|
hideSearchBar();
|
||||||
|
getActionBar().setTitle(R.string.send_to_three_dots);
|
||||||
|
} else {
|
||||||
MainActivity activity = (MainActivity) getActivity();
|
MainActivity activity = (MainActivity) getActivity();
|
||||||
|
|
||||||
searchItem.setVisible(callItems.size() > 0);
|
searchItem.setVisible(callItems.size() > 0);
|
||||||
|
if (activity != null) {
|
||||||
if (adapter.hasFilter()) {
|
if (adapter.hasFilter()) {
|
||||||
showSearchView(activity, searchView, searchItem);
|
showSearchView(activity, searchView, searchItem);
|
||||||
searchView.setQuery(adapter.getFilter(String.class), false);
|
searchView.setQuery(adapter.getFilter(String.class), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activity != null) {
|
|
||||||
activity.binding.searchText.setOnClickListener(v -> {
|
activity.binding.searchText.setOnClickListener(v -> {
|
||||||
showSearchView(activity, searchView, searchItem);
|
showSearchView(activity, searchView, searchItem);
|
||||||
if (getResources() != null) {
|
if (getResources() != null) {
|
||||||
@ -352,7 +372,7 @@ public class ConversationsListController extends BaseController implements Searc
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
searchView.post(() -> searchView.setQuery("", true));
|
searchView.post(() -> searchView.setQuery(TAG, true));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
@ -390,6 +410,15 @@ public class ConversationsListController extends BaseController implements Searc
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasActivityActionSendIntent() {
|
||||||
|
if (getActivity() != null) {
|
||||||
|
return Intent.ACTION_SEND.equals(getActivity().getIntent().getAction())
|
||||||
|
|| Intent.ACTION_SEND_MULTIPLE.equals(getActivity().getIntent().getAction());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
protected void showSearchOrToolbar() {
|
protected void showSearchOrToolbar() {
|
||||||
if (TextUtils.isEmpty(searchQuery)) {
|
if (TextUtils.isEmpty(searchQuery)) {
|
||||||
@ -716,39 +745,70 @@ public class ConversationsListController extends BaseController implements Searc
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onItemClick(View view, int position) {
|
public boolean onItemClick(View view, int position) {
|
||||||
Object clickedItem = adapter.getItem(position);
|
selectedConversation = getConversation(position);
|
||||||
if (clickedItem != null && getActivity() != null) {
|
if (selectedConversation != null && getActivity() != null) {
|
||||||
Conversation conversation;
|
if (showShareToScreen) {
|
||||||
if (shouldUseLastMessageLayout) {
|
shareToScreenWasShown = true;
|
||||||
conversation = ((ConversationItem) clickedItem).getModel();
|
showShareToConfirmDialog();
|
||||||
} else {
|
} else {
|
||||||
conversation = ((CallItem) clickedItem).getModel();
|
openConversation();
|
||||||
}
|
|
||||||
|
|
||||||
Bundle bundle = new Bundle();
|
|
||||||
bundle.putParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY(), currentUser);
|
|
||||||
bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), conversation.getToken());
|
|
||||||
bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_ID(), conversation.getRoomId());
|
|
||||||
|
|
||||||
if (conversation.hasPassword && (conversation.participantType.equals(Participant.ParticipantType.GUEST) ||
|
|
||||||
conversation.participantType.equals(Participant.ParticipantType.USER_FOLLOWING_LINK))) {
|
|
||||||
bundle.putInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE(), 99);
|
|
||||||
prepareAndShowBottomSheetWithBundle(bundle, false);
|
|
||||||
} else {
|
|
||||||
currentUser = userUtils.getCurrentUser();
|
|
||||||
|
|
||||||
bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ACTIVE_CONVERSATION(), Parcels.wrap(conversation));
|
|
||||||
ConductorRemapping.INSTANCE.remapChatController(getRouter(), currentUser.getId(),
|
|
||||||
conversation.getToken(), bundle, false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showShareToConfirmDialog() {
|
||||||
|
if (UploadAndShareFilesWorker.Companion.isStoragePermissionGranted(context)) {
|
||||||
|
collectFilesToShareFromIntent();
|
||||||
|
|
||||||
|
StringBuilder fileNamesWithLineBreaks = new StringBuilder("\n");
|
||||||
|
|
||||||
|
for (String file : filesToShare) {
|
||||||
|
String filename = UriUtils.Companion.getFileName(Uri.parse(file), context);
|
||||||
|
fileNamesWithLineBreaks.append(filename).append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
String confirmationQuestion;
|
||||||
|
if (filesToShare.size() == 1) {
|
||||||
|
confirmationQuestion =
|
||||||
|
String.format(getResources().getString(R.string.nc_upload_confirm_send_single),
|
||||||
|
selectedConversation.getDisplayName());
|
||||||
|
} else {
|
||||||
|
confirmationQuestion =
|
||||||
|
String.format(getResources().getString(R.string.nc_upload_confirm_send_multiple),
|
||||||
|
selectedConversation.getDisplayName());
|
||||||
|
}
|
||||||
|
|
||||||
|
new LovelyStandardDialog(getActivity())
|
||||||
|
.setPositiveButtonColorRes(R.color.nc_darkGreen)
|
||||||
|
.setTitle(confirmationQuestion)
|
||||||
|
.setMessage(fileNamesWithLineBreaks.toString())
|
||||||
|
.setPositiveButton(R.string.nc_yes, new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
upload();
|
||||||
|
openConversation();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.nc_no, new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
Log.d(TAG, "sharing files aborted");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.show();
|
||||||
|
} else {
|
||||||
|
UploadAndShareFilesWorker.Companion.requestStoragePermission(ConversationsListController.this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onItemLongClick(int position) {
|
public void onItemLongClick(int position) {
|
||||||
if (currentUser.hasSpreedFeatureCapability("last-room-activity")) {
|
|
||||||
|
if (showShareToScreen) {
|
||||||
|
Log.d(TAG, "sharing to multiple rooms not yet implemented. onItemLongClick is ignored.");
|
||||||
|
|
||||||
|
} else if (currentUser.hasSpreedFeatureCapability("last-room-activity")) {
|
||||||
Object clickedItem = adapter.getItem(position);
|
Object clickedItem = adapter.getItem(position);
|
||||||
if (clickedItem != null) {
|
if (clickedItem != null) {
|
||||||
Conversation conversation;
|
Conversation conversation;
|
||||||
@ -764,6 +824,100 @@ public class ConversationsListController extends BaseController implements Searc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void collectFilesToShareFromIntent() {
|
||||||
|
filesToShare = new ArrayList<>();
|
||||||
|
if (getActivity() != null && getActivity().getIntent() != null) {
|
||||||
|
Intent intent = getActivity().getIntent();
|
||||||
|
if (Intent.ACTION_SEND.equals(intent.getAction())
|
||||||
|
|| Intent.ACTION_SEND_MULTIPLE.equals(intent.getAction())) {
|
||||||
|
if (intent.getClipData() != null) {
|
||||||
|
for (int i = 0; i < intent.getClipData().getItemCount(); i++) {
|
||||||
|
filesToShare.add(intent.getClipData().getItemAt(i).getUri().toString());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
filesToShare.add(intent.getData().toString());
|
||||||
|
}
|
||||||
|
if (filesToShare.isEmpty()) {
|
||||||
|
Log.e(TAG, "failed to get files from intent");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void upload() {
|
||||||
|
if (selectedConversation == null) {
|
||||||
|
Log.e(TAG, "not able to upload any files because conversation was null.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
String[] filesToShareArray = new String[filesToShare.size()];
|
||||||
|
filesToShareArray = filesToShare.toArray(filesToShareArray);
|
||||||
|
|
||||||
|
Data data = new Data.Builder()
|
||||||
|
.putStringArray(UploadAndShareFilesWorker.DEVICE_SOURCEFILES, filesToShareArray)
|
||||||
|
.putString(UploadAndShareFilesWorker.NC_TARGETPATH, currentUser.getAttachmentFolder())
|
||||||
|
.putString(UploadAndShareFilesWorker.ROOM_TOKEN, selectedConversation.getToken())
|
||||||
|
.build();
|
||||||
|
OneTimeWorkRequest uploadWorker = new OneTimeWorkRequest.Builder(UploadAndShareFilesWorker.class)
|
||||||
|
.setInputData(data)
|
||||||
|
.build();
|
||||||
|
WorkManager.getInstance().enqueue(uploadWorker);
|
||||||
|
|
||||||
|
Toast.makeText(
|
||||||
|
context, context.getResources().getString(R.string.nc_upload_in_progess),
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show();
|
||||||
|
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
Toast.makeText(context, context.getResources().getString(R.string.nc_upload_failed), Toast.LENGTH_LONG).show();
|
||||||
|
Log.e(TAG, "Something went wrong when trying to upload file", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||||
|
if (requestCode == UploadAndShareFilesWorker.REQUEST_PERMISSION &&
|
||||||
|
grantResults.length > 0 &&
|
||||||
|
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
Log.d(TAG, "upload starting after permissions were granted");
|
||||||
|
showShareToConfirmDialog();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(context, context.getString(R.string.read_storage_no_permission), Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openConversation() {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY(), currentUser);
|
||||||
|
bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), selectedConversation.getToken());
|
||||||
|
bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_ID(), selectedConversation.getRoomId());
|
||||||
|
|
||||||
|
if (selectedConversation.hasPassword && selectedConversation.participantType ==
|
||||||
|
Participant.ParticipantType.GUEST ||
|
||||||
|
selectedConversation.participantType == Participant.ParticipantType.USER_FOLLOWING_LINK) {
|
||||||
|
bundle.putInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE(), 99);
|
||||||
|
prepareAndShowBottomSheetWithBundle(bundle, false);
|
||||||
|
} else {
|
||||||
|
currentUser = userUtils.getCurrentUser();
|
||||||
|
|
||||||
|
bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ACTIVE_CONVERSATION(), Parcels.wrap(selectedConversation));
|
||||||
|
ConductorRemapping.INSTANCE.remapChatController(getRouter(), currentUser.getId(),
|
||||||
|
selectedConversation.getToken(), bundle, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Conversation getConversation(int position) {
|
||||||
|
Object clickedItem = adapter.getItem(position);
|
||||||
|
Conversation conversation;
|
||||||
|
if (shouldUseLastMessageLayout) {
|
||||||
|
conversation = ((ConversationItem) clickedItem).getModel();
|
||||||
|
} else {
|
||||||
|
conversation = ((CallItem) clickedItem).getModel();
|
||||||
|
}
|
||||||
|
return conversation;
|
||||||
|
}
|
||||||
|
|
||||||
@Subscribe(sticky = true, threadMode = ThreadMode.BACKGROUND)
|
@Subscribe(sticky = true, threadMode = ThreadMode.BACKGROUND)
|
||||||
public void onMessageEvent(EventStatus eventStatus) {
|
public void onMessageEvent(EventStatus eventStatus) {
|
||||||
if (currentUser != null && eventStatus.getUserId() == currentUser.getId()) {
|
if (currentUser != null && eventStatus.getUserId() == currentUser.getId()) {
|
||||||
|
@ -169,13 +169,7 @@ public abstract class BaseController extends ButterKnifeController {
|
|||||||
R.animator.appbar_elevation_off)
|
R.animator.appbar_elevation_off)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
activity.binding.searchToolbar.setVisibility(View.GONE);
|
hideSearchBar();
|
||||||
activity.binding.toolbar.setVisibility(View.VISIBLE);
|
|
||||||
layoutParams.setScrollFlags(0);
|
|
||||||
activity.binding.appBar.setStateListAnimator(AnimatorInflater.loadStateListAnimator(
|
|
||||||
activity.binding.appBar.getContext(),
|
|
||||||
R.animator.appbar_elevation_on)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
activity.binding.searchToolbar.setLayoutParams(layoutParams);
|
activity.binding.searchToolbar.setLayoutParams(layoutParams);
|
||||||
@ -204,6 +198,20 @@ public abstract class BaseController extends ButterKnifeController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void hideSearchBar() {
|
||||||
|
MainActivity activity = (MainActivity) getActivity();
|
||||||
|
AppBarLayout.LayoutParams layoutParams =
|
||||||
|
(AppBarLayout.LayoutParams) activity.binding.searchToolbar.getLayoutParams();
|
||||||
|
|
||||||
|
activity.binding.searchToolbar.setVisibility(View.GONE);
|
||||||
|
activity.binding.toolbar.setVisibility(View.VISIBLE);
|
||||||
|
layoutParams.setScrollFlags(0);
|
||||||
|
activity.binding.appBar.setStateListAnimator(AnimatorInflater.loadStateListAnimator(
|
||||||
|
activity.binding.appBar.getContext(),
|
||||||
|
R.animator.appbar_elevation_on)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDetach(@NonNull View view) {
|
protected void onDetach(@NonNull View view) {
|
||||||
super.onDetach(view);
|
super.onDetach(view);
|
||||||
|
@ -20,15 +20,19 @@
|
|||||||
|
|
||||||
package com.nextcloud.talk.jobs
|
package com.nextcloud.talk.jobs
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import androidx.core.content.PermissionChecker
|
||||||
import androidx.work.Data
|
import androidx.work.Data
|
||||||
import androidx.work.OneTimeWorkRequest
|
import androidx.work.OneTimeWorkRequest
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import androidx.work.Worker
|
import androidx.work.Worker
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
import autodagger.AutoInjector
|
import autodagger.AutoInjector
|
||||||
|
import com.bluelinelabs.conductor.Controller
|
||||||
import com.nextcloud.talk.api.NcApi
|
import com.nextcloud.talk.api.NcApi
|
||||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||||
import com.nextcloud.talk.models.database.UserEntity
|
import com.nextcloud.talk.models.database.UserEntity
|
||||||
@ -69,6 +73,15 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
|
|||||||
override fun doWork(): Result {
|
override fun doWork(): Result {
|
||||||
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
|
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
|
||||||
|
|
||||||
|
if (!isStoragePermissionGranted(context)) {
|
||||||
|
Log.w(
|
||||||
|
TAG, "Storage permission is not granted. As a developer please make sure you check for permissions " +
|
||||||
|
"via UploadAndShareFilesWorker.isStoragePermissionGranted() and UploadAndShareFilesWorker" +
|
||||||
|
".requestStoragePermission() beforehand. If you already did but end up with this warning, the user " +
|
||||||
|
"most likely revoked the permission"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val currentUser = userUtils.currentUser
|
val currentUser = userUtils.currentUser
|
||||||
val sourcefiles = inputData.getStringArray(DEVICE_SOURCEFILES)
|
val sourcefiles = inputData.getStringArray(DEVICE_SOURCEFILES)
|
||||||
@ -172,8 +185,37 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "UploadFileWorker"
|
const val TAG = "UploadFileWorker"
|
||||||
|
const val REQUEST_PERMISSION = 3123
|
||||||
const val DEVICE_SOURCEFILES = "DEVICE_SOURCEFILES"
|
const val DEVICE_SOURCEFILES = "DEVICE_SOURCEFILES"
|
||||||
const val NC_TARGETPATH = "NC_TARGETPATH"
|
const val NC_TARGETPATH = "NC_TARGETPATH"
|
||||||
const val ROOM_TOKEN = "ROOM_TOKEN"
|
const val ROOM_TOKEN = "ROOM_TOKEN"
|
||||||
|
|
||||||
|
fun isStoragePermissionGranted(context: Context): Boolean {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
if (PermissionChecker.checkSelfPermission(
|
||||||
|
context,
|
||||||
|
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||||
|
) == PermissionChecker.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
|
Log.d(TAG, "Permission is granted")
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "Permission is revoked")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else { //permission is automatically granted on sdk<23 upon installation
|
||||||
|
Log.d(TAG, "Permission is granted")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun requestStoragePermission(controller: Controller) {
|
||||||
|
controller.requestPermissions(
|
||||||
|
arrayOf(
|
||||||
|
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||||
|
),
|
||||||
|
REQUEST_PERMISSION
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,8 @@ import android.net.Uri
|
|||||||
import android.provider.OpenableColumns
|
import android.provider.OpenableColumns
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
|
||||||
object UriUtils {
|
class UriUtils {
|
||||||
|
companion object {
|
||||||
fun getFileName(uri: Uri, context: Context?): String {
|
fun getFileName(uri: Uri, context: Context?): String {
|
||||||
var filename: String? = null
|
var filename: String? = null
|
||||||
if (uri.scheme == "content" && context != null) {
|
if (uri.scheme == "content" && context != null) {
|
||||||
@ -41,7 +41,7 @@ object UriUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (filename == null) {
|
if (filename == null) {
|
||||||
Log.e("UriUtils", "failed to get DISPLAY_NAME from uri. using fallback.")
|
Log.d("UriUtils", "failed to get DISPLAY_NAME from uri. using fallback.")
|
||||||
filename = uri.path
|
filename = uri.path
|
||||||
val lastIndexOfSlash = filename!!.lastIndexOf('/')
|
val lastIndexOfSlash = filename!!.lastIndexOf('/')
|
||||||
if (lastIndexOfSlash != -1) {
|
if (lastIndexOfSlash != -1) {
|
||||||
@ -51,3 +51,4 @@ object UriUtils {
|
|||||||
return filename
|
return filename
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -362,6 +362,8 @@
|
|||||||
|
|
||||||
<string name="share">Share</string>
|
<string name="share">Share</string>
|
||||||
<string name="send_to">Send to</string>
|
<string name="send_to">Send to</string>
|
||||||
|
<string name="send_to_three_dots">Send to …</string>
|
||||||
|
<string name="read_storage_no_permission">Sharing files from storage is not possible without permissions</string>
|
||||||
<string name="open_in_files_app">Open in Files app</string>
|
<string name="open_in_files_app">Open in Files app</string>
|
||||||
|
|
||||||
<!-- Upload -->
|
<!-- Upload -->
|
||||||
@ -425,6 +427,7 @@
|
|||||||
|
|
||||||
<!-- Non-translatable strings -->
|
<!-- Non-translatable strings -->
|
||||||
<string name="tooManyUnreadMessages" translatable="false">999+</string>
|
<string name="tooManyUnreadMessages" translatable="false">999+</string>
|
||||||
|
|
||||||
<string name="nc_action_open_main_menu">Open main menu</string>
|
<string name="nc_action_open_main_menu">Open main menu</string>
|
||||||
<string name="failed_to_save">Failed to save %1$s</string>
|
<string name="failed_to_save">Failed to save %1$s</string>
|
||||||
<string name="selected_list_item">selected</string>
|
<string name="selected_list_item">selected</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user