|
@@ -1,14 +1,380 @@
|
|
package me.yoqi.android.compass;
|
|
package me.yoqi.android.compass;
|
|
|
|
|
|
|
|
+import android.Manifest;
|
|
|
|
+import android.content.Context;
|
|
|
|
+import android.content.pm.PackageManager;
|
|
|
|
+import android.hardware.Sensor;
|
|
|
|
+import android.hardware.SensorEvent;
|
|
|
|
+import android.hardware.SensorEventListener;
|
|
|
|
+import android.hardware.SensorManager;
|
|
|
|
+import android.location.Criteria;
|
|
|
|
+import android.location.Location;
|
|
|
|
+import android.location.LocationListener;
|
|
|
|
+import android.location.LocationManager;
|
|
|
|
+import android.location.LocationProvider;
|
|
|
|
+import android.os.Bundle;
|
|
|
|
+import android.os.Handler;
|
|
|
|
+import android.text.TextUtils;
|
|
|
|
+import android.view.View;
|
|
|
|
+import android.view.ViewGroup.LayoutParams;
|
|
|
|
+import android.view.animation.AccelerateInterpolator;
|
|
|
|
+import android.widget.ImageView;
|
|
|
|
+import android.widget.LinearLayout;
|
|
|
|
+import android.widget.TextView;
|
|
|
|
+
|
|
import androidx.appcompat.app.AppCompatActivity;
|
|
import androidx.appcompat.app.AppCompatActivity;
|
|
|
|
+import androidx.core.app.ActivityCompat;
|
|
|
|
|
|
-import android.os.Bundle;
|
|
|
|
|
|
+import java.util.Locale;
|
|
|
|
+
|
|
|
|
+import me.yoqi.android.compass.ui.CompassView;
|
|
|
|
|
|
public class MainActivity extends AppCompatActivity {
|
|
public class MainActivity extends AppCompatActivity {
|
|
|
|
|
|
|
|
+ private final float MAX_ROATE_DEGREE = 1.0f;
|
|
|
|
+ private SensorManager mSensorManager;
|
|
|
|
+ private Sensor mOrientationSensor;
|
|
|
|
+ private LocationManager mLocationManager;
|
|
|
|
+ private String mLocationProvider;
|
|
|
|
+ private float mDirection;
|
|
|
|
+ private float mTargetDirection;
|
|
|
|
+ private AccelerateInterpolator mInterpolator;
|
|
|
|
+ protected final Handler mHandler = new Handler();
|
|
|
|
+ private boolean mStopDrawing;
|
|
|
|
+ private boolean mChinease;
|
|
|
|
+ Context mContext;
|
|
|
|
+
|
|
|
|
+ View mCompassView;
|
|
|
|
+ CompassView mPointer;
|
|
|
|
+ TextView mLocationTextView;
|
|
|
|
+ LinearLayout mDirectionLayout;
|
|
|
|
+ LinearLayout mAngleLayout;
|
|
|
|
+
|
|
|
|
+ protected Runnable mCompassViewUpdater = new Runnable() {
|
|
|
|
+ @Override
|
|
|
|
+ public void run() {
|
|
|
|
+ if (mPointer != null && !mStopDrawing) {
|
|
|
|
+ if (mDirection != mTargetDirection) {
|
|
|
|
+
|
|
|
|
+ // calculate the short routine
|
|
|
|
+ float to = mTargetDirection;
|
|
|
|
+ if (to - mDirection > 180) {
|
|
|
|
+ to -= 360;
|
|
|
|
+ } else if (to - mDirection < -180) {
|
|
|
|
+ to += 360;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // limit the max speed to MAX_ROTATE_DEGREE
|
|
|
|
+ float distance = to - mDirection;
|
|
|
|
+ if (Math.abs(distance) > MAX_ROATE_DEGREE) {
|
|
|
|
+ distance = distance > 0 ? MAX_ROATE_DEGREE : (-1.0f * MAX_ROATE_DEGREE);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // need to slow down if the distance is short
|
|
|
|
+ mDirection = normalizeDegree(mDirection
|
|
|
|
+ + ((to - mDirection) * mInterpolator.getInterpolation(Math
|
|
|
|
+ .abs(distance) > MAX_ROATE_DEGREE ? 0.4f : 0.3f)));
|
|
|
|
+ mPointer.updateDirection(mDirection);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ updateDirection();
|
|
|
|
+
|
|
|
|
+ mHandler.postDelayed(mCompassViewUpdater, 20);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
@Override
|
|
@Override
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
super.onCreate(savedInstanceState);
|
|
setContentView(R.layout.activity_main);
|
|
setContentView(R.layout.activity_main);
|
|
|
|
+ mContext = this;
|
|
|
|
+ initResources();
|
|
|
|
+ initServices();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ protected void onResume() {
|
|
|
|
+ super.onResume();
|
|
|
|
+ //权限检测
|
|
|
|
+ if (mLocationProvider != null) {
|
|
|
|
+ if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
|
|
|
|
+ // TODO: Consider calling
|
|
|
|
+ // ActivityCompat#requestPermissions
|
|
|
|
+ // here to request the missing permissions, and then overriding
|
|
|
|
+ // public void onRequestPermissionsResult(int requestCode, String[] permissions,
|
|
|
|
+ // int[] grantResults)
|
|
|
|
+ // to handle the case where the user grants the permission. See the documentation
|
|
|
|
+ // for ActivityCompat#requestPermissions for more details.
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ updateLocation(mLocationManager.getLastKnownLocation(mLocationProvider));
|
|
|
|
+ mLocationManager.requestLocationUpdates(mLocationProvider, 2000, 10, mLocationListener);
|
|
|
|
+ } else {
|
|
|
|
+ mLocationTextView.setText(R.string.cannot_get_location);
|
|
|
|
+ }
|
|
|
|
+ if (mOrientationSensor != null) {
|
|
|
|
+ mSensorManager.registerListener(mOrientationSensorEventListener, mOrientationSensor,
|
|
|
|
+ SensorManager.SENSOR_DELAY_GAME);
|
|
|
|
+ }
|
|
|
|
+ mStopDrawing = false;
|
|
|
|
+ mHandler.postDelayed(mCompassViewUpdater, 20);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ protected void onPause() {
|
|
|
|
+ super.onPause();
|
|
|
|
+ mStopDrawing = true;
|
|
|
|
+ if (mOrientationSensor != null) {
|
|
|
|
+ mSensorManager.unregisterListener(mOrientationSensorEventListener);
|
|
|
|
+ }
|
|
|
|
+ if (mLocationProvider != null) {
|
|
|
|
+ mLocationManager.removeUpdates(mLocationListener);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void initResources() {
|
|
|
|
+ mDirection = 0.0f;
|
|
|
|
+ mTargetDirection = 0.0f;
|
|
|
|
+ mInterpolator = new AccelerateInterpolator();
|
|
|
|
+ mStopDrawing = true;
|
|
|
|
+ mChinease = TextUtils.equals(Locale.getDefault().getLanguage(), "zh");
|
|
|
|
+
|
|
|
|
+ mCompassView = findViewById(R.id.view_compass);
|
|
|
|
+ mPointer = (CompassView) findViewById(R.id.compass_pointer);
|
|
|
|
+ mLocationTextView = (TextView) findViewById(R.id.textview_location);
|
|
|
|
+ mDirectionLayout = (LinearLayout) findViewById(R.id.layout_direction);
|
|
|
|
+ mAngleLayout = (LinearLayout) findViewById(R.id.layout_angle);
|
|
|
|
+
|
|
|
|
+ mPointer.setImageResource(mChinease ? R.mipmap.compass_cn : R.mipmap.compass);
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ private void initServices() {
|
|
|
|
+ // sensor manager
|
|
|
|
+ mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
|
|
|
|
+ mOrientationSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
|
|
|
|
+
|
|
|
|
+ // location manager
|
|
|
|
+ mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
|
|
|
|
+ Criteria criteria = new Criteria();
|
|
|
|
+ criteria.setAccuracy(Criteria.ACCURACY_FINE);
|
|
|
|
+ criteria.setAltitudeRequired(false);
|
|
|
|
+ criteria.setBearingRequired(false);
|
|
|
|
+ criteria.setCostAllowed(true);
|
|
|
|
+ criteria.setPowerRequirement(Criteria.POWER_LOW);
|
|
|
|
+ mLocationProvider = mLocationManager.getBestProvider(criteria, true);
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void updateDirection() {
|
|
|
|
+ LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
|
|
|
+
|
|
|
|
+ mDirectionLayout.removeAllViews();
|
|
|
|
+ mAngleLayout.removeAllViews();
|
|
|
|
+
|
|
|
|
+ ImageView east = null;
|
|
|
|
+ ImageView west = null;
|
|
|
|
+ ImageView south = null;
|
|
|
|
+ ImageView north = null;
|
|
|
|
+ float direction = normalizeDegree(mTargetDirection * -1.0f);
|
|
|
|
+ if (direction > 22.5f && direction < 157.5f) {
|
|
|
|
+ // east
|
|
|
|
+ east = new ImageView(this);
|
|
|
|
+ east.setImageResource(mChinease ? R.mipmap.e_cn : R.mipmap.e);
|
|
|
|
+ east.setLayoutParams(lp);
|
|
|
|
+ } else if (direction > 202.5f && direction < 337.5f) {
|
|
|
|
+ // west
|
|
|
|
+ west = new ImageView(this);
|
|
|
|
+ west.setImageResource(mChinease ? R.mipmap.w_cn : R.mipmap.w);
|
|
|
|
+ west.setLayoutParams(lp);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (direction > 112.5f && direction < 247.5f) {
|
|
|
|
+ // south
|
|
|
|
+ south = new ImageView(this);
|
|
|
|
+ south.setImageResource(mChinease ? R.mipmap.s_cn : R.mipmap.s);
|
|
|
|
+ south.setLayoutParams(lp);
|
|
|
|
+ } else if (direction < 67.5 || direction > 292.5f) {
|
|
|
|
+ // north
|
|
|
|
+ north = new ImageView(this);
|
|
|
|
+ north.setImageResource(mChinease ? R.mipmap.n_cn : R.mipmap.n);
|
|
|
|
+ north.setLayoutParams(lp);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (mChinease) {
|
|
|
|
+ // east/west should be before north/south
|
|
|
|
+ if (east != null) {
|
|
|
|
+ mDirectionLayout.addView(east);
|
|
|
|
+ }
|
|
|
|
+ if (west != null) {
|
|
|
|
+ mDirectionLayout.addView(west);
|
|
|
|
+ }
|
|
|
|
+ if (south != null) {
|
|
|
|
+ mDirectionLayout.addView(south);
|
|
|
|
+ }
|
|
|
|
+ if (north != null) {
|
|
|
|
+ mDirectionLayout.addView(north);
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ // north/south should be before east/west
|
|
|
|
+ if (south != null) {
|
|
|
|
+ mDirectionLayout.addView(south);
|
|
|
|
+ }
|
|
|
|
+ if (north != null) {
|
|
|
|
+ mDirectionLayout.addView(north);
|
|
|
|
+ }
|
|
|
|
+ if (east != null) {
|
|
|
|
+ mDirectionLayout.addView(east);
|
|
|
|
+ }
|
|
|
|
+ if (west != null) {
|
|
|
|
+ mDirectionLayout.addView(west);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ int direction2 = (int) direction;
|
|
|
|
+ boolean show = false;
|
|
|
|
+ if (direction2 >= 100) {
|
|
|
|
+ mAngleLayout.addView(getNumberImage(direction2 / 100));
|
|
|
|
+ direction2 %= 100;
|
|
|
|
+ show = true;
|
|
|
|
+ }
|
|
|
|
+ if (direction2 >= 10 || show) {
|
|
|
|
+ mAngleLayout.addView(getNumberImage(direction2 / 10));
|
|
|
|
+ direction2 %= 10;
|
|
|
|
+ }
|
|
|
|
+ mAngleLayout.addView(getNumberImage(direction2));
|
|
|
|
+
|
|
|
|
+ ImageView degreeImageView = new ImageView(this);
|
|
|
|
+ degreeImageView.setImageResource(R.mipmap.degree);
|
|
|
|
+ degreeImageView.setLayoutParams(lp);
|
|
|
|
+ mAngleLayout.addView(degreeImageView);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private ImageView getNumberImage(int number) {
|
|
|
|
+ ImageView image = new ImageView(this);
|
|
|
|
+ LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
|
|
|
+ switch (number) {
|
|
|
|
+ case 0:
|
|
|
|
+ image.setImageResource(R.mipmap.number_0);
|
|
|
|
+ break;
|
|
|
|
+ case 1:
|
|
|
|
+ image.setImageResource(R.mipmap.number_1);
|
|
|
|
+ break;
|
|
|
|
+ case 2:
|
|
|
|
+ image.setImageResource(R.mipmap.number_2);
|
|
|
|
+ break;
|
|
|
|
+ case 3:
|
|
|
|
+ image.setImageResource(R.mipmap.number_3);
|
|
|
|
+ break;
|
|
|
|
+ case 4:
|
|
|
|
+ image.setImageResource(R.mipmap.number_4);
|
|
|
|
+ break;
|
|
|
|
+ case 5:
|
|
|
|
+ image.setImageResource(R.mipmap.number_5);
|
|
|
|
+ break;
|
|
|
|
+ case 6:
|
|
|
|
+ image.setImageResource(R.mipmap.number_6);
|
|
|
|
+ break;
|
|
|
|
+ case 7:
|
|
|
|
+ image.setImageResource(R.mipmap.number_7);
|
|
|
|
+ break;
|
|
|
|
+ case 8:
|
|
|
|
+ image.setImageResource(R.mipmap.number_8);
|
|
|
|
+ break;
|
|
|
|
+ case 9:
|
|
|
|
+ image.setImageResource(R.mipmap.number_9);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ image.setLayoutParams(lp);
|
|
|
|
+ return image;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void updateLocation(Location location) {
|
|
|
|
+ if (location == null) {
|
|
|
|
+ mLocationTextView.setText(R.string.getting_location);
|
|
|
|
+ } else {
|
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
|
+ double latitude = location.getLatitude();
|
|
|
|
+ double longitude = location.getLongitude();
|
|
|
|
+
|
|
|
|
+ if (latitude >= 0.0f) {
|
|
|
|
+ sb.append(getString(R.string.location_north, getLocationString(latitude)));
|
|
|
|
+ } else {
|
|
|
|
+ sb.append(getString(R.string.location_south, getLocationString(-1.0 * latitude)));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ sb.append(" ");
|
|
|
|
+
|
|
|
|
+ if (longitude >= 0.0f) {
|
|
|
|
+ sb.append(getString(R.string.location_east, getLocationString(longitude)));
|
|
|
|
+ } else {
|
|
|
|
+ sb.append(getString(R.string.location_west, getLocationString(-1.0 * longitude)));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ mLocationTextView.setText(sb.toString());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private String getLocationString(double input) {
|
|
|
|
+ int du = (int) input;
|
|
|
|
+ int fen = (((int) ((input - du) * 3600))) / 60;
|
|
|
|
+ int miao = (((int) ((input - du) * 3600))) % 60;
|
|
|
|
+ return String.valueOf(du) + "°" + String.valueOf(fen) + "'" + String.valueOf(miao) + "\"";
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private SensorEventListener mOrientationSensorEventListener = new SensorEventListener() {
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void onSensorChanged(SensorEvent event) {
|
|
|
|
+ float direction = event.values[0] * -1.0f;
|
|
|
|
+ mTargetDirection = normalizeDegree(direction);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ private float normalizeDegree(float degree) {
|
|
|
|
+ return (degree + 720) % 360;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ LocationListener mLocationListener = new LocationListener() {
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void onStatusChanged(String provider, int status, Bundle extras) {
|
|
|
|
+ if (status != LocationProvider.OUT_OF_SERVICE) {
|
|
|
|
+ //权限检测
|
|
|
|
+ if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) !=
|
|
|
|
+ PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(mContext,
|
|
|
|
+ Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
|
|
|
|
+ // TODO: Consider calling
|
|
|
|
+ // ActivityCompat#requestPermissions
|
|
|
|
+ // here to request the missing permissions, and then overriding
|
|
|
|
+ // public void onRequestPermissionsResult(int requestCode, String[] permissions,
|
|
|
|
+ // int[] grantResults)
|
|
|
|
+ // to handle the case where the user grants the permission. See the documentation
|
|
|
|
+ // for ActivityCompat#requestPermissions for more details.
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ updateLocation(mLocationManager.getLastKnownLocation(mLocationProvider));
|
|
|
|
+ } else {
|
|
|
|
+ mLocationTextView.setText(R.string.cannot_get_location);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void onProviderEnabled(String provider) {
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void onProviderDisabled(String provider) {
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void onLocationChanged(Location location) {
|
|
|
|
+ updateLocation(location);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ };
|
|
}
|
|
}
|