Skip to content

Commit abea63a

Browse files
committed
Merge branch 'dev'
2 parents 43d0bb8 + 751c099 commit abea63a

17 files changed

+213
-31
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
A simple app to create and manage georeferenced notes (text and photos) on a map. The goal is to create the notes very fast and without any unnecessary UI/UX overhead.
55

66
<p align="center">
7-
<img src="screenshots.png" alt="GeoNotes Screenshots" height="500"/>
7+
<img src="screenshots.png" alt="GeoNotes Screenshots"/>
88
</p>
99

1010
## Get it

app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ android {
1010
applicationId "de.hauke_stieler.geonotes"
1111
minSdkVersion 16
1212
targetSdkVersion 30
13-
versionCode 1004001
14-
versionName "1.4.1"
13+
versionCode 1004002
14+
versionName "1.4.2"
1515

1616
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
1717
}

app/src/main/java/de/hauke_stieler/geonotes/Injector.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,6 @@ private static SharedPreferences buildSharedPreferences() {
7272

7373
private static de.hauke_stieler.geonotes.map.Map buildMap() {
7474
MapView mapView = activity.findViewById(R.id.map);
75-
return new de.hauke_stieler.geonotes.map.Map(context, mapView, get(Database.class));
75+
return new de.hauke_stieler.geonotes.map.Map(context, mapView, get(Database.class), get(SharedPreferences.class));
7676
}
7777
}

app/src/main/java/de/hauke_stieler/geonotes/MainActivity.java

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,17 @@ private void createMap() {
107107
}
108108

109109
void loadPreferences() {
110-
for (String key : preferences.getAll().keySet()) {
111-
preferenceChanged(preferences, key);
112-
}
110+
boolean showZoomButtons = preferences.getBoolean(getString(R.string.pref_zoom_buttons), true);
111+
map.setZoomButtonVisibility(showZoomButtons);
112+
113+
float mapScale = preferences.getFloat(getString(R.string.pref_map_scaling), 1.0f);
114+
map.setMapScaleFactor(mapScale);
115+
116+
boolean snapNoteToGps = preferences.getBoolean(getString(R.string.pref_snap_note_gps), false);
117+
map.setSnapNoteToGps(snapNoteToGps);
118+
119+
boolean enableRotatingMap1 = preferences.getBoolean(getString(R.string.pref_enable_rotating_map), false);
120+
map.updateMapRotationBehavior(enableRotatingMap1);
113121

114122
float lat = preferences.getFloat(getString(R.string.pref_last_location_lat), 0f);
115123
float lon = preferences.getFloat(getString(R.string.pref_last_location_lon), 0f);
@@ -118,19 +126,6 @@ void loadPreferences() {
118126
map.setLocation(lat, lon, zoom);
119127
}
120128

121-
private void preferenceChanged(SharedPreferences pref, String key) {
122-
if (getString(R.string.pref_zoom_buttons).equals(key)) {
123-
boolean showZoomButtons = pref.getBoolean(key, true);
124-
map.setZoomButtonVisibility(showZoomButtons);
125-
} else if (getString(R.string.pref_map_scaling).equals(key)) {
126-
float mapScale = pref.getFloat(key, 1.0f);
127-
map.setMapScaleFactor(mapScale);
128-
} else if (getString(R.string.pref_snap_note_gps).equals(key)) {
129-
boolean snapNoteToGps = pref.getBoolean(key, false);
130-
map.setSnapNoteToGps(snapNoteToGps);
131-
}
132-
}
133-
134129
@Override
135130
public boolean onCreateOptionsMenu(Menu menu) {
136131
getMenuInflater().inflate(R.menu.toolbar_menu, menu);
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package de.hauke_stieler.geonotes.map;
2+
3+
import android.content.Context;
4+
import android.graphics.Point;
5+
import android.view.MotionEvent;
6+
7+
import org.osmdroid.events.MapEventsReceiver;
8+
import org.osmdroid.util.GeoPoint;
9+
import org.osmdroid.views.MapView;
10+
import org.osmdroid.views.overlay.MapEventsOverlay;
11+
import org.osmdroid.views.overlay.compass.CompassOverlay;
12+
13+
public class ClickableMapCompass extends CompassOverlay {
14+
private final SnappableRotationOverlay orientationProvider;
15+
16+
public ClickableMapCompass(Context context, SnappableRotationOverlay orientationProvider, MapView mapView) {
17+
super(context, orientationProvider, mapView);
18+
this.orientationProvider = orientationProvider;
19+
}
20+
21+
@Override
22+
public boolean onSingleTapConfirmed(MotionEvent e, MapView mapView) {
23+
Point pixelPosition = mapView.getProjection().rotateAndScalePoint((int) e.getX(), (int) e.getY(), null);
24+
return hitTest(pixelPosition.x, pixelPosition.y);
25+
}
26+
27+
/**
28+
* Checks in a hacky way whether the given pixel position is roughly within the compass rose.
29+
*/
30+
private boolean hitTest(float x, float y) {
31+
float xStart = mCompassFrameCenterX - mCompassFrameBitmap.getWidth() / 2f;
32+
float xEnd = mCompassFrameCenterX + mCompassFrameBitmap.getWidth() / 2f;
33+
float yStart = mCompassFrameCenterY - mCompassFrameBitmap.getHeight() / 2f;
34+
float yEnd = mCompassFrameCenterY + mCompassFrameBitmap.getHeight() / 2f;
35+
36+
if (x >= xStart && x <= xEnd && y >= yStart && y <= yEnd) {
37+
orientationProvider.resetRotation();
38+
return true; // prevent further propagation of event to other event listeners
39+
}
40+
return false;
41+
}
42+
}

app/src/main/java/de/hauke_stieler/geonotes/map/Map.java

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22

33
import android.annotation.SuppressLint;
44
import android.content.Context;
5+
import android.content.SharedPreferences;
56
import android.graphics.Point;
67
import android.graphics.drawable.BitmapDrawable;
78
import android.graphics.drawable.Drawable;
89
import android.os.PowerManager;
910
import android.util.DisplayMetrics;
10-
import android.view.DragEvent;
1111
import android.view.MotionEvent;
1212
import android.view.View;
1313

@@ -27,21 +27,22 @@
2727
import org.osmdroid.views.overlay.Overlay;
2828
import org.osmdroid.views.overlay.ScaleBarOverlay;
2929
import org.osmdroid.views.overlay.compass.CompassOverlay;
30-
import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider;
3130
import org.osmdroid.views.overlay.mylocation.GpsMyLocationProvider;
3231
import org.osmdroid.views.overlay.mylocation.MyLocationNewOverlay;
3332

3433
import java.io.File;
3534
import java.util.List;
3635

37-
import de.hauke_stieler.geonotes.database.Database;
36+
import de.hauke_stieler.geonotes.Injector;
3837
import de.hauke_stieler.geonotes.R;
38+
import de.hauke_stieler.geonotes.database.Database;
3939
import de.hauke_stieler.geonotes.notes.Note;
4040

4141
public class Map {
4242
private final Context context;
4343
private final PowerManager.WakeLock wakeLock;
4444
private final Database database;
45+
private SharedPreferences preferences;
4546

4647
private final MapView map;
4748
private final IMapController mapController;
@@ -62,12 +63,17 @@ public class Map {
6263
private Marker markerToMove;
6364
private Point dragStartMarkerPosition;
6465

66+
private SnappableRotationOverlay rotationGestureOverlay;
67+
private ClickableMapCompass compassOverlay;
68+
6569
public Map(Context context,
6670
MapView map,
67-
Database database) {
71+
Database database,
72+
SharedPreferences preferences) {
6873
this.context = context;
6974
this.map = map;
7075
this.database = database;
76+
this.preferences = preferences;
7177

7278
// Keep device on
7379
final PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
@@ -93,15 +99,15 @@ public Map(Context context,
9399
GeoPoint startPoint = new GeoPoint(53.563, 9.9866);
94100
mapController.setCenter(startPoint);
95101

96-
createOverlays(context, map, (BitmapDrawable) locationIcon, (BitmapDrawable) arrowIcon);
102+
createOverlays((BitmapDrawable) locationIcon, (BitmapDrawable) arrowIcon);
97103
createMarkerWindow(map);
98104

99105
for (Note n : this.database.getAllNotes()) {
100106
createMarker("" + n.getId(), n.getDescription(), new GeoPoint(n.getLat(), n.getLon()), markerClickListener);
101107
}
102108
}
103109

104-
private void createOverlays(Context context, MapView map, BitmapDrawable locationIcon, BitmapDrawable arrowIcon) {
110+
private void createOverlays(BitmapDrawable locationIcon, BitmapDrawable arrowIcon) {
105111
// Add location icon
106112
gpsLocationProvider = new GpsMyLocationProvider(context);
107113
locationOverlay = new MyLocationNewOverlay(gpsLocationProvider, map);
@@ -110,10 +116,10 @@ private void createOverlays(Context context, MapView map, BitmapDrawable locatio
110116
locationOverlay.setPersonHotspot(32, 32);
111117
map.getOverlays().add(this.locationOverlay);
112118

113-
// Add compass
114-
CompassOverlay compassOverlay = new CompassOverlay(context, new InternalCompassOrientationProvider(context), map);
115-
compassOverlay.enableCompass();
116-
map.getOverlays().add(compassOverlay);
119+
// Add rotation overlay
120+
rotationGestureOverlay = new SnappableRotationOverlay(map);
121+
map.setMultiTouchControls(true);
122+
map.getOverlays().add(rotationGestureOverlay);
117123

118124
// Add scale bar
119125
final DisplayMetrics dm = context.getResources().getDisplayMetrics();
@@ -152,6 +158,17 @@ public boolean longPressHelper(GeoPoint p) {
152158
}
153159
};
154160
map.getOverlays().add(new MapEventsOverlay(mapEventsReceiver));
161+
162+
// Add compass after mapEventReceiver so that a click on the compass does not create a new note
163+
compassOverlay = new ClickableMapCompass(context, rotationGestureOverlay, map);
164+
compassOverlay.enableCompass();
165+
map.getOverlays().add(compassOverlay);
166+
}
167+
168+
public void updateMapRotationBehavior(boolean rotatingMapEnabled) {
169+
rotationGestureOverlay.resetRotation();
170+
rotationGestureOverlay.setEnabled(rotatingMapEnabled);
171+
compassOverlay.setPointerMode(rotatingMapEnabled);
155172
}
156173

157174
@SuppressLint("ClickableViewAccessibility")
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package de.hauke_stieler.geonotes.map;
2+
3+
import android.view.MotionEvent;
4+
5+
import org.osmdroid.views.MapView;
6+
import org.osmdroid.views.overlay.Overlay;
7+
import org.osmdroid.views.overlay.compass.IOrientationConsumer;
8+
import org.osmdroid.views.overlay.compass.IOrientationProvider;
9+
import org.osmdroid.views.overlay.gestures.RotationGestureDetector;
10+
11+
public class SnappableRotationOverlay extends Overlay implements
12+
RotationGestureDetector.RotationListener, IOrientationProvider {
13+
14+
private final RotationGestureDetector mRotationDetector;
15+
private final MapView map;
16+
private IOrientationConsumer orientationConsumer;
17+
18+
private final long deltaTime = 25L;
19+
private long timeLastSet = 0L;
20+
private float currentAngle = 0f;
21+
22+
private final float minSnapAngle = 15; // Amount of rotation before map actually rotates
23+
private float currentSnapAngle = 0f; // Angle rotated since start of interaction
24+
private boolean rotationSnapped = false; // True when the user first reached the minSnapAngle
25+
26+
public SnappableRotationOverlay(MapView mapView) {
27+
super();
28+
map = mapView;
29+
mRotationDetector = new RotationGestureDetector(this);
30+
}
31+
32+
@Override
33+
public boolean onTouchEvent(MotionEvent event, MapView mapView) {
34+
if (event.getPointerCount() < 2) {
35+
rotationSnapped = false;
36+
currentSnapAngle = 0f;
37+
}
38+
mRotationDetector.onTouch(event);
39+
return super.onTouchEvent(event, mapView);
40+
}
41+
42+
@Override
43+
public void onRotate(float deltaAngle) {
44+
if (!isEnabled() || orientationConsumer == null) {
45+
return;
46+
}
47+
48+
currentAngle += deltaAngle;
49+
currentSnapAngle += deltaAngle;
50+
51+
if (!rotationSnapped && Math.abs(currentSnapAngle) > minSnapAngle) {
52+
rotationSnapped = true;
53+
}
54+
55+
if (System.currentTimeMillis() - deltaTime > timeLastSet && rotationSnapped) {
56+
timeLastSet = System.currentTimeMillis();
57+
map.setMapOrientation(map.getMapOrientation() + currentAngle);
58+
orientationConsumer.onOrientationChanged(map.getMapOrientation(), this);
59+
}
60+
}
61+
62+
@Override
63+
public boolean startOrientationProvider(IOrientationConsumer orientationConsumer) {
64+
this.orientationConsumer = orientationConsumer;
65+
orientationConsumer.onOrientationChanged(currentAngle, this);
66+
return true;
67+
}
68+
69+
@Override
70+
public void stopOrientationProvider() {
71+
orientationConsumer = null;
72+
}
73+
74+
@Override
75+
public float getLastKnownOrientation() {
76+
return currentAngle;
77+
}
78+
79+
@Override
80+
public void destroy() {
81+
stopOrientationProvider();
82+
}
83+
84+
public void resetRotation() {
85+
currentAngle = 0;
86+
rotationSnapped = false;
87+
currentSnapAngle = 0f;
88+
89+
map.setMapOrientation(currentAngle);
90+
if (orientationConsumer != null) {
91+
orientationConsumer.onOrientationChanged(currentAngle, this);
92+
}
93+
}
94+
}

app/src/main/java/de/hauke_stieler/geonotes/settings/SettingsActivity.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ private void load() {
7272

7373
boolean prefSnapNoteGps = preferences.getBoolean(getString(R.string.pref_snap_note_gps), false);
7474
((Switch) findViewById(R.id.settings_snap_note_gps)).setChecked(prefSnapNoteGps);
75+
76+
boolean prefEnableRotatingMap = preferences.getBoolean(getString(R.string.pref_enable_rotating_map), false);
77+
((Switch) findViewById(R.id.settings_enable_rotating_map)).setChecked(prefEnableRotatingMap);
7578
}
7679

7780
private void save() {
@@ -95,6 +98,9 @@ private void save() {
9598
boolean gpsSnapSwitchChecked = ((Switch) findViewById(R.id.settings_snap_note_gps)).isChecked();
9699
editor.putBoolean(getString(R.string.pref_snap_note_gps), gpsSnapSwitchChecked);
97100

101+
boolean enableRotatingMapChecked = ((Switch) findViewById(R.id.settings_enable_rotating_map)).isChecked();
102+
editor.putBoolean(getString(R.string.pref_enable_rotating_map), enableRotatingMapChecked);
103+
98104
editor.commit();
99105
}
100106

app/src/main/res/layout/settings_activity.xml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,29 @@
8989
android:layout_height="wrap_content" />
9090
</LinearLayout>
9191

92+
<LinearLayout
93+
android:layout_width="match_parent"
94+
android:layout_height="wrap_content"
95+
android:gravity="center_vertical"
96+
android:minHeight="50dp">
97+
98+
<TextView
99+
android:layout_width="wrap_content"
100+
android:layout_height="wrap_content"
101+
android:text="Enable rotating the map"
102+
android:textSize="16dp" />
103+
104+
<Space
105+
android:layout_width="wrap_content"
106+
android:layout_height="wrap_content"
107+
android:layout_weight="1" />
108+
109+
<Switch
110+
android:id="@+id/settings_enable_rotating_map"
111+
android:layout_width="wrap_content"
112+
android:layout_height="wrap_content" />
113+
</LinearLayout>
114+
92115
<Space
93116
android:layout_width="wrap_content"
94117
android:layout_height="wrap_content"

app/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<string name="pref_zoom_buttons">PREF_ZOOM_BUTTONS</string>
88
<string name="pref_map_scaling">PREF_MAP_SCALING</string>
99
<string name="pref_snap_note_gps">PREF_SNAP_NOTE_GPS</string>
10+
<string name="pref_enable_rotating_map">PREF_ENABLE_ROTATING_MAP</string>
1011
<string name="pref_last_location_lat">PREF_LAST_LOCATION_LAT</string>
1112
<string name="pref_last_location_lon">PREF_LAST_LOCATION_LON</string>
1213
<string name="pref_last_location_zoom">PREF_LAST_LOCATION_ZOOM</string>

metadata/en-US/changelogs/1004002.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
1.4.2:
2+
3+
* Add possibility to rotate the map (see "Settings" -> "Enable rotating the map")
4+
* Some minor internal code changes

metadata/en-US/full_description.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
A simple app to create and manage georeferences notes (text and photos) on a map. The goal is to create the notes very fast and without any unnecessary UI/UX overhead. One possible use-case is to collect geospatial data which can be later uploaded to OpenStreetMap.
22

33
Currently the basic features are:
4-
54
<ul>
65
<li>Create, move and delete notes on a map</li>
76
<li>Attach photos to a note</li>
7+
<li>List of all notes</li>
88
<li>Export all notes in GeoJson format</li>
99
<li>Show and follow current location</li>
1010
</ul>
Loading
Loading
Loading
Loading

screenshots.png

-1.08 MB
Loading

0 commit comments

Comments
 (0)