/*
* Copyright (C) 2008-2013 The Android Open Source Project,
* Sean J. Barbeau
*
* 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.
*/
package com.android.gpstest;
import com.android.gpstest.util.GpsTestUtil;
import com.android.gpstest.util.MathUtils;
import com.android.gpstest.util.PreferenceUtils;
import com.android.gpstest.view.ViewPagerMapBevelScroll;
import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.hardware.GeomagneticField;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.location.GnssMeasurement;
import android.location.GnssMeasurementsEvent;
import android.location.GnssNavigationMessage;
import android.location.GnssStatus;
import android.location.GpsStatus;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.location.OnNmeaMessageListener;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.support.annotation.RequiresApi;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.Surface;
import android.view.View;
import android.view.Window;
import android.widget.CompoundButton;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import static com.android.gpstest.util.GpsTestUtil.writeGnssMeasurementToLog;
import static com.android.gpstest.util.GpsTestUtil.writeNavMessageToLog;
import static com.android.gpstest.util.GpsTestUtil.writeNmeaToLog;
public class GpsTestActivity extends AppCompatActivity
implements LocationListener, android.support.v7.app.ActionBar.TabListener,
SensorEventListener {
private static final String TAG = "GpsTestActivity";
private static final int WHATSNEW_DIALOG = 1;
private static final String WHATS_NEW_VER = "whatsNewVer";
private static final int SECONDS_TO_MILLISECONDS = 1000;
static boolean mIsLargeScreen = false;
private static GpsTestActivity sInstance;
// Holds sensor data
private static float[] mRotationMatrix = new float[16];
private static float[] mRemappedMatrix = new float[16];
private static float[] mValues = new float[3];
private static float[] mTruncatedRotationVector = new float[4];
private static boolean mTruncateVector = false;
boolean mStarted;
boolean mFaceTrueNorth;
boolean mWriteGnssMeasurementToLog;
boolean mLogNmea;
boolean mWriteNmeaTimestampToLog;
String mTtff;
org.jraf.android.backport.switchwidget.Switch mSwitch; //GPS on/off switch
SectionsPagerAdapter mSectionsPagerAdapter;
ViewPagerMapBevelScroll mViewPager;
private LocationManager mLocationManager;
private LocationProvider mProvider;
/**
* Android M (6.0.1) and below status and listener
*/
private GpsStatus mLegacyStatus;
private GpsStatus.Listener mLegacyStatusListener;
private GpsStatus.NmeaListener mLegacyNmeaListener;
/**
* Android N (7.0) and above status and listeners
*/
private GnssStatus mGnssStatus;
private GnssStatus.Callback mGnssStatusListener;
private GnssMeasurementsEvent.Callback mGnssMeasurementsListener; // For SNRs
private OnNmeaMessageListener mOnNmeaMessageListener;
private GnssNavigationMessage.Callback mGnssNavMessageListener;
// Listeners for Fragments
private ArrayList<GpsTestListener> mGpsTestListeners = new ArrayList<GpsTestListener>();
private Location mLastLocation;
private GeomagneticField mGeomagneticField;
private long minTime; // Min Time between location updates, in milliseconds
private float minDistance; // Min Distance between location updates, in meters
private SensorManager mSensorManager;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
super.onCreate(savedInstanceState);
sInstance = this;
// Set the default values from the XML file if this is the first
// execution of the app
PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
mProvider = mLocationManager.getProvider(LocationManager.GPS_PROVIDER);
if (mProvider == null) {
Log.e(TAG, "Unable to get GPS_PROVIDER");
Toast.makeText(this, getString(R.string.gps_not_supported),
Toast.LENGTH_SHORT).show();
finish();
return;
}
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
// If we have a large screen, show all the fragments in one layout
if (GpsTestUtil.isLargeScreen(this)) {
setContentView(R.layout.activity_main_large_screen);
mIsLargeScreen = true;
} else {
setContentView(R.layout.activity_main);
}
initActionBar(savedInstanceState);
SharedPreferences settings = Application.getPrefs();
double tempMinTime = Double.valueOf(
settings.getString(getString(R.string.pref_key_gps_min_time),
getString(R.string.pref_gps_min_time_default_sec))
);
minTime = (long) (tempMinTime * SECONDS_TO_MILLISECONDS);
minDistance = Float.valueOf(
settings.getString(getString(R.string.pref_key_gps_min_distance),
getString(R.string.pref_gps_min_distance_default_meters))
);
if (settings.getBoolean(getString(R.string.pref_key_auto_start_gps), true)) {
gpsStart();
}
autoShowWhatsNew();
}
@Override
protected void onResume() {
super.onResume();
addStatusListener();
addOrientationSensorListener();
addNmeaListener();
if (!mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
promptEnableGps();
}
/**
* Check preferences to see how these componenets should be initialized
*/
SharedPreferences settings = Application.getPrefs();
checkKeepScreenOn(settings);
checkTimeAndDistance(settings);
checkTrueNorth(settings);
checkNmeaLog(settings);
if (GpsTestUtil.isGnssStatusListenerSupported()) {
checkGnssMeasurementOutput(settings);
}
if (GpsTestUtil.isGnssStatusListenerSupported()) {
checkNavMessageOutput(settings);
}
}
@Override
protected void onPause() {
mSensorManager.unregisterListener(this);
// Remove status listeners
removeStatusListener();
removeNmeaListener();
if (GpsTestUtil.isGnssStatusListenerSupported()) {
removeNavMessageListener();
}
if (GpsTestUtil.isGnssStatusListenerSupported()) {
removeGnssMeasurementsListener();
}
super.onPause();
}
static GpsTestActivity getInstance() {
return sInstance;
}
void addListener(GpsTestListener listener) {
mGpsTestListeners.add(listener);
}
private synchronized void gpsStart() {
if (!mStarted) {
mLocationManager
.requestLocationUpdates(mProvider.getName(), minTime, minDistance, this);
mStarted = true;
// Show Toast only if the user has set minTime or minDistance to something other than default values
if (minTime != (long) (Double.valueOf(getString(R.string.pref_gps_min_time_default_sec))
* SECONDS_TO_MILLISECONDS) ||
minDistance != Float
.valueOf(getString(R.string.pref_gps_min_distance_default_meters))) {
Toast.makeText(this, String.format(getString(R.string.gps_set_location_listener),
String.valueOf((double) minTime / SECONDS_TO_MILLISECONDS),
String.valueOf(minDistance)), Toast.LENGTH_SHORT).show();
}
// Show the indeterminate progress bar on the action bar until first GPS status is shown
setSupportProgressBarIndeterminateVisibility(Boolean.TRUE);
// Reset the options menu to trigger updates to action bar menu items
invalidateOptionsMenu();
}
for (GpsTestListener listener : mGpsTestListeners) {
listener.gpsStart();
}
}
private synchronized void gpsStop() {
if (mStarted) {
mLocationManager.removeUpdates(this);
mStarted = false;
// Stop progress bar
setSupportProgressBarIndeterminateVisibility(Boolean.FALSE);
// Reset the options menu to trigger updates to action bar menu items
invalidateOptionsMenu();
}
for (GpsTestListener listener : mGpsTestListeners) {
listener.gpsStop();
}
}
private boolean sendExtraCommand(String command) {
return mLocationManager.sendExtraCommand(LocationManager.GPS_PROVIDER, command, null);
}
private void addOrientationSensorListener() {
if (GpsTestUtil.isRotationVectorSensorSupported(this)) {
// Use the modern rotation vector sensors
Sensor vectorSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
mSensorManager.registerListener(this, vectorSensor, 16000); // ~60hz
} else {
// Use the legacy orientation sensors
Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
if (sensor != null) {
mSensorManager.registerListener(this, sensor,
SensorManager.SENSOR_DELAY_GAME);
}
}
}
private void addStatusListener() {
SharedPreferences settings = Application.getPrefs();
boolean useGnssApis = settings.getBoolean(getString(R.string.pref_key_use_gnss_apis), true);
if (GpsTestUtil.isGnssStatusListenerSupported() && useGnssApis) {
addGnssStatusListener();
} else {
addLegacyStatusListener();
}
}
@RequiresApi(Build.VERSION_CODES.N)
private void addGnssStatusListener() {
mGnssStatusListener = new GnssStatus.Callback() {
@Override
public void onStarted() {
for (GpsTestListener listener : mGpsTestListeners) {
listener.onGnssStarted();
}
}
@Override
public void onStopped() {
for (GpsTestListener listener : mGpsTestListeners) {
listener.onGnssStopped();
}
}
@Override
public void onFirstFix(int ttffMillis) {
if (ttffMillis == 0) {
mTtff = "";
} else {
ttffMillis = (ttffMillis + 500) / 1000;
mTtff = Integer.toString(ttffMillis) + " sec";
}
for (GpsTestListener listener : mGpsTestListeners) {
listener.onGnssFirstFix(ttffMillis);
}
}
@Override
public void onSatelliteStatusChanged(GnssStatus status) {
mGnssStatus = status;
// Stop progress bar after the first status information is obtained
setSupportProgressBarIndeterminateVisibility(Boolean.FALSE);
for (GpsTestListener listener : mGpsTestListeners) {
listener.onSatelliteStatusChanged(mGnssStatus);
}
}
};
mLocationManager.registerGnssStatusCallback(mGnssStatusListener);
}
@RequiresApi(api = Build.VERSION_CODES.N)
private void addGnssMeasurementsListener() {
mGnssMeasurementsListener = new GnssMeasurementsEvent.Callback() {
@Override
public void onGnssMeasurementsReceived(GnssMeasurementsEvent event) {
for (GpsTestListener listener : mGpsTestListeners) {
listener.onGnssMeasurementsReceived(event);
}
if (mWriteGnssMeasurementToLog) {
for (GnssMeasurement m : event.getMeasurements()) {
writeGnssMeasurementToLog(m);
}
}
}
@Override
public void onStatusChanged(int status) {
}
};
mLocationManager.registerGnssMeasurementsCallback(mGnssMeasurementsListener);
}
private void addLegacyStatusListener() {
mLegacyStatusListener = new GpsStatus.Listener() {
@Override
public void onGpsStatusChanged(int event) {
mLegacyStatus = mLocationManager.getGpsStatus(mLegacyStatus);
switch (event) {
case GpsStatus.GPS_EVENT_STARTED:
break;
case GpsStatus.GPS_EVENT_STOPPED:
break;
case GpsStatus.GPS_EVENT_FIRST_FIX:
int ttff = mLegacyStatus.getTimeToFirstFix();
if (ttff == 0) {
mTtff = "";
} else {
ttff = (ttff + 500) / 1000;
mTtff = Integer.toString(ttff) + " sec";
}
break;
case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
// Stop progress bar after the first status information is obtained
setSupportProgressBarIndeterminateVisibility(Boolean.FALSE);
break;
}
for (GpsTestListener listener : mGpsTestListeners) {
listener.onGpsStatusChanged(event, mLegacyStatus);
}
}
};
mLocationManager.addGpsStatusListener(mLegacyStatusListener);
}
private void removeStatusListener() {
SharedPreferences settings = Application.getPrefs();
boolean useGnssApis = settings.getBoolean(getString(R.string.pref_key_use_gnss_apis), true);
if (GpsTestUtil.isGnssStatusListenerSupported() && useGnssApis) {
removeGnssStatusListener();
} else {
removeLegacyStatusListener();
}
}
@RequiresApi(Build.VERSION_CODES.N)
private void removeGnssStatusListener() {
mLocationManager.unregisterGnssStatusCallback(mGnssStatusListener);
}
@RequiresApi(api = Build.VERSION_CODES.N)
private void removeGnssMeasurementsListener() {
if (mLocationManager != null && mGnssMeasurementsListener != null) {
mLocationManager.unregisterGnssMeasurementsCallback(mGnssMeasurementsListener);
}
}
private void removeLegacyStatusListener() {
if (mLocationManager != null && mLegacyStatusListener != null) {
mLocationManager.removeGpsStatusListener(mLegacyStatusListener);
}
}
private void addNmeaListener() {
if (GpsTestUtil.isGnssStatusListenerSupported()) {
addNmeaListenerAndroidN();
} else {
addLegacyNmeaListener();
}
}
@RequiresApi(api = Build.VERSION_CODES.N)
private void addNmeaListenerAndroidN() {
if (mOnNmeaMessageListener == null) {
mOnNmeaMessageListener = new OnNmeaMessageListener() {
@Override
public void onNmeaMessage(String message, long timestamp) {
for (GpsTestListener listener : mGpsTestListeners) {
listener.onNmeaMessage(message, timestamp);
}
if (mLogNmea) {
writeNmeaToLog(message,
mWriteNmeaTimestampToLog ? timestamp : Long.MIN_VALUE);
}
}
};
}
mLocationManager.addNmeaListener(mOnNmeaMessageListener);
}
private void addLegacyNmeaListener() {
if (mLegacyNmeaListener == null) {
mLegacyNmeaListener = new GpsStatus.NmeaListener() {
@Override
public void onNmeaReceived(long timestamp, String nmea) {
for (GpsTestListener listener : mGpsTestListeners) {
listener.onNmeaMessage(nmea, timestamp);
}
if (mLogNmea) {
writeNmeaToLog(nmea, mWriteNmeaTimestampToLog ? timestamp : Long.MIN_VALUE);
}
}
};
}
mLocationManager.addNmeaListener(mLegacyNmeaListener);
}
private void removeNmeaListener() {
if (GpsTestUtil.isGnssStatusListenerSupported()) {
if (mLocationManager != null && mOnNmeaMessageListener != null) {
mLocationManager.removeNmeaListener(mOnNmeaMessageListener);
}
} else {
if (mLocationManager != null && mLegacyNmeaListener != null) {
mLocationManager.removeNmeaListener(mLegacyNmeaListener);
}
}
}
@RequiresApi(api = Build.VERSION_CODES.N)
private void addNavMessageListener() {
if (mGnssNavMessageListener == null) {
mGnssNavMessageListener = new GnssNavigationMessage.Callback() {
@Override
public void onGnssNavigationMessageReceived(GnssNavigationMessage event) {
writeNavMessageToLog(event);
}
@Override
public void onStatusChanged(int status) {
}
};
}
mLocationManager.registerGnssNavigationMessageCallback(mGnssNavMessageListener);
}
@RequiresApi(api = Build.VERSION_CODES.N)
private void removeNavMessageListener() {
if (mLocationManager != null && mGnssNavMessageListener != null) {
mLocationManager.unregisterGnssNavigationMessageCallback(mGnssNavMessageListener);
}
}
/**
* Ask the user if they want to enable GPS
*/
private void promptEnableGps() {
new AlertDialog.Builder(this)
.setMessage(getString(R.string.enable_gps_message))
.setPositiveButton(getString(R.string.enable_gps_positive_button),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(
Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(intent);
}
}
)
.setNegativeButton(getString(R.string.enable_gps_negative_button),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
}
}
)
.show();
}
private void checkTimeAndDistance(SharedPreferences settings) {
double tempMinTimeDouble = Double
.valueOf(settings.getString(getString(R.string.pref_key_gps_min_time), "1"));
long minTimeLong = (long) (tempMinTimeDouble * SECONDS_TO_MILLISECONDS);
if (minTime != minTimeLong ||
minDistance != Float.valueOf(
settings.getString(getString(R.string.pref_key_gps_min_distance), "0"))) {
// User changed preference values, get the new ones
minTime = minTimeLong;
minDistance = Float.valueOf(
settings.getString(getString(R.string.pref_key_gps_min_distance), "0"));
// If the GPS is started, reset the location listener with the new values
if (mStarted) {
mLocationManager
.requestLocationUpdates(mProvider.getName(), minTime, minDistance, this);
Toast.makeText(this, String.format(getString(R.string.gps_set_location_listener),
String.valueOf(tempMinTimeDouble), String.valueOf(minDistance)),
Toast.LENGTH_SHORT
).show();
}
}
}
private void checkKeepScreenOn(SharedPreferences settings) {
if (mViewPager != null) {
if (settings.getBoolean(getString(R.string.pref_key_keep_screen_on), true)) {
mViewPager.setKeepScreenOn(true);
} else {
mViewPager.setKeepScreenOn(false);
}
} else {
View v = findViewById(R.id.large_screen_layout);
if (v != null && mIsLargeScreen) {
if (settings.getBoolean(getString(R.string.pref_key_keep_screen_on), true)) {
v.setKeepScreenOn(true);
} else {
v.setKeepScreenOn(false);
}
}
}
}
private void checkTrueNorth(SharedPreferences settings) {
mFaceTrueNorth = settings.getBoolean(getString(R.string.pref_key_true_north), true);
}
@RequiresApi(api = Build.VERSION_CODES.N)
private void checkGnssMeasurementOutput(SharedPreferences settings) {
mWriteGnssMeasurementToLog = settings
.getBoolean(getString(R.string.pref_key_measurement_output), false);
if (mWriteGnssMeasurementToLog) {
addGnssMeasurementsListener();
}
}
private void checkNmeaLog(SharedPreferences settings) {
mLogNmea = settings.getBoolean(getString(R.string.pref_key_nmea_output), true);
mWriteNmeaTimestampToLog = settings
.getBoolean(getString(R.string.pref_key_nmea_timestamp_output), true);
}
@RequiresApi(api = Build.VERSION_CODES.N)
private void checkNavMessageOutput(SharedPreferences settings) {
boolean logNavMessage = settings
.getBoolean(getString(R.string.pref_key_navigation_message_output), false);
if (logNavMessage) {
addNavMessageListener();
} else {
removeNavMessageListener();
}
}
@Override
protected void onDestroy() {
mLocationManager.removeUpdates(this);
super.onDestroy();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.gps_menu, menu);
initGpsSwitch(menu);
return true;
}
private void initGpsSwitch(Menu menu) {
MenuItem item = menu.findItem(R.id.gps_switch);
if (item != null) {
mSwitch = (org.jraf.android.backport.switchwidget.Switch) MenuItemCompat
.getActionView(item);
if (mSwitch != null) {
// Initialize state of GPS switch before we set the listener, so we don't double-trigger start or stop
mSwitch.setChecked(mStarted);
// Set up listener for GPS on/off switch, since custom menu items on Action Bar don't play
// well with ABS and we can't handle in onOptionsItemSelected()
mSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// Turn GPS on or off
if (!isChecked && mStarted) {
gpsStop();
} else {
if (isChecked && !mStarted) {
gpsStart();
}
}
}
});
}
}
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem item;
item = menu.findItem(R.id.send_location);
if (item != null) {
item.setVisible(mLastLocation != null);
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
boolean success;
// Handle menu item selection
switch (item.getItemId()) {
case R.id.gps_switch:
// Do nothing - this is handled by a separate listener added in onCreateOptionsMenu()
return true;
case R.id.delete_aiding_data:
// If GPS is currently running, stop it
boolean lastStartState = mStarted;
if (mStarted) {
gpsStop();
}
success = sendExtraCommand(getString(R.string.delete_aiding_data_command));
if (success) {
Toast.makeText(this, getString(R.string.delete_aiding_data_success),
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, getString(R.string.delete_aiding_data_failure),
Toast.LENGTH_SHORT).show();
}
if (lastStartState) {
Handler h = new Handler();
// Restart the GPS, if it was previously started, with a slight delay,
// to refresh the assistance data
h.postDelayed(new Runnable() {
public void run() {
gpsStart();
}
}, 500);
}
return true;
case R.id.send_location:
sendLocation();
return true;
case R.id.force_time_injection:
success = sendExtraCommand(getString(R.string.force_time_injection_command));
if (success) {
Toast.makeText(this, getString(R.string.force_time_injection_success),
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, getString(R.string.force_time_injection_failure),
Toast.LENGTH_SHORT).show();
}
return true;
case R.id.force_xtra_injection:
success = sendExtraCommand(getString(R.string.force_xtra_injection_command));
if (success) {
Toast.makeText(this, getString(R.string.force_xtra_injection_success),
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, getString(R.string.force_xtra_injection_failure),
Toast.LENGTH_SHORT).show();
}
return true;
case R.id.menu_settings:
// Show settings menu
startActivity(new Intent(this, Preferences.class));
return true;
case R.id.menu_help:
// Show help
startActivity(new Intent(this, HelpActivity.class));
return true;
default:
return super.onOptionsItemSelected(item);
}
}
public void onLocationChanged(Location location) {
mLastLocation = location;
updateGeomagneticField();
// Reset the options menu to trigger updates to action bar menu items
invalidateOptionsMenu();
for (GpsTestListener listener : mGpsTestListeners) {
listener.onLocationChanged(location);
}
}
public void onStatusChanged(String provider, int status, Bundle extras) {
for (GpsTestListener listener : mGpsTestListeners) {
listener.onStatusChanged(provider, status, extras);
}
}
public void onProviderEnabled(String provider) {
for (GpsTestListener listener : mGpsTestListeners) {
listener.onProviderEnabled(provider);
}
}
public void onProviderDisabled(String provider) {
for (GpsTestListener listener : mGpsTestListeners) {
listener.onProviderDisabled(provider);
}
}
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
@Override
public void onSensorChanged(SensorEvent event) {
double orientation = Double.NaN;
double tilt = Double.NaN;
switch (event.sensor.getType()) {
case Sensor.TYPE_ROTATION_VECTOR:
// Modern rotation vector sensors
if (!mTruncateVector) {
try {
SensorManager.getRotationMatrixFromVector(mRotationMatrix, event.values);
} catch (IllegalArgumentException e) {
// On some Samsung devices, an exception is thrown if this vector > 4 (see #39)
// Truncate the array, since we can deal with only the first four values
Log.e(TAG, "Samsung device error? Will truncate vectors - " + e);
mTruncateVector = true;
// Do the truncation here the first time the exception occurs
getRotationMatrixFromTruncatedVector(event.values);
}
} else {
// Truncate the array to avoid the exception on some devices (see #39)
getRotationMatrixFromTruncatedVector(event.values);
}
int rot = getWindowManager().getDefaultDisplay().getRotation();
switch (rot) {
case Surface.ROTATION_0:
// No orientation change, use default coordinate system
SensorManager.getOrientation(mRotationMatrix, mValues);
// Log.d(TAG, "Rotation-0");
break;
case Surface.ROTATION_90:
// Log.d(TAG, "Rotation-90");
SensorManager.remapCoordinateSystem(mRotationMatrix, SensorManager.AXIS_Y,
SensorManager.AXIS_MINUS_X, mRemappedMatrix);
SensorManager.getOrientation(mRemappedMatrix, mValues);
break;
case Surface.ROTATION_180:
// Log.d(TAG, "Rotation-180");
SensorManager
.remapCoordinateSystem(mRotationMatrix, SensorManager.AXIS_MINUS_X,
SensorManager.AXIS_MINUS_Y, mRemappedMatrix);
SensorManager.getOrientation(mRemappedMatrix, mValues);
break;
case Surface.ROTATION_270:
// Log.d(TAG, "Rotation-270");
SensorManager
.remapCoordinateSystem(mRotationMatrix, SensorManager.AXIS_MINUS_Y,
SensorManager.AXIS_X, mRemappedMatrix);
SensorManager.getOrientation(mRemappedMatrix, mValues);
break;
default:
// This shouldn't happen - assume default orientation
SensorManager.getOrientation(mRotationMatrix, mValues);
// Log.d(TAG, "Rotation-Unknown");
break;
}
orientation = Math.toDegrees(mValues[0]); // azimuth
tilt = Math.toDegrees(mValues[1]);
break;
case Sensor.TYPE_ORIENTATION:
// Legacy orientation sensors
orientation = event.values[0];
break;
default:
// A sensor we're not using, so return
return;
}
// Correct for true north, if preference is set
if (mFaceTrueNorth && mGeomagneticField != null) {
orientation += mGeomagneticField.getDeclination();
// Make sure value is between 0-360
orientation = MathUtils.mod((float) orientation, 360.0f);
}
for (GpsTestListener listener : mGpsTestListeners) {
listener.onOrientationChanged(orientation, tilt);
}
}
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
private void getRotationMatrixFromTruncatedVector(float[] vector) {
System.arraycopy(vector, 0, mTruncatedRotationVector, 0, 4);
SensorManager.getRotationMatrixFromVector(mRotationMatrix, mTruncatedRotationVector);
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
private void updateGeomagneticField() {
mGeomagneticField = new GeomagneticField((float) mLastLocation.getLatitude(),
(float) mLastLocation.getLongitude(), (float) mLastLocation.getAltitude(),
mLastLocation.getTime());
}
private void sendLocation() {
if (mLastLocation != null) {
Intent intent = new Intent(Intent.ACTION_SENDTO,
Uri.fromParts("mailto", "", null));
String location = "http://maps.google.com/maps?geocode=&q=" +
Double.toString(mLastLocation.getLatitude()) + "," +
Double.toString(mLastLocation.getLongitude());
intent.putExtra(Intent.EXTRA_TEXT, location);
startActivity(intent);
}
}
@Override
public void onTabSelected(android.support.v7.app.ActionBar.Tab tab, FragmentTransaction ft) {
// When the given tab is selected, switch to the corresponding page in
// the ViewPager.
if (mViewPager != null) {
mViewPager.setCurrentItem(tab.getPosition());
}
}
@Override
public void onTabUnselected(android.support.v7.app.ActionBar.Tab tab, FragmentTransaction ft) {
}
@Override
public void onTabReselected(android.support.v7.app.ActionBar.Tab tab, FragmentTransaction ft) {
}
private void initActionBar(Bundle savedInstanceState) {
// Set up the action bar.
final android.support.v7.app.ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(android.support.v7.app.ActionBar.NAVIGATION_MODE_TABS);
actionBar.setTitle(getApplicationContext().getText(R.string.app_name));
// If we don't have a large screen, set up the tabs using the ViewPager
if (!mIsLargeScreen) {
// page adapter contains all the fragment registrations
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPagerMapBevelScroll) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
mViewPager.setOffscreenPageLimit(2);
// When swiping between different sections, select the corresponding
// tab. We can also use ActionBar.Tab#select() to do this if we have a
// reference to the Tab.
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
actionBar.setSelectedNavigationItem(position);
}
});
// For each of the sections in the app, add a tab to the action bar.
for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) {
// Create a tab with text corresponding to the page title defined by
// the adapter. Also specify this Activity object, which implements
// the TabListener interface, as the listener for when this tab is
// selected.
actionBar.addTab(actionBar.newTab()
.setText(mSectionsPagerAdapter.getPageTitle(i))
.setTabListener(this));
}
}
}
/**
* Show the "What's New" message if a new version was just installed
*/
@SuppressWarnings("deprecation")
private void autoShowWhatsNew() {
SharedPreferences settings = Application.getPrefs();
// Get the current app version.
PackageManager pm = getPackageManager();
PackageInfo appInfo = null;
try {
appInfo = pm.getPackageInfo(getPackageName(),
PackageManager.GET_META_DATA);
} catch (PackageManager.NameNotFoundException e) {
// Do nothing
return;
}
final int oldVer = settings.getInt(WHATS_NEW_VER, 0);
final int newVer = appInfo.versionCode;
if (oldVer < newVer) {
showDialog(WHATSNEW_DIALOG);
PreferenceUtils.saveInt(WHATS_NEW_VER, appInfo.versionCode);
}
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case WHATSNEW_DIALOG:
return createWhatsNewDialog();
}
return super.onCreateDialog(id);
}
@SuppressWarnings("deprecation")
private Dialog createWhatsNewDialog() {
TextView textView = (TextView) getLayoutInflater().inflate(R.layout.whats_new_dialog, null);
textView.setText(R.string.main_help_whatsnew);
android.support.v7.app.AlertDialog.Builder builder
= new android.support.v7.app.AlertDialog.Builder(this);
builder.setTitle(R.string.main_help_whatsnew_title);
builder.setIcon(R.drawable.gpstest_icon);
builder.setView(textView);
builder.setNeutralButton(R.string.main_help_close,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dismissDialog(WHATSNEW_DIALOG);
}
}
);
return builder.create();
}
}