Whats the best way to implement hidden debug options? - java

I'm deploying an app in my company to keep track of production etc. Now I wonder how I should implement a hidden debug menu. In my C# programs I usually make a 2px by 2px hidden button in the bottom left corner of the main window, but this seems impractical for an android touch app. In case something goes wrong i need to have a way to open a menu that nobody, who doesnt know exactly how to open it, will ever find. I tried googling but all I found was how to hide/disable the dev settings in the android settings.
So what would be practical solutions to this problems without having to plug it into a PC or have to go to a separate app?

Here is a suggestion : listen to a multitouch pattern (only the sequence of added / removed touches, not the position).
It is easy to do in Activity.dispatchTouchEvent
without interfering with the UI, and difficult to trigger by chance.
A simple implementation (sorry it's in Kotlin, but it should be fairly easy to translate in Java) :
class DebuggableActivity : Activity() {
// This pattern means :
// 1, 2, 3, 4 : touch with 4 fingers (adding one at a time)
// 3, 2 : removes 2 any touches (again one at a time)
// 3, 2 : add, then remove one touch
val pattern = listOf(1, 2, 3, 4, 3, 2, 3, 2)
// current number of touches
val pointers = mutableSetOf<Int>()
// current position in pattern
var patternIndex = 0
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
when (ev.actionMasked) {
MotionEvent.ACTION_DOWN -> {
// new gesture, reset
pointers.clear()
patternIndex = 0
pointers.add(ev.getPointerId(ev.actionIndex))
checkPattern()
}
MotionEvent.ACTION_POINTER_DOWN -> {
// new touch
pointers.add(ev.getPointerId(ev.actionIndex))
checkPattern()
}
MotionEvent.ACTION_POINTER_UP -> {
// touch released
pointers.remove(ev.getPointerId(ev.actionIndex))
checkPattern()
}
}
return super.dispatchTouchEvent(ev)
}
fun checkPattern() {
if (pattern[patternIndex] == pointers.size) {
// progressing
patternIndex++
if (patternIndex == pattern.size) {
// triggered debug mode
patternIndex = 0
showDebugDialog()
}
} else {
// failed, reset
patternIndex = 0
}
}
fun showDebugDialog() {
AlertDialog.Builder(this)
.setTitle("Debug mode")
.setItems(arrayOf<String>("option 1", "option2", "option3"), { dialog, which ->
Log.d(TAG, "Clicked on " + which)
})
.show()
}
....
}

Inspired by the answer of #bwt, I have updated the implementation to have a true pattern based on the number of fingers.
// ---- DEBUG ----
// The number in the pattern means the number of fingers touching on the current activity.
private val pattern = listOf(1, 2, 3, 2, 1)
// current position in pattern
private var patternIndex = 0
private var shouldReadPointerCount = true
private var pointerCount = 0
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
val action: Int = event.action and MotionEvent.ACTION_MASK
if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN) {
pointerCount = 0
shouldReadPointerCount = true
} else if (action == MotionEvent.ACTION_POINTER_UP || action == MotionEvent.ACTION_UP) {
if (shouldReadPointerCount) {
pointerCount = event.pointerCount
shouldReadPointerCount = false
}
if (event.pointerCount == pointerCount) {
Log.i(TAG, pointerCount.toString())
checkPattern(pointerCount)
}
}
return super.dispatchTouchEvent(event)
}
private fun checkPattern(count: Int) {
if (pattern[patternIndex] == count) {
patternIndex++
if (patternIndex == pattern.size) {
patternIndex = 0
showDebugDialog()
}
} else {
patternIndex = 0
}
}

My suggestion: after some screen element gets clicked N times you then open an input dialog and ask for a secret that leads to the hidden debug screen. You can create an "About" menu that directs to a not-so-usefull screen with the name and version of the App and program the "magical clicks" on some label or image on that screen.

#bwt's example translated into Java:
// Variables required
// Change pattern to indicate the sequence of touches required to activate
// the secret action.
private int[] pattern = {1, 2, 3, 4, 3, 2, 3, 2};
private HashSet pointers = new HashSet();
private int patternIndex = 0;
// We override dispatchTouchEvent and count the number of fingers down
// based on the values in pattern. See:
// https://stackoverflow.com/questions/48441348/whats-the-best-way-to-implement-hidden-debug-options
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch(ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
// new gesture, reset
pointers.clear();
patternIndex = 0;
pointers.add(Integer.valueOf(ev.getPointerId(ev.getActionIndex())));
checkPattern();
break;
case MotionEvent.ACTION_POINTER_DOWN:
// new touch
pointers.add(Integer.valueOf(ev.getPointerId(ev.getActionIndex())));
checkPattern();
break;
case MotionEvent.ACTION_POINTER_UP:
// touch released
pointers.remove(Integer.valueOf(ev.getPointerId(ev.getActionIndex())));
checkPattern();
}
return super.dispatchTouchEvent(ev);
}
private void checkPattern() {
if (pattern[patternIndex] == pointers.size()) {
// progressing
patternIndex++;
if (patternIndex == pattern.length) {
// triggered, show debug screen
patternIndex = 0;
showDebugScreen();
}
} else {
// reset
patternIndex = 0;
}
}
private void showDebugScreen() {
// Open your diagnostic screen here.
}
An interesting enhancement is to check the value of a text widget in showDebugScreen, and if a particular value isn't there, return. That helps to reduce the chance that a user will stumble on it accidentally by drumming on the phone.

Related

How to wait on a button for user input in order to set a variable

I am writing a class that has certain variables that don't need to be set except in certain fringe cases. As such, I am working on writing a requestVariable() method that asks for user input to set that variable when it is needed. However, I am struggling to figure out how to wait for that input before moving on. Let me show you what I have.
SkillApplication AR_D_Atk_Def_1_app = (Unit) -> {
if (Unit.getAttackStatus() != com.codecademy.myapplication.Unit.AttackStatus.DEFENDER) {
return;
}
else {
// Make sure structuresAR is set
Unit.setStructuresAR(requestVariable( /* some parameters */ );
int X;
if (Unit.getStructuresAR() >= 5) {
X = 4;
}
else if (Unit.getStructuresAR() == 4) {
X = 3;
}
else if (Unit.getStructuresAR() == 3) {
X = 2;
}
else {
X = 1;
}
Unit.addCombatAtk(X);
Unit.addCombatDef(X);
}
};
This is a lambda function for a certain skill's application. If this skill needs to be applied, it will run this lambda function. This is one of the fringe cases where the member "structuresAR" of Unit needs to be used. It's very rarely used, and rather than having the user set it every time, I have it set in this lambda function.
VariableRequest<Integer> requestInteger = (VariableRequest<Integer>) (questionMessage, choices, layout) -> {
final Integer[] retVal = new Integer[1];
TextView questionView = new TextView(layout.getContext());
questionView.setText(questionMessage);
EditText textEntry = new EditText(layout.getContext());
textEntry.setInputType(InputType.TYPE_CLASS_NUMBER);
Button submitButton = new Button(layout.getContext());
submitButton.setText("Submit");
layout.addView(questionView);
layout.addView(textEntry);
layout.addView(submitButton);
submitButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
retVal[0] = Integer.parseInt(String.valueOf(textEntry.getText()));
}
});
};
Here's what I have written so far for that function. It sets a question, options, and submit button to a layout that updates a return value with what is in the entry box when a button is clicked.
This problem is, this just keeps going. The rest of whatever I've written will be run while the onClickListener is still there, and I don't know how to wait until that's been clicked. I'm coming from C++ knowledge where just writing cin >> variable would pause and wait for you to enter. I'm trying to replicate that with a button.
There's also other problems I can spot with this such as getting the layout from inside a static method, but I struggle to come up with another method as I'm very new to Android development.

Playing snake game using reinforcement learning and DQN (with deeplearning4j)

I am trying to make an AI to play simple Snake game following this article. With my current solution snake is able to come up to score of around 35 points. For some reason it will handle the walls correctly but would almost every time die by hitting its own tail.
I am not able to pinpoint my mistake, but I assume its the way I use fit function on the neural network.
for (int i = 0; i < EPOCHS; i++) {
epsilon = 0.9;
board.initGame();
State state = board.getState();
while (state.isInGame()) {
// Get next action to perform
final Action action = epsilonGreedyAction(state);
// Play action and get reward
final double reward = board.getRewardForAction(state, action);
// Get next state
final State nextState = board.getState();
// Update neural network
update(state, action, reward, nextState);
// Apply next state
state = nextState;
}
}
private Action epsilonGreedyAction(final State state) {
epsilon -=0.001;
double random = SnakeUtil.getRandom();
if (random < epsilon) {
randomActions++;
return Action.randomAction(state);
}
return SnakeUtil.getMaxQAction(state, Q_TABLE);
}
getMaxQAction will return any action that is in the Q_TABLE for this state that has had high reward up until now (max reward).
Q_TABLE is updated also in the update method
private void update(final State state, final Action action, final double reward, final State nextState) {
MaxQ maxQ = SnakeUtil.getMaxQ(nextState, Q_TABLE);
double targetReward = reward + (0.90 * maxQ.getReward());
SnakeUtil.updateQTable(state, action, targetReward, Q_TABLE);
net.fit(buildObservation(state).getData(), Nd4j.create(fromAction(action)));
}
private double[][] fromAction(final Action action) {
return new double[][] {
{
action.equals(Action.UP) ? 1 : 0,
action.equals(Action.RIGHT) ? 1 : 0,
action.equals(Action.DOWN) ? 1 : 0,
action.equals(Action.LEFT) ? 1 : 0
}
};
}
private Observation buildObservation(final State state) {
return new Observation(Nd4j.create(new boolean[][]{
{
state.isCanGoUp(),
state.isCanGoRight(),
state.isCanGoDown(),
state.isCanGoLeft(),
state.isFoodUp(),
state.isFoodUpRight(),
state.isFoodRight(),
state.isFoodDownRight(),
state.isFoodDown(),
state.isFoodDownLeft(),
state.isFoodLeft(),
state.isFoodUpLeft()
}
}));
}
My question is if the fit method is receiving correct parameters. If yes then my problem must be somwhere else.
In addition reward is calculated in a way that every time when snake steps in direction of the food its rewarded with 1, otherwise it's punished with -1.5. If it eats the food it's rewarded with 30.
Any hints and suggestions are welcome.

How to make one way platform on which i can come down if button pressed

So, what i want:
If my player's body linear velocity.y > 0 then move through platform.
If my player's body colliding with platform and button "DOWN" pressed, move down through it
What i've tried:
switch (fixA.getFilterData().categoryBits | fixB.getFilterData().categoryBits) {
case Game.PLATFORM_BIT | Game.PLAYER_BIT:
if (playerBody == contactEntityA.getBody()) {
if (playerBody.getBody().getPosition().y <
contactEntityB.getBody().getPosition().y + .5 || playerController.isDownPressed()) { // .5 is half height of platform body
contact.isEnabled = false;
}
} else {
if (playerBody.getBody().getPosition().y <
contactEntityA.getBody().getPosition().y + .5 || playerController.isDownPressed()) { // .5 is half height of platform body
contact.isEnabled = false;
}
}
break;
}
My problem is:
Box2d doesn't make contacts if body doesn't move for a few seconds. So after some time, contact doesn't occur and i can't move down.
Try sensors in box 2D so that it will not collide with any object and surely it will accept events.
when you need physical aspects then it is better to use box2D
You could try something like this:
object DisableContact: ContactListener {
override fun beginContact(contact: Contact) {
}
override fun endContact(contact: Contact) {
}
override fun preSolve(contact: Contact, oldManifold: Manifold?) {
val playerBody = contact.fixtureA.body!! // need to identify if fixtureA or fixtureB is the player body
if (playerBody.linearVelocity.y < -3.5f) {
contact.isEnabled = false
}
}
override fun postSolve(contact: Contact, impulse: ContactImpulse?) {
}
}
Simple hack helped me. I'm just applying little velocity to a body each frame.
playerBody.applyLinearImpulse(0f, .000001f, .5f, .5f, true);
I'm not sure that is the best solution, write me please if you know how to do better.

Delete body box2d

Could anyone tell me why this is returning the error
'AL lib: (EE) alc_cleanup: 1 device not closed'
//mouseJoint collision callback
private QueryCallback queryCallback = new QueryCallback() {
#Override
public boolean reportFixture(Fixture fixture) {
if(fixture.getBody() == chest){
//add to remove list
bodiesToRemove.add(chest);
}
if (fixture.testPoint(tmp.x, tmp.y)){
reportFixture = fixture.getBody();
}
if (!fixture.testPoint(tmp.x, tmp.y))
return false;
//assigning bodyB to fixture
jointDef.bodyB = fixture.getBody();
jointDef.target.set(fixture.getBody().getWorldCenter());
//jointDef.target.set(tmp.x, tmp.y);// initial target point coincide with body anchor
joint = (MouseJoint) world.createJoint(jointDef);// creating the join the physics world
return false;
}
};
//main rendering loop
public void render(float delta) {
// TODO Auto-generated method stub
//code clears the screen with the given RGB colour (black)
Gdx.gl.glClearColor( 0f, 0f, 0f, 1f );
Gdx.gl.glClear( GL20.GL_COLOR_BUFFER_BIT );
stage.setCamera(camera);
stage.act(Gdx.graphics.getDeltaTime());
camera.position.x= chest.getPosition().x;
camera.update();
stage.draw();
world.step(1 / 60f, 8, 3);
renderer.render(world, camera.combined);
/////////////////////////////////////////////////////////////////
// this is the code that is causing the error when I try to delete a body
if(bodiesToRemove != null){
for(int i = 0; i <bodiesToRemove.size; i++){
Body b = bodiesToRemove.get(i);
if(b != null){
world.destroyBody(b);
b.setUserData(null);
b = null;
}
bodiesToRemove.clear();
}
}
////////////////////////////////////////////////////////////
}
////////////////////////////////////////////////////////////////////////////
I've changed this and it work's(pasted below), if anyone would like to explain the right way to do this or if this is the correct way that would be helpful.
I just added a boolean to prevent it from destroying the joint
// ////////////////// TOUCH EVENT ///////////////////////////////
//Called when a finger was lifted or a mouse button was released.
#Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
//If no mouseJoint do nothing
System.out.println("touch" + (screenY*0.01f));
System.out.println("touch" + (screenX*0.01f));
if (joint == null)
return false;
//When user is not touching Screen destroy the
//mouseJoint/set to null
if(ran == false){
world.destroyJoint(joint);
joint = null;}
ran = false;
return true;
}
////////////////////////////////
/////////////////////////////////
if(!world.isLocked() && world.getBodyCount() > 0 ){
for(int i = 0; i <bodiesToRemove.size; i++){
Body b = bodiesToRemove.get(i);
if(b == null){
bodiesToRemove.removeIndex(i);
continue;
}
world.destroyBody(b);
ran = true;
bodiesToRemove.removeIndex(i);
}
}else{
bodiesToRemove.clear();
}
////////////////////////////////////////////////////////////
Box2D throws some odd errors, this is not one of them.
I assume you are using LibGDX, basically what is happening is a crash due to Box2D (I also assume it pops up saying "java or whatever has stopped responding"), this error you are seeing is thrown when the app exits while OpenAL is closing streams and cleaning up resources. Don't worry about that too much.
Ensure you are checking of the world is locked before destroying bodies, it usually throws an error and prints a clear message telling you this, depending on the way you are removing bodies.
if(!world.isLocked())
// do clean up code here
You don't need to clean up the variable inside local scope, or clear the userdata inside the body as the body as it will no longer exist in reference (unless you have it stored somewhere else).
So the proper code would be something like this:
if(!world.isLocked()){
for(int i = bodiesToRemove.size - 1; i--){
Body b = bodiesToRemove.get(i);
if(b == null){
bodiesToRemove.removeAt(i);
continue;
}
world.destroyBody(b);
bodiesToRemove.removeAt(i);
}
}
You are also clearing a list during iteration, not a good idea.
EDIT
The only other thing I can think of that is causing this problem is trying to delete a body that is not actually in the worlds body list.
Try the following:
// Ensure world is not locked AND that there is even bodies in the world
if(!world.isLocked() && world.getBodyCount() > 0){
for(int i = bodiesToRemove.size - 1; i--){
Body b = bodiesToRemove.get(i);
if(b == null){
bodiesToRemove.removeAt(i);
continue;
}
world.destroyBody(b);
bodiesToRemove.removeAt(i);
}
}else{
// There is no bodies in the world, so the bodies in our list are just
// random memory hogs, leave them for the GC by clearing the list
bodiesToRemove.clear();
}
EDIT
As stated by the OP, the problem lies with this block of code:
private QueryCallback queryCallback = new QueryCallback() {
#Override
public boolean reportFixture(Fixture fixture) {
if(fixture.getBody() == chest){
//add to remove list
bodiesToRemove.add(chest);
}
if (fixture.testPoint(tmp.x, tmp.y)){
reportFixture = fixture.getBody();
}
if (!fixture.testPoint(tmp.x, tmp.y))
return false;
//assigning bodyB to fixture
jointDef.bodyB = fixture.getBody();
jointDef.target.set(fixture.getBody().getWorldCenter());
//jointDef.target.set(tmp.x, tmp.y);
joint = (MouseJoint) world.createJoint(jointDef);
return false;
}
};
More specifically, this line:
joint = (MouseJoint) world.createJoint(jointDef);
The query callback is triggered during a world step. In Box2D, you can not create or destroy bodies, fixtures, and joints during the world step.
You have to create it outside this method, the simplest way to do it is via a flag. Set a flag to true and store the instance of the fixture somewhere and handle it outside in the game loop using said flag and fixture.

Custom Touch Handling in Android

I needed access to the ACTION_UP that occurs after the ACTION_MOVE event, and the SimpleOnGestureListener didn't directly support that, so I tried to implement custom touch handling in an activity's onTouchEvent method, in order to get more familiar with it.
The activity is an alarm clock's alert screen (appears when an alarm goes off).
There are 5 kinds of gestures I need to handle. They are:
Single tap - Visual feedback
Double tap - Snooze the alarm
Swipe along the Y access - visual feedback based on distance swiped.
ACTION_UP after an ACTION_MOVE with a distance of less than 50% of the screen's Y axis - visual feedback.
ACTION_UP after an ACTION_MOVE with a distance of greater than 50% of the screen's Y axis - dismiss the alarm.
The function I wrote doesn't work correctly, and I'm pretty sure that it is because the variable "startEvent" is being set on every call to onTouchEvent, when it should be only set on the ACTION_DOWN events of a swipe or the first tap of a double tap.
Here is my code:
private final int DOUBLE_TAP_TIMEOUT = 500; // ms
private boolean isScrolling = false; // flag used to detect the ACTION_UP after a scroll
private boolean possibleDoubleTap = false; // flag used to detect a double tap
private MotionEvent startEvent; // the event that starts the gesture we are trying to detect
private float yPercentScrolled; // the percent the along the (Y axis / 2) that has been scrolled.
#Override
public boolean onTouchEvent(MotionEvent event){
switch (event.getActionMasked()){
case(MotionEvent.ACTION_DOWN):
Log.d("MOTION", "DOWN");
// reset the startEvent given the proper conditions
if(
(!isScrolling && !possibleDoubleTap)
|| (possibleDoubleTap && event.getEventTime() - startEvent.getEventTime() <= DOUBLE_TAP_TIMEOUT)
){
startEvent = event;
}
return true;
case(MotionEvent.ACTION_UP):
Log.d("MOTION", "UP");
if(isScrolling){
isScrolling = false;
if(yPercentScrolled >= 1f){
dismissAlarm();
}
else {
layout.setBackgroundColor(bgColor);
startEvent = null;
possibleDoubleTap = false;
isScrolling = false;
}
}
else if(possibleDoubleTap){
// if we have a double tap
if(event.getEventTime() - startEvent.getEventTime() <= DOUBLE_TAP_TIMEOUT){
snoozeAlarm();
return true;
}
// if we don't have a double tap, do nothing
}
else if(!possibleDoubleTap){
possibleDoubleTap = true;
layout.setBackgroundColor(Color.parseColor("#000000"));
}
return true;
case(MotionEvent.ACTION_MOVE):
Log.d("MOTION", "MOVE");
if(!isScrolling){
isScrolling = true;
}
else {
yPercentScrolled = handleScroll(event, startEvent);
}
return true;
default:
Log.d("MOTION", "other:" + String.valueOf(event.getActionMasked()));
return super.onTouchEvent(event);
}
}
Any ideas on why "startEvent" is being reset when it shouldn't? There are no "MOTION:DOWN" in logcat when it is being reset, so this on is stumping me. This is also the only location where startEvent is being assigned to something other than null.
Thanks.
it seems that startEvent = event is assigning a reference to object event instead of the object itself. so when the value of object event changed. the value of startEvent is also changed.
Try to take and store just the value that you need (in your case is the X, Y, and EventTime) instead of the whole event object.

Categories

Resources