how can I get degrees from the values of the Accelerometer ? I'm useing libGDX and code in Java with Android Studio.
I ve got a sprite animation, which walks straight. The point of View is orthogonal from top, and I want to rotate the sprite when I tilt the smartphone.
How can I get the 360° degrees on the screen, for example like a compass just instead that it points to north it should point to the direction where the smartphone is tilted. How is it possible with the Accelerometer Sensor ? Or what other possibility do I have ?
Sorry for my English
A simple way of doing this is to use a SensorManager and implement SensorEventListener. The basic idea is you use the SensorManager to register the Orientation sensor and then respond to changes in the orientation of the device in the onSensorChanged delegate method implemented with SensorEventListener. Make sure you unregister the listener onPause() or else it will kill your battery.
As a high level example:
public class SensorActivity extends Activity implements SensorEventListener {
private Sensor mOrientationSensor;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SensorManager mSensorManager = (SensorManager) getActivity().getSystemService(Context.SENSOR_SERVICE);
mOrientationSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
}
#Override
public void onResume() {
super.onResume();
if (mSensorManager != null) {
mSensorManager.registerListener(this, mOrientationSensor, SensorManager.SENSOR_DELAY_UI);
}
}
#Override
public void onPause() {
super.onPause();
mSensorManager.unregisterListener(this, mOrientationSensor);
}
#Override
public void onSensorChanged(SensorEvent event) {
float degree = Math.round(event.values[0]);
// do something here
}
note: The orientation sensor has been deprecated, although I still think it works best. The updated approach is below if you'd like to try that.
From the android documentation: https://developer.android.com/guide/topics/sensors/sensors_position.html#sensors-pos-orient
public class SensorActivity extends Activity implements SensorEventListener {
private SensorManager mSensorManager;
private final float[] mAccelerometerReading = new float[3];
private final float[] mMagnetometerReading = new float[3];
private final float[] mRotationMatrix = new float[9];
private final float[] mOrientationAngles = new float[3];
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// Do something here if sensor accuracy changes.
// You must implement this callback in your code.
}
#Override
protected void onResume() {
super.onResume();
// Get updates from the accelerometer and magnetometer at a constant rate.
// To make batch operations more efficient and reduce power consumption,
// provide support for delaying updates to the application.
//
// In this example, the sensor reporting delay is small enough such that
// the application receives an update before the system checks the sensor
// readings again.
mSensorManager.registerListener(this, Sensor.TYPE_ACCELEROMETER,
SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI);
mSensorManager.registerListener(this, Sensor.TYPE_MAGNETIC_FIELD,
SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI);
}
#Override
protected void onPause() {
super.onPause();
// Don't receive any more updates from either sensor.
mSensorManager.unregisterListener(this);
}
// Get readings from accelerometer and magnetometer. To simplify calculations,
// consider storing these readings as unit vectors.
#Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor == Sensor.TYPE_ACCELEROMETER) {
System.arraycopy(event.values, 0, mAccelerometerReading,
0, mAccelerometerReading.length);
}
else if (event.sensor == Sensor.TYPE_MAGNETIC_FIELD) {
System.arraycopy(event.values, 0, mMagnetometerReading,
0, mMagnetometerReading.length);
}
}
// Compute the three orientation angles based on the most recent readings from
// the device's accelerometer and magnetometer.
public void updateOrientationAngles() {
// Update rotation matrix, which is needed to update orientation angles.
mSensorManager.getRotationMatrix(mRotationMatrix, null,
mAccelerometerReading, mMagnetometerReading);
// "mRotationMatrix" now has up-to-date information.
mSensorManager.getOrientation(mRotationMatrix, mOrientationAngles);
// "mOrientationAngles" now has up-to-date information.
}
}
Related
I have a simple step counter taken from Google (https://github.com/google/simple-pedometer/blob/master/src/com/google/android/apps/simplepedometer/SimplePedometerActivity.java). I have adapted the Pedometer as below:
public class ActivityFragment extends Fragment implements SensorEventListener, StepListener {
private TextView textView;
private SimpleStepDetector simpleStepDetector;
private SensorManager sensorManager;
private Sensor accel;
private static final String TEXT_NUM_STEPS = "";
private int numSteps;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_activity, container, false);
textView = view.findViewById(R.id.textViewStepCounter);
textView.setTextSize(30);
// Get an instance of the SensorManager
sensorManager = (SensorManager) getActivity().getSystemService(SENSOR_SERVICE);
accel = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
simpleStepDetector = new SimpleStepDetector();
simpleStepDetector.registerListener(this);
return view;
}
#Override
public void onResume() {
super.onResume();
textView.setText(TEXT_NUM_STEPS + numSteps);
sensorManager.registerListener(this, accel, SensorManager.SENSOR_DELAY_NORMAL);
}
#Override
public void onPause() {
super.onPause();
sensorManager.unregisterListener(this);
}
#Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
simpleStepDetector.updateAccel(
event.timestamp, event.values[0], event.values[1], event.values[2]);
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
#Override
public void step(long timeNs) {
numSteps++;
textView.setText(TEXT_NUM_STEPS + numSteps);
}
}
It works perfectly, however, whenever I close the application the counter returns to 0, is there any way to prevent this happening? Had a look online, however could only find options to reset, not prevent reset.
Additional question whilst I am here, is there any way to store this to Firebase?
You will have to look into local state persistence, Firebase is an online first suite. you can use Firestore Caching and rely on the cached version when offline, but it is highly recommended to look into local storage and updating your online presence infrequently.
Using local storage, you can handle real-time changes within your app and preserve any changes between the state when restarting or force closed by the user.
A few resources I was able to find:
https://www.androidauthority.com/how-to-store-data-locally-in-android-app-717190/
https://developer.android.com/training/data-storage
https://stackoverflow.com/a/61638579/2301161
How do I reset my step counter to 0 every time my app launches? If I close my app and relaunch It, the counter continues from where it previously left off. I would like it to start over. I have already tried setting my steps taken textview to 0 every time my app launches but it doesn't change anything.
public class HomeActivity extends AppCompatActivity implements SensorEventListener {
private TextView stepsTakenTextView;
private TextView distanceTraveledTextView;
private SensorManager sensorManager;
private Boolean running = false;
private UserModel userModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
stepsTakenTextView = findViewById(R.id.stepsTakenTextView);
distanceTraveledTextView = findViewById(R.id.distanceTraveledTextView);
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
}
#Override
protected void onResume() {
super.onResume();
running = true;
Sensor countSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);
if(countSensor != null){
sensorManager.registerListener(this, countSensor, SensorManager.SENSOR_DELAY_UI);
}else{
Toast.makeText(this, "Sensor not found", Toast.LENGTH_SHORT).show();
}
}
#Override
protected void onPause() {
super.onPause();
running = false;
}
#Override
public void onSensorChanged(SensorEvent event) {
if(running){
float stepsTaken= event.values[0];
The sensor will only be restarted when the device is rebooted.
Android Sensor API
A sensor of this type returns the number of steps taken by the user since the last reboot
while activated. The value is returned as a float (with the fractional
part set to zero) and is reset to zero only on a system reboot...
Instead, you can do the following:
As soon as you start the app, save the first value returned by the sensor.
Every time the sensor emits a new value, subtract the initial value from it.
I'm having a lot of trouble tonight with sensors, but it appear it is because of onSensorChanged() is not called. Sorry for the eventual duplicate question, but I didn't see any solutions.
Here's my code :
public SensorManager manager;
public Sensor rotation_vector;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
manager = (SensorManager)getSystemService(SENSOR_SERVICE);
rotation_vector = manager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
}
#Override
public void onSensorChanged(SensorEvent event) {
int xValue = (int) event.values[0];
int yValue = (int) event.values[1];
int zValue = (int) event.values[2];
Toast.makeText(getApplicationContext(),"this doesn't appear...",Toast.LENGTH_LONG);
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
//TODO: we'll see later about it
}
public void onPause() {
/*Et on le dés-enregistre quand on sort de l'activité, pour économiser de la batterie*/
super.onPause();
manager.unregisterListener(this);
}
#Override
protected void onResume() {
/*On enregistre le sensor quand l'utilisateur revient sur l'activité*/
super.onResume();
manager.registerListener(this, rotation_vector, SensorManager.SENSOR_DELAY_NORMAL);
if (null != rotation_vector) {
} else {
Toast.makeText(getApplicationContext(),
"There is no gyroscope on your device",
Toast.LENGTH_LONG).show();
}
}
I have seen a lot of similar code on different forums, but appart from there and some others topic without solutions, I haven't seen such a problem yet...
Do I have to add something in AndroidManifest ?
Is it a common problem ?
It there a solution ?
Thanks,
Thomas
(sorry for my bad english, I'm French^^)
I was not working with Sensors so far, but it looks like you have obtained SensorManager but did not register any listener for changes that should occur. Just ask yourself how could be onSensorChanged called?
Have a look at the sample on official android tutorial site about sensors and you could see this in onResume() method:
#Override
protected void onResume() {
super.onResume();
mSensorManager.registerListener(this, mLight, SensorManager.SENSOR_DELAY_NORMAL);
}
Also don't forget to study whole page it can help you a lot.
Edit:
I had to copy paste your sources because I haven't seen any problem. There are 2 basicaly:
Toast is not appearing because you haven't told him to... Toast.show() will display it. But you rather use Log to output changes into console. It's up to you
Do not retype float into int in onSensorChanged() method because all values are 0 or (1/-1 if you are lucky)
So result is following:
#Override
public void onSensorChanged(SensorEvent event) {
float xValue = event.values[0];
float yValue = event.values[1];
float zValue = event.values[2];
Log.d(LOG_TAG, "x:"+xValue +";y:"+yValue+";z:"+zValue);
}
other methods are correct
Your device probably does not have Sensor.TYPE_ROTATION_VECTOR. You should check rotation_vector for null.
I want to get true and magnetic heading of compass in android.
I have read many tutorials but not give my required output.
It gives just heading.
My code is here...
public class MainActivity extends Activity implements SensorEventListener {
// device sensor manager
private SensorManager mSensorManager;
TextView tvHeading;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// TextView that will tell the user what degree is he heading
tvHeading = (TextView) findViewById(R.id.tvHeading);
// initialize your android device sensor capabilities
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
}
#Override
protected void onResume() {
super.onResume();
// for the system's orientation sensor registered listeners
mSensorManager.registerListener(this, mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION),
SensorManager.SENSOR_DELAY_GAME);
}
#Override
protected void onPause() {
super.onPause();
// to stop the listener and save battery
mSensorManager.unregisterListener(this);
}
#Override
public void onSensorChanged(SensorEvent event) {
// get the angle around the z-axis rotated
float degree = Math.round(event.values[0]);
tvHeading.setText("Heading: " + Float.toString(degree) + " degrees");
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// not in use
}
}
This code give accurate value for a compass.
How can i modify this code for getting magnetic heading and true heading?
what is missing here?
Any one can help me.
Thanks
Hi,
I'm a newbie in developing ArcGIS maps. Currently, I am able to display the map. But how can I create a default location using the longitude and latitude and implement this in my code?
For example, I want to set these coordinates: (1.3002, 103.8641). When you open the map, it will automatically zoom in to those coordinates.
Thank you in advance.
This is my code:
public class HelloWorld extends Activity {
MapView mMapView = null;
ArcGISTiledMapServiceLayer tileLayer;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Retrieve the map and initial extent from XML layout
mMapView = (MapView)findViewById(R.id.map);
/* create a #ArcGISTiledMapServiceLayer */
tileLayer = new ArcGISTiledMapServiceLayer(
"http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer");
// Add tiled layer to MapView
mMapView.addLayer(tileLayer);
}
#Override
protected void onPause() {
super.onPause();
mMapView.pause();
}
#Override
protected void onResume() {
super.onResume();
mMapView.unpause();
}
}
Before adding any layers to the MapView, set its OnStatusChangedListener. Add code to the listener that projects the longitude/latitude coordinates to map coordinates and centers the map at the map coordinates.
You could do it like this:
mMapView = (MapView)findViewById(R.id.map);
final double longitude = 103.8641;
final double latitude = 1.3002;
mMapView.setOnStatusChangedListener(new OnStatusChangedListener() {
#Override
public void onStatusChanged(Object source, STATUS status) {
if (STATUS.INITIALIZED.equals(status)) {
Point mapPoint = GeometryEngine.project(longitude, latitude, mMapView.getSpatialReference());
mMapView.centerAt(mapPoint, false);
}
}
});
/* create a #ArcGISTiledMapServiceLayer */
tileLayer = new ArcGISTiledMapServiceLayer(
"http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer");
// Add tiled layer to MapView
mMapView.addLayer(tileLayer);
If you want to both center and zoom to a certain scale, you can call zoomToScale instead of centerAt.