- How to work with Compass
- How to work with GPS
- How to work with Camera
- Add confirmation dialog
- Add error dialog
- Select directory
- Add line in view
- Custom view
- Vector 3D
- Add
DeviceCompass
class to the activity. The default value forazimuthStep
is 0 degrees. If set, the azimuth (and other orientation angles) will be returned only if the change is bigger than the azimuth step:
private DeviceCompass deviceCompass;
...
// pass Context
this.deviceCompass = new DeviceCompass(this);
// you can set the azimuth step in degrees:
this.deviceCompass.setAzimuthStep(5);
- Implement
DeviceCompass.OnOrientationChangedEventListener
listener in the activity:
this.deviceCompass.setOnOrientationChangedEventListener(this);
...
@Override
public void onOrientationChanged(float azimuth, float pitch, float roll) {
// display azimuth value
((TextView) findViewById(R.id.textAzimuth)).setText(Long.toString(Math.round(azimuth)));
// rotate image view
this.mCompassDial.setRotation((float) (360 - azimuth));
// pitch * -1 - if used in Android.graphics.Camera
}
- Attach/detach to process. While in background we no longger need to listen for changes to the azimuth value:
@Override
protected void onResume() {
super.onResume();
this.deviceCompass.startListening();
}
@Override
protected void onPause() {
super.onPause();
this.deviceCompass.stopListening();
}
- Add permissions to android manifest:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
- Add
DeviceLocation
class - Add
startGPS()
andstopGPS()
methods:
private void startGPS() {
registerReceiver(receiver, new IntentFilter(DeviceLocation.DEVICE_LOCATION));
Intent intent = new Intent(getApplicationContext(), DeviceLocation.class);
intent.putExtra(DeviceLocation.LOCATION_INTERVAL, 10000); // wait atleast 10 seconds
intent.putExtra(DeviceLocation.LOCATION_DISTANCE, 50f); // call on distance changed with 50 meters
startService(intent);
}
private void stopGPS() {
unregisterReceiver(receiver);
stopService(new Intent(this, DeviceLocation.class));
}
- You can set some of the parameters when starting the service:
- minimum location interval (in miliseconds), default is
10000
; - minimum location distance (in meters), default is
0f
; - to create a notification or not, default is
true
.
Intent intent = new Intent(getApplicationContext(), DeviceLocation.class);
intent.putExtra(DeviceLocation.LOCATION_INTERVAL, 1000); // 1 second
intent.putExtra(DeviceLocation.LOCATION_DISTANCE, 50f); // 50 meters
intent.putExtra(DeviceLocation.CREATE_NOTIFICATION, false); // do not create a notification
startService(intent);
- Add
BroadcastReceiver
toMainActivity
. InonReceive()
you can get device location information:latitude
,longitude
,altitude
andaccuracy
.
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
if (bundle != null) {
double latitude = (double) bundle.get(DeviceLocation.EXTRA_LATITUDE);
double longitude = (double) bundle.get(DeviceLocation.EXTRA_LONGITUDE);
double altitude = (double) bundle.get(DeviceLocation.EXTRA_ALTITUDE);
float accuracy = (float) bundle.get(DeviceLocation.EXTRA_ACCURACY);
}
}
};
- Available navigation data:
DeviceLocation.EXTRA_LATITUDE
- geographic latitude (WGS84);DeviceLocation.EXTRA_LONGITUDE
- geographic longitude (WGS84);DeviceLocation.EXTRA_ALTITUDE
- GPS altitude in meters;DeviceLocation.EXTRA_ACCURACY
- position accuracy in meters;DeviceLocation.EXTRA_TIME
- UTC time of this fix, in milliseconds since January 1, 1970DeviceLocation.EXTRA_DECLINATION
- get magnetic declination for this location in degrees.
- Check GPS permissions:
private void checkPermissions() {
int permission = ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION);
if (permission != PackageManager.PERMISSION_GRANTED) {
// ask user for permissions
ActivityCompat.requestPermissions(
this,
PERMISSIONS,
REQUEST_PERMISSION
);
}
}
- Add permissions to android manifest:
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera2.full" />
- Add
CameraPreviewActivity
activity,CameraFragment
fragment andAutoFitTextureView
class. You'll also needlayout/activity_camera
andlayout/fragment_camera
xml files.
- you can add only
CameraFragment
and just add a fragment in your activity:
<fragment android:name="PACKAGE.NAME.CameraFragment"
android:id="@+id/cameraPreview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
@Override
protected void onResume() {
super.onResume();
if (this.mCameraFragment == null) {
this.mCameraFragment = (CameraFragment) getFragmentManager().findFragmentById(R.id.cameraPreview);
// work with camera fragment
}
}
- Check camera permissions:
private static final int REQUEST_PERMISSION = 1;
private void checkCameraPermissions() {
int permission = ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
if (permission != PackageManager.PERMISSION_GRANTED) {
// ask user for permissions
ActivityCompat.requestPermissions(
this,
new String[]{Manifest.permission.CAMERA},
REQUEST_PERMISSION
);
}
}
- Using the camera preview:
- you can call camera from the main activity using:
Intent camera = new Intent(getApplicationContext(), CameraPreviewActivity.class);
startActivity(camera);
- Whatch out for
private static Size chooseOptimalSize(Size[], int, int, int, int, Size)
. In it we can define the max preview size according to screen size! If not used as a full screen activity, you must updatemaxHeight
andmaxWidth
. - To calculate horizontal and vertical field of view you can use:
private float getHorizontalFieldOfView(CameraCharacteristics info) {
SizeF sensorSize = info.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE);
float[] focalLengths = info.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
if (focalLengths != null && focalLengths.length > 0) {
return (float) (2.0f * Math.atan(sensorSize.getWidth() / (2.0f * focalLengths[0])));
}
return 1.1f;
}
private float getVerticalFieldOfView(CameraCharacteristics info) {
SizeF sensorSize = info.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE);
float[] focalLengths = info.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
if (focalLengths != null && focalLengths.length > 0) {
return (float) (2.0f * Math.atan(sensorSize.getHeight() / (2.0f * focalLengths[0])));
}
return 1.1f;
}
...
int orientation = getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
this.mVerticalFOV = Math.toDegrees(this.getVerticalFieldOfView(characteristics));
this.mHorizontalFOV = Math.toDegrees(this.getHorizontalFieldOfView(characteristics));
} else {
this.mVerticalFOV = Math.toDegrees(this.getHorizontalFieldOfView(characteristics));
this.mHorizontalFOV = Math.toDegrees(this.getVerticalFieldOfView(characteristics));
}
- Add class:
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
public class DialogAlert {
public static Dialog create(Activity activity, String message, String title) {
title = (title != null ? title : "Alert");
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setMessage(message)
.setTitle(title).setCancelable(true)
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
}
});
return builder.create();
}
}
- Use it:
DialogAlert.create(this, "Message", "Title").show();
private void chooseDirectory() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.addCategory(Intent.CATEGORY_DEFAULT);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(intent, REQUEST_CODE);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
Uri uri = data.getData(); // get selected directory URI
}
}
- For a verical line:
<View
android:id="@+id/targetLine"
android:layout_width="1dp"
android:layout_height="fill_parent"
android:layout_marginBottom="50dp"
android:background="@color/primaryColor"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
- For a horizontal line:
<View
android:id="@+id/targetLine"
android:layout_width="fill_parent"
android:layout_height="1dp"
android:layout_marginBottom="50dp"
android:background="@color/primaryColor"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
- Create a custom view class -
DirectionView
. - Add view to layout:
<PACKAGE.NAME.DirectionView
android:id="@+id/directionView"
android:layout_width="255dp"
android:layout_height="259dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
or programmatically:
// this - context
DirectionView view = new DirectionView(this);
container.addView(view);
- The test view -
DirectionView
is only an arrow object, pointing to a location. You can update it by calling:
directionView = (TargetView) findViewById(R.id.directionView);
// current direction - can be set to phone view direction in degrees
// targetDirection - is the bearuing to the target - the desired rotation of DirectionView
// tilt - phone pitch angle * -1
directionView.updateRotation(currentDirection, targetDirection, tilt);
- http://www.euclideanspace.com/maths/algebra/vectors/applications/normals/index.htm
- https://introcs.cs.princeton.edu/java/33design/Vector.java.html
https://stackoverflow.com/questions/701504/perspective-projection-help-a-noob/701978#701978
Here's a very general answer. Say the camera's at (Xc
, Yc
, Zc
) and the point you want to project is P = (X
, Y
, Z
). The distance from the camera to the 2D plane onto which you are projecting is F
(so the equation of the plane is Z - Zc = F
). The 2D coordinates of P projected onto the plane are (X'
, Y'
).
Then, very simply:
X' = ((X - Xc) * (F/Z)) + Xc
Y' = ((Y - Yc) * (F/Z)) + Yc
If your camera is the origin, then this simplifies to:
X' = X * (F/Z)
Y' = Y * (F/Z)
When the phone tilt is changed then calculate the camera position:
camera.rotateX(tilt) // or tilt * -1?