mirror of
https://github.com/nextcloud/talk-android
synced 2025-07-15 08:45:04 +01:00
Merge pull request #1252 from nextcloud/feature/308/share-with
add ability to "share to" files to nextcloud talk app
This commit is contained in:
commit
e23fd77609
@ -63,6 +63,7 @@
|
||||
android:name="android.permission.USE_CREDENTIALS"
|
||||
android:maxSdkVersion="22" />
|
||||
<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.USE_BIOMETRIC" />
|
||||
@ -96,6 +97,18 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</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>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
@ -307,9 +307,7 @@ class MainActivity : BaseActivity(), ActionBarProvider {
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
super.onNewIntent(intent)
|
||||
|
||||
handleActionFromContact(intent)
|
||||
|
||||
if (intent.hasExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL)) {
|
||||
if (intent.getBooleanExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL, false)) {
|
||||
router!!.pushController(
|
||||
|
@ -26,6 +26,7 @@ import android.app.Activity.RESULT_OK
|
||||
import android.content.ClipData
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.PorterDuff
|
||||
@ -263,6 +264,9 @@ class ChatController(args: Bundle) :
|
||||
var pastPreconditionFailed = false
|
||||
var futurePreconditionFailed = false
|
||||
|
||||
val filesToUpload: MutableList<String> = ArrayList()
|
||||
var sharedText: String
|
||||
|
||||
init {
|
||||
setHasOptionsMenu(true)
|
||||
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
|
||||
@ -270,6 +274,7 @@ class ChatController(args: Bundle) :
|
||||
this.conversationUser = args.getParcelable(BundleKeys.KEY_USER_ENTITY)
|
||||
this.roomId = args.getString(BundleKeys.KEY_ROOM_ID, "")
|
||||
this.roomToken = args.getString(BundleKeys.KEY_ROOM_TOKEN, "")
|
||||
this.sharedText = args.getString(BundleKeys.KEY_SHARED_TEXT, "")
|
||||
|
||||
if (args.containsKey(BundleKeys.KEY_ACTIVE_CONVERSATION)) {
|
||||
this.currentConversation =
|
||||
@ -547,7 +552,7 @@ class ChatController(args: Bundle) :
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||
if (s.length >= lengthFilter) {
|
||||
messageInput?.error = String.format(
|
||||
Objects.requireNonNull<Resources> (resources).getString(R.string.nc_limit_hit),
|
||||
Objects.requireNonNull<Resources>(resources).getString(R.string.nc_limit_hit),
|
||||
Integer.toString(lengthFilter)
|
||||
)
|
||||
} else {
|
||||
@ -580,6 +585,7 @@ class ChatController(args: Bundle) :
|
||||
}
|
||||
})
|
||||
|
||||
messageInput?.setText(sharedText)
|
||||
messageInputView?.setAttachmentsListener {
|
||||
activity?.let { AttachmentDialog(it, this).show() }
|
||||
}
|
||||
@ -683,27 +689,27 @@ class ChatController(args: Bundle) :
|
||||
if (resultCode == RESULT_OK) {
|
||||
try {
|
||||
checkNotNull(intent)
|
||||
val files: MutableList<String> = ArrayList()
|
||||
filesToUpload.clear()
|
||||
intent.clipData?.let {
|
||||
for (index in 0 until it.itemCount) {
|
||||
files.add(it.getItemAt(index).uri.toString())
|
||||
filesToUpload.add(it.getItemAt(index).uri.toString())
|
||||
}
|
||||
} ?: run {
|
||||
checkNotNull(intent.data)
|
||||
intent.data.let {
|
||||
files.add(intent.data.toString())
|
||||
filesToUpload.add(intent.data.toString())
|
||||
}
|
||||
}
|
||||
require(files.isNotEmpty())
|
||||
require(filesToUpload.isNotEmpty())
|
||||
|
||||
val filenamesWithLinebreaks = StringBuilder("\n")
|
||||
|
||||
for (file in files) {
|
||||
for (file in filesToUpload) {
|
||||
val filename = UriUtils.getFileName(Uri.parse(file), context)
|
||||
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 {
|
||||
String.format(it, title)
|
||||
}
|
||||
@ -717,11 +723,11 @@ class ChatController(args: Bundle) :
|
||||
.setTitle(confirmationQuestion)
|
||||
.setMessage(filenamesWithLinebreaks.toString())
|
||||
.setPositiveButton(R.string.nc_yes) { v ->
|
||||
uploadFiles(files)
|
||||
Toast.makeText(
|
||||
context, context?.resources?.getString(R.string.nc_upload_in_progess),
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
if (UploadAndShareFilesWorker.isStoragePermissionGranted(context!!)) {
|
||||
uploadFiles(filesToUpload)
|
||||
} else {
|
||||
UploadAndShareFilesWorker.requestStoragePermission(this)
|
||||
}
|
||||
}
|
||||
.setNegativeButton(R.string.nc_no) {}
|
||||
.show()
|
||||
@ -738,6 +744,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>) {
|
||||
try {
|
||||
require(files.isNotEmpty())
|
||||
@ -750,6 +765,11 @@ class ChatController(args: Bundle) :
|
||||
.setInputData(data)
|
||||
.build()
|
||||
WorkManager.getInstance().enqueue(uploadWorker)
|
||||
|
||||
Toast.makeText(
|
||||
context, context?.getString(R.string.nc_upload_in_progess),
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
} catch (e: IllegalArgumentException) {
|
||||
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)
|
||||
@ -1307,9 +1327,9 @@ class ChatController(args: Bundle) :
|
||||
TextUtils.isEmpty(chatMessageList[i + 1].systemMessage) &&
|
||||
chatMessageList[i + 1].actorId == chatMessageList[i].actorId &&
|
||||
countGroupedMessages < 4 && DateFormatter.isSameDay(
|
||||
chatMessageList[i].createdAt,
|
||||
chatMessageList[i + 1].createdAt
|
||||
)
|
||||
chatMessageList[i].createdAt,
|
||||
chatMessageList[i + 1].createdAt
|
||||
)
|
||||
) {
|
||||
chatMessageList[i].isGrouped = true
|
||||
countGroupedMessages++
|
||||
@ -1620,7 +1640,8 @@ class ChatController(args: Bundle) :
|
||||
true
|
||||
}
|
||||
R.id.action_reply_privately -> {
|
||||
val apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1))
|
||||
val apiVersion =
|
||||
ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1))
|
||||
val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
|
||||
apiVersion,
|
||||
conversationUser?.baseUrl,
|
||||
@ -1669,6 +1690,7 @@ class ChatController(args: Bundle) :
|
||||
override fun onError(e: Throwable) {
|
||||
Log.e(TAG, e.message, e)
|
||||
}
|
||||
|
||||
override fun onComplete() {}
|
||||
})
|
||||
}
|
||||
@ -1676,6 +1698,7 @@ class ChatController(args: Bundle) :
|
||||
override fun onError(e: Throwable) {
|
||||
Log.e(TAG, e.message, e)
|
||||
}
|
||||
|
||||
override fun onComplete() {}
|
||||
})
|
||||
true
|
||||
|
@ -25,8 +25,12 @@ package com.nextcloud.talk.controllers;
|
||||
import android.animation.AnimatorInflater;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.SearchManager;
|
||||
import android.content.ClipData;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
@ -42,6 +46,7 @@ import android.view.ViewGroup;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.bluelinelabs.conductor.RouterTransaction;
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
|
||||
@ -73,6 +78,7 @@ import com.nextcloud.talk.interfaces.ConversationMenuInterface;
|
||||
import com.nextcloud.talk.jobs.AccountRemovalWorker;
|
||||
import com.nextcloud.talk.jobs.ContactAddressBookWorker;
|
||||
import com.nextcloud.talk.jobs.DeleteConversationWorker;
|
||||
import com.nextcloud.talk.jobs.UploadAndShareFilesWorker;
|
||||
import com.nextcloud.talk.models.database.UserEntity;
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation;
|
||||
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.DisplayUtils;
|
||||
import com.nextcloud.talk.utils.KeyboardUtils;
|
||||
import com.nextcloud.talk.utils.UriUtils;
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys;
|
||||
import com.nextcloud.talk.utils.database.user.UserUtils;
|
||||
import com.nextcloud.talk.utils.preferences.AppPreferences;
|
||||
@ -128,7 +135,7 @@ public class ConversationsListController extends BaseController implements Searc
|
||||
FlexibleAdapter.OnItemClickListener, FlexibleAdapter.OnItemLongClickListener, FastScroller
|
||||
.OnScrollStateChangeListener, ConversationMenuInterface {
|
||||
|
||||
public static final String TAG = "ConversationsListController";
|
||||
public static final String TAG = "ConvListController";
|
||||
public static final int ID_DELETE_CONVERSATION_DIALOG = 0;
|
||||
private static final String KEY_SEARCH_QUERY = "ContactsController.searchQuery";
|
||||
@Inject
|
||||
@ -187,6 +194,14 @@ public class ConversationsListController extends BaseController implements Searc
|
||||
|
||||
private Bundle conversationMenuBundle = null;
|
||||
|
||||
private boolean showShareToScreen = false;
|
||||
private boolean shareToScreenWasShown = false;
|
||||
|
||||
private ArrayList<String> filesToShare;
|
||||
private Conversation selectedConversation;
|
||||
|
||||
private String textToPaste = "";
|
||||
|
||||
public ConversationsListController() {
|
||||
super();
|
||||
setHasOptionsMenu(true);
|
||||
@ -262,7 +277,6 @@ public class ConversationsListController extends BaseController implements Searc
|
||||
if (!eventBus.isRegistered(this)) {
|
||||
eventBus.register(this);
|
||||
}
|
||||
|
||||
currentUser = userUtils.getCurrentUser();
|
||||
|
||||
if (currentUser != null) {
|
||||
@ -322,73 +336,88 @@ public class ConversationsListController extends BaseController implements Searc
|
||||
|
||||
searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
|
||||
|
||||
MainActivity activity = (MainActivity) getActivity();
|
||||
showShareToScreen = !shareToScreenWasShown && hasActivityActionSendIntent();
|
||||
|
||||
searchItem.setVisible(callItems.size() > 0);
|
||||
if (adapter.hasFilter()) {
|
||||
showSearchView(activity, searchView, searchItem);
|
||||
searchView.setQuery(adapter.getFilter(String.class), false);
|
||||
}
|
||||
if (showShareToScreen) {
|
||||
hideSearchBar();
|
||||
getActionBar().setTitle(R.string.send_to_three_dots);
|
||||
} else {
|
||||
MainActivity activity = (MainActivity) getActivity();
|
||||
|
||||
if (activity != null) {
|
||||
activity.binding.searchText.setOnClickListener(v -> {
|
||||
showSearchView(activity, searchView, searchItem);
|
||||
if (getResources() != null) {
|
||||
DisplayUtils.applyColorToStatusBar(
|
||||
activity,
|
||||
ResourcesCompat.getColor(getResources(), R.color.appbar, null)
|
||||
);
|
||||
searchItem.setVisible(callItems.size() > 0);
|
||||
if (activity != null) {
|
||||
if (adapter.hasFilter()) {
|
||||
showSearchView(activity, searchView, searchItem);
|
||||
searchView.setQuery(adapter.getFilter(String.class), false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
searchView.setOnCloseListener(() -> {
|
||||
if (TextUtils.isEmpty(searchView.getQuery().toString())) {
|
||||
searchView.onActionViewCollapsed();
|
||||
if (activity != null && getResources() != null) {
|
||||
DisplayUtils.applyColorToStatusBar(
|
||||
activity,
|
||||
ResourcesCompat.getColor(getResources(), R.color.bg_default, null)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
searchView.post(() -> searchView.setQuery("", true));
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
searchItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
|
||||
@Override
|
||||
public boolean onMenuItemActionExpand(MenuItem item) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemActionCollapse(MenuItem item) {
|
||||
searchView.onActionViewCollapsed();
|
||||
MainActivity activity = (MainActivity) getActivity();
|
||||
if (activity != null) {
|
||||
activity.binding.appBar.setStateListAnimator(AnimatorInflater.loadStateListAnimator(
|
||||
activity.binding.appBar.getContext(),
|
||||
R.animator.appbar_elevation_off)
|
||||
);
|
||||
activity.binding.toolbar.setVisibility(View.GONE);
|
||||
activity.binding.searchToolbar.setVisibility(View.VISIBLE);
|
||||
activity.binding.searchText.setOnClickListener(v -> {
|
||||
showSearchView(activity, searchView, searchItem);
|
||||
if (getResources() != null) {
|
||||
DisplayUtils.applyColorToStatusBar(
|
||||
activity,
|
||||
ResourcesCompat.getColor(getResources(), R.color.appbar, null)
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
searchView.setOnCloseListener(() -> {
|
||||
if (TextUtils.isEmpty(searchView.getQuery().toString())) {
|
||||
searchView.onActionViewCollapsed();
|
||||
if (activity != null && getResources() != null) {
|
||||
DisplayUtils.applyColorToStatusBar(
|
||||
activity,
|
||||
ResourcesCompat.getColor(getResources(), R.color.bg_default, null)
|
||||
);
|
||||
}
|
||||
}
|
||||
SmoothScrollLinearLayoutManager layoutManager =
|
||||
(SmoothScrollLinearLayoutManager) recyclerView.getLayoutManager();
|
||||
if (layoutManager != null) {
|
||||
layoutManager.scrollToPositionWithOffset(0, 0);
|
||||
} else {
|
||||
searchView.post(() -> searchView.setQuery(TAG, true));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
searchItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
|
||||
@Override
|
||||
public boolean onMenuItemActionExpand(MenuItem item) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemActionCollapse(MenuItem item) {
|
||||
searchView.onActionViewCollapsed();
|
||||
MainActivity activity = (MainActivity) getActivity();
|
||||
if (activity != null) {
|
||||
activity.binding.appBar.setStateListAnimator(AnimatorInflater.loadStateListAnimator(
|
||||
activity.binding.appBar.getContext(),
|
||||
R.animator.appbar_elevation_off)
|
||||
);
|
||||
activity.binding.toolbar.setVisibility(View.GONE);
|
||||
activity.binding.searchToolbar.setVisibility(View.VISIBLE);
|
||||
if (getResources() != null) {
|
||||
DisplayUtils.applyColorToStatusBar(
|
||||
activity,
|
||||
ResourcesCompat.getColor(getResources(), R.color.bg_default, null)
|
||||
);
|
||||
}
|
||||
}
|
||||
SmoothScrollLinearLayoutManager layoutManager =
|
||||
(SmoothScrollLinearLayoutManager) recyclerView.getLayoutManager();
|
||||
if (layoutManager != null) {
|
||||
layoutManager.scrollToPositionWithOffset(0, 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
@ -414,7 +443,7 @@ public class ConversationsListController extends BaseController implements Searc
|
||||
|
||||
callItems = new ArrayList<>();
|
||||
|
||||
int apiVersion = ApiUtils.getConversationApiVersion(currentUser, new int[] {ApiUtils.APIv4, ApiUtils.APIv3, 1});
|
||||
int apiVersion = ApiUtils.getConversationApiVersion(currentUser, new int[]{ApiUtils.APIv4, ApiUtils.APIv3, 1});
|
||||
|
||||
roomsQueryDisposable = ncApi.getRooms(credentials, ApiUtils.getUrlForRooms(apiVersion,
|
||||
currentUser.getBaseUrl()))
|
||||
@ -716,39 +745,79 @@ public class ConversationsListController extends BaseController implements Searc
|
||||
|
||||
@Override
|
||||
public boolean onItemClick(View view, int position) {
|
||||
Object clickedItem = adapter.getItem(position);
|
||||
if (clickedItem != null && getActivity() != null) {
|
||||
Conversation conversation;
|
||||
if (shouldUseLastMessageLayout) {
|
||||
conversation = ((ConversationItem) clickedItem).getModel();
|
||||
selectedConversation = getConversation(position);
|
||||
if (selectedConversation != null && getActivity() != null) {
|
||||
if (showShareToScreen) {
|
||||
shareToScreenWasShown = true;
|
||||
handleSharedData();
|
||||
} else {
|
||||
conversation = ((CallItem) clickedItem).getModel();
|
||||
}
|
||||
|
||||
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);
|
||||
openConversation();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void handleSharedData() {
|
||||
collectDataFromIntent();
|
||||
if (!textToPaste.isEmpty()) {
|
||||
openConversation(textToPaste);
|
||||
} else if (filesToShare != null && !filesToShare.isEmpty()) {
|
||||
showSendFilesConfirmDialog();
|
||||
} else {
|
||||
Toast.makeText(context, context.getResources().getString(R.string.nc_common_error_sorry), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
private void showSendFilesConfirmDialog() {
|
||||
if (UploadAndShareFilesWorker.Companion.isStoragePermissionGranted(context)) {
|
||||
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
|
||||
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);
|
||||
if (clickedItem != null) {
|
||||
Conversation conversation;
|
||||
@ -764,6 +833,123 @@ public class ConversationsListController extends BaseController implements Searc
|
||||
}
|
||||
}
|
||||
|
||||
private void collectDataFromIntent() {
|
||||
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())) {
|
||||
try {
|
||||
if (intent.getClipData() != null) {
|
||||
for (int i = 0; i < intent.getClipData().getItemCount(); i++) {
|
||||
ClipData.Item item = intent.getClipData().getItemAt(i);
|
||||
if (item.getUri() != null) {
|
||||
filesToShare.add(intent.getClipData().getItemAt(i).getUri().toString());
|
||||
} else if (item.getText() != null) {
|
||||
textToPaste = intent.getClipData().getItemAt(i).getText().toString();
|
||||
break;
|
||||
} else {
|
||||
Log.w(TAG, "datatype not yet implemented for share-to");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
filesToShare.add(intent.getData().toString());
|
||||
}
|
||||
if (filesToShare.isEmpty() && textToPaste.isEmpty()) {
|
||||
Toast.makeText(context, context.getResources().getString(R.string.nc_common_error_sorry),
|
||||
Toast.LENGTH_LONG).show();
|
||||
Log.e(TAG, "failed to get data from intent");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Toast.makeText(context, context.getResources().getString(R.string.nc_common_error_sorry),
|
||||
Toast.LENGTH_LONG).show();
|
||||
Log.e(TAG, "Something went wrong when extracting data from intent");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void upload() {
|
||||
if (selectedConversation == null) {
|
||||
Toast.makeText(context, context.getResources().getString(R.string.nc_common_error_sorry),
|
||||
Toast.LENGTH_LONG).show();
|
||||
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");
|
||||
showSendFilesConfirmDialog();
|
||||
} else {
|
||||
Toast.makeText(context, context.getString(R.string.read_storage_no_permission), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
private void openConversation() {
|
||||
openConversation("");
|
||||
}
|
||||
|
||||
private void openConversation(String textToPaste) {
|
||||
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());
|
||||
bundle.putString(BundleKeys.INSTANCE.getKEY_SHARED_TEXT(), textToPaste);
|
||||
|
||||
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)
|
||||
public void onMessageEvent(EventStatus eventStatus) {
|
||||
if (currentUser != null && eventStatus.getUserId() == currentUser.getId()) {
|
||||
|
@ -169,13 +169,7 @@ public abstract class BaseController extends ButterKnifeController {
|
||||
R.animator.appbar_elevation_off)
|
||||
);
|
||||
} else {
|
||||
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)
|
||||
);
|
||||
hideSearchBar();
|
||||
}
|
||||
|
||||
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
|
||||
protected void onDetach(@NonNull View view) {
|
||||
super.onDetach(view);
|
||||
|
@ -20,15 +20,21 @@
|
||||
|
||||
package com.nextcloud.talk.jobs
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.PermissionChecker
|
||||
import androidx.work.Data
|
||||
import androidx.work.OneTimeWorkRequest
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
import autodagger.AutoInjector
|
||||
import com.bluelinelabs.conductor.Controller
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.api.NcApi
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.models.database.UserEntity
|
||||
@ -69,6 +75,15 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
|
||||
override fun doWork(): Result {
|
||||
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 {
|
||||
val currentUser = userUtils.currentUser
|
||||
val sourcefiles = inputData.getStringArray(DEVICE_SOURCEFILES)
|
||||
@ -88,9 +103,13 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
|
||||
uploadFile(currentUser, ncTargetpath, filename, roomToken, requestBody, sourcefileUri)
|
||||
}
|
||||
} catch (e: IllegalStateException) {
|
||||
Toast.makeText(context, context.resources.getString(R.string.nc_common_error_sorry), Toast.LENGTH_LONG)
|
||||
.show()
|
||||
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
|
||||
return Result.failure()
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Toast.makeText(context, context.resources.getString(R.string.nc_common_error_sorry), Toast.LENGTH_LONG)
|
||||
.show()
|
||||
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
|
||||
return Result.failure()
|
||||
}
|
||||
@ -105,6 +124,8 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
|
||||
while (input.read(buf) != -1)
|
||||
requestBody = RequestBody.create("application/octet-stream".toMediaTypeOrNull(), buf)
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(context, context.resources.getString(R.string.nc_common_error_sorry), Toast.LENGTH_LONG)
|
||||
.show()
|
||||
Log.e(javaClass.simpleName, "failed to create RequestBody for $sourcefileUri", e)
|
||||
}
|
||||
return requestBody
|
||||
@ -133,6 +154,8 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Toast.makeText(context, context.resources.getString(R.string.nc_common_error_sorry), Toast.LENGTH_LONG)
|
||||
.show()
|
||||
Log.e(TAG, "failed to upload file $filename")
|
||||
}
|
||||
|
||||
@ -172,8 +195,37 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
|
||||
|
||||
companion object {
|
||||
const val TAG = "UploadFileWorker"
|
||||
const val REQUEST_PERMISSION = 3123
|
||||
const val DEVICE_SOURCEFILES = "DEVICE_SOURCEFILES"
|
||||
const val NC_TARGETPATH = "NC_TARGETPATH"
|
||||
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,28 +26,29 @@ import android.net.Uri
|
||||
import android.provider.OpenableColumns
|
||||
import android.util.Log
|
||||
|
||||
object UriUtils {
|
||||
|
||||
fun getFileName(uri: Uri, context: Context?): String {
|
||||
var filename: String? = null
|
||||
if (uri.scheme == "content" && context != null) {
|
||||
val cursor: Cursor? = context.contentResolver.query(uri, null, null, null, null)
|
||||
try {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME))
|
||||
class UriUtils {
|
||||
companion object {
|
||||
fun getFileName(uri: Uri, context: Context?): String {
|
||||
var filename: String? = null
|
||||
if (uri.scheme == "content" && context != null) {
|
||||
val cursor: Cursor? = context.contentResolver.query(uri, null, null, null, null)
|
||||
try {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME))
|
||||
}
|
||||
} finally {
|
||||
cursor?.close()
|
||||
}
|
||||
} finally {
|
||||
cursor?.close()
|
||||
}
|
||||
}
|
||||
if (filename == null) {
|
||||
Log.e("UriUtils", "failed to get DISPLAY_NAME from uri. using fallback.")
|
||||
filename = uri.path
|
||||
val lastIndexOfSlash = filename!!.lastIndexOf('/')
|
||||
if (lastIndexOfSlash != -1) {
|
||||
filename = filename.substring(lastIndexOfSlash + 1)
|
||||
if (filename == null) {
|
||||
Log.d("UriUtils", "failed to get DISPLAY_NAME from uri. using fallback.")
|
||||
filename = uri.path
|
||||
val lastIndexOfSlash = filename!!.lastIndexOf('/')
|
||||
if (lastIndexOfSlash != -1) {
|
||||
filename = filename.substring(lastIndexOfSlash + 1)
|
||||
}
|
||||
}
|
||||
return filename
|
||||
}
|
||||
return filename
|
||||
}
|
||||
}
|
||||
|
@ -65,4 +65,5 @@ object BundleKeys {
|
||||
val KEY_ACCOUNT = "KEY_ACCOUNT"
|
||||
val KEY_FILE_ID = "KEY_FILE_ID"
|
||||
val KEY_NOTIFICATION_ID = "KEY_NOTIFICATION_ID"
|
||||
val KEY_SHARED_TEXT = "KEY_SHARED_TEXT"
|
||||
}
|
||||
|
@ -362,6 +362,8 @@
|
||||
|
||||
<string name="share">Share</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>
|
||||
|
||||
<!-- Upload -->
|
||||
@ -425,6 +427,7 @@
|
||||
|
||||
<!-- Non-translatable strings -->
|
||||
<string name="tooManyUnreadMessages" translatable="false">999+</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="selected_list_item">selected</string>
|
||||
|
@ -1 +1 @@
|
||||
458
|
||||
457
|
Loading…
Reference in New Issue
Block a user