diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaActions.java b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaActions.java index b4e3886679..52755969e1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaActions.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaActions.java @@ -10,6 +10,8 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.database.MediaDatabase; import org.thoughtcrime.securesms.permissions.Permissions; @@ -59,11 +61,9 @@ final class MediaActions { recordCount, recordCount); - AlertDialog.Builder builder = new AlertDialog.Builder(context) - .setIcon(R.drawable.ic_warning) - .setTitle(confirmTitle) - .setMessage(confirmMessage) - .setCancelable(true); + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context).setTitle(confirmTitle) + .setMessage(confirmMessage) + .setCancelable(true); builder.setPositiveButton(R.string.delete, (dialogInterface, i) -> new ProgressDialogAsyncTask(context, diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaOverviewPageFragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaOverviewPageFragment.java index 3af063a698..cfecef8a2d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaOverviewPageFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaOverviewPageFragment.java @@ -33,6 +33,8 @@ import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.MediaPreviewActivity; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.attachments.DatabaseAttachment; +import org.thoughtcrime.securesms.components.menu.ActionItem; +import org.thoughtcrime.securesms.components.menu.SignalBottomActionBar; import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaController; import org.thoughtcrime.securesms.components.voice.VoiceNotePlaybackState; import org.thoughtcrime.securesms.database.MediaDatabase; @@ -45,6 +47,8 @@ import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.WindowUtil; +import java.util.Arrays; + public final class MediaOverviewPageFragment extends Fragment implements MediaGalleryAllAdapter.ItemClickListener, MediaGalleryAllAdapter.AudioItemListener, @@ -69,6 +73,7 @@ public final class MediaOverviewPageFragment extends Fragment private MediaGalleryAllAdapter adapter; private GridMode gridMode; private VoiceNoteMediaController voiceNoteMediaController; + private SignalBottomActionBar bottomActionBar; public static @NonNull Fragment newInstance(long threadId, @NonNull MediaLoader.MediaType mediaType, @@ -112,9 +117,10 @@ public final class MediaOverviewPageFragment extends Fragment View view = inflater.inflate(R.layout.media_overview_page_fragment, container, false); int spans = getResources().getInteger(R.integer.media_overview_cols); - this.recyclerView = view.findViewById(R.id.media_grid); - this.noMedia = view.findViewById(R.id.no_images); - this.gridManager = new StickyHeaderGridLayoutManager(spans); + this.recyclerView = view.findViewById(R.id.media_grid); + this.noMedia = view.findViewById(R.id.no_images); + this.bottomActionBar = view.findViewById(R.id.media_overview_bottom_action_bar); + this.gridManager = new StickyHeaderGridLayoutManager(spans); this.adapter = new MediaGalleryAllAdapter(context, GlideApp.with(this), @@ -136,7 +142,7 @@ public final class MediaOverviewPageFragment extends Fragment this.sorting = sorting; adapter.setShowFileSizes(sorting.isRelatedToFileSize()); LoaderManager.getInstance(this).restartLoader(0, null, this); - refreshActionModeTitle(); + updateMultiSelect(); } }); @@ -154,7 +160,7 @@ public final class MediaOverviewPageFragment extends Fragment this.detail = detail; adapter.setDetailView(detail); refreshLayoutManager(); - refreshActionModeTitle(); + updateMultiSelect(); } @Override @@ -214,7 +220,7 @@ public final class MediaOverviewPageFragment extends Fragment if (adapter.getSelectedMediaCount() == 0) { actionMode.finish(); } else { - refreshActionModeTitle(); + updateMultiSelect(); } } @@ -275,33 +281,18 @@ public final class MediaOverviewPageFragment extends Fragment private void handleSelectAllMedia() { getListAdapter().selectAllMedia(); - refreshActionModeTitle(); - } - - private void refreshActionModeTitle() { - if (actionMode != null) { - actionMode.setTitle(getActionModeTitle()); - } + updateMultiSelect(); } private String getActionModeTitle() { - MediaGalleryAllAdapter adapter = getListAdapter(); - int mediaCount = adapter.getSelectedMediaCount(); - boolean showTotalFileSize = detail || - mediaType != MediaLoader.MediaType.GALLERY || - sorting == MediaDatabase.Sorting.Largest; + MediaGalleryAllAdapter adapter = getListAdapter(); + int mediaCount = adapter.getSelectedMediaCount(); + long totalFileSize = adapter.getSelectedMediaTotalFileSize(); - if (showTotalFileSize) { - long totalFileSize = adapter.getSelectedMediaTotalFileSize(); - return getResources().getQuantityString(R.plurals.MediaOverviewActivity_d_items_s, - mediaCount, - mediaCount, - Util.getPrettyFileSize(totalFileSize)); - } else { - return getResources().getQuantityString(R.plurals.MediaOverviewActivity_d_items, - mediaCount, - mediaCount); - } + return getResources().getQuantityString(R.plurals.MediaOverviewActivity_d_selected_s, + mediaCount, + mediaCount, + Util.getPrettyFileSize(totalFileSize)); } private MediaGalleryAllAdapter getListAdapter() { @@ -312,8 +303,35 @@ public final class MediaOverviewPageFragment extends Fragment FragmentActivity activity = requireActivity(); actionMode = ((AppCompatActivity) activity).startSupportActionMode(actionModeCallback); ((MediaOverviewActivity) activity).onEnterMultiSelect(); + ViewUtil.fadeIn(bottomActionBar, 250); + + bottomActionBar.setItems(Arrays.asList( + new ActionItem(R.drawable.ic_save_24, R.string.MediaOverviewActivity_save, () -> { + MediaActions.handleSaveMedia(MediaOverviewPageFragment.this, + getListAdapter().getSelectedMedia(), + this::exitMultiSelect); + }), + new ActionItem(R.drawable.ic_select_24, R.string.MediaOverviewActivity_select_all, this::handleSelectAllMedia), + new ActionItem(R.drawable.ic_delete_24, R.string.MediaOverviewActivity_delete, () -> { + MediaActions.handleDeleteMedia(requireContext(), getListAdapter().getSelectedMedia()); + exitMultiSelect(); + }) + )); } + private void exitMultiSelect() { + actionMode.finish(); + actionMode = null; + ViewUtil.fadeOut(bottomActionBar, 250); + } + + private void updateMultiSelect() { + if (actionMode != null) { + actionMode.setTitle(getActionModeTitle()); + } + } + + @Override public void onPlay(@NonNull Uri audioUri, double progress, long messageId) { voiceNoteMediaController.startSinglePlayback(audioUri, messageId, progress); @@ -346,18 +364,9 @@ public final class MediaOverviewPageFragment extends Fragment private class ActionModeCallback implements ActionMode.Callback { - private int originalStatusBarColor; - @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { - mode.getMenuInflater().inflate(R.menu.media_overview_context, menu); mode.setTitle(getActionModeTitle()); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - Window window = requireActivity().getWindow(); - originalStatusBarColor = window.getStatusBarColor(); - WindowUtil.setStatusBarColor(requireActivity().getWindow(), getResources().getColor(R.color.action_mode_status_bar)); - } return true; } @@ -368,33 +377,17 @@ public final class MediaOverviewPageFragment extends Fragment @Override public boolean onActionItemClicked(ActionMode mode, MenuItem menuItem) { - switch (menuItem.getItemId()) { - case R.id.save: - MediaActions.handleSaveMedia(MediaOverviewPageFragment.this, - getListAdapter().getSelectedMedia(), - () -> actionMode.finish()); - return true; - case R.id.delete: - MediaActions.handleDeleteMedia(requireContext(), getListAdapter().getSelectedMedia()); - actionMode.finish(); - return true; - case R.id.select_all: - handleSelectAllMedia(); - return true; - } return false; } @Override public void onDestroyActionMode(ActionMode mode) { - actionMode = null; getListAdapter().clearSelection(); FragmentActivity activity = requireActivity(); - ((MediaOverviewActivity) activity).onExitMultiSelect(); - WindowUtil.setStatusBarColor(requireActivity().getWindow(), originalStatusBarColor); + exitMultiSelect(); } } diff --git a/app/src/main/res/drawable/ic_save_24.xml b/app/src/main/res/drawable/ic_save_24.xml index 32a936e3ce..70ed503c40 100644 --- a/app/src/main/res/drawable/ic_save_24.xml +++ b/app/src/main/res/drawable/ic_save_24.xml @@ -1,5 +1,9 @@ - - + + diff --git a/app/src/main/res/layout/media_overview_page_fragment.xml b/app/src/main/res/layout/media_overview_page_fragment.xml index d300c067cd..4b826d865e 100644 --- a/app/src/main/res/layout/media_overview_page_fragment.xml +++ b/app/src/main/res/layout/media_overview_page_fragment.xml @@ -1,5 +1,7 @@ - @@ -18,4 +20,16 @@ android:textSize="24sp" android:visibility="gone" /> - + + + diff --git a/app/src/main/res/menu/media_overview_context.xml b/app/src/main/res/menu/media_overview_context.xml deleted file mode 100644 index 7367a52ce9..0000000000 --- a/app/src/main/res/menu/media_overview_context.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e8828e2be3..23bafc7b12 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -944,14 +944,13 @@ Grid view List view Selected + Save + Select all + Delete - - %1$d item %2$s - %1$d items %2$s - - - %1$d item - %1$d items + + %1$d selected (%2$s) + %1$d selected (%2$s) File Audio