Unexpected-Keyboard/srcs/juloo.keyboard2/Keyboard2View.java

455 lines
13 KiB
Java
Raw Normal View History

2015-07-30 20:14:55 +02:00
package juloo.keyboard2;
import android.content.Context;
import android.content.res.Resources;
2015-07-30 20:14:55 +02:00
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
2015-10-13 00:02:34 +02:00
import android.graphics.Typeface;
2015-08-05 01:30:56 +02:00
import android.os.Handler;
import android.os.Message;
2015-08-03 00:01:04 +02:00
import android.os.Vibrator;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
2015-07-30 20:14:55 +02:00
import android.view.MotionEvent;
import android.view.View;
import android.widget.PopupWindow;
2015-10-03 00:19:17 +02:00
import java.util.ArrayList;
2015-07-30 20:14:55 +02:00
public class Keyboard2View extends View
2015-08-05 01:30:56 +02:00
implements View.OnTouchListener, Handler.Callback
2015-07-30 20:14:55 +02:00
{
2015-08-08 15:26:23 +02:00
private static final long VIBRATE_MIN_INTERVAL = 100;
2015-07-30 20:14:55 +02:00
2015-10-29 12:49:40 +01:00
private KeyboardData _keyboard;
2015-07-30 20:14:55 +02:00
2015-10-03 00:19:17 +02:00
private ArrayList<KeyDown> _downKeys = new ArrayList<KeyDown>();
2015-08-01 23:54:38 +02:00
2015-10-29 12:49:40 +01:00
private int _flags = 0;
2015-08-01 16:33:30 +02:00
2015-10-29 12:49:40 +01:00
private Vibrator _vibratorService;
private long _lastVibration = 0;
2015-08-03 00:01:04 +02:00
2015-10-29 12:49:40 +01:00
private Handler _handler;
private static int _currentWhat = 0;
2015-08-05 01:30:56 +02:00
2015-10-29 12:49:40 +01:00
private Config _config;
private float _keyWidth;
private Paint _keyBgPaint = new Paint();
private Paint _keyDownBgPaint = new Paint();
private Paint _keyLabelPaint;
private Paint _keySubLabelPaint;
private Paint _specialKeyLabelPaint;
private Paint _specialKeySubLabelPaint;
private int _lockedColor;
private int _labelColor;
private int _subLabelColor;
2021-04-24 23:38:29 +02:00
private float _labelTextSize;
private float _sublabelTextSize;
2015-10-29 12:49:40 +01:00
private static RectF _tmpRect = new RectF();
2015-07-30 20:14:55 +02:00
public Keyboard2View(Context context, AttributeSet attrs)
{
super(context, attrs);
2015-08-03 00:01:04 +02:00
_vibratorService = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
2015-08-05 01:30:56 +02:00
_handler = new Handler(this);
refreshConfig(((Keyboard2)context).getConfig(), null);
2015-07-30 20:14:55 +02:00
setOnTouchListener(this);
}
/* Internally calls [reset()]. */
public void refreshConfig(Config config, KeyboardData kw)
{
Resources res = getResources();
_config = config;
_lockedColor = res.getColor(R.color.key_label_locked);
_labelColor = res.getColor(R.color.key_label);
_subLabelColor = res.getColor(R.color.key_sub_label);
2021-04-24 23:38:29 +02:00
_labelTextSize = res.getDimension(R.dimen.label_text_size) * config.characterSize;
_sublabelTextSize = res.getDimension(R.dimen.sublabel_text_size) * config.characterSize;
_keyBgPaint.setColor(res.getColor(R.color.key_bg));
_keyDownBgPaint.setColor(res.getColor(R.color.key_down_bg));
2021-04-24 23:38:29 +02:00
_keyLabelPaint = initLabelPaint(Paint.Align.CENTER, null);
_keySubLabelPaint = initLabelPaint(Paint.Align.LEFT, null);
Typeface specialKeysFont = ((Keyboard2)getContext()).getSpecialKeyFont();
2021-04-24 23:38:29 +02:00
_specialKeyLabelPaint = initLabelPaint(Paint.Align.CENTER, specialKeysFont);
_specialKeySubLabelPaint = initLabelPaint(Paint.Align.LEFT, specialKeysFont);
if (kw != null)
setKeyboard(kw); // handle layout options then calls reset().
}
2021-04-24 23:38:29 +02:00
private Paint initLabelPaint(Paint.Align align, Typeface font)
2015-10-13 00:02:34 +02:00
{
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setTextAlign(align);
if (font != null)
paint.setTypeface(font);
2015-10-13 00:02:34 +02:00
return (paint);
}
public void setKeyboard(KeyboardData kw)
2015-10-11 15:30:39 +02:00
{
if (!_config.shouldOfferSwitchingToNextInputMethod)
kw = kw.removeKeys(new KeyboardData.RemoveKeysByEvent(KeyValue.EVENT_CHANGE_METHOD));
if (_config.disableAccentKeys)
kw = kw.removeKeys(new KeyboardData.RemoveKeysByFlags(KeyValue.FLAGS_ACCENTS));
_keyboard = kw;
reset();
2015-08-08 16:47:22 +02:00
}
public void reset()
{
_flags = 0;
_downKeys.clear();
requestLayout();
invalidate();
2015-07-30 20:14:55 +02:00
}
@Override
public boolean onTouch(View v, MotionEvent event)
{
float x;
float y;
2015-07-30 22:30:29 +02:00
float keyW;
int p;
2015-07-30 20:14:55 +02:00
2015-07-31 01:14:35 +02:00
switch (event.getActionMasked())
{
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
onTouchUp(event.getPointerId(event.getActionIndex()));
break ;
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
p = event.getActionIndex();
2015-07-31 01:14:35 +02:00
onTouchDown(event.getX(p), event.getY(p), event.getPointerId(p));
break ;
case MotionEvent.ACTION_MOVE:
for (p = 0; p < event.getPointerCount(); p++)
onTouchMove(event.getX(p), event.getY(p), event.getPointerId(p));
break ;
2015-07-31 01:14:35 +02:00
default:
return (false);
}
return (true);
}
private KeyDown getKeyDown(int pointerId)
{
for (KeyDown k : _downKeys)
{
if (k.pointerId == pointerId)
return (k);
}
return (null);
}
2015-08-02 19:56:23 +02:00
private KeyDown getKeyDown(KeyboardData.Key key)
{
for (KeyDown k : _downKeys)
{
if (k.key == key)
2015-08-02 19:56:23 +02:00
return (k);
}
2015-08-02 19:56:23 +02:00
return (null);
}
private void onTouchMove(float moveX, float moveY, int pointerId)
{
2015-08-03 15:11:11 +02:00
KeyDown key = getKeyDown(pointerId);
KeyValue newValue;
2015-08-03 15:11:11 +02:00
if (key != null)
2015-08-03 00:01:04 +02:00
{
2015-08-03 15:11:11 +02:00
moveX -= key.downX;
moveY -= key.downY;
float absDist = Math.abs(moveX) + Math.abs(moveY);
key.ptrDist = absDist;
if (absDist < _config.subValueDist)
2015-08-03 15:11:11 +02:00
newValue = key.key.key0;
else if (moveX < 0)
newValue = (moveY < 0) ? key.key.key1 : key.key.key3;
else if (moveY < 0)
newValue = key.key.key2;
else
newValue = key.key.key4;
if (newValue != null && newValue != key.value)
{
2015-08-05 01:30:56 +02:00
if (key.timeoutWhat != -1)
2015-08-03 15:11:11 +02:00
{
2015-08-05 01:30:56 +02:00
_handler.removeMessages(key.timeoutWhat);
if ((newValue.flags & KeyValue.FLAG_NOREPEAT) == 0)
2015-10-29 12:49:40 +01:00
_handler.sendEmptyMessageDelayed(key.timeoutWhat, _config.longPressTimeout);
2015-08-03 15:11:11 +02:00
}
2015-08-05 01:30:56 +02:00
key.value = newValue;
key.flags = newValue.flags;
2015-08-05 01:30:56 +02:00
updateFlags();
2015-08-18 01:25:47 +02:00
invalidate();
handleKeyDown(newValue);
2015-08-03 15:11:11 +02:00
}
2015-08-03 00:01:04 +02:00
}
}
private void onTouchDown(float touchX, float touchY, int pointerId)
2015-07-31 01:14:35 +02:00
{
float y = _config.marginTop - _config.keyHeight;
for (KeyboardData.Row row : _keyboard.rows)
2015-07-30 20:14:55 +02:00
{
2015-10-29 12:49:40 +01:00
y += _config.keyHeight;
if (touchY < y || touchY >= (y + _config.keyHeight))
2015-07-31 01:14:35 +02:00
continue ;
float x = _config.horizontalMargin;
for (KeyboardData.Key key : row.keys)
2015-07-30 20:14:55 +02:00
{
x += key.shift * _keyWidth;
float keyW = _keyWidth * key.width;
if (touchX >= x && touchX < (x + keyW))
2015-07-30 22:30:29 +02:00
{
int what = _currentWhat++;
if (key.key0 != null && (key.key0.flags & KeyValue.FLAG_NOREPEAT) == 0)
_handler.sendEmptyMessageDelayed(what, _config.longPressTimeout);
_downKeys.add(new KeyDown(pointerId, key, touchX, touchY, what));
handleKeyDown(key.key0);
2015-08-01 23:54:38 +02:00
updateFlags();
invalidate();
return ;
2015-07-30 22:30:29 +02:00
}
x += keyW;
2015-07-30 20:14:55 +02:00
}
}
2015-07-31 01:14:35 +02:00
}
// Whether a key is already activated (key down but pointer up)
private KeyDown getActivatedKey(KeyValue kv)
{
for (KeyDown k : _downKeys)
{
if (k.value == kv && k.pointerId == -1)
return (k);
}
return (null);
}
2015-07-31 01:14:35 +02:00
private void onTouchUp(int pointerId)
{
KeyDown k = getKeyDown(pointerId);
2015-08-01 16:33:30 +02:00
if (k != null)
2015-08-01 16:33:30 +02:00
{
// Stop key repeat
if (k.timeoutWhat != -1)
{
_handler.removeMessages(k.timeoutWhat);
k.timeoutWhat = -1;
}
KeyDown k_on = getActivatedKey(k.value);
if (k_on != null)
{
_downKeys.remove(k); // Remove dupplicate
// Same key with FLAG_LOCK is already on, do lock
if ((k_on.flags & KeyValue.FLAG_LOCK) != 0)
{
k_on.flags ^= KeyValue.FLAG_LOCK; // Next time, disable it
k_on.flags |= KeyValue.FLAG_LOCKED;
}
// Otherwise, toggle it
else
{
_downKeys.remove(k_on);
}
}
// Key stay activated
else if ((k.flags & KeyValue.FLAG_KEEP_ON) != 0)
{
k.pointerId = -1; // Set pointer up
}
else // Regular key up
{
for (int i = 0; i < _downKeys.size(); i++)
{
KeyDown downKey = _downKeys.get(i);
// Disable other activated keys that aren't locked
if (downKey.pointerId == -1 && (downKey.flags & KeyValue.FLAG_LOCKED) == 0)
_downKeys.remove(i--);
// Other keys currently down won't stay activated
else if ((downKey.flags & KeyValue.FLAG_KEEP_ON) != 0)
downKey.flags ^= KeyValue.FLAG_KEEP_ON;
}
_downKeys.remove(k);
handleKeyUp(k);
}
updateFlags();
invalidate();
2015-07-31 01:14:35 +02:00
}
2015-07-30 20:14:55 +02:00
}
private void handleKeyUp(KeyDown key)
{
if (key.value != null && (key.flags & (KeyValue.FLAG_LOCKED | KeyValue.FLAG_NOCHAR)) == 0)
((Keyboard2)getContext()).handleKeyUp(key.value, _flags);
}
private void handleKeyDown(KeyValue key)
{
2015-10-28 20:56:28 +01:00
if (key == null)
return ;
vibrate();
}
2015-08-01 23:54:38 +02:00
private void updateFlags()
{
_flags = 0;
for (KeyDown k : _downKeys)
_flags |= k.flags;
}
2015-08-03 00:01:04 +02:00
private void vibrate()
{
2015-10-29 12:49:40 +01:00
if (!_config.vibrateEnabled)
2015-08-08 15:26:23 +02:00
return ;
long now = System.currentTimeMillis();
2015-08-03 00:01:04 +02:00
if ((now - _lastVibration) > VIBRATE_MIN_INTERVAL)
{
_lastVibration = now;
try
{
2015-10-29 12:49:40 +01:00
_vibratorService.vibrate(_config.vibrateDuration);
2015-08-03 00:01:04 +02:00
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
2015-08-05 01:30:56 +02:00
@Override
public boolean handleMessage(Message msg)
{
for (KeyDown key : _downKeys)
{
if (key.timeoutWhat == msg.what)
{
long nextInterval = _config.longPressInterval;
2021-04-20 00:34:21 +02:00
if (_config.preciseRepeat && (key.flags & KeyValue.FLAG_PRECISE_REPEAT) != 0)
{
// Modulate repeat interval depending on the distance of the pointer
float accel = Math.min(4.f, Math.max(0.3f, key.ptrDist / (_config.subValueDist * 15.f)));
nextInterval = (long)((float)nextInterval / accel);
}
_handler.sendEmptyMessageDelayed(msg.what, nextInterval);
2015-10-24 16:32:49 +02:00
((Keyboard2)getContext()).handleKeyUp(key.value, _flags);
2015-08-05 01:30:56 +02:00
return (true);
}
}
return (false);
}
2015-07-30 20:14:55 +02:00
@Override
public void onMeasure(int wSpec, int hSpec)
{
DisplayMetrics dm = getContext().getResources().getDisplayMetrics();
int height;
height = (int)(_config.keyHeight * _keyboard.keysHeight
+ _keyboard.rows.size() * _config.keyVerticalInterval
+ _config.marginTop + _config.marginBottom);
setMeasuredDimension(dm.widthPixels, height);
_keyWidth = (getWidth() - (_config.horizontalMargin * 2)) / _keyboard.keysWidth;
2015-07-30 20:14:55 +02:00
}
@Override
protected void onDraw(Canvas canvas)
{
float y = _config.marginTop;
for (KeyboardData.Row row : _keyboard.rows)
2015-07-30 20:14:55 +02:00
{
y += row.shift * _config.keyHeight;
float x = _config.horizontalMargin;
float keyH = row.height * _config.keyHeight;
for (KeyboardData.Key k : row.keys)
2015-07-30 20:14:55 +02:00
{
x += k.shift * _keyWidth + _config.keyHorizontalInterval;
float keyW = _keyWidth * k.width - _config.keyHorizontalInterval;
2015-08-02 20:21:53 +02:00
KeyDown keyDown = getKeyDown(k);
_tmpRect.set(x, y, x + keyW, y + keyH);
2015-08-02 20:21:53 +02:00
if (keyDown != null)
2015-10-03 00:19:17 +02:00
canvas.drawRect(_tmpRect, _keyDownBgPaint);
2015-07-30 22:30:29 +02:00
else
2015-10-29 12:49:40 +01:00
canvas.drawRoundRect(_tmpRect, _config.keyRound, _config.keyRound, _keyBgPaint);
2015-07-30 20:14:55 +02:00
if (k.key0 != null)
drawLabel(canvas, k.key0, keyW / 2f + x, (keyH + _labelTextSize) / 2f + y,
2015-10-13 00:02:34 +02:00
(keyDown != null && (keyDown.flags & KeyValue.FLAG_LOCKED) != 0));
float subPadding = _config.keyPadding;
2015-07-30 20:14:55 +02:00
if (k.key1 != null)
2015-10-13 00:02:34 +02:00
drawSubLabel(canvas, k.key1, x + subPadding, y + subPadding - _keySubLabelPaint.ascent(), false);
2015-07-30 20:14:55 +02:00
if (k.key3 != null)
drawSubLabel(canvas, k.key3, x + subPadding, y + keyH - subPadding - _keySubLabelPaint.descent(), false);
2015-08-03 15:11:11 +02:00
if (k.key2 != null)
drawSubLabel(canvas, k.key2, x + keyW - subPadding, y + subPadding - _keySubLabelPaint.ascent(), true);
2015-07-30 20:14:55 +02:00
if (k.key4 != null)
drawSubLabel(canvas, k.key4, x + keyW - subPadding, y + keyH - subPadding - _keySubLabelPaint.descent(), true);
x += keyW;
2015-07-30 20:14:55 +02:00
}
y += keyH + _config.keyVerticalInterval;
2015-07-30 20:14:55 +02:00
}
}
@Override
public void onDetachedFromWindow()
{
super.onDetachedFromWindow();
}
2015-10-13 00:02:34 +02:00
private void drawLabel(Canvas canvas, KeyValue k, float x, float y, boolean locked)
{
Paint p = ((k.flags & KeyValue.FLAG_KEY_FONT) != 0) ? _specialKeyLabelPaint : _keyLabelPaint;
p.setColor(locked ? _lockedColor : _labelColor);
2021-04-24 23:38:29 +02:00
p.setTextSize(_labelTextSize * scaleTextSize(k));
k = KeyModifier.handleFlags(k, _flags);
canvas.drawText(k.symbol, x, y, p);
2015-10-13 00:02:34 +02:00
}
private void drawSubLabel(Canvas canvas, KeyValue k, float x, float y, boolean right)
{
Paint p = ((k.flags & KeyValue.FLAG_KEY_FONT) != 0) ? _specialKeySubLabelPaint : _keySubLabelPaint;
p.setColor(_subLabelColor);
p.setTextAlign(right ? Paint.Align.RIGHT : Paint.Align.LEFT);
2021-04-24 23:38:29 +02:00
p.setTextSize(_sublabelTextSize * scaleTextSize(k));
k = KeyModifier.handleFlags(k, _flags);
canvas.drawText(k.symbol, x, y, p);
2015-10-13 00:02:34 +02:00
}
2021-04-24 23:38:29 +02:00
private float scaleTextSize(KeyValue k)
{
return (k.symbol.length() < 2) ? 1.f : 0.8f;
}
2015-10-23 14:22:43 +02:00
private static class KeyDown
{
/* -1 if pointer is up. */
public int pointerId;
public KeyValue value;
public KeyboardData.Key key;
public float downX;
public float downY;
/* Manhattan distance of the pointer to the center of the key */
public float ptrDist;
2015-08-01 23:54:38 +02:00
public int flags;
2015-08-05 01:30:56 +02:00
public int timeoutWhat;
2015-08-05 01:30:56 +02:00
public KeyDown(int pointerId, KeyboardData.Key key, float x, float y, int what)
{
this.pointerId = pointerId;
2015-08-05 01:30:56 +02:00
value = key.key0;
this.key = key;
2015-08-01 21:36:40 +02:00
downX = x;
downY = y;
ptrDist = 0.f;
flags = (value == null) ? 0 : value.flags;
2015-08-05 01:30:56 +02:00
timeoutWhat = what;
}
}
2015-07-30 20:14:55 +02:00
}