2019-05-09 14:11:11 -03:00
|
|
|
package org.thoughtcrime.securesms.scribbles;
|
|
|
|
|
|
|
|
import android.content.Context;
|
|
|
|
import android.graphics.Color;
|
2020-08-19 10:06:26 +10:00
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
import androidx.annotation.Nullable;
|
|
|
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
|
|
|
import androidx.recyclerview.widget.RecyclerView;
|
2019-05-09 14:11:11 -03:00
|
|
|
import android.util.AttributeSet;
|
|
|
|
import android.view.View;
|
|
|
|
import android.widget.ImageView;
|
|
|
|
import android.widget.LinearLayout;
|
|
|
|
|
2019-07-24 12:30:23 +10:00
|
|
|
import network.loki.messenger.R;
|
2019-05-09 14:11:11 -03:00
|
|
|
import org.thoughtcrime.securesms.scribbles.widget.ColorPaletteAdapter;
|
|
|
|
import org.thoughtcrime.securesms.scribbles.widget.VerticalSlideColorPicker;
|
|
|
|
|
|
|
|
import java.util.Arrays;
|
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.HashSet;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.Set;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The HUD (heads-up display) that contains all of the tools for interacting with
|
|
|
|
* {@link org.thoughtcrime.securesms.imageeditor.ImageEditorView}
|
|
|
|
*/
|
|
|
|
public final class ImageEditorHud extends LinearLayout {
|
|
|
|
|
|
|
|
private View cropButton;
|
|
|
|
private View cropFlipButton;
|
|
|
|
private View cropRotateButton;
|
|
|
|
private ImageView cropAspectLock;
|
|
|
|
private View drawButton;
|
|
|
|
private View highlightButton;
|
|
|
|
private View textButton;
|
|
|
|
private View stickerButton;
|
|
|
|
private View undoButton;
|
|
|
|
private View deleteButton;
|
|
|
|
private View confirmButton;
|
|
|
|
private VerticalSlideColorPicker colorPicker;
|
|
|
|
private RecyclerView colorPalette;
|
|
|
|
|
|
|
|
@NonNull
|
|
|
|
private EventListener eventListener = NULL_EVENT_LISTENER;
|
|
|
|
@Nullable
|
|
|
|
private ColorPaletteAdapter colorPaletteAdapter;
|
|
|
|
|
|
|
|
private final Map<Mode, Set<View>> visibilityModeMap = new HashMap<>();
|
2019-05-17 16:15:27 -03:00
|
|
|
private final Set<View> allViews = new HashSet<>();
|
|
|
|
|
|
|
|
private Mode currentMode;
|
|
|
|
private boolean undoAvailable;
|
2019-05-09 14:11:11 -03:00
|
|
|
|
|
|
|
public ImageEditorHud(@NonNull Context context) {
|
|
|
|
super(context);
|
|
|
|
initialize();
|
|
|
|
}
|
|
|
|
|
|
|
|
public ImageEditorHud(@NonNull Context context, @Nullable AttributeSet attrs) {
|
|
|
|
super(context, attrs);
|
|
|
|
initialize();
|
|
|
|
}
|
|
|
|
|
|
|
|
public ImageEditorHud(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
|
|
|
super(context, attrs, defStyleAttr);
|
|
|
|
initialize();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void initialize() {
|
|
|
|
inflate(getContext(), R.layout.image_editor_hud, this);
|
|
|
|
setOrientation(VERTICAL);
|
|
|
|
|
|
|
|
cropButton = findViewById(R.id.scribble_crop_button);
|
|
|
|
cropFlipButton = findViewById(R.id.scribble_crop_flip);
|
|
|
|
cropRotateButton = findViewById(R.id.scribble_crop_rotate);
|
|
|
|
cropAspectLock = findViewById(R.id.scribble_crop_aspect_lock);
|
|
|
|
colorPalette = findViewById(R.id.scribble_color_palette);
|
|
|
|
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);
|
|
|
|
colorPicker = findViewById(R.id.scribble_color_picker);
|
|
|
|
|
|
|
|
cropAspectLock.setOnClickListener(v -> {
|
|
|
|
eventListener.onCropAspectLock(!eventListener.isCropAspectLocked());
|
|
|
|
updateCropAspectLockImage(eventListener.isCropAspectLocked());
|
|
|
|
});
|
|
|
|
|
|
|
|
initializeViews();
|
|
|
|
initializeVisibilityMap();
|
|
|
|
setMode(Mode.NONE);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void updateCropAspectLockImage(boolean cropAspectLocked) {
|
|
|
|
cropAspectLock.setImageDrawable(getResources().getDrawable(cropAspectLocked ? R.drawable.ic_crop_lock_32 : R.drawable.ic_crop_unlock_32));
|
|
|
|
}
|
|
|
|
|
|
|
|
private void initializeVisibilityMap() {
|
|
|
|
setVisibleViewsWhenInMode(Mode.NONE, drawButton, highlightButton, textButton, stickerButton, cropButton, undoButton);
|
|
|
|
|
|
|
|
setVisibleViewsWhenInMode(Mode.DRAW, confirmButton, undoButton, colorPicker, colorPalette);
|
|
|
|
|
|
|
|
setVisibleViewsWhenInMode(Mode.HIGHLIGHT, confirmButton, undoButton, colorPicker, colorPalette);
|
|
|
|
|
|
|
|
setVisibleViewsWhenInMode(Mode.TEXT, confirmButton, deleteButton, colorPicker, colorPalette);
|
|
|
|
|
|
|
|
setVisibleViewsWhenInMode(Mode.MOVE_DELETE, confirmButton, deleteButton);
|
|
|
|
|
2019-05-15 14:31:16 -03:00
|
|
|
setVisibleViewsWhenInMode(Mode.CROP, confirmButton, cropFlipButton, cropRotateButton, cropAspectLock, undoButton);
|
2019-05-09 14:11:11 -03:00
|
|
|
|
|
|
|
for (Set<View> views : visibilityModeMap.values()) {
|
|
|
|
allViews.addAll(views);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void setVisibleViewsWhenInMode(Mode mode, View... views) {
|
|
|
|
visibilityModeMap.put(mode, new HashSet<>(Arrays.asList(views)));
|
|
|
|
}
|
|
|
|
|
|
|
|
private void initializeViews() {
|
|
|
|
undoButton.setOnClickListener(v -> eventListener.onUndo());
|
|
|
|
|
|
|
|
deleteButton.setOnClickListener(v -> {
|
|
|
|
eventListener.onDelete();
|
|
|
|
setMode(Mode.NONE);
|
|
|
|
});
|
|
|
|
|
|
|
|
cropButton.setOnClickListener(v -> setMode(Mode.CROP));
|
|
|
|
cropFlipButton.setOnClickListener(v -> eventListener.onFlipHorizontal());
|
|
|
|
cropRotateButton.setOnClickListener(v -> eventListener.onRotate90AntiClockwise());
|
|
|
|
|
|
|
|
confirmButton.setOnClickListener(v -> setMode(Mode.NONE));
|
|
|
|
|
|
|
|
colorPaletteAdapter = new ColorPaletteAdapter();
|
|
|
|
colorPaletteAdapter.setEventListener(colorPicker::setActiveColor);
|
|
|
|
|
|
|
|
colorPalette.setLayoutManager(new LinearLayoutManager(getContext()));
|
|
|
|
colorPalette.setAdapter(colorPaletteAdapter);
|
|
|
|
|
|
|
|
drawButton.setOnClickListener(v -> setMode(Mode.DRAW));
|
|
|
|
highlightButton.setOnClickListener(v -> setMode(Mode.HIGHLIGHT));
|
|
|
|
textButton.setOnClickListener(v -> setMode(Mode.TEXT));
|
|
|
|
stickerButton.setOnClickListener(v -> setMode(Mode.MOVE_DELETE));
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setColorPalette(@NonNull Set<Integer> colors) {
|
|
|
|
if (colorPaletteAdapter != null) {
|
|
|
|
colorPaletteAdapter.setColors(colors);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getActiveColor() {
|
|
|
|
return colorPicker.getActiveColor();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setActiveColor(int color) {
|
|
|
|
colorPicker.setActiveColor(color);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setEventListener(@Nullable EventListener eventListener) {
|
|
|
|
this.eventListener = eventListener != null ? eventListener : NULL_EVENT_LISTENER;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void enterMode(@NonNull Mode mode) {
|
|
|
|
setMode(mode, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void setMode(@NonNull Mode mode) {
|
|
|
|
setMode(mode, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void setMode(@NonNull Mode mode, boolean notify) {
|
2019-05-17 16:15:27 -03:00
|
|
|
this.currentMode = mode;
|
2019-05-09 14:11:11 -03:00
|
|
|
Set<View> visibleButtons = visibilityModeMap.get(mode);
|
|
|
|
for (View button : allViews) {
|
2019-05-17 16:15:27 -03:00
|
|
|
button.setVisibility(buttonIsVisible(visibleButtons, button) ? VISIBLE : GONE);
|
2019-05-09 14:11:11 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (mode) {
|
|
|
|
case CROP: presentModeCrop(); break;
|
|
|
|
case DRAW: presentModeDraw(); break;
|
|
|
|
case HIGHLIGHT: presentModeHighlight(); break;
|
|
|
|
case TEXT: presentModeText(); break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (notify) {
|
|
|
|
eventListener.onModeStarted(mode);
|
|
|
|
}
|
|
|
|
eventListener.onRequestFullScreen(mode != Mode.NONE);
|
|
|
|
}
|
|
|
|
|
2019-05-17 16:15:27 -03:00
|
|
|
private boolean buttonIsVisible(@Nullable Set<View> visibleButtons, @NonNull View button) {
|
|
|
|
return visibleButtons != null &&
|
|
|
|
visibleButtons.contains(button) &&
|
|
|
|
(button != undoButton || undoAvailable);
|
|
|
|
}
|
|
|
|
|
2019-05-09 14:11:11 -03:00
|
|
|
private void presentModeCrop() {
|
|
|
|
updateCropAspectLockImage(eventListener.isCropAspectLocked());
|
|
|
|
}
|
|
|
|
|
|
|
|
private void presentModeDraw() {
|
|
|
|
colorPicker.setOnColorChangeListener(standardOnColorChangeListener);
|
|
|
|
colorPicker.setActiveColor(Color.RED);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void presentModeHighlight() {
|
|
|
|
colorPicker.setOnColorChangeListener(highlightOnColorChangeListener);
|
|
|
|
colorPicker.setActiveColor(Color.YELLOW);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void presentModeText() {
|
|
|
|
colorPicker.setOnColorChangeListener(standardOnColorChangeListener);
|
|
|
|
colorPicker.setActiveColor(Color.WHITE);
|
|
|
|
}
|
|
|
|
|
|
|
|
private final VerticalSlideColorPicker.OnColorChangeListener standardOnColorChangeListener = selectedColor -> eventListener.onColorChange(selectedColor);
|
|
|
|
|
|
|
|
private final VerticalSlideColorPicker.OnColorChangeListener highlightOnColorChangeListener = selectedColor -> eventListener.onColorChange(replaceAlphaWith128(selectedColor));
|
|
|
|
|
|
|
|
private static int replaceAlphaWith128(int color) {
|
|
|
|
return color & ~0xff000000 | 0x80000000;
|
|
|
|
}
|
|
|
|
|
2019-05-17 16:15:27 -03:00
|
|
|
public void setUndoAvailability(boolean undoAvailable) {
|
|
|
|
this.undoAvailable = undoAvailable;
|
|
|
|
|
|
|
|
undoButton.setVisibility(buttonIsVisible(visibilityModeMap.get(currentMode), undoButton) ? VISIBLE : GONE);
|
|
|
|
}
|
|
|
|
|
2019-05-09 14:11:11 -03:00
|
|
|
public enum Mode {
|
|
|
|
NONE, DRAW, HIGHLIGHT, TEXT, MOVE_DELETE, CROP
|
|
|
|
}
|
|
|
|
|
|
|
|
public interface EventListener {
|
|
|
|
void onModeStarted(@NonNull Mode mode);
|
|
|
|
void onColorChange(int color);
|
|
|
|
void onUndo();
|
|
|
|
void onDelete();
|
|
|
|
void onFlipHorizontal();
|
|
|
|
void onRotate90AntiClockwise();
|
|
|
|
void onCropAspectLock(boolean locked);
|
|
|
|
boolean isCropAspectLocked();
|
|
|
|
void onRequestFullScreen(boolean fullScreen);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final EventListener NULL_EVENT_LISTENER = new EventListener() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onModeStarted(@NonNull Mode mode) {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onColorChange(int color) {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onUndo() {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onDelete() {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onFlipHorizontal() {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onRotate90AntiClockwise() {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onCropAspectLock(boolean locked) {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isCropAspectLocked() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onRequestFullScreen(boolean fullScreen) {
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|