added sending of historic events and added exit dialog to AndroidHarness

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7642 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
kim..ng 14 years ago
parent 5135400a9f
commit 026b7f2211
  1. 62
      engine/src/android/com/jme3/app/AndroidHarness.java
  2. 55
      engine/src/android/com/jme3/app/android/AndroidApplication.java
  3. 216
      engine/src/android/com/jme3/input/android/AndroidInput.java

@ -11,7 +11,12 @@ import android.view.Window;
import android.view.WindowManager; import android.view.WindowManager;
import com.jme3.app.Application; import com.jme3.app.Application;
import com.jme3.app.android.AndroidApplication;
import com.jme3.input.TouchInput;
import com.jme3.input.android.AndroidInput; import com.jme3.input.android.AndroidInput;
import com.jme3.input.controls.TouchListener;
import com.jme3.input.controls.TouchTrigger;
import com.jme3.input.event.TouchEvent;
import com.jme3.system.AppSettings; import com.jme3.system.AppSettings;
import com.jme3.system.JmeSystem; import com.jme3.system.JmeSystem;
import com.jme3.system.android.OGLESContext; import com.jme3.system.android.OGLESContext;
@ -22,7 +27,7 @@ import com.jme3.system.android.OGLESContext;
* @author Kirill * @author Kirill
* @author larynx * @author larynx
*/ */
public class AndroidHarness extends Activity implements DialogInterface.OnClickListener public class AndroidHarness extends Activity implements TouchListener, DialogInterface.OnClickListener
{ {
protected final static Logger logger = Logger.getLogger(AndroidHarness.class.getName()); protected final static Logger logger = Logger.getLogger(AndroidHarness.class.getName());
@ -33,6 +38,8 @@ public class AndroidHarness extends Activity implements DialogInterface.OnClickL
protected Application app = null; protected Application app = null;
protected boolean debug = false; protected boolean debug = false;
final private String ESCAPE_EVENT = "TouchEscape";
@Override @Override
public void onCreate(Bundle savedInstanceState) public void onCreate(Bundle savedInstanceState)
@ -70,7 +77,10 @@ public class AndroidHarness extends Activity implements DialogInterface.OnClickL
{ {
view = ctx.createView(input); view = ctx.createView(input);
} }
setContentView(view); setContentView(view);
app.inputManager.addMapping(ESCAPE_EVENT, new TouchTrigger(TouchInput.KEYCODE_BACK));
app.inputManager.addListener(this, new String[]{ESCAPE_EVENT});
} }
@ -154,7 +164,6 @@ public class AndroidHarness extends Activity implements DialogInterface.OnClickL
}); });
} }
/** /**
* Called by the android alert dialog, terminate the activity and OpenGL rendering * Called by the android alert dialog, terminate the activity and OpenGL rendering
@ -162,8 +171,49 @@ public class AndroidHarness extends Activity implements DialogInterface.OnClickL
* @param whichButton * @param whichButton
*/ */
public void onClick(DialogInterface dialog, int whichButton) public void onClick(DialogInterface dialog, int whichButton)
{ {
app.stop(); if (whichButton != -2)
this.finish(); {
app.stop();
this.finish();
}
} }
/**
* Gets called by the InputManager on all touch/drag/scale events
*/
@Override
public void onTouch(String name, TouchEvent evt, float tpf)
{
if (name.equals(ESCAPE_EVENT))
{
switch(evt.getType())
{
case KEY_UP:
this.runOnUiThread(new Runnable()
{
@Override
public void run()
{
AlertDialog dialog = new AlertDialog.Builder(AndroidHarness.this)
// .setIcon(R.drawable.alert_dialog_icon)
.setTitle("Do you want to exit?")
.setPositiveButton("Yes", AndroidHarness.this)
.setNegativeButton("No", AndroidHarness.this)
.setMessage("Use your home key to bring this app into the background or exit to terminate it.")
.create();
dialog.show();
}
});
break;
default:
break;
}
}
}
} }

@ -41,7 +41,11 @@ import android.content.DialogInterface;
import com.jme3.app.Application; import com.jme3.app.Application;
import com.jme3.font.BitmapFont; import com.jme3.font.BitmapFont;
import com.jme3.font.BitmapText; import com.jme3.font.BitmapText;
import com.jme3.input.TouchInput;
import com.jme3.input.android.AndroidInput; import com.jme3.input.android.AndroidInput;
import com.jme3.input.controls.TouchListener;
import com.jme3.input.controls.TouchTrigger;
import com.jme3.input.event.TouchEvent;
import com.jme3.renderer.RenderManager; import com.jme3.renderer.RenderManager;
import com.jme3.renderer.queue.RenderQueue.Bucket; import com.jme3.renderer.queue.RenderQueue.Bucket;
import com.jme3.scene.Node; import com.jme3.scene.Node;
@ -60,8 +64,8 @@ import com.jme3.app.AndroidHarness;
* *
* @deprecated Please use {@link AndroidHarness} instead. * @deprecated Please use {@link AndroidHarness} instead.
*/ */
@Deprecated @Deprecated
public abstract class AndroidApplication extends Application implements DialogInterface.OnClickListener public abstract class AndroidApplication extends Application implements DialogInterface.OnClickListener, TouchListener
{ {
protected final static Logger logger = Logger.getLogger(AndroidApplication.class.getName()); protected final static Logger logger = Logger.getLogger(AndroidApplication.class.getName());
@ -156,6 +160,9 @@ public abstract class AndroidApplication extends Application implements DialogIn
loadFPSText(); loadFPSText();
viewPort.attachScene(rootNode); viewPort.attachScene(rootNode);
guiViewPort.attachScene(guiNode); guiViewPort.attachScene(guiNode);
inputManager.addMapping("TouchEscape", new TouchTrigger(TouchInput.KEYCODE_BACK));
inputManager.addListener(this, new String[]{"TouchEscape"});
// call user code // call user code
init(); init();
@ -286,9 +293,47 @@ public abstract class AndroidApplication extends Application implements DialogIn
* @param whichButton * @param whichButton
*/ */
public void onClick(DialogInterface dialog, int whichButton) public void onClick(DialogInterface dialog, int whichButton)
{ {
this.stop(); if (whichButton != -2)
activity.finish(); {
this.stop();
activity.finish();
}
} }
/**
* Gets called by the InputManager on all touch/drag/scale events
*/
@Override
public void onTouch(String name, TouchEvent evt, float tpf)
{
switch(evt.getType())
{
case KEY_UP:
activity.runOnUiThread(new Runnable()
{
@Override
public void run()
{
AlertDialog dialog = new AlertDialog.Builder(activity)
// .setIcon(R.drawable.alert_dialog_icon)
.setTitle("Do you want to exit?")
.setPositiveButton("Yes", AndroidApplication.this)
.setNegativeButton("No", AndroidApplication.this)
.setMessage("Use your home key to bring this app into the background or Exit to terminate it.")
.create();
dialog.show();
}
});
break;
default:
break;
}
}
} }

@ -35,11 +35,13 @@ public class AndroidInput extends GLSurfaceView implements TouchInput,
final private static int MAX_EVENTS = 1024; final private static int MAX_EVENTS = 1024;
final private RingBuffer<TouchEvent> eventQueue = new RingBuffer<TouchEvent>(MAX_EVENTS); final private RingBuffer<TouchEvent> eventQueue = new RingBuffer<TouchEvent>(MAX_EVENTS);
final private RingBuffer<TouchEvent> eventPoolUnConsumed = new RingBuffer<TouchEvent>(MAX_EVENTS);
final private RingBuffer<TouchEvent> eventPool = new RingBuffer<TouchEvent>(MAX_EVENTS); final private RingBuffer<TouchEvent> eventPool = new RingBuffer<TouchEvent>(MAX_EVENTS);
final private HashMap<Integer, Vector2f> lastPositions = new HashMap<Integer, Vector2f>(); final private HashMap<Integer, Vector2f> lastPositions = new HashMap<Integer, Vector2f>();
public boolean fireMouseEvents = true; public boolean fireMouseEvents = true;
public boolean fireKeyboardEvents = false; public boolean fireKeyboardEvents = false;
public boolean dontSendHistory = false;
private ScaleGestureDetector scaledetector; private ScaleGestureDetector scaledetector;
private GestureDetector detector; private GestureDetector detector;
@ -169,33 +171,67 @@ public class AndroidInput extends GLSurfaceView implements TouchInput,
return getNextFreeTouchEvent(false); return getNextFreeTouchEvent(false);
} }
/**
* Fetches a touch event from the reuse pool
* @param wait
* @return
*/
private TouchEvent getNextFreeTouchEvent(boolean wait) private TouchEvent getNextFreeTouchEvent(boolean wait)
{ {
TouchEvent evt; TouchEvent evt = null;
if (eventPool.isEmpty() && wait) synchronized(eventPoolUnConsumed)
{ {
logger.warning("eventPool buffer underrun"); int size = eventPoolUnConsumed.size();
boolean isEmpty; while (size > 0)
do
{ {
synchronized(eventPool) evt = eventPoolUnConsumed.pop();
if (!evt.isConsumed())
{ {
isEmpty = eventPool.isEmpty(); eventPoolUnConsumed.push(evt);
evt = null;
} }
try { Thread.sleep(50); } catch (InterruptedException e) { } else
{
break;
}
size--;
} }
while (isEmpty);
evt = eventPool.pop();
} }
else if (eventPool.isEmpty())
if (evt == null)
{ {
evt = new TouchEvent(); if (eventPool.isEmpty() && wait)
logger.warning("eventPool buffer underrun"); {
logger.warning("eventPool buffer underrun");
boolean isEmpty;
do
{
synchronized(eventPool)
{
isEmpty = eventPool.isEmpty();
}
try { Thread.sleep(50); } catch (InterruptedException e) { }
}
while (isEmpty);
synchronized(eventPool)
{
evt = eventPool.pop();
}
}
else if (eventPool.isEmpty())
{
evt = new TouchEvent();
logger.warning("eventPool buffer underrun");
}
else
{
synchronized(eventPool)
{
evt = eventPool.pop();
}
}
} }
else
{
evt = eventPool.pop();
}
return evt; return evt;
} }
/** /**
@ -210,17 +246,37 @@ public class AndroidInput extends GLSurfaceView implements TouchInput,
// Try to detect gestures // Try to detect gestures
this.detector.onTouchEvent(event); this.detector.onTouchEvent(event);
this.scaledetector.onTouchEvent(event); this.scaledetector.onTouchEvent(event);
final int historySize = event.getHistorySize();
final int pointerCount = event.getPointerCount();
switch (event.getAction()) switch (event.getAction())
{ {
case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_DOWN:
if (!dontSendHistory)
{
// Process history
for (int h = 0; h < historySize; h++)
{
// Convert all pointers into events
for (int p = 0; p < pointerCount; p++)
{
touch = getNextFreeTouchEvent();
touch.set(Type.DOWN, event.getHistoricalX(p, h), this.getHeight() - event.getHistoricalY(p, h), 0, 0);
touch.setPointerId(event.getPointerId(p));
touch.setTime(event.getHistoricalEventTime(h));
touch.setPressure(event.getHistoricalPressure(p, h));
processEvent(touch);
}
}
}
// Convert all pointers into events // Convert all pointers into events
for (int p = 0; p < event.getPointerCount(); p++) for (int p = 0; p < pointerCount; p++)
{ {
touch = getNextFreeTouchEvent(); touch = getNextFreeTouchEvent();
touch.set(Type.DOWN, event.getX(p), event.getY(p), 0, 0); touch.set(Type.DOWN, event.getX(p), this.getHeight() - event.getY(p), 0, 0);
touch.setPointerId(event.getPointerId(p)); touch.setPointerId(event.getPointerId(p));
touch.setTime(event.getEventTime()); touch.setTime(event.getEventTime());
touch.setPressure(event.getPressure(p)); touch.setPressure(event.getPressure(p));
@ -232,11 +288,30 @@ public class AndroidInput extends GLSurfaceView implements TouchInput,
case MotionEvent.ACTION_UP: case MotionEvent.ACTION_UP:
if (!dontSendHistory)
{
// Process history
for (int h = 0; h < historySize; h++)
{
// Convert all pointers into events
for (int p = 0; p < pointerCount; p++)
{
touch = getNextFreeTouchEvent();
touch.set(Type.UP, event.getHistoricalX(p, h), this.getHeight() - event.getHistoricalY(p, h), 0, 0);
touch.setPointerId(event.getPointerId(p));
touch.setTime(event.getHistoricalEventTime(h));
touch.setPressure(event.getHistoricalPressure(p, h));
processEvent(touch);
}
}
}
// Convert all pointers into events // Convert all pointers into events
for (int p = 0; p < event.getPointerCount(); p++) for (int p = 0; p < pointerCount; p++)
{ {
touch = getNextFreeTouchEvent(); touch = getNextFreeTouchEvent();
touch.set(Type.UP, event.getX(p), event.getY(p), 0, 0); touch.set(Type.UP, event.getX(p), this.getHeight() - event.getY(p), 0, 0);
touch.setPointerId(event.getPointerId(p)); touch.setPointerId(event.getPointerId(p));
touch.setTime(event.getEventTime()); touch.setTime(event.getEventTime());
touch.setPressure(event.getPressure(p)); touch.setPressure(event.getPressure(p));
@ -247,22 +322,50 @@ public class AndroidInput extends GLSurfaceView implements TouchInput,
break; break;
case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_MOVE:
if (!dontSendHistory)
{
// Process history
for (int h = 0; h < historySize; h++)
{
// Convert all pointers into events
for (int p = 0; p < pointerCount; p++)
{
Vector2f lastPos = lastPositions.get(event.getPointerId(p));
if (lastPos == null)
{
lastPos = new Vector2f(event.getHistoricalX(p,h ), this.getHeight() - event.getHistoricalY(p, h));
lastPositions.put(event.getPointerId(p), lastPos);
}
touch = getNextFreeTouchEvent();
touch.set(Type.MOVE, event.getHistoricalX(p, h), this.getHeight() - event.getHistoricalY(p, h),
event.getHistoricalX(p, h) - lastPos.x, this.getHeight() - event.getHistoricalY(p, h) - lastPos.y);
touch.setPointerId(event.getPointerId(p));
touch.setTime(event.getHistoricalEventTime(h));
touch.setPressure(event.getHistoricalPressure(p, h));
processEvent(touch);
lastPos.set(event.getHistoricalX(p, h), this.getHeight() - event.getHistoricalY(p, h));
}
}
}
// Convert all pointers into events // Convert all pointers into events
for (int p = 0; p < event.getPointerCount(); p++) for (int p = 0; p < event.getPointerCount(); p++)
{ {
Vector2f lastPos = lastPositions.get(event.getPointerId(p)); Vector2f lastPos = lastPositions.get(event.getPointerId(p));
if (lastPos == null) if (lastPos == null)
{ {
lastPos = new Vector2f(event.getX(p), event.getY(p)); lastPos = new Vector2f(event.getX(p), this.getHeight() - event.getY(p));
lastPositions.put(event.getPointerId(p), lastPos); lastPositions.put(event.getPointerId(p), lastPos);
} }
touch = getNextFreeTouchEvent(); touch = getNextFreeTouchEvent();
touch.set(Type.MOVE, event.getX(p), event.getY(p), event.getX(p) - lastPos.x, event.getY(p) - lastPos.y); touch.set(Type.MOVE, event.getX(p), this.getHeight() - event.getY(p), event.getX(p) - lastPos.x, this.getHeight() - event.getY(p) - lastPos.y);
touch.setPointerId(event.getPointerId(p)); touch.setPointerId(event.getPointerId(p));
touch.setTime(event.getEventTime()); touch.setTime(event.getEventTime());
touch.setPressure(event.getPressure(p)); touch.setPressure(event.getPressure(p));
processEvent(touch); processEvent(touch);
lastPos.set(event.getX(p), event.getY(p)); lastPos.set(event.getX(p), this.getHeight() - event.getY(p));
} }
bWasHandled = true; bWasHandled = true;
break; break;
@ -296,11 +399,8 @@ public class AndroidInput extends GLSurfaceView implements TouchInput,
// Send the event // Send the event
processEvent(evt); processEvent(evt);
// Handle all keys ourself, except the back button (4) // Handle all keys ourself
if (keyCode == 4) return true;
return false;
else
return true;
} }
@Override @Override
@ -316,11 +416,8 @@ public class AndroidInput extends GLSurfaceView implements TouchInput,
// Send the event // Send the event
processEvent(evt); processEvent(evt);
// Handle all keys ourself, except the back button (4) // Handle all keys ourself
if (keyCode == 4) return true;
return false;
else
return true;
} }
@ -408,8 +505,8 @@ public class AndroidInput extends GLSurfaceView implements TouchInput,
if (fireMouseEvents) if (fireMouseEvents)
{ {
newX = getWidth() - (int) event.getX(); newX = this.getWidth() - (int) event.getX();
newY = (int) event.getY(); newY = this.getHeight() - (int) event.getY();
switch (event.getType()) switch (event.getType())
{ {
case DOWN: case DOWN:
@ -451,10 +548,22 @@ public class AndroidInput extends GLSurfaceView implements TouchInput,
} }
} }
} }
synchronized (eventPool)
if (event.isConsumed() == false)
{ {
eventPool.push(event); synchronized (eventPoolUnConsumed)
} {
eventPoolUnConsumed.push(event);
}
}
else
{
synchronized (eventPool)
{
eventPool.push(event);
}
}
} }
} }
@ -470,7 +579,7 @@ public class AndroidInput extends GLSurfaceView implements TouchInput,
public void onLongPress(MotionEvent event) public void onLongPress(MotionEvent event)
{ {
TouchEvent touch = getNextFreeTouchEvent(); TouchEvent touch = getNextFreeTouchEvent();
touch.set(Type.LONGPRESSED, event.getX(), event.getY(), 0f, 0f); touch.set(Type.LONGPRESSED, event.getX(), this.getHeight() - event.getY(), 0f, 0f);
touch.setPointerId(0); touch.setPointerId(0);
touch.setTime(event.getEventTime()); touch.setTime(event.getEventTime());
processEvent(touch); processEvent(touch);
@ -479,7 +588,7 @@ public class AndroidInput extends GLSurfaceView implements TouchInput,
public boolean onFling(MotionEvent event, MotionEvent event2, float vx, float vy) public boolean onFling(MotionEvent event, MotionEvent event2, float vx, float vy)
{ {
TouchEvent touch = getNextFreeTouchEvent(); TouchEvent touch = getNextFreeTouchEvent();
touch.set(Type.FLING, event.getX(), event.getY(), vx, vy); touch.set(Type.FLING, event.getX(), this.getHeight() - event.getY(), vx, vy);
touch.setPointerId(0); touch.setPointerId(0);
touch.setTime(event.getEventTime()); touch.setTime(event.getEventTime());
processEvent(touch); processEvent(touch);
@ -490,7 +599,7 @@ public class AndroidInput extends GLSurfaceView implements TouchInput,
public boolean onSingleTapConfirmed(MotionEvent event) public boolean onSingleTapConfirmed(MotionEvent event)
{ {
TouchEvent touch = getNextFreeTouchEvent(); TouchEvent touch = getNextFreeTouchEvent();
touch.set(Type.TAP, event.getX(), event.getY(), 0f, 0f); touch.set(Type.TAP, event.getX(), this.getHeight() - event.getY(), 0f, 0f);
touch.setPointerId(0); touch.setPointerId(0);
touch.setTime(event.getEventTime()); touch.setTime(event.getEventTime());
processEvent(touch); processEvent(touch);
@ -501,7 +610,7 @@ public class AndroidInput extends GLSurfaceView implements TouchInput,
public boolean onDoubleTap(MotionEvent event) public boolean onDoubleTap(MotionEvent event)
{ {
TouchEvent touch = getNextFreeTouchEvent(); TouchEvent touch = getNextFreeTouchEvent();
touch.set(Type.DOUBLETAP, event.getX(), event.getY(), 0f, 0f); touch.set(Type.DOUBLETAP, event.getX(), this.getHeight() - event.getY(), 0f, 0f);
touch.setPointerId(0); touch.setPointerId(0);
touch.setTime(event.getEventTime()); touch.setTime(event.getEventTime());
processEvent(touch); processEvent(touch);
@ -524,7 +633,7 @@ public class AndroidInput extends GLSurfaceView implements TouchInput,
public boolean onScale(ScaleGestureDetector scaleGestureDetector) public boolean onScale(ScaleGestureDetector scaleGestureDetector)
{ {
TouchEvent touch = getNextFreeTouchEvent(); TouchEvent touch = getNextFreeTouchEvent();
touch.set(Type.SCALE_MOVE, scaleGestureDetector.getFocusX(), scaleGestureDetector.getFocusY(), 0f, 0f); touch.set(Type.SCALE_MOVE, scaleGestureDetector.getFocusX(), this.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f);
touch.setPointerId(0); touch.setPointerId(0);
touch.setTime(scaleGestureDetector.getEventTime()); touch.setTime(scaleGestureDetector.getEventTime());
touch.setScaleSpan(scaleGestureDetector.getCurrentSpan()); touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());
@ -537,7 +646,7 @@ public class AndroidInput extends GLSurfaceView implements TouchInput,
public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) public void onScaleEnd(ScaleGestureDetector scaleGestureDetector)
{ {
TouchEvent touch = getNextFreeTouchEvent(); TouchEvent touch = getNextFreeTouchEvent();
touch.set(Type.SCALE_END, scaleGestureDetector.getFocusX(), scaleGestureDetector.getFocusY(), 0f, 0f); touch.set(Type.SCALE_END, scaleGestureDetector.getFocusX(), this.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f);
touch.setPointerId(0); touch.setPointerId(0);
touch.setTime(scaleGestureDetector.getEventTime()); touch.setTime(scaleGestureDetector.getEventTime());
touch.setScaleSpan(scaleGestureDetector.getCurrentSpan()); touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());
@ -548,7 +657,7 @@ public class AndroidInput extends GLSurfaceView implements TouchInput,
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
{ {
TouchEvent touch = getNextFreeTouchEvent(); TouchEvent touch = getNextFreeTouchEvent();
touch.set(Type.SCROLL, e1.getX(), e1.getY(), distanceX, distanceY); touch.set(Type.SCROLL, e1.getX(), this.getHeight() - e1.getY(), distanceX, distanceY*(-1));
touch.setPointerId(0); touch.setPointerId(0);
touch.setTime(e1.getEventTime()); touch.setTime(e1.getEventTime());
processEvent(touch); processEvent(touch);
@ -558,7 +667,7 @@ public class AndroidInput extends GLSurfaceView implements TouchInput,
public void onShowPress(MotionEvent event) public void onShowPress(MotionEvent event)
{ {
TouchEvent touch = getNextFreeTouchEvent(); TouchEvent touch = getNextFreeTouchEvent();
touch.set(Type.SHOWPRESS, event.getX(), event.getY(), 0f, 0f); touch.set(Type.SHOWPRESS, event.getX(), this.getHeight() - event.getY(), 0f, 0f);
touch.setPointerId(0); touch.setPointerId(0);
touch.setTime(event.getEventTime()); touch.setTime(event.getEventTime());
processEvent(touch); processEvent(touch);
@ -567,7 +676,7 @@ public class AndroidInput extends GLSurfaceView implements TouchInput,
public boolean onSingleTapUp(MotionEvent event) public boolean onSingleTapUp(MotionEvent event)
{ {
TouchEvent touch = getNextFreeTouchEvent(); TouchEvent touch = getNextFreeTouchEvent();
touch.set(Type.TAP, event.getX(), event.getY(), 0f, 0f); touch.set(Type.TAP, event.getX(), this.getHeight() - event.getY(), 0f, 0f);
touch.setPointerId(0); touch.setPointerId(0);
touch.setTime(event.getEventTime()); touch.setTime(event.getEventTime());
processEvent(touch); processEvent(touch);
@ -585,5 +694,10 @@ public class AndroidInput extends GLSurfaceView implements TouchInput,
{ {
fireKeyboardEvents = simulate; fireKeyboardEvents = simulate;
} }
@Override
public void setOmitHistoricEvents(boolean dontSendHistory)
{
this.dontSendHistory = dontSendHistory;
}
} }

Loading…
Cancel
Save