Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/stefan-niedermann/nextcloud-deck.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsFragment.java')
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsFragment.java492
1 files changed, 375 insertions, 117 deletions
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsFragment.java
index c291c5bdf..07e8d39dd 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsFragment.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsFragment.java
@@ -1,35 +1,42 @@
package it.niedermann.nextcloud.deck.ui.card.attachments;
-import android.Manifest;
-import android.app.Activity;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.Intent;
+import android.content.res.ColorStateList;
import android.net.Uri;
-import android.os.Build;
import android.os.Bundle;
+import android.provider.ContactsContract;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.Toast;
+import androidx.activity.OnBackPressedCallback;
+import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
import androidx.core.app.SharedElementCallback;
+import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.snackbar.Snackbar;
import com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException;
import java.io.File;
import java.io.IOException;
-import java.util.Date;
+import java.time.Instant;
import java.util.List;
import java.util.Map;
+import it.niedermann.android.util.DimensionUtil;
import it.niedermann.nextcloud.deck.DeckLog;
import it.niedermann.nextcloud.deck.R;
import it.niedermann.nextcloud.deck.databinding.FragmentCardEditTabAttachmentsBinding;
@@ -41,10 +48,36 @@ import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.WrappedLiv
import it.niedermann.nextcloud.deck.ui.branding.BrandedFragment;
import it.niedermann.nextcloud.deck.ui.branding.BrandedSnackbar;
import it.niedermann.nextcloud.deck.ui.card.EditCardViewModel;
+import it.niedermann.nextcloud.deck.ui.card.attachments.picker.AbstractPickerAdapter;
+import it.niedermann.nextcloud.deck.ui.card.attachments.picker.ContactAdapter;
+import it.niedermann.nextcloud.deck.ui.card.attachments.picker.FileAdapter;
+import it.niedermann.nextcloud.deck.ui.card.attachments.picker.FileAdapterLegacy;
+import it.niedermann.nextcloud.deck.ui.card.attachments.picker.GalleryAdapter;
+import it.niedermann.nextcloud.deck.ui.card.attachments.picker.GalleryItemDecoration;
+import it.niedermann.nextcloud.deck.ui.card.attachments.previewdialog.PreviewDialog;
+import it.niedermann.nextcloud.deck.ui.card.attachments.previewdialog.PreviewDialogViewModel;
import it.niedermann.nextcloud.deck.ui.exception.ExceptionDialogFragment;
-
+import it.niedermann.nextcloud.deck.ui.takephoto.TakePhotoActivity;
+import it.niedermann.nextcloud.deck.util.DeckColorUtil;
+import it.niedermann.nextcloud.deck.util.VCardUtil;
+
+import static android.Manifest.permission.CAMERA;
+import static android.Manifest.permission.READ_CONTACTS;
+import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+import static android.app.Activity.RESULT_OK;
+import static android.os.Build.VERSION.SDK_INT;
+import static android.os.Build.VERSION_CODES.LOLLIPOP;
+import static android.os.Build.VERSION_CODES.M;
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
+import static androidx.core.content.PermissionChecker.PERMISSION_GRANTED;
+import static androidx.core.content.PermissionChecker.checkSelfPermission;
+import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_COLLAPSED;
+import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HIDDEN;
import static it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper.observeOnce;
import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToFAB;
+import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.isBrandingEnabled;
+import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.readBrandMainColor;
import static it.niedermann.nextcloud.deck.ui.card.attachments.CardAttachmentAdapter.VIEW_TYPE_DEFAULT;
import static it.niedermann.nextcloud.deck.ui.card.attachments.CardAttachmentAdapter.VIEW_TYPE_IMAGE;
import static it.niedermann.nextcloud.deck.util.AttachmentUtil.copyContentUriToTempFile;
@@ -53,14 +86,35 @@ import static java.net.HttpURLConnection.HTTP_CONFLICT;
public class CardAttachmentsFragment extends BrandedFragment implements AttachmentDeletedListener, AttachmentClickedListener {
private FragmentCardEditTabAttachmentsBinding binding;
- private EditCardViewModel viewModel;
+ private EditCardViewModel editViewModel;
+ private PreviewDialogViewModel previewViewModel;
+ private BottomSheetBehavior<LinearLayout> mBottomSheetBehaviour;
+
+ private RecyclerView.ItemDecoration galleryItemDecoration;
+
+ private static final int REQUEST_CODE_PICK_FILE = 1;
+ private static final int REQUEST_CODE_PICK_FILE_PERMISSION = 2;
+ private static final int REQUEST_CODE_PICK_CAMERA = 3;
+ private static final int REQUEST_CODE_PICK_GALLERY_PERMISSION = 4;
+ private static final int REQUEST_CODE_PICK_CONTACT = 5;
+ private static final int REQUEST_CODE_PICK_CONTACT_PICKER_PERMISSION = 6;
- private static final int REQUEST_CODE_ADD_ATTACHMENT = 1;
- private static final int REQUEST_PERMISSION = 2;
+ @ColorInt
+ private int accentColor;
+ @ColorInt
+ private int primaryColor;
- private SyncManager syncManager;
private CardAttachmentAdapter adapter;
+ private AbstractPickerAdapter<?> pickerAdapter;
+
+ private final OnBackPressedCallback backPressedCallback = new OnBackPressedCallback(true) {
+ @Override
+ public void handleOnBackPressed() {
+ mBottomSheetBehaviour.setState(STATE_HIDDEN);
+ }
+ };
+
private int clickedItemPosition;
@Override
@@ -69,30 +123,58 @@ public class CardAttachmentsFragment extends BrandedFragment implements Attachme
Bundle savedInstanceState) {
binding = FragmentCardEditTabAttachmentsBinding.inflate(inflater, container, false);
- viewModel = new ViewModelProvider(requireActivity()).get(EditCardViewModel.class);
+ editViewModel = new ViewModelProvider(requireActivity()).get(EditCardViewModel.class);
+ previewViewModel = new ViewModelProvider(requireActivity()).get(PreviewDialogViewModel.class);
+ binding.bottomNavigation.setOnNavigationItemSelectedListener(item -> {
+ if (item.getItemId() == R.id.gallery) {
+ showGalleryPicker();
+ } else if (item.getItemId() == R.id.contacts) {
+ showContactPicker();
+ } else if (item.getItemId() == R.id.files) {
+ showFilePicker();
+ }
+ return true;
+ });
+ accentColor = ContextCompat.getColor(requireContext(), R.color.accent);
+ primaryColor = ContextCompat.getColor(requireContext(), R.color.primary);
// This might be a zombie fragment with an empty EditCardViewModel after Android killed the activity (but not the fragment instance
// See https://github.com/stefan-niedermann/nextcloud-deck/issues/478
- if (viewModel.getFullCard() == null) {
+ if (editViewModel.getFullCard() == null) {
DeckLog.logError(new IllegalStateException("Cannot populate " + CardAttachmentsFragment.class.getSimpleName() + " because viewModel.getFullCard() is null"));
return binding.getRoot();
}
- syncManager = new SyncManager(requireContext());
adapter = new CardAttachmentAdapter(
- requireContext(),
getChildFragmentManager(),
requireActivity().getMenuInflater(),
this,
- viewModel.getAccount(),
- viewModel.getFullCard().getLocalId());
+ editViewModel.getAccount(),
+ editViewModel.getFullCard().getLocalId());
binding.attachmentsList.setAdapter(adapter);
- updateEmptyContentView();
+ adapter.isEmpty().observe(getViewLifecycleOwner(), (isEmpty) -> {
+ if (isEmpty) {
+ this.binding.emptyContentView.setVisibility(VISIBLE);
+ this.binding.attachmentsList.setVisibility(GONE);
+ } else {
+ this.binding.emptyContentView.setVisibility(GONE);
+ this.binding.attachmentsList.setVisibility(VISIBLE);
+ }
+ });
+ galleryItemDecoration = new GalleryItemDecoration(DimensionUtil.INSTANCE.dpToPx(requireContext(), R.dimen.spacer_1qx));
+ mBottomSheetBehaviour = BottomSheetBehavior.from(binding.bottomSheetParent);
+ mBottomSheetBehaviour.setDraggable(true);
+ mBottomSheetBehaviour.setHideable(true);
+ mBottomSheetBehaviour.setState(STATE_HIDDEN);
+ mBottomSheetBehaviour.addBottomSheetCallback(new CardAttachmentsBottomsheetBehaviorCallback(
+ requireContext(), backPressedCallback, binding.fab, binding.pickerBackdrop, binding.bottomNavigation,
+ R.color.mdtp_transparent_black, android.R.color.transparent, R.dimen.attachments_bottom_navigation_height));
+ binding.pickerBackdrop.setOnClickListener(v -> mBottomSheetBehaviour.setState(STATE_HIDDEN));
final DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
- int spanCount = (int) ((displayMetrics.widthPixels / displayMetrics.density) / getResources().getInteger(R.integer.max_dp_attachment_column));
- GridLayoutManager glm = new GridLayoutManager(getContext(), spanCount);
+ final int spanCount = (int) ((displayMetrics.widthPixels / displayMetrics.density) / getResources().getInteger(R.integer.max_dp_attachment_column));
+ final GridLayoutManager glm = new GridLayoutManager(getContext(), spanCount);
glm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
@@ -106,7 +188,7 @@ public class CardAttachmentsFragment extends BrandedFragment implements Attachme
}
});
binding.attachmentsList.setLayoutManager(glm);
- if (!viewModel.isCreateMode()) {
+ if (!editViewModel.isCreateMode()) {
// https://android-developers.googleblog.com/2018/02/continuous-shared-element-transitions.html?m=1
// https://github.com/android/animation-samples/blob/master/GridToPager/app/src/main/java/com/google/samples/gridtopager/fragment/ImagePagerFragment.java
setExitSharedElementCallback(new SharedElementCallback() {
@@ -119,17 +201,19 @@ public class CardAttachmentsFragment extends BrandedFragment implements Attachme
}
}
});
- adapter.setAttachments(viewModel.getFullCard().getAttachments(), viewModel.getFullCard().getId());
- updateEmptyContentView();
+ adapter.setAttachments(editViewModel.getFullCard().getAttachments(), editViewModel.getFullCard().getId());
}
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && viewModel.canEdit()) {
+ if (editViewModel.canEdit()) {
binding.fab.setOnClickListener(v -> {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
- REQUEST_PERMISSION);
+ if (SDK_INT < LOLLIPOP) {
+ openNativeFilePicker();
} else {
- startFilePickerIntent();
+ binding.bottomNavigation.setSelectedItemId(R.id.gallery);
+ showGalleryPicker();
+ mBottomSheetBehaviour.setState(STATE_COLLAPSED);
+ backPressedCallback.setEnabled(true);
+ requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), backPressedCallback);
}
});
binding.fab.show();
@@ -146,116 +230,281 @@ public class CardAttachmentsFragment extends BrandedFragment implements Attachme
binding.fab.hide();
binding.emptyContentView.hideDescription();
}
+ @Nullable Context context = requireContext();
+ applyBrand(isBrandingEnabled(context)
+ ? readBrandMainColor(context)
+ : ContextCompat.getColor(context, R.color.defaultBrand));
return binding.getRoot();
}
- @RequiresApi(api = Build.VERSION_CODES.KITKAT)
- private void startFilePickerIntent() {
- Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
- intent.addCategory(Intent.CATEGORY_OPENABLE);
- intent.setType("*/*");
- startActivityForResult(intent, REQUEST_CODE_ADD_ATTACHMENT);
+ @Override
+ public void onPause() {
+ super.onPause();
+ backPressedCallback.setEnabled(false);
}
@Override
- public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (requestCode == REQUEST_CODE_ADD_ATTACHMENT && resultCode == Activity.RESULT_OK) {
- if (data == null) {
- ExceptionDialogFragment.newInstance(new UploadAttachmentFailedException("Intent data is null"), viewModel.getAccount()).show(getChildFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
- return;
+ public void onResume() {
+ super.onResume();
+ backPressedCallback.setEnabled(binding.bottomNavigation.getTranslationY() == 0);
+ }
+
+ private void showGalleryPicker() {
+ if (!(pickerAdapter instanceof GalleryAdapter)) {
+ if (isPermissionRequestNeeded(READ_EXTERNAL_STORAGE) || isPermissionRequestNeeded(CAMERA)) {
+ requestPermissions(new String[]{READ_EXTERNAL_STORAGE, CAMERA}, REQUEST_CODE_PICK_GALLERY_PERMISSION);
+ } else {
+ unbindPickerAdapter();
+ pickerAdapter = new GalleryAdapter(requireContext(), (uri, pair) -> {
+ previewViewModel.prepareDialog(pair.first, pair.second);
+ PreviewDialog.newInstance().show(getChildFragmentManager(), PreviewDialog.class.getSimpleName());
+ observeOnce(previewViewModel.getResult(), getViewLifecycleOwner(), (submitPositive) -> {
+ if (submitPositive) {
+ onActivityResult(REQUEST_CODE_PICK_FILE, RESULT_OK, new Intent().setData(uri));
+ }
+ });
+ }, this::openNativeCameraPicker, getViewLifecycleOwner());
+ if (binding.pickerRecyclerView.getItemDecorationCount() == 0) {
+ binding.pickerRecyclerView.addItemDecoration(galleryItemDecoration);
+ }
+ binding.pickerRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), 3));
+ binding.pickerRecyclerView.setAdapter(pickerAdapter);
}
- final Uri sourceUri = data.getData();
- if (sourceUri == null) {
- ExceptionDialogFragment.newInstance(new UploadAttachmentFailedException("sourceUri is null"), viewModel.getAccount()).show(getChildFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
- return;
+ }
+ }
+
+ private void showContactPicker() {
+ if (!(pickerAdapter instanceof ContactAdapter)) {
+ if (isPermissionRequestNeeded(READ_CONTACTS)) {
+ requestPermissions(new String[]{READ_CONTACTS}, REQUEST_CODE_PICK_CONTACT_PICKER_PERMISSION);
+ } else {
+ unbindPickerAdapter();
+ pickerAdapter = new ContactAdapter(requireContext(), (uri, pair) -> {
+ previewViewModel.prepareDialog(pair.first, pair.second);
+ PreviewDialog.newInstance().show(getChildFragmentManager(), PreviewDialog.class.getSimpleName());
+ observeOnce(previewViewModel.getResult(), getViewLifecycleOwner(), (submitPositive) -> {
+ if (submitPositive) {
+ onActivityResult(REQUEST_CODE_PICK_CONTACT, RESULT_OK, new Intent().setData(uri));
+ }
+ });
+ }, this::openNativeContactPicker);
+ removeGalleryItemDecoration();
+ binding.pickerRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
+ binding.pickerRecyclerView.setAdapter(pickerAdapter);
}
- if (!ContentResolver.SCHEME_CONTENT.equals(sourceUri.getScheme())) {
- ExceptionDialogFragment.newInstance(new UploadAttachmentFailedException("Unknown URI scheme: " + sourceUri.getScheme()), viewModel.getAccount()).show(getChildFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
- return;
+ }
+ }
+
+ private void showFilePicker() {
+ if (!(pickerAdapter instanceof FileAdapter) && !(pickerAdapter instanceof FileAdapterLegacy)) {
+ if (isPermissionRequestNeeded(READ_EXTERNAL_STORAGE)) {
+ requestPermissions(new String[]{READ_EXTERNAL_STORAGE}, REQUEST_CODE_PICK_FILE_PERMISSION);
+ } else {
+ unbindPickerAdapter();
+ if (SDK_INT >= LOLLIPOP) {
+// if (SDK_INT >= Build.VERSION_CODES.Q) {
+// // TODO Only usable with Scoped Storage
+// pickerAdapter = new FileAdapter(requireContext(), uri -> onActivityResult(REQUEST_CODE_PICK_FILE, RESULT_OK, new Intent().setData(uri)), this::openNativeFilePicker);
+// } else {
+ pickerAdapter = new FileAdapterLegacy((uri, pair) -> {
+ previewViewModel.prepareDialog(pair.first, pair.second);
+ PreviewDialog.newInstance().show(getChildFragmentManager(), PreviewDialog.class.getSimpleName());
+ observeOnce(previewViewModel.getResult(), getViewLifecycleOwner(), (submitPositive) -> {
+ if (submitPositive) {
+ onActivityResult(REQUEST_CODE_PICK_FILE, RESULT_OK, new Intent().setData(uri));
+ }
+ });
+ }, this::openNativeFilePicker);
+// }
+ removeGalleryItemDecoration();
+ binding.pickerRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
+ binding.pickerRecyclerView.setAdapter(pickerAdapter);
+ }
}
+ }
+ }
- DeckLog.verbose("--- found content URL " + sourceUri.getPath());
- File fileToUpload;
+ private void openNativeCameraPicker() {
+ if (SDK_INT >= LOLLIPOP) {
+ startActivityForResult(TakePhotoActivity.createIntent(requireContext()), REQUEST_CODE_PICK_CAMERA);
+ } else {
+ ExceptionDialogFragment.newInstance(new UnsupportedOperationException("This feature requires Android 5"), editViewModel.getAccount()).show(getChildFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
+ }
+ }
- try {
- DeckLog.verbose("---- so, now copy & upload: " + sourceUri.getPath());
- fileToUpload = copyContentUriToTempFile(requireContext(), sourceUri, viewModel.getAccount().getId(), viewModel.getFullCard().getCard().getLocalId());
- } catch (IllegalArgumentException | IOException e) {
- ExceptionDialogFragment.newInstance(new UploadAttachmentFailedException("Could not copy content URI to temporary file", e), viewModel.getAccount()).show(getChildFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
- return;
- }
+ private void openNativeContactPicker() {
+ final Intent intent = new Intent(Intent.ACTION_PICK).setType(ContactsContract.Contacts.CONTENT_TYPE);
+ if (intent.resolveActivity(requireContext().getPackageManager()) != null) {
+ startActivityForResult(intent, REQUEST_CODE_PICK_CONTACT);
+ }
+ }
+
+ private void openNativeFilePicker() {
+ startActivityForResult(new Intent(Intent.ACTION_GET_CONTENT)
+ .addCategory(Intent.CATEGORY_OPENABLE)
+ .setType("*/*"), REQUEST_CODE_PICK_FILE);
+ }
+
+ /**
+ * Checks the current Android version and whether the permission has already been granted.
+ *
+ * @param permission see {@link android.Manifest.permission}
+ * @return whether or not requesting permission is needed
+ */
+ private boolean isPermissionRequestNeeded(@NonNull String permission) {
+ return SDK_INT >= M && checkSelfPermission(requireActivity(), permission) != PERMISSION_GRANTED;
+ }
+
+ private void unbindPickerAdapter() {
+ if (pickerAdapter != null) {
+ pickerAdapter.onDestroy();
+ }
+ }
+
+ private void removeGalleryItemDecoration() {
+ if (binding.pickerRecyclerView.getItemDecorationCount() > 0) {
+ binding.pickerRecyclerView.removeItemDecoration(galleryItemDecoration);
+ }
+ }
- for (Attachment existingAttachment : viewModel.getFullCard().getAttachments()) {
- final String existingPath = existingAttachment.getLocalPath();
- if (existingPath != null && existingPath.equals(fileToUpload.getAbsolutePath())) {
- BrandedSnackbar.make(binding.coordinatorLayout, R.string.attachment_already_exists, Snackbar.LENGTH_LONG).show();
- return;
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ switch (requestCode) {
+ case REQUEST_CODE_PICK_CONTACT:
+ case REQUEST_CODE_PICK_CAMERA:
+ case REQUEST_CODE_PICK_FILE: {
+ if (resultCode == RESULT_OK) {
+ final Uri sourceUri = requestCode == REQUEST_CODE_PICK_CONTACT
+ ? VCardUtil.getVCardContentUri(requireContext(), Uri.parse(data.getDataString()))
+ : data.getData();
+ try {
+ uploadNewAttachmentFromUri(sourceUri, requestCode == REQUEST_CODE_PICK_CAMERA
+ ? data.getType()
+ : requireContext().getContentResolver().getType(sourceUri));
+ mBottomSheetBehaviour.setState(STATE_HIDDEN);
+ } catch (Exception e) {
+ ExceptionDialogFragment.newInstance(e, editViewModel.getAccount()).show(getChildFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
+ }
}
+ break;
+ }
+ default: {
+ super.onActivityResult(requestCode, resultCode, data);
}
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ if (this.pickerAdapter != null) {
+ this.pickerAdapter.onDestroy();
+ this.binding.pickerRecyclerView.setAdapter(null);
+ }
+ super.onDestroy();
+ }
- final Date now = new Date();
- final Attachment a = new Attachment();
- a.setMimetype(requireContext().getContentResolver().getType(sourceUri));
- a.setData(fileToUpload.getName());
- a.setFilename(fileToUpload.getName());
- a.setBasename(fileToUpload.getName());
- a.setFilesize(fileToUpload.length());
- a.setLocalPath(fileToUpload.getAbsolutePath());
- a.setLastModifiedLocal(now);
- a.setStatusEnum(DBStatus.LOCAL_EDITED);
- a.setCreatedAt(now);
- viewModel.getFullCard().getAttachments().add(a);
- adapter.addAttachment(a);
- if (!viewModel.isCreateMode()) {
- WrappedLiveData<Attachment> liveData = syncManager.addAttachmentToCard(viewModel.getAccount().getId(), viewModel.getFullCard().getLocalId(), a.getMimetype(), fileToUpload);
- observeOnce(liveData, getViewLifecycleOwner(), (next) -> {
- if (liveData.hasError()) {
- Throwable t = liveData.getError();
- if (t instanceof NextcloudHttpRequestFailedException && ((NextcloudHttpRequestFailedException) t).getStatusCode() == HTTP_CONFLICT) {
- // https://github.com/stefan-niedermann/nextcloud-deck/issues/534
- viewModel.getFullCard().getAttachments().remove(a);
- adapter.removeAttachment(a);
- BrandedSnackbar.make(binding.coordinatorLayout, R.string.attachment_already_exists, Snackbar.LENGTH_LONG).show();
+ private void uploadNewAttachmentFromUri(@NonNull Uri sourceUri, String mimeType) throws UploadAttachmentFailedException, IOException {
+ if (sourceUri == null) {
+ throw new UploadAttachmentFailedException("sourceUri is null");
+ }
+ switch (sourceUri.getScheme()) {
+ case ContentResolver.SCHEME_CONTENT:
+ case ContentResolver.SCHEME_FILE: {
+ DeckLog.verbose("--- found content URL " + sourceUri.getPath());
+ final File fileToUpload = copyContentUriToTempFile(requireContext(), sourceUri, editViewModel.getAccount().getId(), editViewModel.getFullCard().getLocalId());
+ for (Attachment existingAttachment : editViewModel.getFullCard().getAttachments()) {
+ final String existingPath = existingAttachment.getLocalPath();
+ if (existingPath != null && existingPath.equals(fileToUpload.getAbsolutePath())) {
+ BrandedSnackbar.make(binding.coordinatorLayout, R.string.attachment_already_exists, Snackbar.LENGTH_LONG).show();
+ return;
+ }
+ }
+ final Instant now = Instant.now();
+ final Attachment a = new Attachment();
+ a.setMimetype(mimeType);
+ a.setData(fileToUpload.getName());
+ a.setFilename(fileToUpload.getName());
+ a.setBasename(fileToUpload.getName());
+ a.setFilesize(fileToUpload.length());
+ a.setLocalPath(fileToUpload.getAbsolutePath());
+ a.setLastModifiedLocal(now);
+ a.setCreatedAt(now);
+ a.setStatusEnum(DBStatus.LOCAL_EDITED);
+ editViewModel.getFullCard().getAttachments().add(0, a);
+ adapter.addAttachment(a);
+ if (!editViewModel.isCreateMode()) {
+ WrappedLiveData<Attachment> liveData = editViewModel.addAttachmentToCard(editViewModel.getAccount().getId(), editViewModel.getFullCard().getLocalId(), a.getMimetype(), fileToUpload);
+ observeOnce(liveData, getViewLifecycleOwner(), (next) -> {
+ if (liveData.hasError()) {
+ Throwable t = liveData.getError();
+ if (t instanceof NextcloudHttpRequestFailedException && ((NextcloudHttpRequestFailedException) t).getStatusCode() == HTTP_CONFLICT) {
+ // https://github.com/stefan-niedermann/nextcloud-deck/issues/534
+ editViewModel.getFullCard().getAttachments().remove(a);
+ adapter.removeAttachment(a);
+ BrandedSnackbar.make(binding.coordinatorLayout, R.string.attachment_already_exists, Snackbar.LENGTH_LONG).show();
+ } else {
+ ExceptionDialogFragment.newInstance(new UploadAttachmentFailedException("Unknown URI scheme", t), editViewModel.getAccount()).show(getChildFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
+ }
} else {
- ExceptionDialogFragment.newInstance(new UploadAttachmentFailedException("Unknown URI scheme", t), viewModel.getAccount()).show(getChildFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
+ editViewModel.getFullCard().getAttachments().remove(a);
+ editViewModel.getFullCard().getAttachments().add(0, next);
+ adapter.replaceAttachment(a, next);
}
- } else {
- viewModel.getFullCard().getAttachments().remove(a);
- adapter.removeAttachment(a);
- viewModel.getFullCard().getAttachments().add(next);
- adapter.addAttachment(next);
- }
- });
+ });
+ }
+ break;
+ }
+ default: {
+ throw new UploadAttachmentFailedException("Unknown URI scheme: " + sourceUri.getScheme());
}
- updateEmptyContentView();
}
-
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
- if (requestCode == REQUEST_PERMISSION) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- startFilePickerIntent();
+ switch (requestCode) {
+ case REQUEST_CODE_PICK_FILE_PERMISSION: {
+ if (checkSelfPermission(requireActivity(), READ_EXTERNAL_STORAGE) == PERMISSION_GRANTED) {
+ showFilePicker();
+ } else {
+ Toast.makeText(requireContext(), R.string.cannot_upload_files_without_permission, Toast.LENGTH_LONG).show();
+ }
+ break;
}
- } else {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ case REQUEST_CODE_PICK_GALLERY_PERMISSION: {
+ if (checkSelfPermission(requireActivity(), READ_EXTERNAL_STORAGE) == PERMISSION_GRANTED && checkSelfPermission(requireActivity(), CAMERA) == PERMISSION_GRANTED) {
+ showGalleryPicker();
+ } else {
+ Toast.makeText(requireContext(), R.string.cannot_upload_files_without_permission, Toast.LENGTH_LONG).show();
+ }
+ break;
+ }
+ case REQUEST_CODE_PICK_CONTACT_PICKER_PERMISSION: {
+ if (checkSelfPermission(requireActivity(), READ_CONTACTS) == PERMISSION_GRANTED) {
+ showContactPicker();
+ } else {
+ Toast.makeText(requireContext(), R.string.cannot_upload_files_without_permission, Toast.LENGTH_LONG).show();
+ }
+ break;
+ }
+ default:
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
- public static Fragment newInstance() {
- return new CardAttachmentsFragment();
- }
-
@Override
public void onAttachmentDeleted(Attachment attachment) {
adapter.removeAttachment(attachment);
- viewModel.getFullCard().getAttachments().remove(attachment);
- if (!viewModel.isCreateMode() && attachment.getLocalId() != null) {
- syncManager.deleteAttachmentOfCard(viewModel.getAccount().getId(), viewModel.getFullCard().getLocalId(), attachment.getLocalId());
+ editViewModel.getFullCard().getAttachments().remove(attachment);
+ if (!editViewModel.isCreateMode() && attachment.getLocalId() != null) {
+ final WrappedLiveData<Void> deleteLiveData = editViewModel.deleteAttachmentOfCard(editViewModel.getAccount().getId(), editViewModel.getFullCard().getLocalId(), attachment.getLocalId());
+ observeOnce(deleteLiveData, this, (next) -> {
+ if (deleteLiveData.hasError() && !SyncManager.ignoreExceptionOnVoidError(deleteLiveData.getError())) {
+ ExceptionDialogFragment.newInstance(deleteLiveData.getError(), editViewModel.getAccount()).show(getChildFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
+ }
+ });
}
- updateEmptyContentView();
}
@Override
@@ -263,19 +512,28 @@ public class CardAttachmentsFragment extends BrandedFragment implements Attachme
this.clickedItemPosition = position;
}
-
- private void updateEmptyContentView() {
- if (this.adapter == null || this.adapter.getItemCount() == 0) {
- this.binding.emptyContentView.setVisibility(View.VISIBLE);
- this.binding.attachmentsList.setVisibility(View.GONE);
- } else {
- this.binding.emptyContentView.setVisibility(View.GONE);
- this.binding.attachmentsList.setVisibility(View.VISIBLE);
- }
- }
-
@Override
public void applyBrand(int mainColor) {
applyBrandToFAB(mainColor, binding.fab);
+ adapter.applyBrand(mainColor);
+ @ColorInt final int finalMainColor = DeckColorUtil.contrastRatioIsSufficient(mainColor, primaryColor)
+ ? mainColor
+ : accentColor;
+ final ColorStateList list = new ColorStateList(
+ new int[][]{
+ new int[]{android.R.attr.state_checked},
+ new int[]{}
+ },
+ new int[]{
+ finalMainColor,
+ accentColor
+ }
+ );
+ binding.bottomNavigation.setItemIconTintList(list);
+ binding.bottomNavigation.setItemTextColor(list);
+ }
+
+ public static Fragment newInstance() {
+ return new CardAttachmentsFragment();
}
}