mirror of
https://github.com/nextcloud/talk-android
synced 2025-07-16 09:15:30 +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: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,9 @@ class ChatController(args: Bundle) :
|
|||||||
var pastPreconditionFailed = false
|
var pastPreconditionFailed = false
|
||||||
var futurePreconditionFailed = false
|
var futurePreconditionFailed = false
|
||||||
|
|
||||||
|
val filesToUpload: MutableList<String> = ArrayList()
|
||||||
|
var sharedText: String
|
||||||
|
|
||||||
init {
|
init {
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
|
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
|
||||||
@ -270,6 +274,7 @@ class ChatController(args: Bundle) :
|
|||||||
this.conversationUser = args.getParcelable(BundleKeys.KEY_USER_ENTITY)
|
this.conversationUser = args.getParcelable(BundleKeys.KEY_USER_ENTITY)
|
||||||
this.roomId = args.getString(BundleKeys.KEY_ROOM_ID, "")
|
this.roomId = args.getString(BundleKeys.KEY_ROOM_ID, "")
|
||||||
this.roomToken = args.getString(BundleKeys.KEY_ROOM_TOKEN, "")
|
this.roomToken = args.getString(BundleKeys.KEY_ROOM_TOKEN, "")
|
||||||
|
this.sharedText = args.getString(BundleKeys.KEY_SHARED_TEXT, "")
|
||||||
|
|
||||||
if (args.containsKey(BundleKeys.KEY_ACTIVE_CONVERSATION)) {
|
if (args.containsKey(BundleKeys.KEY_ACTIVE_CONVERSATION)) {
|
||||||
this.currentConversation =
|
this.currentConversation =
|
||||||
@ -547,7 +552,7 @@ class ChatController(args: Bundle) :
|
|||||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||||
if (s.length >= lengthFilter) {
|
if (s.length >= lengthFilter) {
|
||||||
messageInput?.error = String.format(
|
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)
|
Integer.toString(lengthFilter)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@ -580,6 +585,7 @@ class ChatController(args: Bundle) :
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
messageInput?.setText(sharedText)
|
||||||
messageInputView?.setAttachmentsListener {
|
messageInputView?.setAttachmentsListener {
|
||||||
activity?.let { AttachmentDialog(it, this).show() }
|
activity?.let { AttachmentDialog(it, this).show() }
|
||||||
}
|
}
|
||||||
@ -683,27 +689,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 +723,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 +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>) {
|
private fun uploadFiles(files: MutableList<String>) {
|
||||||
try {
|
try {
|
||||||
require(files.isNotEmpty())
|
require(files.isNotEmpty())
|
||||||
@ -750,6 +765,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)
|
||||||
@ -1620,7 +1640,8 @@ class ChatController(args: Bundle) :
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.action_reply_privately -> {
|
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(
|
val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
|
||||||
apiVersion,
|
apiVersion,
|
||||||
conversationUser?.baseUrl,
|
conversationUser?.baseUrl,
|
||||||
@ -1669,6 +1690,7 @@ class ChatController(args: Bundle) :
|
|||||||
override fun onError(e: Throwable) {
|
override fun onError(e: Throwable) {
|
||||||
Log.e(TAG, e.message, e)
|
Log.e(TAG, e.message, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onComplete() {}
|
override fun onComplete() {}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1676,6 +1698,7 @@ class ChatController(args: Bundle) :
|
|||||||
override fun onError(e: Throwable) {
|
override fun onError(e: Throwable) {
|
||||||
Log.e(TAG, e.message, e)
|
Log.e(TAG, e.message, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onComplete() {}
|
override fun onComplete() {}
|
||||||
})
|
})
|
||||||
true
|
true
|
||||||
|
@ -25,8 +25,12 @@ package com.nextcloud.talk.controllers;
|
|||||||
import android.animation.AnimatorInflater;
|
import android.animation.AnimatorInflater;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.SearchManager;
|
import android.app.SearchManager;
|
||||||
|
import android.content.ClipData;
|
||||||
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 +46,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;
|
||||||
@ -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;
|
||||||
@ -128,7 +135,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 +194,14 @@ 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;
|
||||||
|
|
||||||
|
private String textToPaste = "";
|
||||||
|
|
||||||
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)) {
|
||||||
@ -414,7 +443,7 @@ public class ConversationsListController extends BaseController implements Searc
|
|||||||
|
|
||||||
callItems = new ArrayList<>();
|
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,
|
roomsQueryDisposable = ncApi.getRooms(credentials, ApiUtils.getUrlForRooms(apiVersion,
|
||||||
currentUser.getBaseUrl()))
|
currentUser.getBaseUrl()))
|
||||||
@ -716,39 +745,79 @@ 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();
|
handleSharedData();
|
||||||
} 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 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
|
@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 +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)
|
@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,21 @@
|
|||||||
|
|
||||||
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 android.widget.Toast
|
||||||
|
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.R
|
||||||
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 +75,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)
|
||||||
@ -88,9 +103,13 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
|
|||||||
uploadFile(currentUser, ncTargetpath, filename, roomToken, requestBody, sourcefileUri)
|
uploadFile(currentUser, ncTargetpath, filename, roomToken, requestBody, sourcefileUri)
|
||||||
}
|
}
|
||||||
} catch (e: IllegalStateException) {
|
} 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)
|
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
|
||||||
return Result.failure()
|
return Result.failure()
|
||||||
} catch (e: IllegalArgumentException) {
|
} 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)
|
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
|
||||||
return Result.failure()
|
return Result.failure()
|
||||||
}
|
}
|
||||||
@ -105,6 +124,8 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
|
|||||||
while (input.read(buf) != -1)
|
while (input.read(buf) != -1)
|
||||||
requestBody = RequestBody.create("application/octet-stream".toMediaTypeOrNull(), buf)
|
requestBody = RequestBody.create("application/octet-stream".toMediaTypeOrNull(), buf)
|
||||||
} catch (e: Exception) {
|
} 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)
|
Log.e(javaClass.simpleName, "failed to create RequestBody for $sourcefileUri", e)
|
||||||
}
|
}
|
||||||
return requestBody
|
return requestBody
|
||||||
@ -133,6 +154,8 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onError(e: Throwable) {
|
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")
|
Log.e(TAG, "failed to upload file $filename")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,8 +195,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) {
|
||||||
@ -50,4 +50,5 @@ object UriUtils {
|
|||||||
}
|
}
|
||||||
return filename
|
return filename
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,4 +65,5 @@ object BundleKeys {
|
|||||||
val KEY_ACCOUNT = "KEY_ACCOUNT"
|
val KEY_ACCOUNT = "KEY_ACCOUNT"
|
||||||
val KEY_FILE_ID = "KEY_FILE_ID"
|
val KEY_FILE_ID = "KEY_FILE_ID"
|
||||||
val KEY_NOTIFICATION_ID = "KEY_NOTIFICATION_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="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>
|
||||||
|
@ -1 +1 @@
|
|||||||
458
|
457
|
Loading…
Reference in New Issue
Block a user