diff --git a/app/src/main/java/io/github/sspanak/tt9/ui/main/ResizableMainView.java b/app/src/main/java/io/github/sspanak/tt9/ui/main/ResizableMainView.java index 3777bc34..5d904eef 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ui/main/ResizableMainView.java +++ b/app/src/main/java/io/github/sspanak/tt9/ui/main/ResizableMainView.java @@ -67,6 +67,10 @@ public class ResizableMainView extends MainView implements View.OnAttachStateCha public void onAlign(float deltaX) { + if (!(main instanceof MainLayoutNumpad)) { + return; + } + boolean right = deltaX > 0; SettingsStore settings = tt9.getSettings(); diff --git a/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/ResizeHandle.java b/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/ResizeHandle.java deleted file mode 100644 index bcc92178..00000000 --- a/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/ResizeHandle.java +++ /dev/null @@ -1,85 +0,0 @@ -package io.github.sspanak.tt9.ui.main.keys; - -import android.content.Context; -import android.view.MotionEvent; -import android.view.View; - -import androidx.annotation.NonNull; - -import io.github.sspanak.tt9.R; -import io.github.sspanak.tt9.ui.main.ResizableMainView; - -public class ResizeHandle implements View.OnTouchListener { - @NonNull private final Runnable onClick; - private ResizableMainView mainView; - - private final float ALIGN_THRESHOLD; - private final float RESIZE_THRESHOLD; - - private boolean aligning; - private boolean resizing; - private float startX; - private float startY; - - - ResizeHandle(@NonNull Context context, @NonNull Runnable onClick) { - ALIGN_THRESHOLD = context.getResources().getDimensionPixelSize(R.dimen.numpad_key_height) * 0.75f; - RESIZE_THRESHOLD = context.getResources().getDimensionPixelSize(R.dimen.numpad_key_height) / 4.0f; - this.onClick = onClick; - } - - public void setMainView(ResizableMainView mainView) { - this.mainView = mainView; - } - - @Override - public boolean onTouch(View v, MotionEvent event) { - switch(event.getAction() & MotionEvent.ACTION_MASK) { - case MotionEvent.ACTION_DOWN: - handlePress(event); - break; - case MotionEvent.ACTION_MOVE: - handleDrag(event); - break; - case MotionEvent.ACTION_UP: - handleRelease(event); - break; - } - - return false; - } - - private void handlePress(MotionEvent event) { - startX = event.getRawX(); - startY = event.getRawY(); - } - - private void handleDrag(MotionEvent event) { - if (mainView == null) { - aligning = false; - resizing = false; - return; - } - - if (!resizing && Math.abs(event.getRawY() - startY) >= RESIZE_THRESHOLD) { - mainView.onResizeStart(event.getRawY()); - resizing = true; - } else if (resizing) { - mainView.onResizeThrottled(event.getRawY()); - } else if (!aligning && Math.abs(event.getRawX() - startX) >= ALIGN_THRESHOLD) { - mainView.onAlign(event.getRawX() - startX); - aligning = true; - } - } - - private void handleRelease(MotionEvent event) { - if (mainView != null && resizing) { - mainView.onResize(event.getRawY()); - resizing = false; - } else if (mainView != null && aligning) { - aligning = false; - } else { - onClick.run(); - } - } -} diff --git a/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/SoftKeyCommandPalette.java b/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/SoftKeyCommandPalette.java index 725d9e40..5cc64ee6 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/SoftKeyCommandPalette.java +++ b/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/SoftKeyCommandPalette.java @@ -2,33 +2,20 @@ package io.github.sspanak.tt9.ui.main.keys; import android.content.Context; import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.View; import io.github.sspanak.tt9.R; -import io.github.sspanak.tt9.ui.main.ResizableMainView; - -public class SoftKeyCommandPalette extends SoftKey { - private final ResizeHandle resizeHandle = new ResizeHandle(getContext(), this::showCommandPalette); +public class SoftKeyCommandPalette extends SoftKeySettings { public SoftKeyCommandPalette(Context context) { super(context); } public SoftKeyCommandPalette(Context context, AttributeSet attrs) { super(context, attrs); } public SoftKeyCommandPalette(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } - public void setMainView(ResizableMainView mainView) { - resizeHandle.setMainView(mainView); - } - @Override - public boolean onTouch(View view, MotionEvent event) { - resizeHandle.onTouch(view, event); - return super.onTouch(view, event); - } - - protected void showCommandPalette() { - if (validateTT9Handler()) { + protected boolean handleRelease() { + if (notSwiped() && validateTT9Handler()) { tt9.onKeyCommandPalette(false); } + return true; } @Override diff --git a/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/SoftKeySettings.java b/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/SoftKeySettings.java index 0cd65d6a..f3fcf37a 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/SoftKeySettings.java +++ b/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/SoftKeySettings.java @@ -2,44 +2,58 @@ package io.github.sspanak.tt9.ui.main.keys; import android.content.Context; import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.View; import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.ui.main.ResizableMainView; -public class SoftKeySettings extends SoftKey { - private final ResizeHandle resizeHandle = new ResizeHandle(getContext(), this::showSettings); +public class SoftKeySettings extends SwipeableKey { + private ResizableMainView mainView; public SoftKeySettings(Context context) { super(context); - setOnLongClickListener(null); } - public SoftKeySettings(Context context, AttributeSet attrs) { super(context, attrs); - setOnLongClickListener(null); } - public SoftKeySettings(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - setOnLongClickListener(null); } public void setMainView(ResizableMainView mainView) { - resizeHandle.setMainView(mainView); + this.mainView = mainView; + } + + // this key does not support holding at the moment, so just prevent it + @Override protected float getHoldDurationThreshold() { return 1000; } + @Override protected float getSwipeXThreshold(Context context) { return context.getResources().getDimensionPixelSize(R.dimen.numpad_key_height) * 0.75f; } + @Override protected float getSwipeYThreshold(Context context) { return context.getResources().getDimensionPixelSize(R.dimen.numpad_key_height) / 4.0f; } + + @Override + protected boolean handleRelease() { + if (notSwiped() && validateTT9Handler()) { + tt9.showSettings(); + } + return true; } @Override - public boolean onTouch(View view, MotionEvent event) { - resizeHandle.onTouch(view, event); - return super.onTouch(view, event); + protected void handleStartSwipeX(float p, float delta) { + if (mainView != null) mainView.onAlign(delta); } - protected void showSettings() { - if (validateTT9Handler()) { - tt9.showSettings(); - } + @Override + protected void handleStartSwipeY(float position, float d) { + if (mainView != null) mainView.onResizeStart(position); + } + + @Override + protected void handleSwipeY(float position, float delta) { + if (mainView != null) mainView.onResizeThrottled(position); + } + + @Override + protected void handleEndSwipeY(float position, float delta) { + if (mainView != null) mainView.onResize(position); } @Override diff --git a/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/SwipeableKey.java b/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/SwipeableKey.java new file mode 100644 index 00000000..8a63214c --- /dev/null +++ b/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/SwipeableKey.java @@ -0,0 +1,146 @@ +package io.github.sspanak.tt9.ui.main.keys; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; + +import io.github.sspanak.tt9.R; +import io.github.sspanak.tt9.preferences.settings.SettingsStore; + +abstract public class SwipeableKey extends SoftKey { + private float HOLD_DURATION_THRESHOLD; + protected float SWIPE_X_THRESHOLD; + protected float SWIPE_Y_THRESHOLD; + + private boolean isHolding = false; + private boolean isSwipingX = false; + private boolean isSwipingY = false; + private boolean hasSwiped = false; + + private float startX; + private float startY; + private long startTime; + + + public SwipeableKey(Context context) { + super(context); + init(context); + } + + + public SwipeableKey(Context context, AttributeSet attrs) { + super(context, attrs); + init(context); + } + + + public SwipeableKey(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context); + } + + + private void init(Context context) { + if (HOLD_DURATION_THRESHOLD == 0) { + HOLD_DURATION_THRESHOLD = getHoldDurationThreshold(); + } + if (SWIPE_X_THRESHOLD == 0) { + SWIPE_X_THRESHOLD = getSwipeXThreshold(context); + } + if (SWIPE_Y_THRESHOLD == 0) { + SWIPE_Y_THRESHOLD = getSwipeYThreshold(context); + } + } + + + protected float getHoldDurationThreshold() { return SettingsStore.SOFT_KEY_REPEAT_DELAY * 3; } + protected float getSwipeXThreshold(Context context) { return context.getResources().getDimensionPixelSize(R.dimen.numpad_key_height) / 10.0f; } + protected float getSwipeYThreshold(Context context) { return context.getResources().getDimensionPixelSize(R.dimen.numpad_key_height) / 10.0f; } + + + @Override + public boolean onTouch(View v, MotionEvent event) { + switch(event.getAction() & MotionEvent.ACTION_MASK) { + case MotionEvent.ACTION_DOWN: + onPress(event); + break; + case MotionEvent.ACTION_MOVE: + onMove(v, event); + break; + case MotionEvent.ACTION_UP: + onRelease(event); + break; + } + + return super.onTouch(v, event); + } + + + @Override + public boolean onLongClick(View view) { + if (System.currentTimeMillis() - startTime < HOLD_DURATION_THRESHOLD) { + return true; + } + + isHolding = !isSwipingY && !isSwipingX; + return isSwipingY || isSwipingX || super.onLongClick(view); + } + + + private void onPress(MotionEvent event) { + startTime = System.currentTimeMillis(); + startX = event.getRawX(); + startY = event.getRawY(); + + isHolding = false; + isSwipingX = false; + isSwipingY = false; + } + + + private void onMove(View v, MotionEvent event) { + if (isHolding) { + return; + } + + float deltaY = event.getRawY() - startY; + float deltaX = event.getRawX() - startX; + + if (isSwipingY) { + handleSwipeY(event.getRawY(), deltaY); + } else if (isSwipingX) { + handleSwipeX(event.getRawX(), deltaX); + } else if (Math.abs(deltaY) >= SWIPE_Y_THRESHOLD) { + isSwipingY = true; + handleStartSwipeY(event.getRawY(), deltaY); + } else if (Math.abs(deltaX) >= SWIPE_X_THRESHOLD) { + isSwipingX = true; + handleStartSwipeX(event.getRawX(), deltaX); + } else if (!isHolding && Math.abs(deltaX) < SWIPE_X_THRESHOLD && Math.abs(deltaY) < SWIPE_Y_THRESHOLD) { + onLongClick(v); + } + } + + + private void onRelease(MotionEvent event) { + hasSwiped = !isSwipingY && !isSwipingX; + + if (isSwipingY) { + isSwipingY = false; + handleEndSwipeY(event.getRawY(), event.getRawY() - startY); + } else if (isSwipingX) { + isSwipingX = false; + handleEndSwipeX(event.getRawX(), event.getRawX() - startX); + } + } + + + protected void handleStartSwipeX(float position, float delta) {} + protected void handleStartSwipeY(float position, float delta) {} + protected void handleSwipeX(float position, float delta) {} + protected void handleSwipeY(float position, float delta) {} + protected void handleEndSwipeX(float position, float delta) {} + protected void handleEndSwipeY(float position, float delta) {} + protected boolean notSwiped() { return hasSwiped; } +}