diff options
author | Stefan Niedermann <info@niedermann.it> | 2020-11-03 23:00:54 +0300 |
---|---|---|
committer | Stefan Niedermann <info@niedermann.it> | 2020-11-03 23:00:54 +0300 |
commit | ddc25b51687366fd4d395c8d882341c3692d4185 (patch) | |
tree | 6a9356923b05a5281e66571334dcb6ce3831a6f1 /app/src/main/java/it/niedermann/nextcloud/deck/ui | |
parent | ce54a71de9d40f994078cb93b7e2e411b77dffef (diff) |
Fine tuning
Signed-off-by: Stefan Niedermann <info@niedermann.it>
Diffstat (limited to 'app/src/main/java/it/niedermann/nextcloud/deck/ui')
6 files changed, 78 insertions, 270 deletions
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditActivity.java index 1af96bca5..fcc9d24e6 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditActivity.java @@ -25,6 +25,7 @@ import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.ActivityEditBinding; import it.niedermann.nextcloud.deck.model.Account; +import it.niedermann.nextcloud.deck.model.full.FullCard; import it.niedermann.nextcloud.deck.persistence.sync.SyncManager; import it.niedermann.nextcloud.deck.ui.branding.BrandedActivity; import it.niedermann.nextcloud.deck.ui.branding.BrandedAlertDialogBuilder; @@ -171,12 +172,16 @@ public class EditActivity extends BrandedActivity { @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == R.id.action_card_save) { - saveAndFinish(); + saveAndRun(super::finish); } return super.onOptionsItemSelected(item); } - private void saveAndFinish() { + /** + * Tries to save the current {@link FullCard} from the {@link EditCardViewModel} and then runs the given {@link Runnable} + * @param runnable + */ + private void saveAndRun(@NonNull Runnable runnable) { if (!viewModel.isPendingCreation()) { viewModel.setPendingCreation(true); final String title = viewModel.getFullCard().getCard().getTitle(); @@ -194,9 +199,9 @@ public class EditActivity extends BrandedActivity { .show(); } else { if (viewModel.isCreateMode()) { - observeOnce(syncManager.createFullCard(viewModel.getAccount().getId(), viewModel.getBoardId(), viewModel.getFullCard().getCard().getStackId(), viewModel.getFullCard()), EditActivity.this, (card) -> super.finish()); + observeOnce(syncManager.createFullCard(viewModel.getAccount().getId(), viewModel.getBoardId(), viewModel.getFullCard().getCard().getStackId(), viewModel.getFullCard()), EditActivity.this, (card) -> runnable.run()); } else { - observeOnce(syncManager.updateCard(viewModel.getFullCard()), EditActivity.this, (card) -> super.finish()); + observeOnce(syncManager.updateCard(viewModel.getFullCard()), EditActivity.this, (card) -> runnable.run()); } } } @@ -265,26 +270,15 @@ public class EditActivity extends BrandedActivity { } @Override - public boolean onSupportNavigateUp() { - finish(); // close this activity as oppose to navigating up - return true; - } - - @Override - public void onBackPressed() { - finish(); - } - - @Override public void finish() { if (!viewModel.hasChanges() && viewModel.canEdit()) { new BrandedAlertDialogBuilder(this) .setTitle(R.string.simple_save) .setMessage(R.string.do_you_want_to_save_your_changes) - .setPositiveButton(R.string.simple_save, (dialog, whichButton) -> saveAndFinish()) + .setPositiveButton(R.string.simple_save, (dialog, whichButton) -> saveAndRun(super::finish)) .setNegativeButton(R.string.simple_discard, (dialog, whichButton) -> super.finish()).show(); } else { - directFinish(); + super.finish(); } } 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 2401a4829..e21d2a6c0 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,5 +1,9 @@ package it.niedermann.nextcloud.deck.ui.card.attachments; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ArgbEvaluator; +import android.animation.ValueAnimator; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -7,6 +11,8 @@ import android.content.res.ColorStateList; import android.graphics.Color; import android.net.Uri; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; import android.provider.ContactsContract; import android.util.DisplayMetrics; import android.view.LayoutInflater; @@ -16,6 +22,7 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.Toast; +import androidx.activity.OnBackPressedCallback; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; @@ -26,6 +33,7 @@ import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.bottomsheet.BottomSheetBehavior; +import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.snackbar.Snackbar; import com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException; @@ -91,9 +99,16 @@ public class CardAttachmentsFragment extends BrandedFragment implements Attachme private SyncManager syncManager; private CardAttachmentAdapter adapter; - private ImageView[] brandedViews; + private FloatingActionButton[] brandedViews; private GalleryAdapter galleryAdapter; + private final OnBackPressedCallback backPressedCallback = new OnBackPressedCallback(true) { + @Override + public void handleOnBackPressed() { + mBottomSheetBehaviour.setState(STATE_HIDDEN); + } + }; + private int clickedItemPosition; @Override @@ -107,7 +122,7 @@ public class CardAttachmentsFragment extends BrandedFragment implements Attachme if (SDK_INT < LOLLIPOP) { binding.pickCamera.setVisibility(GONE); } - brandedViews = new ImageView[]{binding.pickCameraIamge, binding.pickContactIamge, binding.pickFileIamge}; + brandedViews = new FloatingActionButton[]{binding.pickCamera, binding.pickContact, binding.pickFile}; binding.pickCamera.setOnClickListener((v) -> { if (SDK_INT >= LOLLIPOP) { pickCamera(); @@ -154,18 +169,16 @@ public class CardAttachmentsFragment extends BrandedFragment implements Attachme switch (newState) { case STATE_HIDDEN: { DeckLog.log("BottomSheet: HIDDEN"); + backPressedCallback.remove(); hidePicker(); - binding.fab.show(); break; } case STATE_EXPANDED: { DeckLog.log("BottomSheet: EXPANDED"); - showPicker(); break; } case STATE_HALF_EXPANDED: { DeckLog.log("BottomSheet: HALF_EXPANDED"); - showPicker(); break; } case BottomSheetBehavior.STATE_COLLAPSED: @@ -228,9 +241,9 @@ public class CardAttachmentsFragment extends BrandedFragment implements Attachme binding.pickerRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), 3)); binding.pickerRecyclerView.setAdapter(galleryAdapter); } - mBottomSheetBehaviour.setState(STATE_EXPANDED); + mBottomSheetBehaviour.setState(STATE_HALF_EXPANDED); showPicker(); - binding.fab.hide(); + requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), backPressedCallback); }); binding.fab.show(); binding.attachmentsList.addOnScrollListener(new RecyclerView.OnScrollListener() { @@ -434,30 +447,35 @@ public class CardAttachmentsFragment extends BrandedFragment implements Attachme } } - @Override - public void onLowMemory() { - super.onLowMemory(); - if (galleryAdapter != null) { - galleryAdapter.onLowMemory(); - } - } - private void hidePicker() { - binding.bottomNavigation - .animate() - .translationY(binding.bottomNavigation.getHeight()) - .setDuration(300) - .start(); - binding.bottomNavigation.setVisibility(GONE); + ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), getResources().getColor(R.color.mdtp_transparent_black), getResources().getColor(android.R.color.transparent)); + colorAnimation.setDuration(250); + colorAnimation.addUpdateListener(animator -> binding.pickerControls.setBackgroundColor((int) animator.getAnimatedValue())); + colorAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + binding.pickerControlsWrapper.setVisibility(GONE); + } + }); + colorAnimation.start(); + new Handler(Looper.getMainLooper()).postDelayed(() -> binding.pickerControlsWrapper.setVisibility(GONE), 250); + for (FloatingActionButton fab : brandedViews) { + fab.hide(); + } + binding.fab.show(); } private void showPicker() { - binding.bottomNavigation.setVisibility(VISIBLE); - binding.bottomNavigation - .animate() - .translationY(0) - .setDuration(300) - .start(); + binding.pickerControlsWrapper.setVisibility(VISIBLE); + ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), getResources().getColor(android.R.color.transparent), getResources().getColor(R.color.mdtp_transparent_black)); + colorAnimation.setDuration(250); // milliseconds + colorAnimation.addUpdateListener(animator -> binding.pickerControls.setBackgroundColor((int) animator.getAnimatedValue())); + colorAnimation.start(); + binding.fab.hide(); + for (FloatingActionButton fab : brandedViews) { + fab.show(); + } } @Override diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/DefaultAttachmentViewHolder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/DefaultAttachmentViewHolder.java index 3f7287c24..9d72990a5 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/DefaultAttachmentViewHolder.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/DefaultAttachmentViewHolder.java @@ -19,7 +19,7 @@ import it.niedermann.nextcloud.deck.util.DateUtil; import it.niedermann.nextcloud.deck.util.MimeTypeUtil; public class DefaultAttachmentViewHolder extends AttachmentViewHolder { - private ItemAttachmentDefaultBinding binding; + private final ItemAttachmentDefaultBinding binding; @SuppressWarnings("WeakerAccess") public DefaultAttachmentViewHolder(ItemAttachmentDefaultBinding binding) { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/picker/CardAttachmentPicker.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/picker/CardAttachmentPicker.java deleted file mode 100644 index d394d9637..000000000 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/picker/CardAttachmentPicker.java +++ /dev/null @@ -1,160 +0,0 @@ -package it.niedermann.nextcloud.deck.ui.card.attachments.picker; - -import android.content.ContentResolver; -import android.content.Context; -import android.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.net.Uri; -import android.os.Bundle; -import android.provider.ContactsContract; -import android.provider.MediaStore; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.ImageView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.DialogFragment; - -import com.bumptech.glide.Glide; -import com.google.android.material.bottomsheet.BottomSheetDialogFragment; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import it.niedermann.nextcloud.deck.DeckLog; -import it.niedermann.nextcloud.deck.R; -import it.niedermann.nextcloud.deck.databinding.DialogAttachmentPickerBinding; -import it.niedermann.nextcloud.deck.ui.branding.Branded; - -import static android.os.Build.VERSION.SDK_INT; -import static android.os.Build.VERSION_CODES.LOLLIPOP; - -public class CardAttachmentPicker extends BottomSheetDialogFragment implements Branded { - - private DialogAttachmentPickerBinding binding; - private CardAttachmentPickerListener listener; - - - public CardAttachmentPicker() { - - } - - @Override - public void onAttach(@NonNull Context context) { - super.onAttach(context); - - if (getParentFragment() instanceof CardAttachmentPickerListener) { - this.listener = (CardAttachmentPickerListener) getParentFragment(); - } else { - throw new IllegalArgumentException("Caller must implement " + CardAttachmentPickerListener.class.getSimpleName()); - } - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - binding = DialogAttachmentPickerBinding.inflate(inflater, container, false); - - - - return binding.getRoot(); - } - - @Override - public void onActivityCreated(@Nullable Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - loadAndDisplayContacts(); - loadAndDisplayGallery(); - } - - private void loadAndDisplayGallery() { - if (SDK_INT >= LOLLIPOP) { - final ContentResolver contentResolver = requireContext().getContentResolver(); - try (final Cursor outerCursor = contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, null)) { - while (outerCursor.moveToNext()) { - Bitmap photo = MediaStore.Images.Thumbnails.getThumbnail( - contentResolver, outerCursor.getLong(outerCursor.getColumnIndex(MediaStore.Images.Media._ID)), - MediaStore.Images.Thumbnails.MINI_KIND, null); - - ImageView iv = new ImageView(requireContext()); - - Glide.with(requireContext()) - .load(photo) - .into(iv); - binding.thumbnails.addView(iv); - } - } - } - } - - private void loadAndDisplayContacts() { - List<Contact> contacts = new ArrayList<>(); - - final ContentResolver contentResolver = requireContext().getContentResolver(); - try (final Cursor outerCursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null)) { - while (outerCursor.moveToNext()) { - Contact contact = new Contact(); - contact.userName = outerCursor.getString(outerCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)); - final String contactId = outerCursor.getString(outerCursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY)); - Cursor pCur = contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, - ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY + " = ?", - new String[]{contactId}, null); - if (pCur.moveToFirst()) { - String phone = pCur.getString( - pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); - contact.phoneNumber = phone; - DeckLog.info("phoneNumber of " + contact.userName + ": " + phone); - } - pCur.close(); - - final String lookupKey = outerCursor.getString(outerCursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY)); - InputStream inputStream = ContactsContract.Contacts.openContactPhotoInputStream(contentResolver, Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, lookupKey)); - - Bitmap photo = BitmapFactory.decodeResource(getResources(), - R.drawable.ic_person_grey600_24dp); - if (inputStream != null) { - DeckLog.info("FOUND IMAGE for " + contact.userName); - photo = BitmapFactory.decodeStream(inputStream); - - Glide.with(requireContext()) - .load(photo) - .into(binding.avatar); - inputStream.close(); - } - - contacts.add(contact); - } - } catch (IOException e) { - e.printStackTrace(); - } - - - ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>( - requireContext(), - android.R.layout.simple_list_item_1, - contacts.stream().map((c) -> c.userName).collect(Collectors.toList())); - - binding.contactsListView.setAdapter(arrayAdapter); - } - - private static class Contact { - String userName; - String phoneNumber; - String email; - } - - public static DialogFragment newInstance() { - return new CardAttachmentPicker(); - } - - @Override - public void applyBrand(int mainColor) {} -} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/picker/CardAttachmentPickerListener.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/picker/CardAttachmentPickerListener.java deleted file mode 100644 index af61bba5c..000000000 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/picker/CardAttachmentPickerListener.java +++ /dev/null @@ -1,13 +0,0 @@ -package it.niedermann.nextcloud.deck.ui.card.attachments.picker; - -import androidx.annotation.RequiresApi; - -import static android.os.Build.VERSION_CODES.LOLLIPOP; - -public interface CardAttachmentPickerListener { - - @RequiresApi(api = LOLLIPOP) - void pickCamera(); - void pickContact(); - void pickFile(); -} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/picker/GalleryAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/picker/GalleryAdapter.java index 0b051ed58..902ff2cb7 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/picker/GalleryAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/picker/GalleryAdapter.java @@ -10,7 +10,6 @@ import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.provider.MediaStore; -import android.util.Pair; import android.util.Size; import android.view.LayoutInflater; import android.view.ViewGroup; @@ -18,14 +17,10 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; -import java.util.HashMap; -import java.util.Map; -import java.util.NoSuchElementException; +import java.io.IOException; import java.util.Objects; -import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.FutureTask; import java.util.function.Consumer; import it.niedermann.nextcloud.deck.databinding.ItemAttachmentImageBinding; @@ -45,8 +40,6 @@ public class GalleryAdapter extends RecyclerView.Adapter<GalleryItemViewHolder> @NonNull private final ContentResolver contentResolver; @NonNull - private final Map<Integer, Pair<Long, FutureTask<Bitmap>>> itemCache = new HashMap<>(); - @NonNull private final ExecutorService bitmapFetcherExecutor = Executors.newCachedThreadPool(); @NonNull private final ExecutorService bitmapWaiterExecutor = Executors.newCachedThreadPool(); @@ -66,8 +59,11 @@ public class GalleryAdapter extends RecyclerView.Adapter<GalleryItemViewHolder> @Override public long getItemId(int position) { - final Pair<Long, ?> itemAtPosition = getImageInformation(position); - return itemAtPosition == null ? NO_ID : itemAtPosition.first; + if (cursor.moveToPosition(position)) { + return cursor.getLong(columnIndex); + } else { + return NO_ID; + } } @NonNull @@ -78,17 +74,22 @@ public class GalleryAdapter extends RecyclerView.Adapter<GalleryItemViewHolder> @Override public void onBindViewHolder(@NonNull GalleryItemViewHolder holder, int position) { - final Pair<Long, FutureTask<Bitmap>> imageInformation = getImageInformation(position); - bitmapFetcherExecutor.execute(imageInformation.second); - bitmapWaiterExecutor.execute(() -> { - try { - final Bitmap image = imageInformation.second.get(); - new Handler(Looper.getMainLooper()).post(() -> holder.bind(ContentUris.withAppendedId( - MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imageInformation.first), image, onSelect)); - } catch (ExecutionException | InterruptedException ignored) { - new Handler(Looper.getMainLooper()).post(holder::bindError); + long id = getItemId(position); + try { + Bitmap thumbnail; + if (SDK_INT >= Q) { + thumbnail = contentResolver.loadThumbnail(ContentUris.withAppendedId( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id), new Size(512, 384), null); + } else { + thumbnail = MediaStore.Images.Thumbnails.getThumbnail( + contentResolver, id, + MediaStore.Images.Thumbnails.MINI_KIND, null); } - }); + new Handler(Looper.getMainLooper()).post(() -> holder.bind(ContentUris.withAppendedId( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id), thumbnail, onSelect)); + } catch (IOException ignored) { + new Handler(Looper.getMainLooper()).post(holder::bindError); + } } @Override @@ -96,38 +97,6 @@ public class GalleryAdapter extends RecyclerView.Adapter<GalleryItemViewHolder> return count; } - private Pair<Long, FutureTask<Bitmap>> getImageInformation(int position) { - if (itemCache.containsKey(position)) { - return itemCache.get(position); - } else { - if (cursor.isClosed()) { - throw new IllegalStateException("This adapter has already been destoryed and can no longer be used."); - } - if (cursor.moveToPosition(position)) { - long id = cursor.getLong(columnIndex); - return itemCache.put(position, new Pair<>(id, new FutureTask<>(() -> { - if (SDK_INT >= Q) { - return contentResolver.loadThumbnail(ContentUris.withAppendedId( - MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id), new Size(512, 384), null); - } else { - return MediaStore.Images.Thumbnails.getThumbnail( - contentResolver, id, - MediaStore.Images.Thumbnails.MINI_KIND, null); - } - }))); - } else { - throw new NoSuchElementException("Could not find ID for position " + position); - } - } - } - - /** - * Call this method in case of low memory. It will clear the internally cached {@link Bitmap}s to free memory. - */ - public void onLowMemory() { - itemCache.clear(); - } - /** * Call this method when the {@link GalleryAdapter} is no longe need to free resources. */ |