LongClickEraser generalized

This commit is contained in:
serso 2016-01-22 23:15:03 +01:00
parent a18f3210c3
commit bbaedc71c9
6 changed files with 117 additions and 226 deletions

View File

@ -37,10 +37,7 @@ import android.widget.TextView;
import org.solovyev.android.Views; import org.solovyev.android.Views;
import org.solovyev.android.calculator.history.History; import org.solovyev.android.calculator.history.History;
import org.solovyev.android.calculator.history.HistoryDragProcessor; import org.solovyev.android.calculator.history.HistoryDragProcessor;
import org.solovyev.android.calculator.view.AngleUnitsButton; import org.solovyev.android.calculator.view.*;
import org.solovyev.android.calculator.view.LongClickEraser;
import org.solovyev.android.calculator.view.NumeralBasesButton;
import org.solovyev.android.calculator.view.ViewsCache;
import org.solovyev.android.views.dragbutton.DirectionDragButton; import org.solovyev.android.views.dragbutton.DirectionDragButton;
import org.solovyev.android.views.dragbutton.DragButton; import org.solovyev.android.views.dragbutton.DragButton;
import org.solovyev.android.views.dragbutton.DragDirection; import org.solovyev.android.views.dragbutton.DragDirection;
@ -216,7 +213,7 @@ public abstract class BaseUi implements SharedPreferences.OnSharedPreferenceChan
final View eraseButton = getButton(views, R.id.cpp_button_erase); final View eraseButton = getButton(views, R.id.cpp_button_erase);
if (eraseButton != null) { if (eraseButton != null) {
LongClickEraser.createAndAttach(eraseButton); EditorLongClickEraser.attachTo(eraseButton);
} }
clearButton = getButton(views, R.id.cpp_button_clear); clearButton = getButton(views, R.id.cpp_button_clear);

View File

@ -1,6 +1,5 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Color; import android.graphics.Color;
@ -12,7 +11,6 @@ import android.support.annotation.IdRes;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.GestureDetector;
import android.view.HapticFeedbackConstants; import android.view.HapticFeedbackConstants;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
@ -23,6 +21,7 @@ import android.widget.ImageButton;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import org.solovyev.android.calculator.view.EditTextLongClickEraser;
import org.solovyev.android.views.dragbutton.DirectionDragButton; import org.solovyev.android.views.dragbutton.DirectionDragButton;
import org.solovyev.android.views.dragbutton.DragButton; import org.solovyev.android.views.dragbutton.DragButton;
import org.solovyev.android.views.dragbutton.DragDirection; import org.solovyev.android.views.dragbutton.DragDirection;
@ -77,7 +76,7 @@ public class KeyboardUi {
addButton(row, 0, "6"); addButton(row, 0, "6");
addOperationButton(row, R.id.cpp_kb_button_divide, "/").setText("%", up).setText("sqrt", down); addOperationButton(row, R.id.cpp_kb_button_divide, "/").setText("%", up).setText("sqrt", down);
final View backspace = addImageButton(row, R.id.cpp_kb_button_backspace, R.drawable.ic_backspace_white_24dp); final View backspace = addImageButton(row, R.id.cpp_kb_button_backspace, R.drawable.ic_backspace_white_24dp);
LongClickEraser.createAndAttach(backspace, user.getEditor()); EditTextLongClickEraser.attachTo(backspace, user.getEditor());
row = makeRow(); row = makeRow();
addButton(row, 0, "1"); addButton(row, 0, "1");
@ -290,125 +289,4 @@ public class KeyboardUi {
return true; return true;
} }
} }
public static final class LongClickEraser implements View.OnTouchListener, View.OnClickListener {
@NonNull
private final View view;
@NonNull
private final EditText editText;
@NonNull
private final GestureDetector gestureDetector;
@NonNull
private final Eraser eraser = new Eraser();
private LongClickEraser(@NonNull final View view, @NonNull EditText editText) {
this.view = view;
this.editText = editText;
this.gestureDetector = new GestureDetector(view.getContext(), new GestureDetector.SimpleOnGestureListener() {
public void onLongPress(MotionEvent e) {
if (eraser.isTracking()) {
eraser.start();
}
}
});
}
public static void createAndAttach(@NonNull View view, @NonNull EditText editText) {
final LongClickEraser l = new LongClickEraser(view, editText);
view.setOnClickListener(l);
view.setOnTouchListener(l);
}
private static void erase(@NonNull EditText editText) {
final int start = clampSelection(editText.getSelectionStart());
final int end = clampSelection(editText.getSelectionEnd());
if (start != end) {
editText.getText().delete(Math.min(start, end), Math.max(start, end));
} else if (start > 0) {
editText.getText().delete(start - 1, start);
}
}
public static int clampSelection(int selection) {
return selection < 0 ? 0 : selection;
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
eraser.stopTracking();
break;
default:
eraser.startTracking();
gestureDetector.onTouchEvent(event);
break;
}
return false;
}
@Override
public void onClick(View v) {
erase(editText);
v.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
}
private class Eraser implements Runnable {
private static final int DELAY = 300;
private long delay;
private boolean erasing;
private boolean tracking = true;
@Override
public void run() {
erase(editText);
if (editText.length() == 0 || clampSelection(editText.getSelectionStart()) == 0) {
stop();
return;
}
delay = Math.max(50, 2 * delay / 3);
view.postDelayed(this, delay);
}
void start() {
if (erasing) {
stop();
}
erasing = true;
delay = DELAY;
view.removeCallbacks(this);
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
run();
}
void stop() {
view.removeCallbacks(this);
if (!erasing) {
return;
}
erasing = false;
}
public void stopTracking() {
stop();
tracking = false;
}
public boolean isTracking() {
return tracking;
}
public void startTracking() {
tracking = true;
}
}
}
} }

View File

@ -5,17 +5,9 @@ import android.view.HapticFeedbackConstants;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import org.solovyev.android.calculator.App;
import org.solovyev.android.calculator.Calculator;
import org.solovyev.android.calculator.Editor;
import org.solovyev.android.calculator.EditorState;
import org.solovyev.android.calculator.Locator;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import static android.text.TextUtils.isEmpty; public abstract class BaseLongClickEraser implements View.OnTouchListener {
public final class LongClickEraser implements View.OnTouchListener {
@Nonnull @Nonnull
private final View view; private final View view;
@ -23,16 +15,10 @@ public final class LongClickEraser implements View.OnTouchListener {
@Nonnull @Nonnull
private final GestureDetector gestureDetector; private final GestureDetector gestureDetector;
@Nonnull
private final Editor editor = App.getEditor();
@Nonnull
private final Calculator calculator = Locator.getInstance().getCalculator();
@Nonnull @Nonnull
private final Eraser eraser = new Eraser(); private final Eraser eraser = new Eraser();
private LongClickEraser(@Nonnull final View view) { protected BaseLongClickEraser(@Nonnull final View view) {
this.view = view; this.view = view;
this.gestureDetector = new GestureDetector(view.getContext(), new GestureDetector.SimpleOnGestureListener() { this.gestureDetector = new GestureDetector(view.getContext(), new GestureDetector.SimpleOnGestureListener() {
public void onLongPress(MotionEvent e) { public void onLongPress(MotionEvent e) {
@ -41,11 +27,7 @@ public final class LongClickEraser implements View.OnTouchListener {
} }
} }
}); });
} this.view.setOnTouchListener(this);
public static void createAndAttach(@Nonnull View view) {
final LongClickEraser l = new LongClickEraser(view);
view.setOnTouchListener(l);
} }
@Override @Override
@ -63,17 +45,21 @@ public final class LongClickEraser implements View.OnTouchListener {
return false; return false;
} }
protected abstract void onStopErase();
protected abstract void onStartErase();
protected abstract boolean erase();
private class Eraser implements Runnable { private class Eraser implements Runnable {
private static final int DELAY = 300; private static final int DELAY = 300;
private long delay; private long delay;
private boolean erasing; private boolean erasing;
private boolean tracking = true; private boolean tracking = true;
private boolean wasCalculatingOnFly;
@Override @Override
public void run() { public void run() {
final EditorState state = editor.erase(); if (!erase()) {
if (isEmpty(state.text)) {
stop(); stop();
return; return;
} }
@ -89,10 +75,7 @@ public final class LongClickEraser implements View.OnTouchListener {
delay = DELAY; delay = DELAY;
view.removeCallbacks(this); view.removeCallbacks(this);
view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
wasCalculatingOnFly = calculator.isCalculateOnFly(); onStartErase();
if (wasCalculatingOnFly) {
calculator.setCalculateOnFly(false);
}
run(); run();
} }
@ -103,9 +86,7 @@ public final class LongClickEraser implements View.OnTouchListener {
} }
erasing = false; erasing = false;
if (wasCalculatingOnFly) { onStopErase();
calculator.setCalculateOnFly(true);
}
} }
public void stopTracking() { public void stopTracking() {

View File

@ -1,66 +0,0 @@
/*
* Copyright 2013 serso aka se.solovyev
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Contact details
*
* Email: se.solovyev@gmail.com
* Site: http://se.solovyev.org
*/
package org.solovyev.android.calculator.view;
import android.content.SharedPreferences;
import android.os.Vibrator;
import org.solovyev.android.views.dragbutton.DragButton;
import org.solovyev.android.views.dragbutton.DragEvent;
import org.solovyev.android.views.dragbutton.DragListener;
import org.solovyev.android.views.dragbutton.DragListenerWrapper;
import org.solovyev.android.view.VibratorContainer;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* User: serso
* Date: 4/20/12
* Time: 3:27 PM
*/
public class DragListenerVibrator extends DragListenerWrapper {
private static final float VIBRATION_TIME_SCALE = 0.5f;
@Nonnull
private final VibratorContainer vibrator;
public DragListenerVibrator(@Nonnull DragListener dragListener,
@Nullable Vibrator vibrator,
@Nonnull SharedPreferences preferences) {
super(dragListener);
this.vibrator = new VibratorContainer(vibrator, preferences, VIBRATION_TIME_SCALE);
}
@Override
public boolean onDrag(@Nonnull DragButton dragButton, @Nonnull DragEvent event) {
boolean consumed = super.onDrag(dragButton, event);
if (consumed) {
vibrator.vibrate();
}
return consumed;
}
}

View File

@ -0,0 +1,54 @@
package org.solovyev.android.calculator.view;
import android.text.Editable;
import android.view.HapticFeedbackConstants;
import android.view.View;
import android.widget.EditText;
import javax.annotation.Nonnull;
public class EditTextLongClickEraser extends BaseLongClickEraser implements View.OnClickListener {
@Nonnull
private final EditText editView;
private EditTextLongClickEraser(@Nonnull View view, @Nonnull EditText editView) {
super(view);
this.editView = editView;
view.setOnClickListener(this);
}
public static void attachTo(@Nonnull View view, @Nonnull EditText editView) {
new EditTextLongClickEraser(view, editView);
}
@Override
protected void onStopErase() {
}
@Override
protected void onStartErase() {
}
@Override
protected boolean erase() {
final int start = editView.getSelectionStart();
final int end = editView.getSelectionEnd();
if (start < 0 || end < 0) {
return false;
}
final Editable text = editView.getText();
if (start != end) {
text.delete(Math.min(start, end), Math.max(start, end));
} else if (start > 0) {
text.delete(start - 1, start);
}
return text.length() != 0;
}
@Override
public void onClick(View v) {
erase();
v.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
}
}

View File

@ -0,0 +1,47 @@
package org.solovyev.android.calculator.view;
import android.view.View;
import org.solovyev.android.calculator.*;
import javax.annotation.Nonnull;
import static android.text.TextUtils.isEmpty;
public class EditorLongClickEraser extends BaseLongClickEraser {
@Nonnull
private final Editor editor = App.getEditor();
@Nonnull
private final Calculator calculator = Locator.getInstance().getCalculator();
private boolean wasCalculatingOnFly;
private EditorLongClickEraser(@Nonnull View view) {
super(view);
}
public static void attachTo(@Nonnull View view) {
new EditorLongClickEraser(view);
}
protected boolean erase() {
final EditorState state = editor.erase();
return !isEmpty(state.text);
}
@Override
protected void onStartErase() {
wasCalculatingOnFly = calculator.isCalculateOnFly();
if (wasCalculatingOnFly) {
calculator.setCalculateOnFly(false);
}
}
@Override
protected void onStopErase() {
if (wasCalculatingOnFly) {
calculator.setCalculateOnFly(true);
}
}
}