Persistent media in multi-send.
9
res/anim/slide_from_left.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/decelerate_interpolator">
|
||||
<translate
|
||||
android:duration="150"
|
||||
android:fromXDelta="-100%"
|
||||
android:toXDelta="0%" />
|
||||
</set>
|
9
res/anim/slide_to_left.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/decelerate_interpolator">
|
||||
<translate
|
||||
android:duration="150"
|
||||
android:fromXDelta="0%"
|
||||
android:toXDelta="-100%" />
|
||||
</set>
|
BIN
res/drawable-hdpi/ic_arrow_right.png
Normal file
After Width: | Height: | Size: 260 B |
BIN
res/drawable-hdpi/ic_select_off.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
res/drawable-hdpi/ic_select_on.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
res/drawable-mdpi/ic_arrow_right.png
Normal file
After Width: | Height: | Size: 238 B |
BIN
res/drawable-mdpi/ic_select_off.png
Normal file
After Width: | Height: | Size: 923 B |
BIN
res/drawable-mdpi/ic_select_on.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
12
res/drawable-v21/media_count_button_background.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/transparent_white_40">
|
||||
<item android:id="@+id/mask">
|
||||
<shape>
|
||||
<corners android:radius="1000dp" />
|
||||
<solid android:color="@color/white" />
|
||||
</shape>
|
||||
</item>
|
||||
<item android:drawable="@drawable/pill" />
|
||||
</ripple>
|
BIN
res/drawable-xhdpi/ic_arrow_right.png
Normal file
After Width: | Height: | Size: 344 B |
BIN
res/drawable-xhdpi/ic_select_off.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
res/drawable-xhdpi/ic_select_on.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
res/drawable-xxhdpi/ic_arrow_right.png
Normal file
After Width: | Height: | Size: 508 B |
BIN
res/drawable-xxhdpi/ic_select_off.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
res/drawable-xxhdpi/ic_select_on.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
res/drawable-xxxhdpi/ic_arrow_right.png
Normal file
After Width: | Height: | Size: 652 B |
BIN
res/drawable-xxxhdpi/ic_select_off.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
res/drawable-xxxhdpi/ic_select_on.png
Normal file
After Width: | Height: | Size: 7.9 KiB |
5
res/drawable/media_count_button_background.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<corners android:radius="1000dp" />
|
||||
<solid android:color="@color/signal_primary" />
|
||||
</shape>
|
5
res/drawable/media_count_number_background.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<corners android:radius="1000dp" />
|
||||
<solid android:color="@color/core_white" />
|
||||
</shape>
|
5
res/drawable/pill.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<corners android:radius="1000dp" />
|
||||
<solid android:color="@color/signal_primary" />
|
||||
</shape>
|
@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/mediapicker_fragment_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</LinearLayout>
|
@ -6,14 +6,16 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="2dp"
|
||||
android:layout_marginBottom="2dp">
|
||||
android:layout_marginBottom="2dp"
|
||||
android:animateLayoutChanges="true">
|
||||
|
||||
<org.thoughtcrime.securesms.components.SquareImageView
|
||||
android:id="@+id/mediapicker_image_item_thumbnail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="centerCrop"/>
|
||||
android:scaleType="centerCrop"
|
||||
tools:src="@drawable/empty_inbox_1"/>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
@ -28,7 +30,7 @@
|
||||
android:layout_gravity="center"
|
||||
android:longClickable="false"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
tools:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="15dp"
|
||||
@ -41,20 +43,29 @@
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/mediapicker_selected"
|
||||
<View
|
||||
android:id="@+id/mediapicker_select_overlay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/transparent_black_90"
|
||||
android:visibility="gone">
|
||||
android:background="@color/transparent_black_90" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/ic_check_white_24dp" />
|
||||
<ImageView
|
||||
android:id="@+id/mediapicker_select_on"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top|right|end"
|
||||
android:padding="6dp"
|
||||
android:src="@drawable/ic_select_on"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</FrameLayout>
|
||||
<ImageView
|
||||
android:id="@+id/mediapicker_select_off"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top|right|end"
|
||||
android:padding="6dp"
|
||||
android:src="@drawable/ic_select_off"
|
||||
android:visibility="gone" />
|
||||
|
||||
</FrameLayout>
|
54
res/layout/mediasend_activity.xml
Normal file
@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/mediasend_fragment_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/mediasend_count_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:layout_marginRight="32dp"
|
||||
android:layout_gravity="bottom|right|end"
|
||||
android:padding="8dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:background="@drawable/media_count_button_background"
|
||||
android:elevation="4dp"
|
||||
tools:parentTag="android.widget.LinearLayout">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/mediasend_count_button_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="28dp"
|
||||
android:paddingLeft="7dp"
|
||||
android:paddingRight="7dp"
|
||||
android:paddingTop="2dp"
|
||||
android:paddingBottom="2dp"
|
||||
android:gravity="center"
|
||||
android:background="@drawable/media_count_number_background"
|
||||
android:textColor="@color/signal_primary"
|
||||
android:textSize="18sp"
|
||||
tools:text="3" />
|
||||
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginLeft="2dp"
|
||||
android:layout_marginStart="2dp"
|
||||
android:src="@drawable/ic_arrow_right"
|
||||
android:tint="@color/core_white"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
@ -15,6 +15,7 @@ import android.support.v7.widget.Toolbar;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
@ -88,6 +89,14 @@ public class MediaPickerFolderFragment extends Fragment implements MediaPickerFo
|
||||
initToolbar(view.findViewById(R.id.mediapicker_toolbar));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
requireActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||
requireActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
|
@ -17,6 +17,7 @@ import org.thoughtcrime.securesms.util.StableIdGenerator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
@ -38,11 +39,7 @@ public class MediaPickerItemAdapter extends RecyclerView.Adapter<MediaPickerItem
|
||||
this.media = new ArrayList<>();
|
||||
this.maxSelection = maxSelection;
|
||||
this.stableIdGenerator = new StableIdGenerator<>();
|
||||
this.selected = new TreeSet<>((m1, m2) -> {
|
||||
if (m1.equals(m2)) return 0;
|
||||
else if (Long.compare(m2.getDate(), m1.getDate()) == 0) return m2.getUri().compareTo(m1.getUri());
|
||||
else return Long.compare(m2.getDate(), m1.getDate());
|
||||
});
|
||||
this.selected = new LinkedHashSet<>();
|
||||
|
||||
setHasStableIds(true);
|
||||
}
|
||||
@ -97,13 +94,17 @@ public class MediaPickerItemAdapter extends RecyclerView.Adapter<MediaPickerItem
|
||||
|
||||
private final ImageView thumbnail;
|
||||
private final View playOverlay;
|
||||
private final View selectedOverlay;
|
||||
private final View selectOn;
|
||||
private final View selectOff;
|
||||
private final View selectOverlay;
|
||||
|
||||
ItemViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
thumbnail = itemView.findViewById(R.id.mediapicker_image_item_thumbnail);
|
||||
playOverlay = itemView.findViewById(R.id.mediapicker_play_overlay);
|
||||
selectedOverlay = itemView.findViewById(R.id.mediapicker_selected);
|
||||
thumbnail = itemView.findViewById(R.id.mediapicker_image_item_thumbnail);
|
||||
playOverlay = itemView.findViewById(R.id.mediapicker_play_overlay);
|
||||
selectOn = itemView.findViewById(R.id.mediapicker_select_on);
|
||||
selectOff = itemView.findViewById(R.id.mediapicker_select_off);
|
||||
selectOverlay = itemView.findViewById(R.id.mediapicker_select_overlay);
|
||||
}
|
||||
|
||||
void bind(@NonNull Media media, boolean multiSelect, Set<Media> selected, int maxSelection, @NonNull GlideRequests glideRequests, @NonNull EventListener eventListener) {
|
||||
@ -113,10 +114,13 @@ public class MediaPickerItemAdapter extends RecyclerView.Adapter<MediaPickerItem
|
||||
.into(thumbnail);
|
||||
|
||||
playOverlay.setVisibility(MediaUtil.isVideoType(media.getMimeType()) ? View.VISIBLE : View.GONE);
|
||||
selectedOverlay.setVisibility(selected.contains(media) ? View.VISIBLE : View.GONE);
|
||||
|
||||
if (selected.isEmpty() && !multiSelect) {
|
||||
itemView.setOnClickListener(v -> eventListener.onMediaChosen(media));
|
||||
selectOn.setVisibility(View.GONE);
|
||||
selectOff.setVisibility(View.GONE);
|
||||
selectOverlay.setVisibility(View.GONE);
|
||||
|
||||
if (maxSelection > 1) {
|
||||
itemView.setOnLongClickListener(v -> {
|
||||
selected.add(media);
|
||||
@ -125,11 +129,17 @@ public class MediaPickerItemAdapter extends RecyclerView.Adapter<MediaPickerItem
|
||||
});
|
||||
}
|
||||
} else if (selected.contains(media)) {
|
||||
selectOff.setVisibility(View.VISIBLE);
|
||||
selectOn.setVisibility(View.VISIBLE);
|
||||
selectOverlay.setVisibility(View.VISIBLE);
|
||||
itemView.setOnClickListener(v -> {
|
||||
selected.remove(media);
|
||||
eventListener.onMediaSelectionChanged(new ArrayList<>(selected));
|
||||
});
|
||||
} else {
|
||||
selectOff.setVisibility(View.VISIBLE);
|
||||
selectOn.setVisibility(View.GONE);
|
||||
selectOverlay.setVisibility(View.GONE);
|
||||
itemView.setOnClickListener(v -> {
|
||||
if (selected.size() < maxSelection) {
|
||||
selected.add(media);
|
||||
|
@ -50,8 +50,6 @@ public class MediaPickerItemFragment extends Fragment implements MediaPickerItem
|
||||
private MediaPickerItemAdapter adapter;
|
||||
private Controller controller;
|
||||
private GridLayoutManager layoutManager;
|
||||
private ActionMode actionMode;
|
||||
private ActionMode.Callback actionModeCallback;
|
||||
|
||||
public static MediaPickerItemFragment newInstance(@NonNull String bucketId, @NonNull String folderTitle, int maxSelection) {
|
||||
Bundle args = new Bundle();
|
||||
@ -70,11 +68,10 @@ public class MediaPickerItemFragment extends Fragment implements MediaPickerItem
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
bucketId = getArguments().getString(KEY_BUCKET_ID);
|
||||
folderTitle = getArguments().getString(KEY_FOLDER_TITLE);
|
||||
maxSelection = getArguments().getInt(KEY_MAX_SELECTION);
|
||||
viewModel = ViewModelProviders.of(requireActivity(), new MediaSendViewModel.Factory(new MediaRepository())).get(MediaSendViewModel.class);
|
||||
actionModeCallback = new ActionModeCallback();
|
||||
bucketId = getArguments().getString(KEY_BUCKET_ID);
|
||||
folderTitle = getArguments().getString(KEY_FOLDER_TITLE);
|
||||
maxSelection = getArguments().getInt(KEY_MAX_SELECTION);
|
||||
viewModel = ViewModelProviders.of(requireActivity(), new MediaSendViewModel.Factory(new MediaRepository())).get(MediaSendViewModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -114,6 +111,8 @@ public class MediaPickerItemFragment extends Fragment implements MediaPickerItem
|
||||
}
|
||||
|
||||
viewModel.getMediaInBucket(requireContext(), bucketId).observe(this, adapter::setMedia);
|
||||
|
||||
initMediaObserver(viewModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -125,16 +124,19 @@ public class MediaPickerItemFragment extends Fragment implements MediaPickerItem
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.mediapicker_default, menu);
|
||||
public void onPrepareOptionsMenu(Menu menu) {
|
||||
requireActivity().getMenuInflater().inflate(R.menu.mediapicker_default, menu);
|
||||
|
||||
MenuItem beginSelectionButton = menu.findItem(R.id.mediapicker_menu_add);
|
||||
|
||||
beginSelectionButton.setVisible(!viewModel.getCountButtonState().getValue().getVisibility());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == R.id.mediapicker_menu_add) {
|
||||
adapter.setForcedMultiSelect(true);
|
||||
actionMode = ((AppCompatActivity) requireActivity()).startSupportActionMode(actionModeCallback);
|
||||
actionMode.setTitle(getResources().getString(R.string.MediaPickerItemFragment_tap_to_select));
|
||||
viewModel.onMultiSelectStarted();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -148,23 +150,13 @@ public class MediaPickerItemFragment extends Fragment implements MediaPickerItem
|
||||
|
||||
@Override
|
||||
public void onMediaChosen(@NonNull Media media) {
|
||||
controller.onMediaSelected(bucketId, Collections.singleton(media));
|
||||
viewModel.onSelectedMediaChanged(requireContext(), Collections.singletonList(media));
|
||||
controller.onMediaSelected(bucketId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaSelectionChanged(@NonNull List<Media> selected) {
|
||||
adapter.notifyDataSetChanged();
|
||||
|
||||
if (actionMode == null && !selected.isEmpty()) {
|
||||
actionMode = ((AppCompatActivity) requireActivity()).startSupportActionMode(actionModeCallback);
|
||||
actionMode.setTitle(String.valueOf(selected.size()));
|
||||
} else if (actionMode != null && selected.isEmpty()) {
|
||||
actionMode.finish();
|
||||
} else if (actionMode != null) {
|
||||
actionMode.setTitle(String.valueOf(selected.size()));
|
||||
}
|
||||
|
||||
viewModel.onSelectedMediaChanged(requireContext(), selected);
|
||||
}
|
||||
|
||||
@ -181,6 +173,12 @@ public class MediaPickerItemFragment extends Fragment implements MediaPickerItem
|
||||
toolbar.setNavigationOnClickListener(v -> requireActivity().onBackPressed());
|
||||
}
|
||||
|
||||
private void initMediaObserver(@NonNull MediaSendViewModel viewModel) {
|
||||
viewModel.getCountButtonState().observe(this, media -> {
|
||||
requireActivity().invalidateOptionsMenu();
|
||||
});
|
||||
}
|
||||
|
||||
private void onScreenWidthChanged(int newWidth) {
|
||||
if (layoutManager != null) {
|
||||
layoutManager.setSpanCount(newWidth / getResources().getDimensionPixelSize(R.dimen.media_picker_item_width));
|
||||
@ -193,55 +191,7 @@ public class MediaPickerItemFragment extends Fragment implements MediaPickerItem
|
||||
return size.x;
|
||||
}
|
||||
|
||||
private class ActionModeCallback implements ActionMode.Callback {
|
||||
|
||||
private int statusBarColor;
|
||||
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||
MenuInflater inflater = mode.getMenuInflater();
|
||||
inflater.inflate(R.menu.mediapicker_multiselect, menu);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
Window window = requireActivity().getWindow();
|
||||
statusBarColor = window.getStatusBarColor();
|
||||
window.setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onActionItemClicked(ActionMode mode, MenuItem menuItem) {
|
||||
if (menuItem.getItemId() == R.id.mediapicker_menu_confirm) {
|
||||
List<Media> selected = new ArrayList<>(adapter.getSelected());
|
||||
actionMode.finish();
|
||||
viewModel.onSelectedMediaChanged(requireContext(), selected);
|
||||
controller.onMediaSelected(bucketId, selected);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyActionMode(ActionMode mode) {
|
||||
actionMode = null;
|
||||
adapter.setSelected(Collections.emptySet());
|
||||
viewModel.onSelectedMediaChanged(requireContext(), Collections.emptyList());
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
requireActivity().getWindow().setStatusBarColor(statusBarColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public interface Controller {
|
||||
void onMediaSelected(@NonNull String bucketId, @NonNull Collection<Media> media);
|
||||
void onMediaSelected(@NonNull String bucketId);
|
||||
}
|
||||
}
|
||||
|
@ -6,11 +6,18 @@ import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.TransportOption;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.mms.MediaConstraints;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.scribbles.ScribbleFragment;
|
||||
@ -23,6 +30,7 @@ import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Encompasses the entire flow of sending media, starting from the selection process to the actual
|
||||
@ -56,10 +64,12 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
|
||||
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
|
||||
|
||||
private Recipient recipient;
|
||||
private String body;
|
||||
private TransportOption transport;
|
||||
private MediaSendViewModel viewModel;
|
||||
|
||||
private View countButton;
|
||||
private TextView countButtonText;
|
||||
|
||||
/**
|
||||
* Get an intent to launch the media send flow starting with the picker.
|
||||
*/
|
||||
@ -94,28 +104,42 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState, boolean ready) {
|
||||
setContentView(R.layout.mediapicker_activity);
|
||||
setContentView(R.layout.mediasend_activity);
|
||||
setResult(RESULT_CANCELED);
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
countButton = findViewById(R.id.mediasend_count_button);
|
||||
countButtonText = findViewById(R.id.mediasend_count_button_text);
|
||||
|
||||
viewModel = ViewModelProviders.of(this, new MediaSendViewModel.Factory(new MediaRepository())).get(MediaSendViewModel.class);
|
||||
recipient = Recipient.from(this, Address.fromSerialized(getIntent().getStringExtra(KEY_ADDRESS)), true);
|
||||
body = getIntent().getStringExtra(KEY_BODY);
|
||||
transport = getIntent().getParcelableExtra(KEY_TRANSPORT);
|
||||
|
||||
viewModel.setMediaConstraints(transport.isSms() ? MediaConstraints.getMmsMediaConstraints(transport.getSimSubscriptionId().or(-1))
|
||||
: MediaConstraints.getPushMediaConstraints());
|
||||
|
||||
viewModel.onBodyChanged(getIntent().getStringExtra(KEY_BODY));
|
||||
|
||||
List<Media> media = getIntent().getParcelableArrayListExtra(KEY_MEDIA);
|
||||
|
||||
if (!Util.isEmpty(media)) {
|
||||
navigateToMediaSend(media, body, transport);
|
||||
viewModel.onSelectedMediaChanged(this, media);
|
||||
|
||||
Fragment fragment = MediaSendFragment.newInstance(transport, dynamicLanguage.getCurrentLocale());
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.replace(R.id.mediasend_fragment_container, fragment, TAG_SEND)
|
||||
.commit();
|
||||
} else {
|
||||
navigateToFolderPicker(recipient);
|
||||
MediaPickerFolderFragment fragment = MediaPickerFolderFragment.newInstance(recipient);
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.replace(R.id.mediasend_fragment_container, fragment, TAG_FOLDER_PICKER)
|
||||
.commit();
|
||||
}
|
||||
|
||||
initializeCountButtonObserver(transport, dynamicLanguage.getCurrentLocale());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -137,41 +161,34 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
|
||||
public void onFolderSelected(@NonNull MediaFolder folder) {
|
||||
viewModel.onFolderSelected(folder.getBucketId());
|
||||
|
||||
MediaPickerItemFragment fragment = MediaPickerItemFragment.newInstance(folder.getBucketId(),
|
||||
folder.getTitle(),
|
||||
transport.isSms() ? MAX_SMS : MAX_PUSH);
|
||||
|
||||
MediaPickerItemFragment fragment = MediaPickerItemFragment.newInstance(folder.getBucketId(), folder.getTitle(), transport.isSms() ? MAX_SMS :MAX_PUSH);
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.setCustomAnimations(R.anim.fade_in, R.anim.fade_out, R.anim.fade_in, R.anim.fade_out)
|
||||
.replace(R.id.mediapicker_fragment_container, fragment, TAG_ITEM_PICKER)
|
||||
.setCustomAnimations(R.anim.slide_from_right, R.anim.slide_to_left, R.anim.slide_from_left, R.anim.slide_to_right)
|
||||
.replace(R.id.mediasend_fragment_container, fragment, TAG_ITEM_PICKER)
|
||||
.addToBackStack(null)
|
||||
.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaSelected(@NonNull String bucketId, @NonNull Collection<Media> media) {
|
||||
MediaSendFragment fragment = MediaSendFragment.newInstance(body, transport, dynamicLanguage.getCurrentLocale());
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.setCustomAnimations(R.anim.fade_in, R.anim.fade_out, R.anim.fade_in, R.anim.fade_out)
|
||||
.replace(R.id.mediapicker_fragment_container, fragment, TAG_SEND)
|
||||
.addToBackStack(null)
|
||||
.commit();
|
||||
public void onMediaSelected(@NonNull String bucketId) {
|
||||
navigateToMediaSend(transport, dynamicLanguage.getCurrentLocale());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAddMediaClicked(@NonNull String bucketId) {
|
||||
// TODO: Get actual folder title somehow
|
||||
MediaPickerFolderFragment folderFragment = MediaPickerFolderFragment.newInstance(recipient);
|
||||
MediaPickerItemFragment itemFragment = MediaPickerItemFragment.newInstance(bucketId,
|
||||
"",
|
||||
transport.isSms() ? MAX_SMS : MAX_PUSH);
|
||||
MediaPickerItemFragment itemFragment = MediaPickerItemFragment.newInstance(bucketId, "", transport.isSms() ? MAX_SMS : MAX_PUSH);
|
||||
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.replace(R.id.mediapicker_fragment_container, folderFragment, TAG_FOLDER_PICKER)
|
||||
.setCustomAnimations(R.anim.stationary, R.anim.slide_to_left, R.anim.slide_from_left, R.anim.slide_to_right)
|
||||
.replace(R.id.mediasend_fragment_container, folderFragment, TAG_FOLDER_PICKER)
|
||||
.addToBackStack(null)
|
||||
.commit();
|
||||
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.replace(R.id.mediapicker_fragment_container, itemFragment, TAG_ITEM_PICKER)
|
||||
.setCustomAnimations(R.anim.slide_from_right, R.anim.stationary, R.anim.slide_from_left, R.anim.slide_to_right)
|
||||
.replace(R.id.mediasend_fragment_container, itemFragment, TAG_ITEM_PICKER)
|
||||
.addToBackStack(null)
|
||||
.commit();
|
||||
}
|
||||
@ -214,20 +231,29 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
|
||||
}
|
||||
}
|
||||
|
||||
private void navigateToMediaSend(List<Media> media, String body, TransportOption transport) {
|
||||
viewModel.setInitialSelectedMedia(this, media);
|
||||
private void initializeCountButtonObserver(@NonNull TransportOption transport, @NonNull Locale locale) {
|
||||
viewModel.getCountButtonState().observe(this, buttonState -> {
|
||||
if (buttonState == null) return;
|
||||
|
||||
MediaSendFragment sendFragment = MediaSendFragment.newInstance(body, transport, dynamicLanguage.getCurrentLocale());
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.replace(R.id.mediapicker_fragment_container, sendFragment, TAG_SEND)
|
||||
.commit();
|
||||
countButton.setVisibility(buttonState.getVisibility() ? View.VISIBLE : View.GONE);
|
||||
countButton.setOnClickListener(v -> navigateToMediaSend(transport, locale));
|
||||
countButtonText.setText(String.valueOf(buttonState.getCount()));
|
||||
});
|
||||
}
|
||||
|
||||
private void navigateToFolderPicker(@NonNull Recipient recipient) {
|
||||
MediaPickerFolderFragment folderFragment = MediaPickerFolderFragment.newInstance(recipient);
|
||||
private void navigateToMediaSend(@NonNull TransportOption transport, @NonNull Locale locale) {
|
||||
MediaSendFragment fragment = MediaSendFragment.newInstance(transport, locale);
|
||||
String backstackTag = null;
|
||||
|
||||
if (getSupportFragmentManager().findFragmentByTag(TAG_SEND) != null) {
|
||||
getSupportFragmentManager().popBackStack(TAG_SEND, FragmentManager.POP_BACK_STACK_INCLUSIVE);
|
||||
backstackTag = TAG_SEND;
|
||||
}
|
||||
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.replace(R.id.mediapicker_fragment_container, folderFragment, TAG_FOLDER_PICKER)
|
||||
.setCustomAnimations(R.anim.slide_from_right, R.anim.slide_to_left, R.anim.slide_from_left, R.anim.slide_to_right)
|
||||
.replace(R.id.mediasend_fragment_container, fragment, TAG_SEND)
|
||||
.addToBackStack(backstackTag)
|
||||
.commit();
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +72,6 @@ public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGl
|
||||
|
||||
private static final String TAG = MediaSendFragment.class.getSimpleName();
|
||||
|
||||
private static final String KEY_BODY = "body";
|
||||
private static final String KEY_TRANSPORT = "transport";
|
||||
private static final String KEY_LOCALE = "locale";
|
||||
|
||||
@ -99,9 +98,8 @@ public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGl
|
||||
|
||||
private final Rect visibleBounds = new Rect();
|
||||
|
||||
public static MediaSendFragment newInstance(@NonNull String body, @NonNull TransportOption transport, @NonNull Locale locale) {
|
||||
public static MediaSendFragment newInstance(@NonNull TransportOption transport, @NonNull Locale locale) {
|
||||
Bundle args = new Bundle();
|
||||
args.putString(KEY_BODY, body);
|
||||
args.putParcelable(KEY_TRANSPORT, transport);
|
||||
args.putSerializable(KEY_LOCALE, locale);
|
||||
|
||||
@ -134,9 +132,6 @@ public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGl
|
||||
locale = (Locale) getArguments().getSerializable(KEY_LOCALE);
|
||||
|
||||
initViewModel();
|
||||
|
||||
requireActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
requireActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -181,7 +176,7 @@ public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGl
|
||||
captionText.clearFocus();
|
||||
composeText.requestFocus();
|
||||
|
||||
fragmentPagerAdapter = new MediaSendFragmentPagerAdapter(requireActivity().getSupportFragmentManager(), locale);
|
||||
fragmentPagerAdapter = new MediaSendFragmentPagerAdapter(getChildFragmentManager(), locale);
|
||||
fragmentPager.setAdapter(fragmentPagerAdapter);
|
||||
|
||||
FragmentPageChangeListener pageChangeListener = new FragmentPageChangeListener();
|
||||
@ -208,7 +203,7 @@ public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGl
|
||||
sendButton.setTransport(transportOption);
|
||||
sendButton.disableTransport(transportOption.getType() == TransportOption.Type.SMS ? TransportOption.Type.TEXTSECURE : TransportOption.Type.SMS);
|
||||
|
||||
composeText.append(getArguments().getString(KEY_BODY));
|
||||
composeText.append(viewModel.getBody());
|
||||
|
||||
|
||||
if (TextSecurePreferences.isSystemEmojiPreferred(getContext())) {
|
||||
@ -221,13 +216,25 @@ public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGl
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
|
||||
fragmentPagerAdapter.restoreState(viewModel.getDrawState());
|
||||
viewModel.onImageEditorStarted();
|
||||
|
||||
requireActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
requireActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHiddenChanged(boolean hidden) {
|
||||
super.onHiddenChanged(hidden);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
fragmentPagerAdapter.saveAllState();
|
||||
viewModel.saveDrawState(fragmentPagerAdapter.getSavedState());
|
||||
viewModel.onImageEditorEnded();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -328,11 +335,13 @@ public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGl
|
||||
});
|
||||
|
||||
viewModel.getBucketId().observe(this, bucketId -> {
|
||||
if (bucketId == null || !bucketId.isPresent() || sendButton.getSelectedTransport().isSms()) {
|
||||
if (bucketId == null) return;
|
||||
|
||||
if (sendButton.getSelectedTransport().isSms()) {
|
||||
addButton.setVisibility(View.GONE);
|
||||
} else {
|
||||
addButton.setVisibility(View.VISIBLE);
|
||||
addButton.setOnClickListener(v -> controller.onAddMediaClicked(bucketId.get()));
|
||||
addButton.setOnClickListener(v -> controller.onAddMediaClicked(bucketId));
|
||||
}
|
||||
});
|
||||
|
||||
@ -505,6 +514,7 @@ public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGl
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
presentCharactersRemaining();
|
||||
viewModel.onBodyChanged(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -9,6 +9,7 @@ import android.support.v4.app.FragmentStatePagerAdapter;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.scribbles.ScribbleFragment;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
@ -106,6 +107,15 @@ class MediaSendFragmentPagerAdapter extends FragmentStatePagerAdapter {
|
||||
return new HashMap<>(savedState);
|
||||
}
|
||||
|
||||
void saveAllState() {
|
||||
for (MediaSendPageFragment fragment : fragments.values()) {
|
||||
Object state = fragment.saveState();
|
||||
if (state != null) {
|
||||
savedState.put(fragment.getUri(), state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void restoreState(@NonNull Map<Uri, Object> state) {
|
||||
savedState.clear();
|
||||
savedState.putAll(state);
|
||||
|
@ -15,7 +15,6 @@ import org.thoughtcrime.securesms.mms.MediaConstraints;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.thoughtcrime.securesms.util.SingleLiveEvent;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@ -31,31 +30,37 @@ class MediaSendViewModel extends ViewModel {
|
||||
private final MutableLiveData<List<Media>> selectedMedia;
|
||||
private final MutableLiveData<List<Media>> bucketMedia;
|
||||
private final MutableLiveData<Integer> position;
|
||||
private final MutableLiveData<Optional<String>> bucketId;
|
||||
private final MutableLiveData<String> bucketId;
|
||||
private final MutableLiveData<List<MediaFolder>> folders;
|
||||
private final MutableLiveData<CountButtonState> countButtonState;
|
||||
private final SingleLiveEvent<Error> error;
|
||||
private final Map<Uri, Object> savedDrawState;
|
||||
|
||||
private MediaConstraints mediaConstraints;
|
||||
private MediaConstraints mediaConstraints;
|
||||
private CharSequence body;
|
||||
private CountButtonState.Visibility countButtonVisibility;
|
||||
|
||||
private MediaSendViewModel(@NonNull MediaRepository repository) {
|
||||
this.repository = repository;
|
||||
this.selectedMedia = new MutableLiveData<>();
|
||||
this.bucketMedia = new MutableLiveData<>();
|
||||
this.position = new MutableLiveData<>();
|
||||
this.bucketId = new MutableLiveData<>();
|
||||
this.folders = new MutableLiveData<>();
|
||||
this.error = new SingleLiveEvent<>();
|
||||
this.savedDrawState = new HashMap<>();
|
||||
this.repository = repository;
|
||||
this.selectedMedia = new MutableLiveData<>();
|
||||
this.bucketMedia = new MutableLiveData<>();
|
||||
this.position = new MutableLiveData<>();
|
||||
this.bucketId = new MutableLiveData<>();
|
||||
this.folders = new MutableLiveData<>();
|
||||
this.countButtonState = new MutableLiveData<>();
|
||||
this.error = new SingleLiveEvent<>();
|
||||
this.savedDrawState = new HashMap<>();
|
||||
this.countButtonVisibility = CountButtonState.Visibility.CONDITIONAL;
|
||||
|
||||
position.setValue(-1);
|
||||
countButtonState.setValue(new CountButtonState(0, CountButtonState.Visibility.CONDITIONAL));
|
||||
}
|
||||
|
||||
void setMediaConstraints(@NonNull MediaConstraints mediaConstraints) {
|
||||
this.mediaConstraints = mediaConstraints;
|
||||
}
|
||||
|
||||
void setInitialSelectedMedia(@NonNull Context context, @NonNull List<Media> newMedia) {
|
||||
void onSelectedMediaChanged(@NonNull Context context, @NonNull List<Media> newMedia) {
|
||||
repository.getPopulatedMedia(context, newMedia, populatedMedia -> {
|
||||
List<Media> filteredMedia = getFilteredMedia(context, populatedMedia, mediaConstraints);
|
||||
|
||||
@ -63,26 +68,48 @@ class MediaSendViewModel extends ViewModel {
|
||||
error.postValue(Error.ITEM_TOO_LARGE);
|
||||
}
|
||||
|
||||
boolean allBucketsPopulated = Stream.of(filteredMedia).reduce(true, (populated, m) -> populated && m.getBucketId().isPresent());
|
||||
if (filteredMedia.size() > 0) {
|
||||
String computedId = Stream.of(filteredMedia)
|
||||
.skip(1)
|
||||
.reduce(filteredMedia.get(0).getBucketId().orNull(), (id, m) -> {
|
||||
if (Util.equals(id, m.getBucketId().orNull())) {
|
||||
return id;
|
||||
} else {
|
||||
return Media.ALL_MEDIA_BUCKET_ID;
|
||||
}
|
||||
});
|
||||
bucketId.postValue(computedId);
|
||||
} else {
|
||||
bucketId.postValue(Media.ALL_MEDIA_BUCKET_ID);
|
||||
countButtonVisibility = CountButtonState.Visibility.CONDITIONAL;
|
||||
}
|
||||
|
||||
selectedMedia.postValue(filteredMedia);
|
||||
bucketId.postValue(allBucketsPopulated ? computeBucketId(filteredMedia) : Optional.absent());
|
||||
countButtonState.postValue(new CountButtonState(filteredMedia.size(), countButtonVisibility));
|
||||
});
|
||||
}
|
||||
|
||||
void onSelectedMediaChanged(@NonNull Context context, @NonNull List<Media> newMedia) {
|
||||
List<Media> filteredMedia = getFilteredMedia(context, newMedia, mediaConstraints);
|
||||
void onMultiSelectStarted() {
|
||||
countButtonVisibility = CountButtonState.Visibility.FORCED_ON;
|
||||
countButtonState.postValue(new CountButtonState(getSelectedMediaOrDefault().size(), countButtonVisibility));
|
||||
}
|
||||
|
||||
if (filteredMedia.size() != newMedia.size()) {
|
||||
error.setValue(Error.ITEM_TOO_LARGE);
|
||||
}
|
||||
void onImageEditorStarted() {
|
||||
countButtonVisibility = CountButtonState.Visibility.FORCED_OFF;
|
||||
countButtonState.postValue(new CountButtonState(getSelectedMediaOrDefault().size(), countButtonVisibility));
|
||||
}
|
||||
|
||||
selectedMedia.setValue(filteredMedia);
|
||||
position.setValue(filteredMedia.isEmpty() ? -1 : 0);
|
||||
void onImageEditorEnded() {
|
||||
countButtonVisibility = CountButtonState.Visibility.CONDITIONAL;
|
||||
countButtonState.postValue(new CountButtonState(getSelectedMediaOrDefault().size(), countButtonVisibility));
|
||||
}
|
||||
|
||||
void onBodyChanged(@NonNull CharSequence body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
void onFolderSelected(@NonNull String bucketId) {
|
||||
this.bucketId.setValue(Optional.of(bucketId));
|
||||
this.bucketId.setValue(bucketId);
|
||||
bucketMedia.setValue(Collections.emptyList());
|
||||
}
|
||||
|
||||
@ -91,7 +118,7 @@ class MediaSendViewModel extends ViewModel {
|
||||
}
|
||||
|
||||
void onMediaItemRemoved(int position) {
|
||||
selectedMedia.getValue().remove(position);
|
||||
getSelectedMediaOrDefault().remove(position);
|
||||
selectedMedia.setValue(selectedMedia.getValue());
|
||||
}
|
||||
|
||||
@ -110,11 +137,11 @@ class MediaSendViewModel extends ViewModel {
|
||||
return savedDrawState;
|
||||
}
|
||||
|
||||
LiveData<List<Media>> getSelectedMedia() {
|
||||
@NonNull LiveData<List<Media>> getSelectedMedia() {
|
||||
return selectedMedia;
|
||||
}
|
||||
|
||||
LiveData<List<Media>> getMediaInBucket(@NonNull Context context, @NonNull String bucketId) {
|
||||
@NonNull LiveData<List<Media>> getMediaInBucket(@NonNull Context context, @NonNull String bucketId) {
|
||||
repository.getMediaInBucket(context, bucketId, bucketMedia::postValue);
|
||||
return bucketMedia;
|
||||
}
|
||||
@ -124,11 +151,19 @@ class MediaSendViewModel extends ViewModel {
|
||||
return folders;
|
||||
}
|
||||
|
||||
@NonNull LiveData<CountButtonState> getCountButtonState() {
|
||||
return countButtonState;
|
||||
}
|
||||
|
||||
CharSequence getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
LiveData<Integer> getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
LiveData<Optional<String>> getBucketId() {
|
||||
LiveData<String> getBucketId() {
|
||||
return bucketId;
|
||||
}
|
||||
|
||||
@ -136,17 +171,9 @@ class MediaSendViewModel extends ViewModel {
|
||||
return error;
|
||||
}
|
||||
|
||||
private Optional<String> computeBucketId(@NonNull List<Media> media) {
|
||||
if (media.isEmpty() || !media.get(0).getBucketId().isPresent()) return Optional.absent();
|
||||
|
||||
String candidate = media.get(0).getBucketId().get();
|
||||
for (int i = 1; i < media.size(); i++) {
|
||||
if (!Util.equals(candidate, media.get(i).getBucketId().orNull())) {
|
||||
return Optional.of(Media.ALL_MEDIA_BUCKET_ID);
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.of(candidate);
|
||||
private @NonNull List<Media> getSelectedMediaOrDefault() {
|
||||
return selectedMedia.getValue() == null ? Collections.emptyList()
|
||||
: selectedMedia.getValue();
|
||||
}
|
||||
|
||||
private @NonNull List<Media> getFilteredMedia(@NonNull Context context, @NonNull List<Media> media, @NonNull MediaConstraints mediaConstraints) {
|
||||
@ -165,6 +192,33 @@ class MediaSendViewModel extends ViewModel {
|
||||
ITEM_TOO_LARGE
|
||||
}
|
||||
|
||||
static class CountButtonState {
|
||||
private final int count;
|
||||
private final Visibility visibility;
|
||||
|
||||
private CountButtonState(int count, @NonNull Visibility visibility) {
|
||||
this.count = count;
|
||||
this.visibility = visibility;
|
||||
}
|
||||
|
||||
int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
boolean getVisibility() {
|
||||
switch (visibility) {
|
||||
case FORCED_ON: return true;
|
||||
case FORCED_OFF: return false;
|
||||
case CONDITIONAL: return count > 0;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
enum Visibility {
|
||||
CONDITIONAL, FORCED_ON, FORCED_OFF
|
||||
}
|
||||
}
|
||||
|
||||
static class Factory extends ViewModelProvider.NewInstanceFactory {
|
||||
|
||||
private final MediaRepository repository;
|
||||
|
@ -147,6 +147,10 @@ public class ScribbleFragment extends Fragment implements ScribbleHud.EventListe
|
||||
public void restoreState(@NonNull Object state) {
|
||||
if (state instanceof ScribbleView.SavedState) {
|
||||
savedState = (ScribbleView.SavedState) state;
|
||||
|
||||
if (scribbleView != null) {
|
||||
scribbleView.restoreState(savedState);
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "Received a bad saved state. Received class: " + state.getClass().getName());
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
|
||||
import com.bumptech.glide.request.target.SimpleTarget;
|
||||
import com.bumptech.glide.request.target.Target;
|
||||
import com.bumptech.glide.request.transition.Transition;
|
||||
@ -87,6 +88,7 @@ public class ScribbleView extends FrameLayout {
|
||||
|
||||
glideRequests.load(new DecryptableUri(uri))
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.transition(DrawableTransitionOptions.withCrossFade())
|
||||
.fitCenter()
|
||||
.into(imageView);
|
||||
}
|
||||
|