From 36fdd6d82b83188d555fd39c78ba7fb4a3b69818 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Sat, 15 May 2021 09:39:02 +0200 Subject: [PATCH] add ability to "share to" files to nextcloud talk app Signed-off-by: Marcel Hibbe --- app/src/main/AndroidManifest.xml | 13 + .../nextcloud/talk/activities/MainActivity.kt | 2 - .../talk/controllers/ChatController.kt | 39 ++- .../ConversationsListController.java | 320 +++++++++++++----- .../talk/controllers/base/BaseController.java | 22 +- .../talk/jobs/UploadAndShareFilesWorker.kt | 42 +++ .../java/com/nextcloud/talk/utils/UriUtils.kt | 39 +-- app/src/main/res/values/strings.xml | 3 + 8 files changed, 358 insertions(+), 122 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8ceb5add2..b92c45f31 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -63,6 +63,7 @@ android:name="android.permission.USE_CREDENTIALS" android:maxSdkVersion="22" /> + @@ -96,6 +97,18 @@ + + + + + + + + + + + + diff --git a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt index 83122dcc9..ab78ce354 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt @@ -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( diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt index e854b589e..acadd3098 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -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,8 @@ class ChatController(args: Bundle) : var pastPreconditionFailed = false var futurePreconditionFailed = false + val filesToUpload: MutableList = ArrayList() + init { setHasOptionsMenu(true) NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) @@ -683,27 +686,27 @@ class ChatController(args: Bundle) : if (resultCode == RESULT_OK) { try { checkNotNull(intent) - val files: MutableList = 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 +720,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 +741,15 @@ class ChatController(args: Bundle) : } } + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, 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) { try { require(files.isNotEmpty()) @@ -750,6 +762,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) diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java b/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java index d060842e4..efe354432 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java @@ -26,7 +26,10 @@ import android.animation.AnimatorInflater; import android.annotation.SuppressLint; import android.app.SearchManager; 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 +45,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; @@ -54,6 +58,7 @@ import com.facebook.imagepipeline.core.ImagePipeline; import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber; import com.facebook.imagepipeline.image.CloseableImage; import com.facebook.imagepipeline.request.ImageRequest; +import com.google.android.material.appbar.AppBarLayout; import com.google.android.material.button.MaterialButton; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.kennyc.bottomsheet.BottomSheet; @@ -73,6 +78,7 @@ import com.nextcloud.talk.interfaces.ConversationMenuInterface; import com.nextcloud.talk.jobs.AccountRemovalWorker; import com.nextcloud.talk.jobs.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; @@ -91,11 +98,13 @@ import org.apache.commons.lang3.builder.CompareToBuilder; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; +import org.jetbrains.annotations.NotNull; import org.parceler.Parcels; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import javax.inject.Inject; @@ -128,7 +137,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 +196,12 @@ public class ConversationsListController extends BaseController implements Searc private Bundle conversationMenuBundle = null; + private boolean showShareToScreen = false; + private boolean shareToScreenWasShown = false; + + private ArrayList filesToShare; + private Conversation selectedConversation; + 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,70 @@ 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; + showShareToConfirmDialog(); } 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 showShareToConfirmDialog() { + if (UploadAndShareFilesWorker.Companion.isStoragePermissionGranted(context)) { + collectFilesToShareFromIntent(); + + StringBuilder fileNamesWithLineBreaks = new StringBuilder("\n"); + + for (String file : filesToShare) { + String filename = UriUtils.Companion.getFileName(Uri.parse(file), context); + fileNamesWithLineBreaks.append(filename).append("\n"); + } + + String confirmationQuestion; + if (filesToShare.size() == 1) { + confirmationQuestion = + String.format(getResources().getString(R.string.nc_upload_confirm_send_single), + selectedConversation.getDisplayName()); + } else { + confirmationQuestion = + String.format(getResources().getString(R.string.nc_upload_confirm_send_multiple), + selectedConversation.getDisplayName()); + } + + new LovelyStandardDialog(getActivity()) + .setPositiveButtonColorRes(R.color.nc_darkGreen) + .setTitle(confirmationQuestion) + .setMessage(fileNamesWithLineBreaks.toString()) + .setPositiveButton(R.string.nc_yes, new View.OnClickListener() { + @Override + public void onClick(View v) { + upload(); + openConversation(); + } + }) + .setNegativeButton(R.string.nc_no, new View.OnClickListener() { + @Override + public void onClick(View v) { + Log.d(TAG, "sharing files aborted"); + } + }) + .show(); + } else { + UploadAndShareFilesWorker.Companion.requestStoragePermission(ConversationsListController.this); + } + } + @Override 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 +824,100 @@ public class ConversationsListController extends BaseController implements Searc } } + private void collectFilesToShareFromIntent() { + filesToShare = new ArrayList<>(); + if (getActivity() != null && getActivity().getIntent() != null) { + Intent intent = getActivity().getIntent(); + if (Intent.ACTION_SEND.equals(intent.getAction()) + || Intent.ACTION_SEND_MULTIPLE.equals(intent.getAction())) { + if (intent.getClipData() != null) { + for (int i = 0; i < intent.getClipData().getItemCount(); i++) { + filesToShare.add(intent.getClipData().getItemAt(i).getUri().toString()); + } + } else { + filesToShare.add(intent.getData().toString()); + } + if (filesToShare.isEmpty()) { + Log.e(TAG, "failed to get files from intent"); + } + } + } + } + + private void upload() { + if (selectedConversation == null) { + Log.e(TAG, "not able to upload any files because conversation was null."); + return; + } + + try { + String[] filesToShareArray = new String[filesToShare.size()]; + filesToShareArray = filesToShare.toArray(filesToShareArray); + + Data data = new Data.Builder() + .putStringArray(UploadAndShareFilesWorker.DEVICE_SOURCEFILES, filesToShareArray) + .putString(UploadAndShareFilesWorker.NC_TARGETPATH, currentUser.getAttachmentFolder()) + .putString(UploadAndShareFilesWorker.ROOM_TOKEN, selectedConversation.getToken()) + .build(); + OneTimeWorkRequest uploadWorker = new OneTimeWorkRequest.Builder(UploadAndShareFilesWorker.class) + .setInputData(data) + .build(); + WorkManager.getInstance().enqueue(uploadWorker); + + Toast.makeText( + context, context.getResources().getString(R.string.nc_upload_in_progess), + Toast.LENGTH_LONG + ).show(); + + } catch (IllegalArgumentException e) { + Toast.makeText(context, context.getResources().getString(R.string.nc_upload_failed), Toast.LENGTH_LONG).show(); + Log.e(TAG, "Something went wrong when trying to upload file", e); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + if (requestCode == UploadAndShareFilesWorker.REQUEST_PERMISSION && + grantResults.length > 0 && + grantResults[0] == PackageManager.PERMISSION_GRANTED) { + Log.d(TAG, "upload starting after permissions were granted"); + showShareToConfirmDialog(); + } else { + Toast.makeText(context, context.getString(R.string.read_storage_no_permission), Toast.LENGTH_LONG).show(); + } + } + + private void openConversation() { + Bundle bundle = new Bundle(); + bundle.putParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY(), currentUser); + bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), selectedConversation.getToken()); + bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_ID(), selectedConversation.getRoomId()); + + if (selectedConversation.hasPassword && selectedConversation.participantType == + Participant.ParticipantType.GUEST || + selectedConversation.participantType == Participant.ParticipantType.USER_FOLLOWING_LINK) { + bundle.putInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE(), 99); + prepareAndShowBottomSheetWithBundle(bundle, false); + } else { + currentUser = userUtils.getCurrentUser(); + + bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ACTIVE_CONVERSATION(), Parcels.wrap(selectedConversation)); + ConductorRemapping.INSTANCE.remapChatController(getRouter(), currentUser.getId(), + selectedConversation.getToken(), bundle, false); + } + } + + private Conversation getConversation(int position) { + Object clickedItem = adapter.getItem(position); + Conversation conversation; + if (shouldUseLastMessageLayout) { + conversation = ((ConversationItem) clickedItem).getModel(); + } else { + conversation = ((CallItem) clickedItem).getModel(); + } + return conversation; + } + @Subscribe(sticky = true, threadMode = ThreadMode.BACKGROUND) public void onMessageEvent(EventStatus eventStatus) { if (currentUser != null && eventStatus.getUserId() == currentUser.getId()) { diff --git a/app/src/main/java/com/nextcloud/talk/controllers/base/BaseController.java b/app/src/main/java/com/nextcloud/talk/controllers/base/BaseController.java index 8282bf63a..8353afbfe 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/base/BaseController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/base/BaseController.java @@ -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); diff --git a/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt index 3b7a12b9a..7b9f0f9d9 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt @@ -20,15 +20,19 @@ 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 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.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.models.database.UserEntity @@ -69,6 +73,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) @@ -172,8 +185,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 + ) + } } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/UriUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/UriUtils.kt index 42dc9eb24..1397e7714 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/UriUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/UriUtils.kt @@ -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 } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 277c784d6..17c40d437 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -362,6 +362,8 @@ Share Send to + Send to … + Sharing files from storage is not possible without permissions Open in Files app @@ -425,6 +427,7 @@ 999+ + Open main menu Failed to save %1$s selected