mirror of
https://github.com/oxen-io/session-android.git
synced 2025-10-25 12:38:39 +00:00
Updated media send UI.
This commit is contained in:
@@ -257,10 +257,10 @@ public class AttachmentTypeSelector extends PopupWindow {
|
||||
|
||||
private class RecentPhotoSelectedListener implements RecentPhotoViewRail.OnItemClickedListener {
|
||||
@Override
|
||||
public void onItemClicked(Uri uri, String mimeType, String bucketId, long dateTaken, int width, int height) {
|
||||
public void onItemClicked(Uri uri, String mimeType, String bucketId, long dateTaken, int width, int height, long size) {
|
||||
animateWindowOutTranslate(getContentView());
|
||||
|
||||
if (listener != null) listener.onQuickAttachment(uri, mimeType, bucketId, dateTaken, width, height);
|
||||
if (listener != null) listener.onQuickAttachment(uri, mimeType, bucketId, dateTaken, width, height, size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,7 +290,7 @@ public class AttachmentTypeSelector extends PopupWindow {
|
||||
|
||||
public interface AttachmentClickedListener {
|
||||
void onClick(int type);
|
||||
void onQuickAttachment(Uri uri, String mimeType, String bucketId, long dateTaken, int width, int height);
|
||||
void onQuickAttachment(Uri uri, String mimeType, String bucketId, long dateTaken, int width, int height, long size);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -110,6 +110,7 @@ public class RecentPhotoViewRail extends FrameLayout implements LoaderManager.Lo
|
||||
String mimeType = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns.MIME_TYPE));
|
||||
String bucketId = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns.BUCKET_ID));
|
||||
int orientation = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns.ORIENTATION));
|
||||
long size = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns.SIZE));
|
||||
int width = Build.VERSION.SDK_INT >= 16 ? cursor.getInt(cursor.getColumnIndexOrThrow(getWidthColumn(orientation))) : 0;
|
||||
int height = Build.VERSION.SDK_INT >= 16 ? cursor.getInt(cursor.getColumnIndexOrThrow(getHeightColumn(orientation))) : 0;
|
||||
|
||||
@@ -124,7 +125,7 @@ public class RecentPhotoViewRail extends FrameLayout implements LoaderManager.Lo
|
||||
.into(viewHolder.imageView);
|
||||
|
||||
viewHolder.imageView.setOnClickListener(v -> {
|
||||
if (clickedListener != null) clickedListener.onItemClicked(uri, mimeType, bucketId, dateTaken, width, height);
|
||||
if (clickedListener != null) clickedListener.onItemClicked(uri, mimeType, bucketId, dateTaken, width, height, size);
|
||||
});
|
||||
|
||||
}
|
||||
@@ -160,6 +161,6 @@ public class RecentPhotoViewRail extends FrameLayout implements LoaderManager.Lo
|
||||
}
|
||||
|
||||
public interface OnItemClickedListener {
|
||||
void onItemClicked(Uri uri, String mimeType, String bucketId, long dateTaken, int width, int height);
|
||||
void onItemClicked(Uri uri, String mimeType, String bucketId, long dateTaken, int width, int height, long size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2365,10 +2365,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQuickAttachment(Uri uri, String mimeType, String bucketId, long dateTaken, int width, int height) {
|
||||
public void onQuickAttachment(Uri uri, String mimeType, String bucketId, long dateTaken, int width, int height, long size) {
|
||||
linkPreviewViewModel.onUserCancel();
|
||||
// TODO: Carry over size?
|
||||
Media media = new Media(uri, mimeType, dateTaken, width, height, 0, Optional.of(Media.ALL_MEDIA_BUCKET_ID), Optional.absent());
|
||||
Media media = new Media(uri, mimeType, dateTaken, width, height, size, Optional.of(Media.ALL_MEDIA_BUCKET_ID), Optional.absent());
|
||||
startActivityForResult(MediaSendActivity.buildEditorIntent(ConversationActivity.this, Collections.singletonList(media), recipient, composeText.getTextTrimmed(), sendButton.getSelectedTransport()), MEDIA_SENDER);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,8 @@ public class RecentPhotosLoader extends CursorLoader {
|
||||
MediaStore.Images.ImageColumns.DATE_MODIFIED,
|
||||
MediaStore.Images.ImageColumns.ORIENTATION,
|
||||
MediaStore.Images.ImageColumns.MIME_TYPE,
|
||||
MediaStore.Images.ImageColumns.BUCKET_ID
|
||||
MediaStore.Images.ImageColumns.BUCKET_ID,
|
||||
MediaStore.Images.ImageColumns.SIZE
|
||||
};
|
||||
|
||||
private static final String[] PROJECTION_16 = new String[] {
|
||||
@@ -31,6 +32,7 @@ public class RecentPhotosLoader extends CursorLoader {
|
||||
MediaStore.Images.ImageColumns.ORIENTATION,
|
||||
MediaStore.Images.ImageColumns.MIME_TYPE,
|
||||
MediaStore.Images.ImageColumns.BUCKET_ID,
|
||||
MediaStore.Images.ImageColumns.SIZE,
|
||||
MediaStore.Images.ImageColumns.WIDTH,
|
||||
MediaStore.Images.ImageColumns.HEIGHT
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.thoughtcrime.securesms.mediapreview;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -8,6 +9,7 @@ import android.view.ViewGroup;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.components.ThumbnailView;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.mediasend.Media;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.util.StableIdGenerator;
|
||||
@@ -17,19 +19,23 @@ import java.util.List;
|
||||
|
||||
public class MediaRailAdapter extends RecyclerView.Adapter<MediaRailAdapter.MediaRailViewHolder> {
|
||||
|
||||
private static final int TYPE_MEDIA = 1;
|
||||
private static final int TYPE_BUTTON = 2;
|
||||
|
||||
private final GlideRequests glideRequests;
|
||||
private final List<Media> media;
|
||||
private final RailItemListener listener;
|
||||
private final boolean deleteEnabled;
|
||||
private final boolean editable;
|
||||
private final StableIdGenerator<Media> stableIdGenerator;
|
||||
|
||||
private RailItemAddListener addListener;
|
||||
private int activePosition;
|
||||
|
||||
public MediaRailAdapter(@NonNull GlideRequests glideRequests, @NonNull RailItemListener listener, boolean deleteEnabled) {
|
||||
public MediaRailAdapter(@NonNull GlideRequests glideRequests, @NonNull RailItemListener listener, boolean editable) {
|
||||
this.glideRequests = glideRequests;
|
||||
this.media = new ArrayList<>();
|
||||
this.listener = listener;
|
||||
this.deleteEnabled = deleteEnabled;
|
||||
this.editable = editable;
|
||||
this.stableIdGenerator = new StableIdGenerator<>();
|
||||
|
||||
setHasStableIds(true);
|
||||
@@ -37,13 +43,38 @@ public class MediaRailAdapter extends RecyclerView.Adapter<MediaRailAdapter.Medi
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public MediaRailViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
|
||||
return new MediaRailViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.media_preview_album_rail_item, viewGroup, false));
|
||||
public MediaRailViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int type) {
|
||||
switch (type) {
|
||||
case TYPE_MEDIA:
|
||||
return new MediaViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.mediarail_media_item, viewGroup, false));
|
||||
case TYPE_BUTTON:
|
||||
return new ButtonViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.mediarail_button_item, viewGroup, false));
|
||||
default:
|
||||
throw new UnsupportedOperationException("Unsupported view type: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull MediaRailViewHolder mediaRailViewHolder, int i) {
|
||||
mediaRailViewHolder.bind(media.get(i), i == activePosition, glideRequests, listener, i - activePosition, deleteEnabled);
|
||||
public void onBindViewHolder(@NonNull MediaRailViewHolder viewHolder, int i) {
|
||||
switch (getItemViewType(i)) {
|
||||
case TYPE_MEDIA:
|
||||
((MediaViewHolder) viewHolder).bind(media.get(i), i == activePosition, glideRequests, listener, i - activePosition, editable);
|
||||
break;
|
||||
case TYPE_BUTTON:
|
||||
((ButtonViewHolder) viewHolder).bind(addListener);
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException("Unsupported view type: " + getItemViewType(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if (editable && position == getItemCount() - 1) {
|
||||
return TYPE_BUTTON;
|
||||
} else {
|
||||
return TYPE_MEDIA;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -53,12 +84,19 @@ public class MediaRailAdapter extends RecyclerView.Adapter<MediaRailAdapter.Medi
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return media.size();
|
||||
return editable ? media.size() + 1 : media.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return stableIdGenerator.getId(media.get(position));
|
||||
switch (getItemViewType(position)) {
|
||||
case TYPE_MEDIA:
|
||||
return stableIdGenerator.getId(media.get(position));
|
||||
case TYPE_BUTTON:
|
||||
return Long.MAX_VALUE;
|
||||
default:
|
||||
throw new UnsupportedOperationException("Unsupported view type: " + getItemViewType(position));
|
||||
}
|
||||
}
|
||||
|
||||
public void setMedia(@NonNull List<Media> media) {
|
||||
@@ -79,25 +117,45 @@ public class MediaRailAdapter extends RecyclerView.Adapter<MediaRailAdapter.Medi
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
static class MediaRailViewHolder extends RecyclerView.ViewHolder {
|
||||
public void setAddButtonListener(@Nullable RailItemAddListener addListener) {
|
||||
this.addListener = addListener;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
static abstract class MediaRailViewHolder extends RecyclerView.ViewHolder {
|
||||
public MediaRailViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
}
|
||||
|
||||
abstract void recycle();
|
||||
}
|
||||
|
||||
static class MediaViewHolder extends MediaRailViewHolder {
|
||||
|
||||
private final ThumbnailView image;
|
||||
private final View outline;
|
||||
private final View deleteButton;
|
||||
private final View captionIndicator;
|
||||
|
||||
MediaRailViewHolder(@NonNull View itemView) {
|
||||
MediaViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
image = itemView.findViewById(R.id.rail_item_image);
|
||||
deleteButton = itemView.findViewById(R.id.rail_item_delete);
|
||||
image = itemView.findViewById(R.id.rail_item_image);
|
||||
outline = itemView.findViewById(R.id.rail_item_outline);
|
||||
deleteButton = itemView.findViewById(R.id.rail_item_delete);
|
||||
captionIndicator = itemView.findViewById(R.id.rail_item_caption);
|
||||
}
|
||||
|
||||
void bind(@NonNull Media media, boolean isActive, @NonNull GlideRequests glideRequests,
|
||||
@NonNull RailItemListener railItemListener, int distanceFromActive, boolean deleteEnabled)
|
||||
@NonNull RailItemListener railItemListener, int distanceFromActive, boolean editable)
|
||||
{
|
||||
image.setImageResource(glideRequests, media.getUri());
|
||||
image.setBackgroundResource(isActive ? R.drawable.media_rail_item_background : 0);
|
||||
image.setOnClickListener(v -> railItemListener.onRailItemClicked(distanceFromActive));
|
||||
|
||||
if (deleteEnabled && isActive) {
|
||||
outline.setVisibility(isActive ? View.VISIBLE : View.GONE);
|
||||
|
||||
captionIndicator.setVisibility(media.getCaption().isPresent() ? View.VISIBLE : View.GONE);
|
||||
|
||||
if (editable && isActive) {
|
||||
deleteButton.setVisibility(View.VISIBLE);
|
||||
deleteButton.setOnClickListener(v -> railItemListener.onRailItemDeleteClicked(distanceFromActive));
|
||||
} else {
|
||||
@@ -111,8 +169,30 @@ public class MediaRailAdapter extends RecyclerView.Adapter<MediaRailAdapter.Medi
|
||||
}
|
||||
}
|
||||
|
||||
static class ButtonViewHolder extends MediaRailViewHolder {
|
||||
|
||||
public ButtonViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
}
|
||||
|
||||
void bind(@Nullable RailItemAddListener addListener) {
|
||||
if (addListener != null) {
|
||||
itemView.setOnClickListener(v -> addListener.onRailItemAddClicked());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void recycle() {
|
||||
itemView.setOnClickListener(null);
|
||||
}
|
||||
}
|
||||
|
||||
public interface RailItemListener {
|
||||
void onRailItemClicked(int distanceFromActive);
|
||||
void onRailItemDeleteClicked(int distanceFromActive);
|
||||
}
|
||||
|
||||
public interface RailItemAddListener {
|
||||
void onRailItemAddClicked();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.thoughtcrime.securesms.camera;
|
||||
package org.thoughtcrime.securesms.mediasend;
|
||||
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.hardware.Camera;
|
||||
@@ -11,21 +11,21 @@ import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class Camera1Controller {
|
||||
class Camera1Controller {
|
||||
|
||||
private static final String TAG = Camera1Controller.class.getSimpleName();
|
||||
|
||||
private final int screenWidth;
|
||||
private final int screenHeight;
|
||||
private final int screenWidth;
|
||||
private final int screenHeight;
|
||||
private final OrderEnforcer<Stage> enforcer;
|
||||
private final EventListener eventListener;
|
||||
|
||||
private Camera camera;
|
||||
private int cameraId;
|
||||
private OrderEnforcer<Stage> enforcer;
|
||||
private EventListener eventListener;
|
||||
private SurfaceTexture previewSurface;
|
||||
private int screenRotation;
|
||||
|
||||
public Camera1Controller(int preferredDirection, int screenWidth, int screenHeight, @NonNull EventListener eventListener) {
|
||||
Camera1Controller(int preferredDirection, int screenWidth, int screenHeight, @NonNull EventListener eventListener) {
|
||||
this.eventListener = eventListener;
|
||||
this.enforcer = new OrderEnforcer<>(Stage.INITIALIZED, Stage.PREVIEW_STARTED);
|
||||
this.cameraId = Camera.getNumberOfCameras() > 1 ? preferredDirection : Camera.CameraInfo.CAMERA_FACING_BACK;
|
||||
@@ -33,10 +33,11 @@ public class Camera1Controller {
|
||||
this.screenHeight = screenHeight;
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
void initialize() {
|
||||
Log.d(TAG, "initialize()");
|
||||
|
||||
if (Camera.getNumberOfCameras() <= 0) {
|
||||
Log.w(TAG, "Device doesn't have any cameras.");
|
||||
onCameraUnavailable();
|
||||
return;
|
||||
}
|
||||
@@ -44,11 +45,13 @@ public class Camera1Controller {
|
||||
try {
|
||||
camera = Camera.open(cameraId);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Failed to open camera.", e);
|
||||
onCameraUnavailable();
|
||||
return;
|
||||
}
|
||||
|
||||
if (camera == null) {
|
||||
Log.w(TAG, "Null camera instance.");
|
||||
onCameraUnavailable();
|
||||
return;
|
||||
}
|
||||
@@ -80,9 +83,9 @@ public class Camera1Controller {
|
||||
eventListener.onPropertiesAvailable(getProperties());
|
||||
}
|
||||
|
||||
public void release() {
|
||||
void release() {
|
||||
Log.d(TAG, "release() called");
|
||||
enforcer.run(Stage.PREVIEW_STARTED, () -> {
|
||||
enforcer.run(Stage.INITIALIZED, () -> {
|
||||
Log.d(TAG, "release() executing");
|
||||
previewSurface = null;
|
||||
camera.stopPreview();
|
||||
@@ -91,7 +94,7 @@ public class Camera1Controller {
|
||||
});
|
||||
}
|
||||
|
||||
public void linkSurface(@NonNull SurfaceTexture surfaceTexture) {
|
||||
void linkSurface(@NonNull SurfaceTexture surfaceTexture) {
|
||||
Log.d(TAG, "linkSurface() called");
|
||||
enforcer.run(Stage.INITIALIZED, () -> {
|
||||
try {
|
||||
@@ -108,7 +111,7 @@ public class Camera1Controller {
|
||||
});
|
||||
}
|
||||
|
||||
public void capture(@NonNull CaptureCallback callback) {
|
||||
void capture(@NonNull CaptureCallback callback) {
|
||||
enforcer.run(Stage.PREVIEW_STARTED, () -> {
|
||||
camera.takePicture(null, null, null, (data, camera) -> {
|
||||
callback.onCaptureAvailable(data, cameraId == Camera.CameraInfo.CAMERA_FACING_FRONT);
|
||||
@@ -116,7 +119,7 @@ public class Camera1Controller {
|
||||
});
|
||||
}
|
||||
|
||||
public int flip() {
|
||||
int flip() {
|
||||
Log.d(TAG, "flip()");
|
||||
SurfaceTexture surfaceTexture = previewSurface;
|
||||
cameraId = (cameraId == Camera.CameraInfo.CAMERA_FACING_BACK) ? Camera.CameraInfo.CAMERA_FACING_FRONT : Camera.CameraInfo.CAMERA_FACING_BACK;
|
||||
@@ -129,7 +132,7 @@ public class Camera1Controller {
|
||||
return cameraId;
|
||||
}
|
||||
|
||||
public void setScreenRotation(int screenRotation) {
|
||||
void setScreenRotation(int screenRotation) {
|
||||
Log.d(TAG, "setScreenRotation(" + screenRotation + ") called");
|
||||
enforcer.run(Stage.PREVIEW_STARTED, () -> {
|
||||
Log.d(TAG, "setScreenRotation(" + screenRotation + ") executing");
|
||||
@@ -221,7 +224,7 @@ public class Camera1Controller {
|
||||
private final int previewWidth;
|
||||
private final int previewHeight;
|
||||
|
||||
public Properties(int cameraCount, int previewWidth, int previewHeight) {
|
||||
Properties(int cameraCount, int previewWidth, int previewHeight) {
|
||||
this.cameraCount = cameraCount;
|
||||
this.previewWidth = previewWidth;
|
||||
this.previewHeight = previewHeight;
|
||||
@@ -231,11 +234,11 @@ public class Camera1Controller {
|
||||
return cameraCount;
|
||||
}
|
||||
|
||||
public int getPreviewWidth() {
|
||||
int getPreviewWidth() {
|
||||
return previewWidth;
|
||||
}
|
||||
|
||||
public int getPreviewHeight() {
|
||||
int getPreviewHeight() {
|
||||
return previewHeight;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.thoughtcrime.securesms.camera;
|
||||
package org.thoughtcrime.securesms.mediasend;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.arch.lifecycle.ViewModelProviders;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Matrix;
|
||||
@@ -21,8 +22,11 @@ import android.view.TextureView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.view.animation.RotateAnimation;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageButton;
|
||||
|
||||
@@ -55,6 +59,7 @@ public class Camera1Fragment extends Fragment implements TextureView.SurfaceText
|
||||
private Controller controller;
|
||||
private OrderEnforcer<Stage> orderEnforcer;
|
||||
private Camera1Controller.Properties properties;
|
||||
private MediaSendViewModel viewModel;
|
||||
|
||||
public static Camera1Fragment newInstance() {
|
||||
return new Camera1Fragment();
|
||||
@@ -76,6 +81,7 @@ public class Camera1Fragment extends Fragment implements TextureView.SurfaceText
|
||||
controller = (Controller) getActivity();
|
||||
camera = new Camera1Controller(TextSecurePreferences.getDirectCaptureCameraId(getContext()), displaySize.x, displaySize.y, this);
|
||||
orderEnforcer = new OrderEnforcer<>(Stage.SURFACE_AVAILABLE, Stage.CAMERA_PROPERTIES_AVAILABLE);
|
||||
viewModel = ViewModelProviders.of(requireActivity(), new MediaSendViewModel.Factory(requireActivity().getApplication(), new MediaRepository())).get(MediaSendViewModel.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -103,11 +109,22 @@ public class Camera1Fragment extends Fragment implements TextureView.SurfaceText
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
viewModel.onCameraStarted();
|
||||
camera.initialize();
|
||||
|
||||
if (cameraPreview.isAvailable()) {
|
||||
orderEnforcer.markCompleted(Stage.SURFACE_AVAILABLE);
|
||||
}
|
||||
|
||||
if (properties != null) {
|
||||
orderEnforcer.markCompleted(Stage.CAMERA_PROPERTIES_AVAILABLE);
|
||||
}
|
||||
|
||||
orderEnforcer.run(Stage.SURFACE_AVAILABLE, () -> {
|
||||
camera.linkSurface(cameraPreview.getSurfaceTexture());
|
||||
camera.setScreenRotation(controller.getDisplayRotation());
|
||||
});
|
||||
|
||||
orderEnforcer.run(Stage.CAMERA_PROPERTIES_AVAILABLE, this::updatePreviewScale);
|
||||
|
||||
requireActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
@@ -118,6 +135,7 @@ public class Camera1Fragment extends Fragment implements TextureView.SurfaceText
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
camera.release();
|
||||
orderEnforcer.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -128,6 +146,7 @@ public class Camera1Fragment extends Fragment implements TextureView.SurfaceText
|
||||
|
||||
@Override
|
||||
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
|
||||
Log.d(TAG, "onSurfaceTextureAvailable");
|
||||
orderEnforcer.markCompleted(Stage.SURFACE_AVAILABLE);
|
||||
}
|
||||
|
||||
@@ -159,10 +178,6 @@ public class Camera1Fragment extends Fragment implements TextureView.SurfaceText
|
||||
controller.onCameraError();
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
orderEnforcer.reset();
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private void initControls() {
|
||||
flipButton = getView().findViewById(R.id.camera_flip_button);
|
||||
@@ -193,14 +208,14 @@ public class Camera1Fragment extends Fragment implements TextureView.SurfaceText
|
||||
orderEnforcer.run(Stage.CAMERA_PROPERTIES_AVAILABLE, () -> {
|
||||
if (properties.getCameraCount() > 1) {
|
||||
flipButton.setVisibility(properties.getCameraCount() > 1 ? View.VISIBLE : View.GONE);
|
||||
flipButton.setImageResource(TextSecurePreferences.getDirectCaptureCameraId(getContext()) == Camera.CameraInfo.CAMERA_FACING_BACK ? R.drawable.ic_camera_front
|
||||
: R.drawable.ic_camera_rear);
|
||||
flipButton.setOnClickListener(v -> {
|
||||
int newCameraId = camera.flip();
|
||||
flipButton.setImageResource(newCameraId == Camera.CameraInfo.CAMERA_FACING_BACK ? R.drawable.ic_camera_front
|
||||
: R.drawable.ic_camera_rear);
|
||||
|
||||
TextSecurePreferences.setDirectCaptureCameraId(getContext(), newCameraId);
|
||||
|
||||
Animation animation = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
|
||||
animation.setDuration(200);
|
||||
animation.setInterpolator(new DecelerateInterpolator());
|
||||
flipButton.startAnimation(animation);
|
||||
});
|
||||
} else {
|
||||
flipButton.setVisibility(View.GONE);
|
||||
@@ -209,7 +224,7 @@ public class Camera1Fragment extends Fragment implements TextureView.SurfaceText
|
||||
}
|
||||
|
||||
private void onCaptureClicked() {
|
||||
reset();
|
||||
orderEnforcer.reset();
|
||||
|
||||
Stopwatch fastCaptureTimer = new Stopwatch("Capture");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.thoughtcrime.securesms.camera;
|
||||
package org.thoughtcrime.securesms.mediasend;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
@@ -93,6 +93,7 @@ public class MediaPickerFolderFragment extends Fragment implements MediaPickerFo
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
viewModel.onFolderPickerStarted();
|
||||
requireActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||
requireActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
}
|
||||
|
||||
@@ -4,33 +4,27 @@ import android.arch.lifecycle.ViewModelProviders;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Point;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.view.ActionMode;
|
||||
import android.support.v7.widget.GridLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.util.ThemeUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -119,25 +113,27 @@ public class MediaPickerItemFragment extends Fragment implements MediaPickerItem
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
viewModel.onItemPickerStarted();
|
||||
requireActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||
requireActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
}
|
||||
|
||||
@Override
|
||||
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());
|
||||
if (viewModel.getCountButtonState().getValue() != null && viewModel.getCountButtonState().getValue().isVisible()) {
|
||||
requireActivity().getMenuInflater().inflate(R.menu.mediapicker_multiselect, menu);
|
||||
} else {
|
||||
requireActivity().getMenuInflater().inflate(R.menu.mediapicker_default, menu);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == R.id.mediapicker_menu_add) {
|
||||
adapter.setForcedMultiSelect(true);
|
||||
viewModel.onMultiSelectStarted();
|
||||
return true;
|
||||
switch (item.getItemId()) {
|
||||
case R.id.mediapicker_menu_add:
|
||||
adapter.setForcedMultiSelect(true);
|
||||
viewModel.onMultiSelectStarted();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -162,7 +158,7 @@ public class MediaPickerItemFragment extends Fragment implements MediaPickerItem
|
||||
|
||||
@Override
|
||||
public void onMediaSelectionOverflow(int maxSelection) {
|
||||
Toast.makeText(requireContext(), getResources().getQuantityString(R.plurals.MediaPickerItemFragment_cant_share_more_than_n_items, maxSelection, maxSelection), Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(requireContext(), getResources().getQuantityString(R.plurals.MediaSendActivity_cant_share_more_than_n_items, maxSelection, maxSelection), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
private void initToolbar(Toolbar toolbar) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.thoughtcrime.securesms.mediasend;
|
||||
|
||||
import android.Manifest;
|
||||
import android.arch.lifecycle.ViewModelProviders;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -19,10 +20,10 @@ import android.widget.Toast;
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.TransportOption;
|
||||
import org.thoughtcrime.securesms.camera.Camera1Fragment;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.mms.MediaConstraints;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.providers.BlobProvider;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.scribbles.ScribbleFragment;
|
||||
@@ -58,8 +59,6 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
|
||||
public static final String EXTRA_MESSAGE = "message";
|
||||
public static final String EXTRA_TRANSPORT = "transport";
|
||||
|
||||
private static final int MAX_PUSH = 32;
|
||||
private static final int MAX_SMS = 1;
|
||||
|
||||
private static final String KEY_ADDRESS = "address";
|
||||
private static final String KEY_BODY = "body";
|
||||
@@ -81,6 +80,7 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
|
||||
|
||||
private View countButton;
|
||||
private TextView countButtonText;
|
||||
private View cameraButton;
|
||||
|
||||
/**
|
||||
* Get an intent to launch the media send flow starting with the picker.
|
||||
@@ -134,14 +134,13 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
|
||||
|
||||
countButton = findViewById(R.id.mediasend_count_button);
|
||||
countButtonText = findViewById(R.id.mediasend_count_button_text);
|
||||
cameraButton = findViewById(R.id.mediasend_camera_button);
|
||||
|
||||
viewModel = ViewModelProviders.of(this, new MediaSendViewModel.Factory(getApplication(), new MediaRepository())).get(MediaSendViewModel.class);
|
||||
recipient = Recipient.from(this, Address.fromSerialized(getIntent().getStringExtra(KEY_ADDRESS)), true);
|
||||
transport = getIntent().getParcelableExtra(KEY_TRANSPORT);
|
||||
|
||||
viewModel.setMediaConstraints(transport.isSms() ? MediaConstraints.getMmsMediaConstraints(transport.getSimSubscriptionId().or(-1))
|
||||
: MediaConstraints.getPushMediaConstraints());
|
||||
|
||||
viewModel.setTransport(transport);
|
||||
viewModel.onBodyChanged(getIntent().getStringExtra(KEY_BODY));
|
||||
|
||||
List<Media> media = getIntent().getParcelableArrayListExtra(KEY_MEDIA);
|
||||
@@ -156,7 +155,7 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
|
||||
} else if (!Util.isEmpty(media)) {
|
||||
viewModel.onSelectedMediaChanged(this, media);
|
||||
|
||||
Fragment fragment = MediaSendFragment.newInstance(transport, dynamicLanguage.getCurrentLocale());
|
||||
Fragment fragment = MediaSendFragment.newInstance(recipient, transport, dynamicLanguage.getCurrentLocale());
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.replace(R.id.mediasend_fragment_container, fragment, TAG_SEND)
|
||||
.commit();
|
||||
@@ -168,6 +167,18 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
|
||||
}
|
||||
|
||||
initializeCountButtonObserver(transport, dynamicLanguage.getCurrentLocale());
|
||||
initializeCameraButtonObserver();
|
||||
initializeErrorObserver();
|
||||
|
||||
cameraButton.setOnClickListener(v -> {
|
||||
int maxSelection = viewModel.getMaxSelection();
|
||||
|
||||
if (viewModel.getSelectedMedia().getValue() != null && viewModel.getSelectedMedia().getValue().size() >= maxSelection) {
|
||||
Toast.makeText(this, getResources().getQuantityString(R.plurals.MediaSendActivity_cant_share_more_than_n_items, maxSelection, maxSelection), Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
navigateToCamera();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -189,11 +200,16 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
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(), viewModel.getMaxSelection());
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.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)
|
||||
@@ -203,14 +219,14 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
|
||||
|
||||
@Override
|
||||
public void onMediaSelected(@NonNull String bucketId) {
|
||||
navigateToMediaSend(transport, dynamicLanguage.getCurrentLocale());
|
||||
navigateToMediaSend(recipient, 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, "", viewModel.getMaxSelection());
|
||||
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.setCustomAnimations(R.anim.stationary, R.anim.slide_to_left, R.anim.slide_from_left, R.anim.slide_to_right)
|
||||
@@ -302,7 +318,7 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
|
||||
Log.i(TAG, "Camera capture stored: " + media.getUri().toString());
|
||||
|
||||
viewModel.onImageCaptured(media);
|
||||
navigateToMediaSend(transport, dynamicLanguage.getCurrentLocale());
|
||||
navigateToMediaSend(recipient, transport, dynamicLanguage.getCurrentLocale());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -316,26 +332,42 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
|
||||
if (buttonState == null) return;
|
||||
|
||||
countButtonText.setText(String.valueOf(buttonState.getCount()));
|
||||
countButton.setEnabled(buttonState.getVisibility());
|
||||
animateCountButtonVisibility(countButton, countButton.getVisibility(), buttonState.getVisibility() ? View.VISIBLE : View.GONE);
|
||||
countButton.setEnabled(buttonState.isVisible());
|
||||
animateButtonVisibility(countButton, countButton.getVisibility(), buttonState.isVisible() ? View.VISIBLE : View.GONE);
|
||||
|
||||
if (buttonState.getCount() > 0) {
|
||||
countButton.setOnClickListener(v -> {
|
||||
Camera1Fragment fragment = (Camera1Fragment) getSupportFragmentManager().findFragmentByTag(TAG_CAMERA);
|
||||
if (fragment != null) {
|
||||
fragment.reset();
|
||||
}
|
||||
|
||||
navigateToMediaSend(transport, locale);
|
||||
});
|
||||
countButton.setOnClickListener(v -> navigateToMediaSend(recipient, transport, locale));
|
||||
} else {
|
||||
countButton.setOnClickListener(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void navigateToMediaSend(@NonNull TransportOption transport, @NonNull Locale locale) {
|
||||
MediaSendFragment fragment = MediaSendFragment.newInstance(transport, locale);
|
||||
private void initializeCameraButtonObserver() {
|
||||
viewModel.getCameraButtonVisibility().observe(this, visible -> {
|
||||
if (visible == null) return;
|
||||
animateButtonVisibility(cameraButton, cameraButton.getVisibility(), visible ? View.VISIBLE : View.GONE);
|
||||
});
|
||||
}
|
||||
|
||||
private void initializeErrorObserver() {
|
||||
viewModel.getError().observe(this, error -> {
|
||||
if (error == null) return;
|
||||
|
||||
switch (error) {
|
||||
case ITEM_TOO_LARGE:
|
||||
Toast.makeText(this, R.string.MediaSendActivity_an_item_was_removed_because_it_exceeded_the_size_limit, Toast.LENGTH_LONG).show();
|
||||
break;
|
||||
case TOO_MANY_ITEMS:
|
||||
int maxSelection = viewModel.getMaxSelection();
|
||||
Toast.makeText(this, getResources().getQuantityString(R.plurals.MediaSendActivity_cant_share_more_than_n_items, maxSelection, maxSelection), Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void navigateToMediaSend(@NonNull Recipient recipient, @NonNull TransportOption transport, @NonNull Locale locale) {
|
||||
MediaSendFragment fragment = MediaSendFragment.newInstance(recipient, transport, locale);
|
||||
String backstackTag = null;
|
||||
|
||||
if (getSupportFragmentManager().findFragmentByTag(TAG_SEND) != null) {
|
||||
@@ -350,19 +382,44 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
|
||||
.commit();
|
||||
}
|
||||
|
||||
private void animateCountButtonVisibility(View countButton, int oldVisibility, int newVisibility) {
|
||||
private void navigateToCamera() {
|
||||
Permissions.with(this)
|
||||
.request(Manifest.permission.CAMERA)
|
||||
.ifNecessary()
|
||||
.withRationaleDialog(getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera), R.drawable.ic_photo_camera_white_48dp)
|
||||
.withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video))
|
||||
.onAllGranted(() -> {
|
||||
Camera1Fragment fragment = getOrCreateCameraFragment();
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.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_CAMERA)
|
||||
.addToBackStack(null)
|
||||
.commit();
|
||||
})
|
||||
.onAnyDenied(() -> Toast.makeText(MediaSendActivity.this, R.string.ConversationActivity_signal_needs_camera_permissions_to_take_photos_or_video, Toast.LENGTH_LONG).show())
|
||||
.execute();
|
||||
}
|
||||
|
||||
private Camera1Fragment getOrCreateCameraFragment() {
|
||||
Camera1Fragment fragment = (Camera1Fragment) getSupportFragmentManager().findFragmentByTag(TAG_CAMERA);
|
||||
|
||||
return fragment != null ? fragment
|
||||
: Camera1Fragment.newInstance();
|
||||
}
|
||||
|
||||
private void animateButtonVisibility(View button, int oldVisibility, int newVisibility) {
|
||||
if (oldVisibility == newVisibility) return;
|
||||
|
||||
if (countButton.getAnimation() != null) {
|
||||
countButton.getAnimation().cancel();
|
||||
countButton.setVisibility(newVisibility);
|
||||
if (button.getAnimation() != null) {
|
||||
button.getAnimation().cancel();
|
||||
button.setVisibility(newVisibility);
|
||||
} else if (newVisibility == View.VISIBLE) {
|
||||
countButton.setVisibility(View.VISIBLE);
|
||||
button.setVisibility(View.VISIBLE);
|
||||
|
||||
Animation animation = new ScaleAnimation(0, 1, 0, 1, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
|
||||
animation.setDuration(250);
|
||||
animation.setInterpolator(new OvershootInterpolator());
|
||||
countButton.startAnimation(animation);
|
||||
button.startAnimation(animation);
|
||||
} else {
|
||||
Animation animation = new ScaleAnimation(1, 0, 1, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
|
||||
animation.setDuration(150);
|
||||
@@ -370,12 +427,11 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
|
||||
animation.setAnimationListener(new SimpleAnimationListener() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
countButton.setVisibility(View.GONE);
|
||||
button.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
|
||||
countButton.startAnimation(animation);
|
||||
button.startAnimation(animation);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.mediapreview.MediaRailAdapter;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.providers.BlobProvider;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.scribbles.widget.ScribbleView;
|
||||
import org.thoughtcrime.securesms.util.CharacterCalculator.CharacterState;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
@@ -51,6 +52,7 @@ import org.thoughtcrime.securesms.util.ThemeUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
|
||||
import org.thoughtcrime.securesms.util.views.Stub;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -72,12 +74,12 @@ public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGl
|
||||
|
||||
private static final String TAG = MediaSendFragment.class.getSimpleName();
|
||||
|
||||
private static final String KEY_ADDRESS = "address";
|
||||
private static final String KEY_TRANSPORT = "transport";
|
||||
private static final String KEY_LOCALE = "locale";
|
||||
|
||||
private InputAwareLayout hud;
|
||||
private SendButton sendButton;
|
||||
private View addButton;
|
||||
private ComposeText composeText;
|
||||
private ViewGroup composeContainer;
|
||||
private EmojiEditText captionText;
|
||||
@@ -98,8 +100,9 @@ public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGl
|
||||
|
||||
private final Rect visibleBounds = new Rect();
|
||||
|
||||
public static MediaSendFragment newInstance(@NonNull TransportOption transport, @NonNull Locale locale) {
|
||||
public static MediaSendFragment newInstance(@NonNull Recipient recipient, @NonNull TransportOption transport, @NonNull Locale locale) {
|
||||
Bundle args = new Bundle();
|
||||
args.putParcelable(KEY_ADDRESS, recipient.getAddress());
|
||||
args.putParcelable(KEY_TRANSPORT, transport);
|
||||
args.putSerializable(KEY_LOCALE, locale);
|
||||
|
||||
@@ -145,7 +148,6 @@ public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGl
|
||||
emojiDrawer = new Stub<>(view.findViewById(R.id.mediasend_emoji_drawer_stub));
|
||||
fragmentPager = view.findViewById(R.id.mediasend_pager);
|
||||
mediaRail = view.findViewById(R.id.mediasend_media_rail);
|
||||
addButton = view.findViewById(R.id.mediasend_add_button);
|
||||
playbackControlsContainer = view.findViewById(R.id.mediasend_playback_controls_container);
|
||||
charactersLeft = view.findViewById(R.id.mediasend_characters_left);
|
||||
|
||||
@@ -205,6 +207,11 @@ public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGl
|
||||
|
||||
composeText.append(viewModel.getBody());
|
||||
|
||||
Recipient recipient = Recipient.from(requireContext(), getArguments().getParcelable(KEY_ADDRESS), false);
|
||||
String displayName = Optional.fromNullable(recipient.getName())
|
||||
.or(Optional.fromNullable(recipient.getProfileName())
|
||||
.or(recipient.getAddress().serialize()));
|
||||
composeText.setHint(getString(R.string.MediaSendActivity_message_to_s, displayName), null);
|
||||
|
||||
if (TextSecurePreferences.isSystemEmojiPreferred(getContext())) {
|
||||
emojiToggle.setVisibility(View.GONE);
|
||||
@@ -264,12 +271,24 @@ public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGl
|
||||
|
||||
@Override
|
||||
public void onKeyboardShown() {
|
||||
if (composeText.hasFocus()) {
|
||||
if (sendButton.getSelectedTransport().isSms()) {
|
||||
mediaRail.setVisibility(View.GONE);
|
||||
composeContainer.setVisibility(View.VISIBLE);
|
||||
captionText.setVisibility(View.GONE);
|
||||
} else if (captionText.hasFocus()) {
|
||||
mediaRail.setVisibility(View.GONE);
|
||||
composeContainer.setVisibility(View.GONE);
|
||||
} else {
|
||||
if (captionText.hasFocus()) {
|
||||
mediaRail.setVisibility(View.VISIBLE);
|
||||
composeContainer.setVisibility(View.GONE);
|
||||
captionText.setVisibility(View.VISIBLE);
|
||||
} else if (composeText.hasFocus()) {
|
||||
mediaRail.setVisibility(View.VISIBLE);
|
||||
composeContainer.setVisibility(View.VISIBLE);
|
||||
captionText.setVisibility(View.GONE);
|
||||
} else {
|
||||
mediaRail.setVisibility(View.GONE);
|
||||
composeContainer.setVisibility(View.VISIBLE);
|
||||
captionText.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,9 +296,15 @@ public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGl
|
||||
public void onKeyboardHidden() {
|
||||
composeContainer.setVisibility(View.VISIBLE);
|
||||
|
||||
if (!Util.isEmpty(viewModel.getSelectedMedia().getValue()) && viewModel.getSelectedMedia().getValue().size() > 1) {
|
||||
if (sendButton.getSelectedTransport().isSms()) {
|
||||
mediaRail.setVisibility(View.GONE);
|
||||
captionText.setVisibility(View.GONE);
|
||||
} else {
|
||||
mediaRail.setVisibility(View.VISIBLE);
|
||||
captionText.setVisibility(View.VISIBLE);
|
||||
|
||||
if (!Util.isEmpty(viewModel.getSelectedMedia().getValue()) && viewModel.getSelectedMedia().getValue().size() > 1) {
|
||||
captionText.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,7 +331,7 @@ public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGl
|
||||
|
||||
fragmentPagerAdapter.setMedia(media);
|
||||
|
||||
mediaRail.setVisibility(media.size() > 1 ? View.VISIBLE : View.GONE);
|
||||
mediaRail.setVisibility(sendButton.getSelectedTransport().isSms() ? View.GONE : View.VISIBLE);
|
||||
captionText.setVisibility((media.size() > 1 || media.get(0).getCaption().isPresent()) ? View.VISIBLE : View.GONE);
|
||||
mediaRailAdapter.setMedia(media);
|
||||
});
|
||||
@@ -318,7 +343,7 @@ public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGl
|
||||
mediaRailAdapter.setActivePosition(position);
|
||||
mediaRail.smoothScrollToPosition(position);
|
||||
|
||||
if (!fragmentPagerAdapter.getAllMedia().isEmpty()) {
|
||||
if (fragmentPagerAdapter.getAllMedia().size() > position) {
|
||||
captionText.setText(fragmentPagerAdapter.getAllMedia().get(position).getCaption().or(""));
|
||||
}
|
||||
|
||||
@@ -337,18 +362,7 @@ public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGl
|
||||
viewModel.getBucketId().observe(this, bucketId -> {
|
||||
if (bucketId == null) return;
|
||||
|
||||
if (sendButton.getSelectedTransport().isSms()) {
|
||||
addButton.setVisibility(View.GONE);
|
||||
} else {
|
||||
addButton.setVisibility(View.VISIBLE);
|
||||
addButton.setOnClickListener(v -> controller.onAddMediaClicked(bucketId));
|
||||
}
|
||||
});
|
||||
|
||||
viewModel.getError().observe(this, error -> {
|
||||
if (error == MediaSendViewModel.Error.ITEM_TOO_LARGE) {
|
||||
Toast.makeText(requireContext(), R.string.MediaSendActivity_an_item_was_removed_because_it_exceeded_the_size_limit, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
mediaRailAdapter.setAddButtonListener(() -> controller.onAddMediaClicked(bucketId));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import android.text.TextUtils;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import org.thoughtcrime.securesms.TransportOption;
|
||||
import org.thoughtcrime.securesms.mms.MediaConstraints;
|
||||
import org.thoughtcrime.securesms.providers.BlobProvider;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
@@ -30,6 +31,9 @@ import java.util.Map;
|
||||
*/
|
||||
class MediaSendViewModel extends ViewModel {
|
||||
|
||||
private static final int MAX_PUSH = 32;
|
||||
private static final int MAX_SMS = 1;
|
||||
|
||||
private final Application application;
|
||||
private final MediaRepository repository;
|
||||
private final MutableLiveData<List<Media>> selectedMedia;
|
||||
@@ -38,6 +42,7 @@ class MediaSendViewModel extends ViewModel {
|
||||
private final MutableLiveData<String> bucketId;
|
||||
private final MutableLiveData<List<MediaFolder>> folders;
|
||||
private final MutableLiveData<CountButtonState> countButtonState;
|
||||
private final MutableLiveData<Boolean> cameraButtonVisibility;
|
||||
private final SingleLiveEvent<Error> error;
|
||||
private final Map<Uri, Object> savedDrawState;
|
||||
|
||||
@@ -46,27 +51,37 @@ class MediaSendViewModel extends ViewModel {
|
||||
private CountButtonState.Visibility countButtonVisibility;
|
||||
private boolean sentMedia;
|
||||
private Optional<Media> lastImageCapture;
|
||||
private int maxSelection;
|
||||
|
||||
private MediaSendViewModel(@NonNull Application application, @NonNull MediaRepository repository) {
|
||||
this.application = application;
|
||||
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;
|
||||
this.lastImageCapture = Optional.absent();
|
||||
this.application = application;
|
||||
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.cameraButtonVisibility = new MutableLiveData<>();
|
||||
this.error = new SingleLiveEvent<>();
|
||||
this.savedDrawState = new HashMap<>();
|
||||
this.countButtonVisibility = CountButtonState.Visibility.CONDITIONAL;
|
||||
this.lastImageCapture = Optional.absent();
|
||||
this.body = "";
|
||||
|
||||
position.setValue(-1);
|
||||
countButtonState.setValue(new CountButtonState(0, CountButtonState.Visibility.CONDITIONAL));
|
||||
cameraButtonVisibility.setValue(false);
|
||||
}
|
||||
|
||||
void setMediaConstraints(@NonNull MediaConstraints mediaConstraints) {
|
||||
this.mediaConstraints = mediaConstraints;
|
||||
void setTransport(@NonNull TransportOption transport) {
|
||||
if (transport.isSms()) {
|
||||
maxSelection = MAX_SMS;
|
||||
mediaConstraints = MediaConstraints.getMmsMediaConstraints(transport.getSimSubscriptionId().or(-1));
|
||||
} else {
|
||||
maxSelection = MAX_PUSH;
|
||||
mediaConstraints = MediaConstraints.getPushMediaConstraints();
|
||||
}
|
||||
}
|
||||
|
||||
void onSelectedMediaChanged(@NonNull Context context, @NonNull List<Media> newMedia) {
|
||||
@@ -75,13 +90,16 @@ class MediaSendViewModel extends ViewModel {
|
||||
|
||||
if (filteredMedia.size() != newMedia.size()) {
|
||||
error.postValue(Error.ITEM_TOO_LARGE);
|
||||
} else if (filteredMedia.size() > maxSelection) {
|
||||
filteredMedia = filteredMedia.subList(0, maxSelection);
|
||||
error.postValue(Error.TOO_MANY_ITEMS);
|
||||
}
|
||||
|
||||
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())) {
|
||||
.reduce(filteredMedia.get(0).getBucketId().or(Media.ALL_MEDIA_BUCKET_ID), (id, m) -> {
|
||||
if (Util.equals(id, m.getBucketId().or(Media.ALL_MEDIA_BUCKET_ID))) {
|
||||
return id;
|
||||
} else {
|
||||
return Media.ALL_MEDIA_BUCKET_ID;
|
||||
@@ -106,6 +124,7 @@ class MediaSendViewModel extends ViewModel {
|
||||
void onImageEditorStarted() {
|
||||
countButtonVisibility = CountButtonState.Visibility.FORCED_OFF;
|
||||
countButtonState.setValue(new CountButtonState(getSelectedMediaOrDefault().size(), countButtonVisibility));
|
||||
cameraButtonVisibility.setValue(false);
|
||||
}
|
||||
|
||||
void onImageEditorEnded() {
|
||||
@@ -113,6 +132,18 @@ class MediaSendViewModel extends ViewModel {
|
||||
countButtonState.setValue(new CountButtonState(getSelectedMediaOrDefault().size(), countButtonVisibility));
|
||||
}
|
||||
|
||||
void onCameraStarted() {
|
||||
cameraButtonVisibility.setValue(false);
|
||||
}
|
||||
|
||||
void onItemPickerStarted() {
|
||||
cameraButtonVisibility.setValue(true);
|
||||
}
|
||||
|
||||
void onFolderPickerStarted() {
|
||||
cameraButtonVisibility.setValue(true);
|
||||
}
|
||||
|
||||
void onBodyChanged(@NonNull CharSequence body) {
|
||||
this.body = body;
|
||||
}
|
||||
@@ -143,6 +174,11 @@ class MediaSendViewModel extends ViewModel {
|
||||
selected = new LinkedList<>();
|
||||
}
|
||||
|
||||
if (selected.size() >= maxSelection) {
|
||||
error.postValue(Error.TOO_MANY_ITEMS);
|
||||
return;
|
||||
}
|
||||
|
||||
lastImageCapture = Optional.of(media);
|
||||
|
||||
selected.add(media);
|
||||
@@ -208,22 +244,30 @@ class MediaSendViewModel extends ViewModel {
|
||||
return countButtonState;
|
||||
}
|
||||
|
||||
CharSequence getBody() {
|
||||
@NonNull LiveData<Boolean> getCameraButtonVisibility() {
|
||||
return cameraButtonVisibility;
|
||||
}
|
||||
|
||||
@NonNull CharSequence getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
LiveData<Integer> getPosition() {
|
||||
@NonNull LiveData<Integer> getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
LiveData<String> getBucketId() {
|
||||
@NonNull LiveData<String> getBucketId() {
|
||||
return bucketId;
|
||||
}
|
||||
|
||||
LiveData<Error> getError() {
|
||||
@NonNull LiveData<Error> getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
int getMaxSelection() {
|
||||
return maxSelection;
|
||||
}
|
||||
|
||||
private @NonNull List<Media> getSelectedMediaOrDefault() {
|
||||
return selectedMedia.getValue() == null ? Collections.emptyList()
|
||||
: selectedMedia.getValue();
|
||||
@@ -252,7 +296,7 @@ class MediaSendViewModel extends ViewModel {
|
||||
}
|
||||
|
||||
enum Error {
|
||||
ITEM_TOO_LARGE
|
||||
ITEM_TOO_LARGE, TOO_MANY_ITEMS
|
||||
}
|
||||
|
||||
static class CountButtonState {
|
||||
@@ -268,7 +312,7 @@ class MediaSendViewModel extends ViewModel {
|
||||
return count;
|
||||
}
|
||||
|
||||
boolean getVisibility() {
|
||||
boolean isVisible() {
|
||||
switch (visibility) {
|
||||
case FORCED_ON: return true;
|
||||
case FORCED_OFF: return false;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package org.thoughtcrime.securesms.camera;
|
||||
package org.thoughtcrime.securesms.mediasend;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.Stack;
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public class OrderEnforcer<E> {
|
||||
@@ -22,21 +22,20 @@ public class OrderEnforcer<E> {
|
||||
if (isCompletedThrough(stage)) {
|
||||
r.run();
|
||||
} else {
|
||||
stages.get(stage).getActions().add(r);
|
||||
stages.get(stage).addAction(r);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void markCompleted(@NonNull E stage) {
|
||||
stages.get(stage).setCompleted(true);
|
||||
stages.get(stage).markCompleted();
|
||||
|
||||
for (E s : stages.keySet()) {
|
||||
StageDetails details = stages.get(s);
|
||||
|
||||
if (details.isCompleted()) {
|
||||
for (Runnable r : details.getActions()) {
|
||||
r.run();
|
||||
while (details.hasAction()) {
|
||||
details.popAction().run();
|
||||
}
|
||||
details.getActions().clear();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -45,8 +44,7 @@ public class OrderEnforcer<E> {
|
||||
|
||||
public synchronized void reset() {
|
||||
for (StageDetails details : stages.values()) {
|
||||
details.setCompleted(false);
|
||||
details.getActions().clear();
|
||||
details.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,19 +60,32 @@ public class OrderEnforcer<E> {
|
||||
}
|
||||
|
||||
private static class StageDetails {
|
||||
private boolean completed = false;
|
||||
private List<Runnable> actions = new CopyOnWriteArrayList<>();
|
||||
private boolean completed = false;
|
||||
private Stack<Runnable> actions = new Stack<>();
|
||||
|
||||
@NonNull List<Runnable> getActions() {
|
||||
return actions;
|
||||
boolean hasAction() {
|
||||
return !actions.isEmpty();
|
||||
}
|
||||
|
||||
@Nullable Runnable popAction() {
|
||||
return actions.pop();
|
||||
}
|
||||
|
||||
void addAction(@NonNull Runnable runnable) {
|
||||
actions.push(runnable);
|
||||
}
|
||||
|
||||
void reset() {
|
||||
actions.clear();
|
||||
completed = false;
|
||||
}
|
||||
|
||||
boolean isCompleted() {
|
||||
return completed;
|
||||
}
|
||||
|
||||
void setCompleted(boolean completed) {
|
||||
this.completed = completed;
|
||||
void markCompleted() {
|
||||
completed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,11 +41,10 @@ import java.util.Set;
|
||||
public class ScribbleHud extends InputAwareLayout implements ViewTreeObserver.OnGlobalLayoutListener {
|
||||
|
||||
private View drawButton;
|
||||
private View highlightButton;
|
||||
private View textButton;
|
||||
private View stickerButton;
|
||||
private View undoButton;
|
||||
private View deleteButton;
|
||||
private View confirmButton;
|
||||
private View saveButton;
|
||||
private VerticalSlideColorPicker colorPicker;
|
||||
private RecyclerView colorPalette;
|
||||
@@ -111,11 +110,10 @@ public class ScribbleHud extends InputAwareLayout implements ViewTreeObserver.On
|
||||
setOrientation(VERTICAL);
|
||||
|
||||
drawButton = findViewById(R.id.scribble_draw_button);
|
||||
highlightButton = findViewById(R.id.scribble_highlight_button);
|
||||
textButton = findViewById(R.id.scribble_text_button);
|
||||
stickerButton = findViewById(R.id.scribble_sticker_button);
|
||||
undoButton = findViewById(R.id.scribble_undo_button);
|
||||
deleteButton = findViewById(R.id.scribble_delete_button);
|
||||
confirmButton = findViewById(R.id.scribble_confirm_button);
|
||||
saveButton = findViewById(R.id.scribble_save_button);
|
||||
colorPicker = findViewById(R.id.scribble_color_picker);
|
||||
colorPalette = findViewById(R.id.scribble_color_palette);
|
||||
@@ -163,6 +161,8 @@ public class ScribbleHud extends InputAwareLayout implements ViewTreeObserver.On
|
||||
setMode(Mode.NONE);
|
||||
});
|
||||
|
||||
confirmButton.setOnClickListener(v -> setMode(Mode.NONE));
|
||||
|
||||
sendButton.addOnTransportChangedListener((newTransport, manuallySelected) -> {
|
||||
presentCharactersRemaining();
|
||||
composeText.setTransport(newTransport);
|
||||
@@ -255,84 +255,70 @@ public class ScribbleHud extends InputAwareLayout implements ViewTreeObserver.On
|
||||
|
||||
private void presentModeNone() {
|
||||
drawButton.setVisibility(VISIBLE);
|
||||
highlightButton.setVisibility(VISIBLE);
|
||||
textButton.setVisibility(VISIBLE);
|
||||
stickerButton.setVisibility(VISIBLE);
|
||||
|
||||
undoButton.setVisibility(GONE);
|
||||
deleteButton.setVisibility(GONE);
|
||||
confirmButton.setVisibility(GONE);
|
||||
colorPicker.setVisibility(GONE);
|
||||
colorPalette.setVisibility(GONE);
|
||||
|
||||
drawButton.setOnClickListener(v -> setMode(Mode.DRAW));
|
||||
highlightButton.setOnClickListener(v -> setMode(Mode.HIGHLIGHT));
|
||||
textButton.setOnClickListener(v -> setMode(Mode.TEXT));
|
||||
stickerButton.setOnClickListener(v -> setMode(Mode.STICKER));
|
||||
}
|
||||
|
||||
private void presentModeDraw() {
|
||||
drawButton.setVisibility(VISIBLE);
|
||||
confirmButton.setVisibility(VISIBLE);
|
||||
undoButton.setVisibility(VISIBLE);
|
||||
colorPicker.setVisibility(VISIBLE);
|
||||
colorPalette.setVisibility(VISIBLE);
|
||||
|
||||
highlightButton.setVisibility(GONE);
|
||||
drawButton.setVisibility(GONE);
|
||||
textButton.setVisibility(GONE);
|
||||
stickerButton.setVisibility(GONE);
|
||||
deleteButton.setVisibility(GONE);
|
||||
|
||||
drawButton.setOnClickListener(v -> setMode(Mode.NONE));
|
||||
|
||||
colorPicker.setOnColorChangeListener(standardOnColorChangeListener);
|
||||
colorPicker.setActiveColor(Color.RED);
|
||||
}
|
||||
|
||||
private void presentModeHighlight() {
|
||||
highlightButton.setVisibility(VISIBLE);
|
||||
confirmButton.setVisibility(VISIBLE);
|
||||
undoButton.setVisibility(VISIBLE);
|
||||
colorPicker.setVisibility(VISIBLE);
|
||||
colorPalette.setVisibility(VISIBLE);
|
||||
|
||||
drawButton.setVisibility(GONE);
|
||||
textButton.setVisibility(GONE);
|
||||
stickerButton.setVisibility(GONE);
|
||||
deleteButton.setVisibility(GONE);
|
||||
|
||||
highlightButton.setOnClickListener(v -> setMode(Mode.NONE));
|
||||
|
||||
colorPicker.setOnColorChangeListener(highlightOnColorChangeListener);
|
||||
colorPicker.setActiveColor(Color.YELLOW);
|
||||
}
|
||||
|
||||
private void presentModeText() {
|
||||
textButton.setVisibility(VISIBLE);
|
||||
confirmButton.setVisibility(VISIBLE);
|
||||
deleteButton.setVisibility(VISIBLE);
|
||||
colorPicker.setVisibility(VISIBLE);
|
||||
colorPalette.setVisibility(VISIBLE);
|
||||
|
||||
textButton.setVisibility(GONE);
|
||||
drawButton.setVisibility(GONE);
|
||||
highlightButton.setVisibility(GONE);
|
||||
stickerButton.setVisibility(GONE);
|
||||
undoButton.setVisibility(GONE);
|
||||
|
||||
textButton.setOnClickListener(v -> setMode(Mode.NONE));
|
||||
|
||||
colorPicker.setOnColorChangeListener(standardOnColorChangeListener);
|
||||
colorPicker.setActiveColor(Color.WHITE);
|
||||
}
|
||||
|
||||
private void presentModeSticker() {
|
||||
stickerButton.setVisibility(VISIBLE);
|
||||
deleteButton.setVisibility(VISIBLE);
|
||||
confirmButton.setVisibility(VISIBLE);
|
||||
|
||||
drawButton.setVisibility(GONE);
|
||||
highlightButton.setVisibility(GONE);
|
||||
textButton.setVisibility(GONE);
|
||||
undoButton.setVisibility(GONE);
|
||||
colorPicker.setVisibility(GONE);
|
||||
colorPalette.setVisibility(GONE);
|
||||
|
||||
stickerButton.setOnClickListener(v -> setMode(Mode.NONE));
|
||||
}
|
||||
|
||||
private void presentCharactersRemaining() {
|
||||
|
||||
@@ -136,6 +136,7 @@ public class MotionView extends FrameLayout implements TextWatcher {
|
||||
this.addView(editText);
|
||||
this.editText.clearFocus();
|
||||
this.editText.addTextChangedListener(this);
|
||||
this.editText.setId(R.id.motion_view_edittext);
|
||||
|
||||
// init listeners
|
||||
this.scaleGestureDetector = new ScaleGestureDetector(context, new ScaleListener());
|
||||
|
||||
@@ -43,7 +43,7 @@ import org.thoughtcrime.securesms.R;
|
||||
|
||||
public class VerticalSlideColorPicker extends View {
|
||||
|
||||
private static final float INDICATOR_TO_BAR_WIDTH_RATIO = 0.8f;
|
||||
private static final float INDICATOR_TO_BAR_WIDTH_RATIO = 0.5f;
|
||||
|
||||
private Paint paint;
|
||||
private Paint strokePaint;
|
||||
@@ -131,9 +131,9 @@ public class VerticalSlideColorPicker extends View {
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
path.addCircle(centerX, borderWidth + colorPickerRadius, colorPickerRadius, Path.Direction.CW);
|
||||
path.addCircle(centerX, borderWidth + colorPickerRadius + indicatorRadius, colorPickerRadius, Path.Direction.CW);
|
||||
path.addRect(colorPickerBody, Path.Direction.CW);
|
||||
path.addCircle(centerX, viewHeight - (borderWidth + colorPickerRadius), colorPickerRadius, Path.Direction.CW);
|
||||
path.addCircle(centerX, viewHeight - (borderWidth + colorPickerRadius + indicatorRadius), colorPickerRadius, Path.Direction.CW);
|
||||
|
||||
bitmapCanvas.drawColor(Color.TRANSPARENT);
|
||||
|
||||
@@ -178,7 +178,10 @@ public class VerticalSlideColorPicker extends View {
|
||||
indicatorRadius = (viewWidth / 2) - borderWidth;
|
||||
colorPickerRadius = (barWidth / 2) - borderWidth;
|
||||
|
||||
colorPickerBody = new RectF(centerX - colorPickerRadius, borderWidth + colorPickerRadius, centerX + colorPickerRadius, viewHeight - (borderWidth + colorPickerRadius));
|
||||
colorPickerBody = new RectF(centerX - colorPickerRadius,
|
||||
borderWidth + colorPickerRadius + indicatorRadius,
|
||||
centerX + colorPickerRadius,
|
||||
viewHeight - (borderWidth + colorPickerRadius + indicatorRadius));
|
||||
|
||||
LinearGradient gradient = new LinearGradient(0, colorPickerBody.top, 0, colorPickerBody.bottom, colors, null, Shader.TileMode.CLAMP);
|
||||
paint.setShader(gradient);
|
||||
|
||||
Reference in New Issue
Block a user