Skip to content

Commit

Permalink
broken down the SoftKey into smaller files
Browse files Browse the repository at this point in the history
  • Loading branch information
sspanak committed Feb 2, 2025
1 parent 7d32a3f commit c8c1fe7
Show file tree
Hide file tree
Showing 14 changed files with 358 additions and 323 deletions.
4 changes: 2 additions & 2 deletions app/src/main/java/io/github/sspanak/tt9/ui/Vibration.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import androidx.annotation.NonNull;

import io.github.sspanak.tt9.preferences.settings.SettingsStore;
import io.github.sspanak.tt9.ui.main.keys.SoftKey;
import io.github.sspanak.tt9.ui.main.keys.BaseClickableKey;
import io.github.sspanak.tt9.ui.main.keys.SoftKeyNumber;

public class Vibration {
Expand All @@ -23,7 +23,7 @@ public static int getNoVibration() {
return -1;
}

public static int getPressVibration(SoftKey key) {
public static int getPressVibration(BaseClickableKey key) {
return key instanceof SoftKeyNumber ? HapticFeedbackConstants.KEYBOARD_TAP : HapticFeedbackConstants.VIRTUAL_KEY;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package io.github.sspanak.tt9.ui.main.keys;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import io.github.sspanak.tt9.ime.TraditionalT9;
import io.github.sspanak.tt9.preferences.settings.SettingsStore;
import io.github.sspanak.tt9.ui.Vibration;
import io.github.sspanak.tt9.util.Logger;

public class BaseClickableKey extends com.google.android.material.button.MaterialButton implements View.OnTouchListener, View.OnLongClickListener {
private final String LOG_TAG = getClass().getSimpleName();

protected TraditionalT9 tt9;
protected Vibration vibration;

private boolean hold = false;
private boolean repeat = false;
private long lastLongClickTime = 0;
private final Handler repeatHandler = new Handler(Looper.getMainLooper());

private static int lastPressedKey = -1;
private boolean ignoreLastPressedKey = false;


public BaseClickableKey(Context context) {
super(context);
setHapticFeedbackEnabled(false);
setOnTouchListener(this);
setOnLongClickListener(this);
}


public BaseClickableKey(Context context, AttributeSet attrs) {
super(context, attrs);
setHapticFeedbackEnabled(false);
setOnTouchListener(this);
setOnLongClickListener(this);
}


public BaseClickableKey(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setHapticFeedbackEnabled(false);
setOnTouchListener(this);
setOnLongClickListener(this);
}


public void setTT9(TraditionalT9 tt9) {
this.tt9 = tt9;
}


protected boolean validateTT9Handler() {
if (tt9 == null) {
Logger.w(LOG_TAG, "Traditional T9 handler is not set. Ignoring key press.");
return false;
}

return true;
}


@Override
public boolean onTouch(View view, MotionEvent event) {
super.onTouchEvent(event);

int action = (event.getAction() & MotionEvent.ACTION_MASK);

if (action == MotionEvent.ACTION_DOWN) {
return handlePress();
} else if (action == MotionEvent.ACTION_UP) {
if (!repeat || hold) {
hold = false;
repeat = false;
boolean result = handleRelease();
lastPressedKey = ignoreLastPressedKey ? -1 : getId();
return result;
}
repeat = false;
}
return false;
}


@Override
public boolean onLongClick(View view) {
// sometimes this gets called twice, so we debounce the call to the repeating function
final long now = System.currentTimeMillis();
if (now - lastLongClickTime < SettingsStore.SOFT_KEY_DOUBLE_CLICK_DELAY) {
return false;
}

hold = true;
lastLongClickTime = now;
repeatOnLongPress();
return true;
}


/**
* repeatOnLongPress
* Repeatedly calls "handleHold()" upon holding the respective SoftKey, to simulate physical keyboard behavior.
*/
private void repeatOnLongPress() {
if (hold) {
repeat = true;
handleHold();
lastPressedKey = ignoreLastPressedKey ? -1 : getId();
repeatHandler.removeCallbacks(this::repeatOnLongPress);
repeatHandler.postDelayed(this::repeatOnLongPress, SettingsStore.SOFT_KEY_REPEAT_DELAY);
}
}


/**
* preventRepeat
* Prevents "handleHold()" from being called repeatedly when the SoftKey is being held.
*/
protected void preventRepeat() {
hold = false;
repeatHandler.removeCallbacks(this::repeatOnLongPress);
}


protected static int getLastPressedKey() {
return lastPressedKey;
}


protected void ignoreLastPressedKey() {
ignoreLastPressedKey = true;
}


protected boolean handlePress() {
if (validateTT9Handler()) {
vibrate(Vibration.getPressVibration(this));
}

return false;
}


protected void handleHold() {}


protected boolean handleRelease() {
return false;
}


public boolean isHoldEnabled() {
return true;
}


protected void vibrate(int vibrationType) {
if (tt9 != null) {
vibration = vibration == null ? new Vibration(tt9.getSettings(), this) : vibration;
vibration.vibrate(vibrationType);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package io.github.sspanak.tt9.ui.main.keys;

import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;

import androidx.annotation.Nullable;
import androidx.appcompat.content.res.AppCompatResources;

public class BaseSoftKeyWithIcons extends SoftKey {
private Drawable icon = null;
private Drawable holdIcon = null;


public BaseSoftKeyWithIcons(Context context) { super(context); }
public BaseSoftKeyWithIcons(Context context, AttributeSet attrs) { super(context, attrs); }
public BaseSoftKeyWithIcons(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); }


/**
* Returns the central icon resource ID. If the key does not have a central icon, return -1. The scale
*/
protected int getCentralIcon() { return -1; }


/**
* A fail-safe method to get the central icon drawable.
*/
private Drawable getCentralIconCompat() {
if (icon == null && getCentralIcon() > 0) {
icon = AppCompatResources.getDrawable(getContext(), getCentralIcon());
} else if (getCentralIcon() <= 0) {
icon = null;
}

return icon;
}


/**
* Same as getTitleScale(), but for keys that have icons instead of text.
*/
protected float getCentralIconScale() {
float keyboardSizeScale = Math.max(0.7f, Math.min(getTT9Width(), getTT9Height()));
keyboardSizeScale = Math.min(1.15f, keyboardSizeScale);
return keyboardSizeScale * Math.min(getScreenScaleX(), getScreenScaleY());
}


/**
* Returns the hold icon resource ID. If the key does not have a hold icon, return -1. The scale
* is controlled by super.getHoldElementScale().
*/
protected int getHoldIcon() { return -1; }


/**
* A fail-safe method to get the hold icon drawable.
*/
private Drawable getHoldIconCompat() {
if (holdIcon == null && getHoldIcon() > 0) {
holdIcon = AppCompatResources.getDrawable(getContext(), getHoldIcon());
} else if (getHoldIcon() <= 0) {
holdIcon = null;
}

return holdIcon;
}


protected void resetIconCache() {
icon = null;
holdIcon = null;
}


/**
* Renders one of the key icons. It could be either the central icon, in the place of the main title,
* or a hold icon, displayed in the upper right corner.
*/
private void renderOverlayDrawable(String elementTag, @Nullable Drawable drawable, float scale, boolean isEnabled) {
if (overlay == null) {
return;
}

View element = ((RelativeLayout) getParent()).findViewWithTag(elementTag);
if (!(element instanceof ImageView el)) {
return;
}

el.setImageDrawable(drawable);
if (!isEnabled) {
el.setColorFilter(Color.GRAY);
} else {
el.clearColorFilter();
}

if (drawable != null) {
el.setScaleX(scale);
el.setScaleY(scale);
}
}


public void render() {
boolean isKeyEnabled = isEnabled();

getOverlayWrapper();
renderOverlayDrawable("overlay_icon", getCentralIconCompat(), getCentralIconScale(), isKeyEnabled);
renderOverlayDrawable("overlay_hold_icon", getHoldIconCompat(), getHoldElementScale(), isKeyEnabled && isHoldEnabled());

super.render();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package io.github.sspanak.tt9.ui.main.keys;

import android.content.Context;
import android.util.AttributeSet;

public class BaseSoftKeyWithSideText extends BaseSoftKeyWithIcons {
public BaseSoftKeyWithSideText(Context context) { super(context); }
public BaseSoftKeyWithSideText(Context context, AttributeSet attrs) { super(context, attrs); }
public BaseSoftKeyWithSideText(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); }

protected String getTopText() { return null; }
protected String getRightText() { return null; }
protected String getBottomText() { return null; }
protected String getLeftText() { return null; }

@Override
public void render() {
boolean isKeyEnabled = isEnabled();

getOverlayWrapper();
renderOverlayText("overlay_top_text", getTopText(), getHoldElementScale(), isKeyEnabled);
renderOverlayText("overlay_right_text", getRightText(), getHoldElementScale(), isKeyEnabled);
renderOverlayText("overlay_bottom_text", getBottomText(), getHoldElementScale(), isKeyEnabled);
renderOverlayText("overlay_left_text", getLeftText(), getHoldElementScale(), isKeyEnabled);
super.render();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
import io.github.sspanak.tt9.util.Logger;
import io.github.sspanak.tt9.util.Timer;

abstract public class SwipeableKey extends SoftKey {
private static final String LOG_TAG = SwipeableKey.class.getSimpleName();
abstract public class BaseSwipeableKey extends BaseSoftKeyWithSideText {
private static final String LOG_TAG = BaseSwipeableKey.class.getSimpleName();

private float HOLD_DURATION_THRESHOLD;
protected float SWIPE_X_THRESHOLD;
Expand All @@ -32,17 +32,17 @@ abstract public class SwipeableKey extends SoftKey {
private long swipeProcessingTimeAverage = 50;


public SwipeableKey(Context context) {
public BaseSwipeableKey(Context context) {
super(context);
}


public SwipeableKey(Context context, AttributeSet attrs) {
public BaseSwipeableKey(Context context, AttributeSet attrs) {
super(context, attrs);
}


public SwipeableKey(Context context, AttributeSet attrs, int defStyleAttr) {
public BaseSwipeableKey(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

Expand Down
Loading

0 comments on commit c8c1fe7

Please sign in to comment.