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:
Marcel Hibbe 2021-05-25 11:25:18 +02:00 committed by GitHub
commit e23fd77609
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 413 additions and 128 deletions

View File

@ -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" />

View File

@ -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(

View File

@ -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

View File

@ -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()) {

View File

@ -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);

View File

@ -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
)
}
}
}

View File

@ -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
}
}

View File

@ -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"
}

View File

@ -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>

View File

@ -1 +1 @@
458
457