mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 12:23:58 +01:00
* Resolves issue #2239 by adding an arrow for direction * Removed unnecessary change in styles.xml * spacing * javadoc --------- Co-authored-by: Nicolas Raoul <nicolas.raoul@gmail.com>
This commit is contained in:
parent
e99ff1c044
commit
e5c789e874
6 changed files with 105 additions and 5 deletions
|
|
@ -1,15 +1,21 @@
|
||||||
package fr.free.nrw.commons.contributions;
|
package fr.free.nrw.commons.contributions;
|
||||||
|
|
||||||
|
import static android.content.Context.SENSOR_SERVICE;
|
||||||
import static fr.free.nrw.commons.contributions.Contribution.STATE_FAILED;
|
import static fr.free.nrw.commons.contributions.Contribution.STATE_FAILED;
|
||||||
import static fr.free.nrw.commons.contributions.Contribution.STATE_PAUSED;
|
import static fr.free.nrw.commons.contributions.Contribution.STATE_PAUSED;
|
||||||
import static fr.free.nrw.commons.nearby.fragments.NearbyParentFragment.WLM_URL;
|
import static fr.free.nrw.commons.nearby.fragments.NearbyParentFragment.WLM_URL;
|
||||||
import static fr.free.nrw.commons.profile.ProfileActivity.KEY_USERNAME;
|
import static fr.free.nrw.commons.profile.ProfileActivity.KEY_USERNAME;
|
||||||
|
import static fr.free.nrw.commons.utils.LengthUtils.computeBearing;
|
||||||
import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween;
|
import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.Manifest.permission;
|
import android.Manifest.permission;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.hardware.Sensor;
|
||||||
|
import android.hardware.SensorEvent;
|
||||||
|
import android.hardware.SensorEventListener;
|
||||||
|
import android.hardware.SensorManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
|
|
@ -83,6 +89,7 @@ public class ContributionsFragment
|
||||||
OnBackStackChangedListener,
|
OnBackStackChangedListener,
|
||||||
LocationUpdateListener,
|
LocationUpdateListener,
|
||||||
MediaDetailProvider,
|
MediaDetailProvider,
|
||||||
|
SensorEventListener,
|
||||||
ICampaignsView, ContributionsContract.View, Callback{
|
ICampaignsView, ContributionsContract.View, Callback{
|
||||||
@Inject @Named("default_preferences") JsonKvStore store;
|
@Inject @Named("default_preferences") JsonKvStore store;
|
||||||
@Inject NearbyController nearbyController;
|
@Inject NearbyController nearbyController;
|
||||||
|
|
@ -122,6 +129,10 @@ public class ContributionsFragment
|
||||||
|
|
||||||
String userName;
|
String userName;
|
||||||
private boolean isUserProfile;
|
private boolean isUserProfile;
|
||||||
|
|
||||||
|
private SensorManager mSensorManager;
|
||||||
|
private Sensor mLight;
|
||||||
|
private float direction;
|
||||||
private ActivityResultLauncher<String[]> nearbyLocationPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback<Map<String, Boolean>>() {
|
private ActivityResultLauncher<String[]> nearbyLocationPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback<Map<String, Boolean>>() {
|
||||||
@Override
|
@Override
|
||||||
public void onActivityResult(Map<String, Boolean> result) {
|
public void onActivityResult(Map<String, Boolean> result) {
|
||||||
|
|
@ -162,6 +173,8 @@ public class ContributionsFragment
|
||||||
userName = getArguments().getString(KEY_USERNAME);
|
userName = getArguments().getString(KEY_USERNAME);
|
||||||
isUserProfile = true;
|
isUserProfile = true;
|
||||||
}
|
}
|
||||||
|
mSensorManager = (SensorManager) getActivity().getSystemService(SENSOR_SERVICE);
|
||||||
|
mLight = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|
@ -428,6 +441,7 @@ public class ContributionsFragment
|
||||||
super.onPause();
|
super.onPause();
|
||||||
locationManager.removeLocationListener(this);
|
locationManager.removeLocationListener(this);
|
||||||
locationManager.unregisterLocationManager();
|
locationManager.unregisterLocationManager();
|
||||||
|
mSensorManager.unregisterListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -464,6 +478,7 @@ public class ContributionsFragment
|
||||||
fetchCampaigns();
|
fetchCampaigns();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mSensorManager.registerListener(this, mLight, SensorManager.SENSOR_DELAY_UI);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkPermissionsAndShowNearbyCardView() {
|
private void checkPermissionsAndShowNearbyCardView() {
|
||||||
|
|
@ -521,7 +536,8 @@ public class ContributionsFragment
|
||||||
Place closestNearbyPlace = nearbyPlacesInfo.placeList.get(0);
|
Place closestNearbyPlace = nearbyPlacesInfo.placeList.get(0);
|
||||||
String distance = formatDistanceBetween(curLatLng, closestNearbyPlace.location);
|
String distance = formatDistanceBetween(curLatLng, closestNearbyPlace.location);
|
||||||
closestNearbyPlace.setDistance(distance);
|
closestNearbyPlace.setDistance(distance);
|
||||||
nearbyNotificationCardView.updateContent(closestNearbyPlace);
|
direction = (float) computeBearing(curLatLng, closestNearbyPlace.location);
|
||||||
|
nearbyNotificationCardView.updateContent(closestNearbyPlace, direction);
|
||||||
} else {
|
} else {
|
||||||
// Means that no close nearby place is found
|
// Means that no close nearby place is found
|
||||||
nearbyNotificationCardView.setVisibility(View.GONE);
|
nearbyNotificationCardView.setVisibility(View.GONE);
|
||||||
|
|
@ -793,6 +809,17 @@ public class ContributionsFragment
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the device rotates, rotate the Nearby banner's compass arrow in tandem.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onSensorChanged(SensorEvent event) {
|
||||||
|
float rotateDegree = Math.round(event.values[0]);
|
||||||
|
nearbyNotificationCardView.rotateCompass(rotateDegree, direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||||
|
// Nothing to do.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ public class NearbyNotificationCardView extends SwipableCardView {
|
||||||
private TextView notificationTitle;
|
private TextView notificationTitle;
|
||||||
private TextView notificationDistance;
|
private TextView notificationDistance;
|
||||||
private ImageView notificationIcon;
|
private ImageView notificationIcon;
|
||||||
|
private ImageView notificationCompass;
|
||||||
private ProgressBar progressBar;
|
private ProgressBar progressBar;
|
||||||
|
|
||||||
public CardViewVisibilityState cardViewVisibilityState;
|
public CardViewVisibilityState cardViewVisibilityState;
|
||||||
|
|
@ -64,6 +65,7 @@ public class NearbyNotificationCardView extends SwipableCardView {
|
||||||
notificationDistance = rootView.findViewById(R.id.nearby_distance);
|
notificationDistance = rootView.findViewById(R.id.nearby_distance);
|
||||||
|
|
||||||
notificationIcon = rootView.findViewById(R.id.nearby_icon);
|
notificationIcon = rootView.findViewById(R.id.nearby_icon);
|
||||||
|
notificationCompass = rootView.findViewById(R.id.nearby_compass);
|
||||||
|
|
||||||
progressBar = rootView.findViewById(R.id.progressBar);
|
progressBar = rootView.findViewById(R.id.progressBar);
|
||||||
|
|
||||||
|
|
@ -111,10 +113,11 @@ public class NearbyNotificationCardView extends SwipableCardView {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pass place information to views.
|
* Pass place information to views and set compass arrow direction
|
||||||
* @param place Closes place where we will get information from
|
* @param place Closes place where we will get information from
|
||||||
|
* @param direction Direction in which compass arrow needs to be set
|
||||||
*/
|
*/
|
||||||
public void updateContent(Place place) {
|
public void updateContent(Place place, float direction) {
|
||||||
Timber.d("Update nearby card notification content");
|
Timber.d("Update nearby card notification content");
|
||||||
this.setVisibility(VISIBLE);
|
this.setVisibility(VISIBLE);
|
||||||
cardViewVisibilityState = CardViewVisibilityState.READY;
|
cardViewVisibilityState = CardViewVisibilityState.READY;
|
||||||
|
|
@ -129,7 +132,7 @@ public class NearbyNotificationCardView extends SwipableCardView {
|
||||||
notificationIcon.setVisibility(VISIBLE);
|
notificationIcon.setVisibility(VISIBLE);
|
||||||
notificationTitle.setText(place.name);
|
notificationTitle.setText(place.name);
|
||||||
notificationDistance.setText(place.distance);
|
notificationDistance.setText(place.distance);
|
||||||
|
notificationCompass.setRotation(direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -151,6 +154,7 @@ public class NearbyNotificationCardView extends SwipableCardView {
|
||||||
notificationTitle.setVisibility(VISIBLE);
|
notificationTitle.setVisibility(VISIBLE);
|
||||||
notificationDistance.setVisibility(VISIBLE);
|
notificationDistance.setVisibility(VISIBLE);
|
||||||
notificationIcon.setVisibility(VISIBLE);
|
notificationIcon.setVisibility(VISIBLE);
|
||||||
|
notificationCompass.setVisibility(VISIBLE);
|
||||||
break;
|
break;
|
||||||
case LOADING:
|
case LOADING:
|
||||||
permissionRequestButton.setVisibility(GONE);
|
permissionRequestButton.setVisibility(GONE);
|
||||||
|
|
@ -160,6 +164,7 @@ public class NearbyNotificationCardView extends SwipableCardView {
|
||||||
notificationTitle.setVisibility(GONE);
|
notificationTitle.setVisibility(GONE);
|
||||||
notificationDistance.setVisibility(GONE);
|
notificationDistance.setVisibility(GONE);
|
||||||
notificationIcon.setVisibility(GONE);
|
notificationIcon.setVisibility(GONE);
|
||||||
|
notificationCompass.setVisibility(GONE);
|
||||||
permissionRequestButton.setVisibility(GONE);
|
permissionRequestButton.setVisibility(GONE);
|
||||||
break;
|
break;
|
||||||
case ASK_PERMISSION:
|
case ASK_PERMISSION:
|
||||||
|
|
@ -192,4 +197,14 @@ public class NearbyNotificationCardView extends SwipableCardView {
|
||||||
ENABLE_LOCATION_PERMISSION, // For only after Marshmallow
|
ENABLE_LOCATION_PERMISSION, // For only after Marshmallow
|
||||||
NO_PERMISSION_NEEDED
|
NO_PERMISSION_NEEDED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotates the compass arrow in tandem with the rotation of device
|
||||||
|
*
|
||||||
|
* @param rotateDegree Degree by which device was rotated
|
||||||
|
* @param direction Direction in which arrow has to point
|
||||||
|
*/
|
||||||
|
public void rotateCompass(float rotateDegree, float direction){
|
||||||
|
notificationCompass.setRotation(-(rotateDegree-direction));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -123,4 +123,23 @@ public class LengthUtils {
|
||||||
double sinHalf = Math.sin(x * 0.5D);
|
double sinHalf = Math.sin(x * 0.5D);
|
||||||
return sinHalf * sinHalf;
|
return sinHalf * sinHalf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes bearing between the two given points
|
||||||
|
*
|
||||||
|
* @see <a href="https://www.movable-type.co.uk/scripts/latlong.html">Bearing</a>
|
||||||
|
* @param point1 Coordinates of first point
|
||||||
|
* @param point2 Coordinates of second point
|
||||||
|
* @return Bearing between the two end points in degrees
|
||||||
|
* @throws NullPointerException if one or both the points are null
|
||||||
|
*/
|
||||||
|
public static double computeBearing(@NonNull LatLng point1, @NonNull LatLng point2) {
|
||||||
|
double diffLongitute = Math.toRadians(point2.getLongitude() - point1.getLongitude());
|
||||||
|
double lat1 = Math.toRadians(point1.getLatitude());
|
||||||
|
double lat2 = Math.toRadians(point2.getLatitude());
|
||||||
|
double y = Math.sin(diffLongitute) * Math.cos(lat2);
|
||||||
|
double x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(diffLongitute);
|
||||||
|
double bearing = Math.atan2(y, x);
|
||||||
|
return (Math.toDegrees(bearing) + 360) % 360;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
5
app/src/main/res/drawable/baseline_arrow_upward_24.xml
Normal file
5
app/src/main/res/drawable/baseline_arrow_upward_24.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<vector android:height="24dp" android:tint="?attr/tabTextColor"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="?attr/tabTextColor" android:pathData="M4,12l1.41,1.41L11,7.83V20h2V7.83l5.58,5.59L20,12l-8,-8 -8,8z"/>
|
||||||
|
</vector>
|
||||||
|
|
@ -93,6 +93,13 @@
|
||||||
|
|
||||||
</TextView>
|
</TextView>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/nearby_compass"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
app:srcCompat="@drawable/baseline_arrow_upward_24"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,29 @@ class LengthUtilsTest {
|
||||||
assertDistanceBetween(20015115.07, pointA, pointB)
|
assertDistanceBetween(20015115.07, pointA, pointB)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test LengthUtils.formatDistanceBetween()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testBearingPoleToPole() {
|
||||||
|
val pointA = LatLng(90.0, 0.0, 0f)
|
||||||
|
val pointB = LatLng(-90.0, 0.0, 0f)
|
||||||
|
assertBearing(180.00, pointA, pointB)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testBearingRandomPoints() {
|
||||||
|
val pointA = LatLng(27.17, 78.04, 0f)
|
||||||
|
val pointB = LatLng(-40.69, 04.13, 0f)
|
||||||
|
assertBearing(227.46, pointA, pointB)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testBearingSamePlace() {
|
||||||
|
val pointA = LatLng(90.0, 0.0, 0f)
|
||||||
|
val pointB = LatLng(90.0, 0.0, 0f)
|
||||||
|
assertBearing(0.0, pointA, pointB)
|
||||||
|
}
|
||||||
|
|
||||||
// Test assertion helper functions
|
// Test assertion helper functions
|
||||||
|
|
||||||
private fun assertFormattedDistanceBetween(expected: String, pointA: LatLng, pointB: LatLng) =
|
private fun assertFormattedDistanceBetween(expected: String, pointA: LatLng, pointB: LatLng) =
|
||||||
|
|
@ -117,4 +140,8 @@ class LengthUtilsTest {
|
||||||
private fun assertDistanceBetween(expected: Double, pointA: LatLng, pointB: LatLng) =
|
private fun assertDistanceBetween(expected: Double, pointA: LatLng, pointB: LatLng) =
|
||||||
// Acceptable error: 1cm
|
// Acceptable error: 1cm
|
||||||
assertEquals(expected, LengthUtils.computeDistanceBetween(pointA, pointB), 0.01)
|
assertEquals(expected, LengthUtils.computeDistanceBetween(pointA, pointB), 0.01)
|
||||||
|
|
||||||
|
private fun assertBearing(expected: Double, pointA: LatLng, pointB: LatLng) =
|
||||||
|
// Acceptable error: 1 degree
|
||||||
|
assertEquals(expected, LengthUtils.computeBearing(pointA,pointB), 1.0)
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue