• After 15+ years, we've made a big change: Android Forums is now Early Bird Club. Learn more here.

Starting and Stopping a Loop with Button

thecause17

Newbie
Oct 9, 2017
12
1
Hi everyone, I'm new to Android Studio and am trying to make an app that uses the phone's orientation as a control for a device that it will communicate to through wifi. So far, I've been able to access the sensors I need and output the readings to the app screen. At this point, it only updates a new sensor value each time a button is pressed. What I want though is for it to start giving continuous updates once the button is pressed, and stop when the button is pressed again and so on. Here is the first iteration of code, the one that gives an update at each button press.

Code:
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.app.Activity;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.widget.TextView;
import android.widget.Button;
import android.view.View;


public class MainActivity extends AppCompatActivity
{

    int toggle = 0;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        SensorManager sensorManager = (SensorManager) this.getSystemService(SENSOR_SERVICE);

        final float[] mValuesMagnet = new float[3];
        final float[] mValuesAccel = new float[3];
        final float[] mValuesOrientation = new float[3];
        final float[] mRotationMatrix = new float[9];

        final Button btn_valider = (Button) findViewById(R.id.btn1);
        final TextView txt1 = (TextView) findViewById(R.id.textView1);
        final SensorEventListener mEventListener = new SensorEventListener() {
            public void onAccuracyChanged(Sensor sensor, int accuracy) {
            }

            public void onSensorChanged(SensorEvent event) {
                switch (event.sensor.getType()) {
                    case Sensor.TYPE_ACCELEROMETER:
                        System.arraycopy(event.values, 0, mValuesAccel, 0, 3);
                        break;

                    case Sensor.TYPE_MAGNETIC_FIELD:
                        System.arraycopy(event.values, 0, mValuesMagnet, 0, 3);
                        break;
                }
            }

            ;
        };

        setListners(sensorManager, mEventListener);

        btn_valider.setOnClickListener(new View.OnClickListener()
        {

            public void onClick(View view)
            {



                    SensorManager.getRotationMatrix(mRotationMatrix, null, mValuesAccel, mValuesMagnet);
                    SensorManager.getOrientation(mRotationMatrix, mValuesOrientation);


                    final CharSequence test;
                    test = "Roll/Pitch (degrees): " + /*mValuesOrientation[0]*(180/Math.PI) + " "+ "," + " " +*/
                            mValuesOrientation[1] * (180 / Math.PI) + " " + "/" + " " +
                            mValuesOrientation[2] * (-180 / Math.PI);

                    txt1.setText(test);


            }
        });

    }

    public void setListners(SensorManager sensorManager, SensorEventListener mEventListener)
    {
        sensorManager.registerListener(mEventListener, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
                SensorManager.SENSOR_DELAY_NORMAL);
        sensorManager.registerListener(mEventListener, sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),
                SensorManager.SENSOR_DELAY_NORMAL);
    }
}


Originally for the continuous updates I was just trying to run a do while loop in the onClick method but it wasn't working. Someone else told me to run the loop in a background thread instead. So I edited my code as best as I could with their help but I have errors and I'm not sure how to progress further. Can someone help me set this up? Here's the current code:

Code:
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.app.Activity;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.widget.TextView;
import android.widget.Button;
import android.view.View;




public class MainActivity extends AppCompatActivity
{
    MySensorUpdateThread mySensorUpdateThread = null;

    @Override

    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        SensorManager sensorManager = (SensorManager) this.getSystemService(SENSOR_SERVICE);

        final float[] mValuesMagnet = new float[3];
        final float[] mValuesAccel = new float[3];
        final float[] mValuesOrientation = new float[3];
        final float[] mRotationMatrix = new float[9];

        final Button btn_valider = (Button) findViewById(R.id.btn1);
        final TextView txt1 = (TextView) findViewById(R.id.textView1);
        final SensorEventListener mEventListener = new SensorEventListener() {
            public void onAccuracyChanged(Sensor sensor, int accuracy) {
            }

            public void onSensorChanged(SensorEvent event) {
                switch (event.sensor.getType()) {
                    case Sensor.TYPE_ACCELEROMETER:
                        System.arraycopy(event.values, 0, mValuesAccel, 0, 3);
                        break;

                    case Sensor.TYPE_MAGNETIC_FIELD:
                        System.arraycopy(event.values, 0, mValuesMagnet, 0, 3);
                        break;
                }
            }
            ;
        };

        setListners(sensorManager, mEventListener);

        btn_valider.setOnClickListener(new View.OnClickListener()
            {

            public void onClick(View view)
            {
                mySensorUpdateThread.toggleThread();
                if (mySensorUpdateThread.isRunning())
                {
                    mySensorUpdateThread.start();
                }
            }

            });
    }


    public void setListners(SensorManager sensorManager, SensorEventListener mEventListener)
    {
        sensorManager.registerListener(mEventListener, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
                SensorManager.SENSOR_DELAY_NORMAL);
        sensorManager.registerListener(mEventListener, sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),
                SensorManager.SENSOR_DELAY_NORMAL);
    }


    public class MySensorUpdateThread extends Thread
    {
        private boolean keepRunning = false;
        private String sensorResults = "";

        public void toggleThread()
        {
            this.keepRunning = !this.keepRunning;
        }

        public void isRunning()
        {
            return this.keepRunning;
        }

        public String getSensorResults()
        {
            return this.sensorResults;
        }

        @Override
        public void run()
        {

            int i = 0;
            int maxIterations = 100;
            try{
                while(this.keepRunning)
                {
                    // This keeps the thread from going on too long in case
                    if(i > maxIterations)
                    {
                        this.keepRunning = false;
                        break;
                    }

                  
                    Thread.sleep(100);

                    SensorManager.getRotationMatrix(mRotationMatrix, null, mValuesAccel, mValuesMagnet);
                    SensorManager.getOrientation(mRotationMatrix, mValuesOrientation);

                  
                    sensorResults = "Roll/Pitch (degrees): " + /*mValuesOrientation[0]*(180/Math.PI) + " "+ "," + " " +*/
                            mValuesOrientation[1] * (180 / Math.PI) + " " + "/" + " " +
                            mValuesOrientation[2] * (-180 / Math.PI);

                    // Now post the results to the UI Thread
                    runOnUiThread(new Runnable(){
                        @Override
                        public void run(){
                            txt1.setText(getSensorResults());
                        }
                    });
                }
            }
            catch()
            {
                Log.e(TAG, ex.getMessage());
            }
        }
    }
}

I have errors at the following:

Line 63, "incompatible types"
Line 92, "cannot return a value of a method with void result type"
Lines 122-141 - various errors for not being able to resolve symbols such as mRotationMatrix, etc...

I apologize if I'm missing silly elementary things, it's been quite a while since I did any coding and as I said earlier, Android Studio is new to me. Thanks in advance for any help.
 
Thanks, I can't believe I didn't notice that. I also got rid of the error on the Thread.sleep but handling it with a try/catch, but I left the catch empty as I don't need to know. The point of the sleep was just to delay the iterations.

I'm still not sure what's going on with the other errors though.
 
Upvote 0
Thanks! Below is the latest code. The errors are as follows:

Line 126 - "cannot resolve symbol" for mRotationMatrix, mValuesAccel, and mValuesMagnet
Line 127 - same error for mRotationMatrix and mValuesOrientation
Line 130 and 131 - same error, mValuesOrientation
Line 137 same error, txt1
Line 144, "cannot resolve symbol" for Log and ex
Line 144, 'TAG' has private access in 'android.support.v4.app.FragmentActivity'

For the resolve issues, is it because I don't have them defined in public void run()?


Code:
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.app.Activity;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.widget.TextView;
import android.widget.Button;
import android.view.View;




public class MainActivity extends AppCompatActivity
{
    MySensorUpdateThread mySensorUpdateThread = null;

    @Override

    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        SensorManager sensorManager = (SensorManager) this.getSystemService(SENSOR_SERVICE);

        final float[] mValuesMagnet = new float[3];
        final float[] mValuesAccel = new float[3];
        final float[] mValuesOrientation = new float[3];
        final float[] mRotationMatrix = new float[9];

        final Button btn_valider = (Button) findViewById(R.id.btn1);
        final TextView txt1 = (TextView) findViewById(R.id.textView1);
        final SensorEventListener mEventListener = new SensorEventListener() {
            public void onAccuracyChanged(Sensor sensor, int accuracy) {
            }

            public void onSensorChanged(SensorEvent event) {
                switch (event.sensor.getType()) {
                    case Sensor.TYPE_ACCELEROMETER:
                        System.arraycopy(event.values, 0, mValuesAccel, 0, 3);
                        break;

                    case Sensor.TYPE_MAGNETIC_FIELD:
                        System.arraycopy(event.values, 0, mValuesMagnet, 0, 3);
                        break;
                }
            }
            ;
        };

        setListners(sensorManager, mEventListener);

        btn_valider.setOnClickListener(new View.OnClickListener()
            {

            public void onClick(View view)
            {
                mySensorUpdateThread.toggleThread();
                if (mySensorUpdateThread.isRunning())
                {
                    mySensorUpdateThread.start();
                }
            }

            });
    }
   
    public void setListners(SensorManager sensorManager, SensorEventListener mEventListener)
    {
        sensorManager.registerListener(mEventListener, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
                SensorManager.SENSOR_DELAY_NORMAL);
        sensorManager.registerListener(mEventListener, sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),
                SensorManager.SENSOR_DELAY_NORMAL);
    }
   
    public class MySensorUpdateThread extends Thread
    {
        private boolean keepRunning = false;
        private String sensorResults = "";

        public void toggleThread()
        {
            this.keepRunning = !this.keepRunning;
        }

        public boolean isRunning()
        {
            return this.keepRunning;
        }

        public String getSensorResults()
        {
            return this.sensorResults;
        }

        @Override
        public void run()
        {

            int i = 0;
            int maxIterations = 100;
            try{
                while(this.keepRunning)
                {
                    // This keeps the thread from going on too long in case
                    if(i > maxIterations)
                    {
                        this.keepRunning = false;
                        break;
                    }

                  // This causes the thread to rest for 50ms to
                 // slow things down

                    try
                    {
                        Thread.sleep(50);
                    }
                    catch(InterruptedException e)
                    {
                    }
                    SensorManager.getRotationMatrix(mRotationMatrix, null, mValuesAccel, mValuesMagnet);
                    SensorManager.getOrientation(mRotationMatrix, mValuesOrientation);
                   
                    sensorResults = "Roll/Pitch (degrees): " + /*mValuesOrientation[0]*(180/Math.PI) + " "+ "," + " " +*/
                            mValuesOrientation[1] * (180 / Math.PI) + " " + "/" + " " +
                            mValuesOrientation[2] * (-180 / Math.PI);

                    // Now post the results to the UI Thread
                    runOnUiThread(new Runnable(){
                        @Override
                        public void run(){
                            txt1.setText(getSensorResults());
                        }
                    });
                }
            }
            catch()
            {
                Log.e(TAG, ex.getMessage());
            }
        }
    }
}
 
Upvote 0
You're getting the undefined variable errors because your class MySensorUpdateThread is a nested inner class. The variables you're trying to access are defined in the enclosing class MainActivity.
To access these variables, you must do it through a reference to the MainActivity class.
So you could pass in a reference to MainActivity via the constructor e.g.

Code:
public MySensorUpdateThread(Context context) {
  ..
}

And by the way, you never initialise variable mySensorUpdateThread, it remains null. So you're going to get a NullPointerException when this line runs

Code:
mySensorUpdateThread.toggleThread();
 
Upvote 0
I apologize, I understand what you mean about the variables being in another class, but I don't understand how to pass the reference with your example. As far initializing, I understand how it would be null, but not sure how I'd have to initialize it.

You will need to create a new instance of class MySensorUpdateThread. So you could do it with a line like this in your onCreate() method:

Code:
mySensorUpdateThread = new MySensorUpdateThread(this);

So what I did there was create a new MySensorUpdateThread, using the current instance of the MainActivity class.
And in the MySensorUpdateThread constructor you would do this

Code:
public class MySensorUpdateThread extends Thread {

  private MainActivity mActivity;

  public MySensorUpdateThread(MainActivity activity) {
    this.mActivity = activity;
  }
}

So now you have a class variable, initialised as a reference to your MainActivity. You need more code to get access to the variables such as mRotationMatrix within the class. I'll let you have a think about how you could do that.
 
Upvote 0
You will need to create a new instance of class MySensorUpdateThread. So you could do it with a line like this in your onCreate() method:

Code:
mySensorUpdateThread = new MySensorUpdateThread(this);

So what I did there was create a new MySensorUpdateThread, using the current instance of the MainActivity class.
And in the MySensorUpdateThread constructor you would do this

Code:
public class MySensorUpdateThread extends Thread {

  private MainActivity mActivity;

  public MySensorUpdateThread(MainActivity activity) {
    this.mActivity = activity;
  }
}

So now you have a class variable, initialised as a reference to your MainActivity. You need more code to get access to the variables such as mRotationMatrix within the class. I'll let you have a think about how you could do that.

Wouldn't I have to provide a parameter in public void run()? I also notice that your line private MainActivity mActivity shows as never being accessed?
 
Upvote 0

BEST TECH IN 2023

We've been tracking upcoming products and ranking the best tech since 2007. Thanks for trusting our opinion: we get rewarded through affiliate links that earn us a commission and we invite you to learn more about us.

Smartphones