Merge branch 'mapping' of https://github.com/SamWhited/Conversations into SamWhited-mapping
110
art/marker.svg
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="48"
|
||||||
|
height="48"
|
||||||
|
viewBox="0 0 48 48"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.91 r13725"
|
||||||
|
sodipodi:docname="marker.svg">
|
||||||
|
<metadata
|
||||||
|
id="metadata10">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs8">
|
||||||
|
<radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3913"
|
||||||
|
id="radialGradient3883"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.2039074,-0.09024614,0.07170697,0.16216229,-92.579229,-90.973095)"
|
||||||
|
cx="262.33273"
|
||||||
|
cy="945.23846"
|
||||||
|
fx="262.33273"
|
||||||
|
fy="945.23846"
|
||||||
|
r="185.49754" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="linearGradient3913">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3915" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3917" />
|
||||||
|
</linearGradient>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath4167">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="M 24,4.0000001 C 16.27,4.0000001 10,10.27 10,18 10,28.5 24,44 24,44 24,44 38,28.5 38,18 38,10.27 31.73,4.0000001 24,4.0000001 Z M 24,23 c -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 0,2.76 -2.24,5 -5,5 z"
|
||||||
|
id="path4169"
|
||||||
|
style="fill:#000000;fill-opacity:1" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath4321">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 24,4.0001492 c -7.73,0 -14,6.2699998 -14,14.0000008 0,10.5 14,26 14,26 0,0 14,-15.5 14,-26 C 38,10.270149 31.73,4.0001492 24,4.0001492 Z M 24,23.00015 c -2.76,0 -5,-2.24 -5,-5 0,-2.760001 2.24,-5.000001 5,-5.000001 2.76,0 5,2.24 5,5.000001 0,2.76 -2.24,5 -5,5 z"
|
||||||
|
id="path4323"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:18, 3;stroke-dashoffset:0;stroke-opacity:0.53333285" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1010"
|
||||||
|
id="namedview6"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="4.9166667"
|
||||||
|
inkscape:cx="-15.254237"
|
||||||
|
inkscape:cy="12.20339"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="41"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<path
|
||||||
|
d="M24 4c-7.73 0-14 6.27-14 14 0 10.5 14 26 14 26s14-15.5 14-26c0-7.73-6.27-14-14-14zm0 19c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"
|
||||||
|
id="path4"
|
||||||
|
style="fill:#00a000;fill-opacity:1;stroke:none;stroke-opacity:0.53333336;stroke-width:1.70000002;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||||
|
<path
|
||||||
|
style="display:inline;opacity:0.19211821;fill:url(#radialGradient3883);fill-opacity:1;stroke:none"
|
||||||
|
d="m 53.884912,1.7373006 c -18.322492,0 -33.173092,14.5823714 -33.173092,32.5686504 0,3.794038 0.661899,7.436601 1.877335,10.821463 1.505391,0.209531 3.044508,0.317391 4.607513,0.317391 5.584539,0 9.890238,-1.147853 14.805425,-2.934259 l 15.611481,6.295152 a 2.0568126,2.0577227 0 0 0 2.766588,-2.403594 l -4.227888,-17.09591 c 2.717518,-4.771967 3.645449,-10.205846 3.645449,-15.810885 0,-4.0761111 -0.781533,-7.9714274 -2.20495,-11.5551094 -1.217366,-0.132888 -2.454715,-0.202899 -3.707861,-0.202899 z"
|
||||||
|
id="path3878"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
clip-path="url(#clipPath4167)" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="M 24,4.0000003 C 16.27,4.0000003 10,10.27 10,18 10,28.5 24,44 24,44 24,44 38,28.5 38,18 38,10.27 31.73,4.0000003 24,4.0000003 Z M 24,23 c -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 0,2.76 -2.24,5 -5,5 z"
|
||||||
|
id="path4-3"
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:30,5;stroke-opacity:0.53333336;stroke-dashoffset:44"
|
||||||
|
clip-path="url(#clipPath4321)" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 4.7 KiB |
|
@ -68,6 +68,7 @@ images = {
|
||||||
'message_bubble_sent_grey.svg' => ['message_bubble_sent_grey.9', 0],
|
'message_bubble_sent_grey.svg' => ['message_bubble_sent_grey.9', 0],
|
||||||
'date_bubble_white.svg' => ['date_bubble_white.9', 0],
|
'date_bubble_white.svg' => ['date_bubble_white.9', 0],
|
||||||
'date_bubble_grey.svg' => ['date_bubble_grey.9', 0],
|
'date_bubble_grey.svg' => ['date_bubble_grey.9', 0],
|
||||||
|
'marker.svg' => ['marker', 0]
|
||||||
}
|
}
|
||||||
|
|
||||||
# Executable paths for Mac OSX
|
# Executable paths for Mac OSX
|
||||||
|
|
|
@ -52,6 +52,7 @@ dependencies {
|
||||||
implementation "com.wefika:flowlayout:0.4.1"
|
implementation "com.wefika:flowlayout:0.4.1"
|
||||||
implementation 'net.ypresto.androidtranscoder:android-transcoder:0.2.0'
|
implementation 'net.ypresto.androidtranscoder:android-transcoder:0.2.0'
|
||||||
implementation 'rocks.xmpp:xmpp-addr:0.8.0-SNAPSHOT'
|
implementation 'rocks.xmpp:xmpp-addr:0.8.0-SNAPSHOT'
|
||||||
|
implementation 'org.osmdroid:osmdroid-android:6.0.1'
|
||||||
}
|
}
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
|
|
|
@ -13,6 +13,13 @@
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||||
|
|
||||||
|
<uses-feature android:name="android.hardware.location" android:required="false" />
|
||||||
|
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
|
||||||
|
<uses-feature android:name="android.hardware.location.network" android:required="false" />
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.CAMERA" />
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
|
|
||||||
|
@ -49,7 +56,27 @@
|
||||||
<action android:name="android.media.RINGER_MODE_CHANGED" />
|
<action android:name="android.media.RINGER_MODE_CHANGED" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
<activity
|
||||||
|
android:name=".ui.ShareLocationActivity"
|
||||||
|
android:label="@string/title_activity_share_location" >
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="eu.siacs.conversations.location.request" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<activity
|
||||||
|
android:name=".ui.ShowLocationActivity"
|
||||||
|
android:label="@string/title_activity_show_location" >
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="eu.siacs.conversations.location.show" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<data android:scheme="geo" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.ConversationActivity"
|
android:name=".ui.ConversationActivity"
|
||||||
android:theme="@style/SplashTheme">
|
android:theme="@style/SplashTheme">
|
||||||
|
|
|
@ -2,7 +2,8 @@ package eu.siacs.conversations;
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import org.osmdroid.util.GeoPoint;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -10,8 +11,6 @@ import eu.siacs.conversations.xmpp.chatstate.ChatState;
|
||||||
import rocks.xmpp.addr.Jid;
|
import rocks.xmpp.addr.Jid;
|
||||||
|
|
||||||
public final class Config {
|
public final class Config {
|
||||||
|
|
||||||
|
|
||||||
private static final int UNENCRYPTED = 1;
|
private static final int UNENCRYPTED = 1;
|
||||||
private static final int OPENPGP = 2;
|
private static final int OPENPGP = 2;
|
||||||
private static final int OTR = 4;
|
private static final int OTR = 4;
|
||||||
|
@ -160,4 +159,15 @@ public final class Config {
|
||||||
|
|
||||||
private Config() {
|
private Config() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final class Map {
|
||||||
|
public final static double INITIAL_ZOOM_LEVEL = 4;
|
||||||
|
public final static double FINAL_ZOOM_LEVEL = 15;
|
||||||
|
public final static GeoPoint INITIAL_POS = new GeoPoint(33.805278, -84.171389);
|
||||||
|
public final static int MY_LOCATION_INDICATOR_SIZE = 10;
|
||||||
|
public final static int MY_LOCATION_INDICATOR_OUTLINE_SIZE = 3;
|
||||||
|
public final static long LOCATION_FIX_TIME_DELTA = 1000 * 10; // ms
|
||||||
|
public final static float LOCATION_FIX_SPACE_DELTA = 10; // m
|
||||||
|
public final static int LOCATION_FIX_SIGNIFICANT_TIME_DELTA = 1000 * 60 * 2; // ms
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package eu.siacs.conversations.ui;
|
||||||
|
|
||||||
|
import android.support.v7.app.ActionBar;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
|
||||||
|
public abstract class ActionBarActivity extends AppCompatActivity {
|
||||||
|
public static void configureActionBar(ActionBar actionBar) {
|
||||||
|
if (actionBar != null) {
|
||||||
|
actionBar.setHomeButtonEnabled(true);
|
||||||
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
314
src/main/java/eu/siacs/conversations/ui/LocationActivity.java
Normal file
|
@ -0,0 +1,314 @@
|
||||||
|
package eu.siacs.conversations.ui;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.location.Location;
|
||||||
|
import android.location.LocationListener;
|
||||||
|
import android.location.LocationManager;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.provider.Settings;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
|
||||||
|
import org.osmdroid.api.IGeoPoint;
|
||||||
|
import org.osmdroid.api.IMapController;
|
||||||
|
import org.osmdroid.config.Configuration;
|
||||||
|
import org.osmdroid.config.IConfigurationProvider;
|
||||||
|
import org.osmdroid.tileprovider.tilesource.XYTileSource;
|
||||||
|
import org.osmdroid.util.GeoPoint;
|
||||||
|
import org.osmdroid.views.MapView;
|
||||||
|
import org.osmdroid.views.overlay.Overlay;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.BuildConfig;
|
||||||
|
import eu.siacs.conversations.Config;
|
||||||
|
import eu.siacs.conversations.R;
|
||||||
|
import eu.siacs.conversations.ui.util.LocationHelper;
|
||||||
|
import eu.siacs.conversations.ui.widget.Marker;
|
||||||
|
import eu.siacs.conversations.ui.widget.MyLocation;
|
||||||
|
import eu.siacs.conversations.utils.ThemeHelper;
|
||||||
|
|
||||||
|
public abstract class LocationActivity extends ActionBarActivity implements LocationListener {
|
||||||
|
protected LocationManager locationManager;
|
||||||
|
protected boolean hasLocationFeature;
|
||||||
|
|
||||||
|
public static final int REQUEST_CODE_CREATE = 0;
|
||||||
|
public static final int REQUEST_CODE_FAB_PRESSED = 1;
|
||||||
|
public static final int REQUEST_CODE_SNACKBAR_PRESSED = 2;
|
||||||
|
|
||||||
|
protected static final String KEY_LOCATION = "loc";
|
||||||
|
protected static final String KEY_ZOOM_LEVEL = "zoom";
|
||||||
|
|
||||||
|
protected Location myLoc = null;
|
||||||
|
protected MapView map = null;
|
||||||
|
protected IMapController mapController = null;
|
||||||
|
|
||||||
|
protected Bitmap marker_icon;
|
||||||
|
|
||||||
|
protected void clearMarkers() {
|
||||||
|
synchronized (this.map.getOverlays()) {
|
||||||
|
for (final Overlay overlay : this.map.getOverlays()) {
|
||||||
|
if (overlay instanceof Marker || overlay instanceof MyLocation) {
|
||||||
|
this.map.getOverlays().remove(overlay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void updateLocationMarkers() {
|
||||||
|
clearMarkers();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected XYTileSource tileSource() {
|
||||||
|
return new XYTileSource("OpenStreetMap",
|
||||||
|
0, 19, 256, ".png", new String[] {
|
||||||
|
"https://a.tile.openstreetmap.org/",
|
||||||
|
"https://b.tile.openstreetmap.org/",
|
||||||
|
"https://c.tile.openstreetmap.org/" },"© OpenStreetMap contributors");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(final Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
final Context ctx = getApplicationContext();
|
||||||
|
setTheme(ThemeHelper.find(this));
|
||||||
|
|
||||||
|
final PackageManager packageManager = ctx.getPackageManager();
|
||||||
|
hasLocationFeature = packageManager.hasSystemFeature(PackageManager.FEATURE_LOCATION) ||
|
||||||
|
packageManager.hasSystemFeature(PackageManager.FEATURE_LOCATION_GPS) ||
|
||||||
|
packageManager.hasSystemFeature(PackageManager.FEATURE_LOCATION_NETWORK);
|
||||||
|
this.locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
|
||||||
|
this.marker_icon = BitmapFactory.decodeResource(ctx.getResources(), R.drawable.marker);
|
||||||
|
|
||||||
|
// Ask for location permissions if location services are enabled and we're
|
||||||
|
// just starting the activity (we don't want to keep pestering them on every
|
||||||
|
// screen rotation or if there's no point because it's disabled anyways).
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && savedInstanceState == null) {
|
||||||
|
requestPermissions(REQUEST_CODE_CREATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
final IConfigurationProvider config = Configuration.getInstance();
|
||||||
|
config.load(ctx, getPreferences());
|
||||||
|
config.setUserAgentValue(BuildConfig.APPLICATION_ID + "_" + BuildConfig.VERSION_CODE);
|
||||||
|
|
||||||
|
final File f = new File(ctx.getCacheDir() + "/tiles");
|
||||||
|
try {
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
f.mkdirs();
|
||||||
|
} catch (final SecurityException ignored) {
|
||||||
|
}
|
||||||
|
if (f.exists() && f.isDirectory() && f.canRead() && f.canWrite()) {
|
||||||
|
Log.d(Config.LOGTAG, "Using tile cache at: " + f.getAbsolutePath());
|
||||||
|
config.setOsmdroidTileCache(f.getAbsoluteFile());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSaveInstanceState(@NonNull final Bundle outState) {
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
|
||||||
|
final IGeoPoint center = map.getMapCenter();
|
||||||
|
outState.putParcelable(KEY_LOCATION, new GeoPoint(
|
||||||
|
center.getLatitude(),
|
||||||
|
center.getLongitude()
|
||||||
|
));
|
||||||
|
outState.putDouble(KEY_ZOOM_LEVEL, map.getZoomLevelDouble());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRestoreInstanceState(@NonNull final Bundle savedInstanceState) {
|
||||||
|
super.onRestoreInstanceState(savedInstanceState);
|
||||||
|
|
||||||
|
if (savedInstanceState.containsKey(KEY_LOCATION)) {
|
||||||
|
mapController.setCenter(savedInstanceState.getParcelable(KEY_LOCATION));
|
||||||
|
}
|
||||||
|
if (savedInstanceState.containsKey(KEY_ZOOM_LEVEL)) {
|
||||||
|
mapController.setZoom(savedInstanceState.getDouble(KEY_ZOOM_LEVEL));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setupMapView(final GeoPoint pos) {
|
||||||
|
// Get map view and configure it.
|
||||||
|
map = findViewById(R.id.map);
|
||||||
|
map.setTileSource(tileSource());
|
||||||
|
map.setBuiltInZoomControls(false);
|
||||||
|
map.setMultiTouchControls(true);
|
||||||
|
map.setTilesScaledToDpi(getPreferences().getBoolean("scale_tiles_for_high_dpi", false));
|
||||||
|
mapController = map.getController();
|
||||||
|
mapController.setZoom(Config.Map.INITIAL_ZOOM_LEVEL);
|
||||||
|
mapController.setCenter(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void gotoLoc() {
|
||||||
|
gotoLoc(map.getZoomLevelDouble() == Config.Map.INITIAL_ZOOM_LEVEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void gotoLoc(final boolean setZoomLevel);
|
||||||
|
|
||||||
|
protected abstract void setMyLoc(final Location location);
|
||||||
|
|
||||||
|
protected void requestLocationUpdates() {
|
||||||
|
if (!hasLocationFeature || locationManager == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(Config.LOGTAG, "Requesting location updates...");
|
||||||
|
final Location lastKnownLocationGps;
|
||||||
|
final Location lastKnownLocationNetwork;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (locationManager.getAllProviders().contains(LocationManager.GPS_PROVIDER)) {
|
||||||
|
lastKnownLocationGps = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
|
||||||
|
|
||||||
|
if (lastKnownLocationGps != null) {
|
||||||
|
setMyLoc(lastKnownLocationGps);
|
||||||
|
}
|
||||||
|
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, Config.Map.LOCATION_FIX_TIME_DELTA,
|
||||||
|
Config.Map.LOCATION_FIX_SPACE_DELTA, this);
|
||||||
|
} else {
|
||||||
|
lastKnownLocationGps = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (locationManager.getAllProviders().contains(LocationManager.NETWORK_PROVIDER)) {
|
||||||
|
lastKnownLocationNetwork = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
|
||||||
|
if (lastKnownLocationNetwork != null && LocationHelper.isBetterLocation(lastKnownLocationNetwork,
|
||||||
|
lastKnownLocationGps)) {
|
||||||
|
setMyLoc(lastKnownLocationNetwork);
|
||||||
|
}
|
||||||
|
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, Config.Map.LOCATION_FIX_TIME_DELTA,
|
||||||
|
Config.Map.LOCATION_FIX_SPACE_DELTA, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If something else is also querying for location more frequently than we are, the battery is already being
|
||||||
|
// drained. Go ahead and use the existing locations as often as we can get them.
|
||||||
|
if (locationManager.getAllProviders().contains(LocationManager.PASSIVE_PROVIDER)) {
|
||||||
|
locationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, 0, 0, this);
|
||||||
|
}
|
||||||
|
} catch (final SecurityException ignored) {
|
||||||
|
// Do nothing if the users device has no location providers.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void pauseLocationUpdates() throws SecurityException {
|
||||||
|
if (locationManager != null) {
|
||||||
|
locationManager.removeUpdates(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case android.R.id.home:
|
||||||
|
finish();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
Configuration.getInstance().save(this, getPreferences());
|
||||||
|
map.onPause();
|
||||||
|
try {
|
||||||
|
pauseLocationUpdates();
|
||||||
|
} catch (final SecurityException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void updateUi();
|
||||||
|
|
||||||
|
protected boolean mapAtInitialLoc() {
|
||||||
|
return map.getZoomLevelDouble() == Config.Map.INITIAL_ZOOM_LEVEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
Configuration.getInstance().load(this, getPreferences());
|
||||||
|
map.onResume();
|
||||||
|
this.setMyLoc(null);
|
||||||
|
requestLocationUpdates();
|
||||||
|
updateLocationMarkers();
|
||||||
|
updateUi();
|
||||||
|
map.setTileSource(tileSource());
|
||||||
|
map.setTilesScaledToDpi(getPreferences().getBoolean("scale_tiles_for_high_dpi", false));
|
||||||
|
|
||||||
|
if (mapAtInitialLoc()) {
|
||||||
|
gotoLoc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.M)
|
||||||
|
protected boolean hasLocationPermissions() {
|
||||||
|
return (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED ||
|
||||||
|
checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.M)
|
||||||
|
protected void requestPermissions(final int request_code) {
|
||||||
|
if (!hasLocationPermissions()) {
|
||||||
|
requestPermissions(
|
||||||
|
new String[]{
|
||||||
|
Manifest.permission.ACCESS_FINE_LOCATION,
|
||||||
|
Manifest.permission.ACCESS_COARSE_LOCATION,
|
||||||
|
},
|
||||||
|
request_code
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(final int requestCode,
|
||||||
|
@NonNull final String[] permissions,
|
||||||
|
@NonNull final int[] grantResults) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
|
for (int i = 0; i < grantResults.length; i++) {
|
||||||
|
if (Manifest.permission.ACCESS_FINE_LOCATION.equals(permissions[i]) ||
|
||||||
|
Manifest.permission.ACCESS_COARSE_LOCATION.equals(permissions[i])) {
|
||||||
|
if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
requestLocationUpdates();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SharedPreferences getPreferences() {
|
||||||
|
return PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||||
|
private boolean isLocationEnabledKitkat() {
|
||||||
|
try {
|
||||||
|
final int locationMode = Settings.Secure.getInt(getContentResolver(), Settings.Secure.LOCATION_MODE);
|
||||||
|
return locationMode != Settings.Secure.LOCATION_MODE_OFF;
|
||||||
|
} catch( final Settings.SettingNotFoundException e ){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
private boolean isLocationEnabledLegacy() {
|
||||||
|
final String locationProviders = Settings.Secure.getString(getContentResolver(),
|
||||||
|
Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
|
||||||
|
return !TextUtils.isEmpty(locationProviders);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isLocationEnabled() {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
|
return isLocationEnabledKitkat();
|
||||||
|
} else {
|
||||||
|
return isLocationEnabledLegacy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,248 @@
|
||||||
|
package eu.siacs.conversations.ui;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.location.Location;
|
||||||
|
import android.location.LocationListener;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.design.widget.CoordinatorLayout;
|
||||||
|
import android.support.design.widget.FloatingActionButton;
|
||||||
|
import android.support.design.widget.Snackbar;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
|
||||||
|
import org.osmdroid.api.IGeoPoint;
|
||||||
|
import org.osmdroid.util.GeoPoint;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.Config;
|
||||||
|
import eu.siacs.conversations.R;
|
||||||
|
import eu.siacs.conversations.ui.util.LocationHelper;
|
||||||
|
import eu.siacs.conversations.ui.widget.Marker;
|
||||||
|
import eu.siacs.conversations.ui.widget.MyLocation;
|
||||||
|
|
||||||
|
public class ShareLocationActivity extends LocationActivity implements LocationListener {
|
||||||
|
|
||||||
|
private Snackbar snackBar;
|
||||||
|
private boolean marker_fixed_to_loc = false;
|
||||||
|
private static final String KEY_FIXED_TO_LOC = "fixed_to_loc";
|
||||||
|
private Boolean noAskAgain = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSaveInstanceState(@NonNull final Bundle outState) {
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
|
||||||
|
outState.putBoolean(KEY_FIXED_TO_LOC, marker_fixed_to_loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRestoreInstanceState(@NonNull final Bundle savedInstanceState) {
|
||||||
|
super.onRestoreInstanceState(savedInstanceState);
|
||||||
|
|
||||||
|
if (savedInstanceState.containsKey(KEY_FIXED_TO_LOC)) {
|
||||||
|
this.marker_fixed_to_loc = savedInstanceState.getBoolean(KEY_FIXED_TO_LOC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(final Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
setContentView(R.layout.activity_share_location);
|
||||||
|
setSupportActionBar(findViewById(R.id.toolbar));
|
||||||
|
configureActionBar(getSupportActionBar());
|
||||||
|
setupMapView(Config.Map.INITIAL_POS);
|
||||||
|
|
||||||
|
// Setup the cancel button
|
||||||
|
final Button cancelButton = findViewById(R.id.cancel_button);
|
||||||
|
cancelButton.setOnClickListener(view -> {
|
||||||
|
setResult(RESULT_CANCELED);
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
|
||||||
|
final CoordinatorLayout snackBarCoordinator = findViewById(R.id.snackbarCoordinator);
|
||||||
|
if (snackBarCoordinator != null) {
|
||||||
|
this.snackBar = Snackbar.make(snackBarCoordinator, R.string.location_disabled, Snackbar.LENGTH_INDEFINITE);
|
||||||
|
snackBar.setAction(R.string.enable, view -> {
|
||||||
|
if (isLocationEnabledAndAllowed()) {
|
||||||
|
updateUi();
|
||||||
|
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !hasLocationPermissions()) {
|
||||||
|
requestPermissions(REQUEST_CODE_SNACKBAR_PRESSED);
|
||||||
|
} else if (!isLocationEnabled()) {
|
||||||
|
startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the share button
|
||||||
|
final Button shareButton = findViewById(R.id.share_button);
|
||||||
|
if (shareButton != null) {
|
||||||
|
shareButton.setOnClickListener(view -> {
|
||||||
|
final Intent result = new Intent();
|
||||||
|
|
||||||
|
if (marker_fixed_to_loc && myLoc != null) {
|
||||||
|
result.putExtra("latitude", myLoc.getLatitude());
|
||||||
|
result.putExtra("longitude", myLoc.getLongitude());
|
||||||
|
result.putExtra("altitude", myLoc.getAltitude());
|
||||||
|
result.putExtra("accuracy", (int) myLoc.getAccuracy());
|
||||||
|
} else {
|
||||||
|
final IGeoPoint markerPoint = map.getMapCenter();
|
||||||
|
result.putExtra("latitude", markerPoint.getLatitude());
|
||||||
|
result.putExtra("longitude", markerPoint.getLongitude());
|
||||||
|
}
|
||||||
|
|
||||||
|
setResult(RESULT_OK, result);
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.marker_fixed_to_loc = isLocationEnabledAndAllowed();
|
||||||
|
|
||||||
|
// Setup the fab button
|
||||||
|
final FloatingActionButton toggleFixedMarkerButton = findViewById(R.id.fab);
|
||||||
|
toggleFixedMarkerButton.setOnClickListener(view -> {
|
||||||
|
if (!marker_fixed_to_loc) {
|
||||||
|
if (!isLocationEnabled()) {
|
||||||
|
startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
|
||||||
|
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
requestPermissions(REQUEST_CODE_FAB_PRESSED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toggleFixedLocation();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(final int requestCode,
|
||||||
|
@NonNull final String[] permissions,
|
||||||
|
@NonNull final int[] grantResults) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
|
|
||||||
|
if (grantResults.length > 0 &&
|
||||||
|
grantResults[0] != PackageManager.PERMISSION_GRANTED &&
|
||||||
|
Build.VERSION.SDK_INT >= 23 &&
|
||||||
|
permissions.length > 0 &&
|
||||||
|
(
|
||||||
|
Manifest.permission.LOCATION_HARDWARE.equals(permissions[0]) ||
|
||||||
|
Manifest.permission.ACCESS_FINE_LOCATION.equals(permissions[0]) ||
|
||||||
|
Manifest.permission.ACCESS_COARSE_LOCATION.equals(permissions[0])
|
||||||
|
) &&
|
||||||
|
!shouldShowRequestPermissionRationale(permissions[0])) {
|
||||||
|
noAskAgain = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!noAskAgain && requestCode == REQUEST_CODE_SNACKBAR_PRESSED && !isLocationEnabled() && hasLocationPermissions()) {
|
||||||
|
startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
|
||||||
|
}
|
||||||
|
updateUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void gotoLoc(final boolean setZoomLevel) {
|
||||||
|
if (this.myLoc != null && mapController != null) {
|
||||||
|
if (setZoomLevel) {
|
||||||
|
mapController.setZoom(Config.Map.FINAL_ZOOM_LEVEL);
|
||||||
|
}
|
||||||
|
mapController.animateTo(new GeoPoint(this.myLoc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setMyLoc(final Location location) {
|
||||||
|
this.myLoc = location;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateLocationMarkers() {
|
||||||
|
super.updateLocationMarkers();
|
||||||
|
if (this.myLoc != null) {
|
||||||
|
this.map.getOverlays().add(new MyLocation(this, null, this.myLoc));
|
||||||
|
if (this.marker_fixed_to_loc) {
|
||||||
|
map.getOverlays().add(new Marker(marker_icon, new GeoPoint(this.myLoc)));
|
||||||
|
} else {
|
||||||
|
map.getOverlays().add(new Marker(marker_icon));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
map.getOverlays().add(new Marker(marker_icon));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLocationChanged(final Location location) {
|
||||||
|
if (this.myLoc == null) {
|
||||||
|
this.marker_fixed_to_loc = true;
|
||||||
|
}
|
||||||
|
updateUi();
|
||||||
|
if (LocationHelper.isBetterLocation(location, this.myLoc)) {
|
||||||
|
final Location oldLoc = this.myLoc;
|
||||||
|
this.myLoc = location;
|
||||||
|
|
||||||
|
// Don't jump back to the users location if they're not moving (more or less).
|
||||||
|
if (oldLoc == null || (this.marker_fixed_to_loc && this.myLoc.distanceTo(oldLoc) > 1)) {
|
||||||
|
gotoLoc();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateLocationMarkers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStatusChanged(final String provider, final int status, final Bundle extras) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProviderEnabled(final String provider) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProviderDisabled(final String provider) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isLocationEnabledAndAllowed() {
|
||||||
|
return this.hasLocationFeature && (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || this.hasLocationPermissions()) && this.isLocationEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void toggleFixedLocation() {
|
||||||
|
this.marker_fixed_to_loc = isLocationEnabledAndAllowed() && !this.marker_fixed_to_loc;
|
||||||
|
if (this.marker_fixed_to_loc) {
|
||||||
|
gotoLoc(false);
|
||||||
|
}
|
||||||
|
updateLocationMarkers();
|
||||||
|
updateUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateUi() {
|
||||||
|
if (!hasLocationFeature || noAskAgain || isLocationEnabledAndAllowed()) {
|
||||||
|
this.snackBar.dismiss();
|
||||||
|
} else {
|
||||||
|
this.snackBar.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the fab button
|
||||||
|
final FloatingActionButton fab = findViewById(R.id.fab);
|
||||||
|
if (isLocationEnabledAndAllowed()) {
|
||||||
|
fab.setVisibility(View.VISIBLE);
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
fab.setImageResource(marker_fixed_to_loc ? R.drawable.ic_gps_fixed_white_24dp :
|
||||||
|
R.drawable.ic_gps_not_fixed_white_24dp);
|
||||||
|
fab.setContentDescription(getResources().getString(
|
||||||
|
marker_fixed_to_loc ? R.string.action_unfix_from_location : R.string.action_fix_to_location
|
||||||
|
));
|
||||||
|
fab.invalidate();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
fab.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,234 @@
|
||||||
|
package eu.siacs.conversations.ui;
|
||||||
|
|
||||||
|
import android.app.ActionBar;
|
||||||
|
import android.content.ActivityNotFoundException;
|
||||||
|
import android.content.ClipData;
|
||||||
|
import android.content.ClipboardManager;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.location.Location;
|
||||||
|
import android.location.LocationListener;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.design.widget.FloatingActionButton;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.osmdroid.util.GeoPoint;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.Config;
|
||||||
|
import eu.siacs.conversations.R;
|
||||||
|
import eu.siacs.conversations.ui.util.LocationHelper;
|
||||||
|
import eu.siacs.conversations.ui.util.UriHelper;
|
||||||
|
import eu.siacs.conversations.ui.widget.Marker;
|
||||||
|
import eu.siacs.conversations.ui.widget.MyLocation;
|
||||||
|
|
||||||
|
|
||||||
|
public class ShowLocationActivity extends LocationActivity implements LocationListener {
|
||||||
|
|
||||||
|
private GeoPoint loc = Config.Map.INITIAL_POS;
|
||||||
|
private FloatingActionButton navigationButton;
|
||||||
|
|
||||||
|
|
||||||
|
private Uri createGeoUri() {
|
||||||
|
return Uri.parse("geo:" + this.loc.getLatitude() + "," + this.loc.getLongitude());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(final Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
final ActionBar actionBar = getActionBar();
|
||||||
|
if (actionBar != null) {
|
||||||
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
setContentView(R.layout.activity_show_location);
|
||||||
|
setSupportActionBar(findViewById(R.id.toolbar));
|
||||||
|
configureActionBar(getSupportActionBar());
|
||||||
|
setupMapView(this.loc);
|
||||||
|
|
||||||
|
// Setup the fab button
|
||||||
|
this.navigationButton = findViewById(R.id.fab);
|
||||||
|
this.navigationButton.setOnClickListener(view -> startNavigation());
|
||||||
|
|
||||||
|
final Intent intent = getIntent();
|
||||||
|
if (intent != null) {
|
||||||
|
final String action = intent.getAction();
|
||||||
|
if (action == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (action) {
|
||||||
|
case "eu.siacs.conversations.location.show":
|
||||||
|
if (intent.hasExtra("longitude") && intent.hasExtra("latitude")) {
|
||||||
|
final double longitude = intent.getDoubleExtra("longitude", 0);
|
||||||
|
final double latitude = intent.getDoubleExtra("latitude", 0);
|
||||||
|
this.loc = new GeoPoint(latitude, longitude);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Intent.ACTION_VIEW:
|
||||||
|
final Uri geoUri = intent.getData();
|
||||||
|
|
||||||
|
// Attempt to set zoom level if the geo URI specifies it
|
||||||
|
if (geoUri != null) {
|
||||||
|
final HashMap<String, String> query = UriHelper.parseQueryString(geoUri.getQuery());
|
||||||
|
|
||||||
|
// Check for zoom level.
|
||||||
|
final String z = query.get("z");
|
||||||
|
if (z != null) {
|
||||||
|
try {
|
||||||
|
mapController.setZoom(Double.valueOf(z));
|
||||||
|
} catch (final Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for the actual geo query.
|
||||||
|
boolean posInQuery = false;
|
||||||
|
final String q = query.get("q");
|
||||||
|
if (q != null) {
|
||||||
|
final Pattern latlng = Pattern.compile("/^([-+]?[0-9]+(\\.[0-9]+)?),([-+]?[0-9]+(\\.[0-9]+)?)(\\(.*\\))?/");
|
||||||
|
final Matcher m = latlng.matcher(q);
|
||||||
|
if (m.matches()) {
|
||||||
|
try {
|
||||||
|
this.loc = new GeoPoint(Double.valueOf(m.group(1)), Double.valueOf(m.group(3)));
|
||||||
|
posInQuery = true;
|
||||||
|
} catch (final Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final String schemeSpecificPart = geoUri.getSchemeSpecificPart();
|
||||||
|
if (schemeSpecificPart != null && !schemeSpecificPart.isEmpty()) {
|
||||||
|
try {
|
||||||
|
final GeoPoint latlong = LocationHelper.parseLatLong(schemeSpecificPart);
|
||||||
|
if (latlong != null && !posInQuery) {
|
||||||
|
this.loc = latlong;
|
||||||
|
}
|
||||||
|
} catch (final NumberFormatException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
updateLocationMarkers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void gotoLoc(final boolean setZoomLevel) {
|
||||||
|
if (this.loc != null && mapController != null) {
|
||||||
|
if (setZoomLevel) {
|
||||||
|
mapController.setZoom(Config.Map.FINAL_ZOOM_LEVEL);
|
||||||
|
}
|
||||||
|
mapController.animateTo(new GeoPoint(this.loc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(final int requestCode,
|
||||||
|
@NonNull final String[] permissions,
|
||||||
|
@NonNull final int[] grantResults) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
|
updateUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setMyLoc(final Location location) {
|
||||||
|
this.myLoc = location;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(final Menu menu) {
|
||||||
|
// Inflate the menu; this adds items to the action bar if it is present.
|
||||||
|
getMenuInflater().inflate(R.menu.menu_show_location, menu);
|
||||||
|
updateUi();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateLocationMarkers() {
|
||||||
|
super.updateLocationMarkers();
|
||||||
|
if (this.myLoc != null) {
|
||||||
|
this.map.getOverlays().add(new MyLocation(this, null, this.myLoc));
|
||||||
|
}
|
||||||
|
this.map.getOverlays().add(new Marker(this.marker_icon, this.loc));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.action_copy_location:
|
||||||
|
final ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
|
||||||
|
if (clipboard != null) {
|
||||||
|
final ClipData clip = ClipData.newPlainText("location", createGeoUri().toString());
|
||||||
|
clipboard.setPrimaryClip(clip);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case R.id.action_share_location:
|
||||||
|
final Intent shareIntent = new Intent();
|
||||||
|
shareIntent.setAction(Intent.ACTION_SEND);
|
||||||
|
shareIntent.putExtra(Intent.EXTRA_TEXT, createGeoUri().toString());
|
||||||
|
shareIntent.setType("text/plain");
|
||||||
|
try {
|
||||||
|
startActivity(Intent.createChooser(shareIntent, getText(R.string.share_with)));
|
||||||
|
} catch (final ActivityNotFoundException e) {
|
||||||
|
//This should happen only on faulty androids because normally chooser is always available
|
||||||
|
Toast.makeText(this, R.string.no_application_found_to_open_file, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startNavigation() {
|
||||||
|
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(
|
||||||
|
"google.navigation:q=" +
|
||||||
|
String.valueOf(this.loc.getLatitude()) + "," + String.valueOf(this.loc.getLongitude())
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateUi() {
|
||||||
|
final Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("google.navigation:q=0,0"));
|
||||||
|
final ComponentName component = i.resolveActivity(getPackageManager());
|
||||||
|
if (this.navigationButton != null) {
|
||||||
|
this.navigationButton.setVisibility(component == null ? View.GONE : View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLocationChanged(final Location location) {
|
||||||
|
if (LocationHelper.isBetterLocation(location, this.myLoc)) {
|
||||||
|
this.myLoc = location;
|
||||||
|
updateLocationMarkers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStatusChanged(final String provider, final int status, final Bundle extras) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProviderEnabled(final String provider) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProviderDisabled(final String provider) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,9 +3,6 @@ package eu.siacs.conversations.ui;
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.support.v7.app.ActionBar;
|
|
||||||
import android.support.v7.app.AlertDialog;
|
|
||||||
import android.support.v7.app.AlertDialog.Builder;
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.ActivityNotFoundException;
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.ClipData;
|
import android.content.ClipData;
|
||||||
|
@ -37,7 +34,8 @@ import android.os.PowerManager;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.support.v7.app.AlertDialog.Builder;
|
||||||
import android.support.v7.app.AppCompatDelegate;
|
import android.support.v7.app.AppCompatDelegate;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
|
@ -76,7 +74,7 @@ import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
|
||||||
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
|
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
|
||||||
import rocks.xmpp.addr.Jid;
|
import rocks.xmpp.addr.Jid;
|
||||||
|
|
||||||
public abstract class XmppActivity extends AppCompatActivity {
|
public abstract class XmppActivity extends ActionBarActivity {
|
||||||
|
|
||||||
public static final String EXTRA_ACCOUNT = "account";
|
public static final String EXTRA_ACCOUNT = "account";
|
||||||
protected static final int REQUEST_ANNOUNCE_PGP = 0x0101;
|
protected static final int REQUEST_ANNOUNCE_PGP = 0x0101;
|
||||||
|
@ -610,13 +608,6 @@ public abstract class XmppActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void configureActionBar(ActionBar actionBar) {
|
|
||||||
if (actionBar != null) {
|
|
||||||
actionBar.setHomeButtonEnabled(true);
|
|
||||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean noAccountUsesPgp() {
|
protected boolean noAccountUsesPgp() {
|
||||||
if (!hasPgp()) {
|
if (!hasPgp()) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
package eu.siacs.conversations.ui.util;
|
||||||
|
|
||||||
|
import android.location.Location;
|
||||||
|
|
||||||
|
import org.osmdroid.util.GeoPoint;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.Config;
|
||||||
|
|
||||||
|
public final class LocationHelper {
|
||||||
|
/**
|
||||||
|
* Parses a lat long string in the form "lat,long".
|
||||||
|
*
|
||||||
|
* @param latlong A string in the form "lat,long"
|
||||||
|
* @return A GeoPoint representing the lat,long string.
|
||||||
|
* @throws NumberFormatException If an invalid lat or long is specified.
|
||||||
|
*/
|
||||||
|
public static GeoPoint parseLatLong(final String latlong) throws NumberFormatException {
|
||||||
|
if (latlong == null || latlong.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String[] parts = latlong.split(",");
|
||||||
|
if (parts[1].contains("?")) {
|
||||||
|
parts[1] = parts[1].substring(0, parts[1].indexOf("?"));
|
||||||
|
}
|
||||||
|
return new GeoPoint(Double.valueOf(parts[0]), Double.valueOf(parts[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isSameProvider(final String provider1, final String provider2) {
|
||||||
|
if (provider1 == null) {
|
||||||
|
return provider2 == null;
|
||||||
|
}
|
||||||
|
return provider1.equals(provider2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isBetterLocation(final Location location, final Location prevLoc) {
|
||||||
|
if (prevLoc == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether the new location fix is newer or older
|
||||||
|
final long timeDelta = location.getTime() - prevLoc.getTime();
|
||||||
|
final boolean isSignificantlyNewer = timeDelta > Config.Map.LOCATION_FIX_SIGNIFICANT_TIME_DELTA;
|
||||||
|
final boolean isSignificantlyOlder = timeDelta < -Config.Map.LOCATION_FIX_SIGNIFICANT_TIME_DELTA;
|
||||||
|
final boolean isNewer = timeDelta > 0;
|
||||||
|
|
||||||
|
if (isSignificantlyNewer) {
|
||||||
|
return true;
|
||||||
|
} else if (isSignificantlyOlder) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether the new location fix is more or less accurate
|
||||||
|
final int accuracyDelta = (int) (location.getAccuracy() - prevLoc.getAccuracy());
|
||||||
|
final boolean isLessAccurate = accuracyDelta > 0;
|
||||||
|
final boolean isMoreAccurate = accuracyDelta < 0;
|
||||||
|
final boolean isSignificantlyLessAccurate = accuracyDelta > 200;
|
||||||
|
|
||||||
|
// Check if the old and new location are from the same provider
|
||||||
|
final boolean isFromSameProvider = isSameProvider(location.getProvider(), prevLoc.getProvider());
|
||||||
|
|
||||||
|
// Determine location quality using a combination of timeliness and accuracy
|
||||||
|
if (isMoreAccurate) {
|
||||||
|
return true;
|
||||||
|
} else if (isNewer && !isLessAccurate) {
|
||||||
|
return true;
|
||||||
|
} else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
30
src/main/java/eu/siacs/conversations/ui/util/UriHelper.java
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package eu.siacs.conversations.ui.util;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper methods for parsing URI's.
|
||||||
|
*/
|
||||||
|
public final class UriHelper {
|
||||||
|
/**
|
||||||
|
* Parses a query string into a hashmap.
|
||||||
|
*
|
||||||
|
* @param q The query string to split.
|
||||||
|
* @return A hashmap containing the key-value pairs from the query string.
|
||||||
|
*/
|
||||||
|
public static HashMap<String, String> parseQueryString(final String q) {
|
||||||
|
if (q == null || q.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String[] query = q.split("&");
|
||||||
|
// TODO: Look up the HashMap implementation and figure out what the load factor is and make sure we're not reallocating here.
|
||||||
|
final HashMap<String, String> queryMap = new HashMap<>(query.length);
|
||||||
|
for (final String param : query) {
|
||||||
|
final String[] pair = param.split("=");
|
||||||
|
queryMap.put(pair[0], pair.length == 2 && !pair[1].isEmpty() ? pair[1] : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return queryMap;
|
||||||
|
}
|
||||||
|
}
|
52
src/main/java/eu/siacs/conversations/ui/widget/Marker.java
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
package eu.siacs.conversations.ui.widget;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Point;
|
||||||
|
|
||||||
|
import org.osmdroid.util.GeoPoint;
|
||||||
|
import org.osmdroid.views.MapView;
|
||||||
|
import org.osmdroid.views.overlay.mylocation.SimpleLocationOverlay;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An immutable marker overlay.
|
||||||
|
*/
|
||||||
|
public class Marker extends SimpleLocationOverlay {
|
||||||
|
private final GeoPoint position;
|
||||||
|
private final Bitmap icon;
|
||||||
|
private final Point mapPoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a marker overlay which will be drawn at the current Geographical position.
|
||||||
|
* @param icon A bitmap icon for the marker
|
||||||
|
* @param position The geographic position where the marker will be drawn (if it is inside the view)
|
||||||
|
*/
|
||||||
|
public Marker(final Bitmap icon, final GeoPoint position) {
|
||||||
|
super(icon);
|
||||||
|
this.icon = icon;
|
||||||
|
this.position = position;
|
||||||
|
this.mapPoint = new Point();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a marker overlay which will be drawn centered in the view.
|
||||||
|
* @param icon A bitmap icon for the marker
|
||||||
|
*/
|
||||||
|
public Marker(final Bitmap icon) {
|
||||||
|
this(icon, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(final Canvas c, final MapView view, final boolean shadow) {
|
||||||
|
super.draw(c, view, shadow);
|
||||||
|
|
||||||
|
// If no position was set for the marker, draw it centered in the view.
|
||||||
|
view.getProjection().toPixels(this.position == null ? view.getMapCenter() : position, mapPoint);
|
||||||
|
|
||||||
|
c.drawBitmap(icon,
|
||||||
|
mapPoint.x - icon.getWidth() / 2,
|
||||||
|
mapPoint.y - icon.getHeight(),
|
||||||
|
null);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
package eu.siacs.conversations.ui.widget;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Point;
|
||||||
|
import android.location.Location;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import org.osmdroid.util.GeoPoint;
|
||||||
|
import org.osmdroid.views.MapView;
|
||||||
|
import org.osmdroid.views.overlay.mylocation.SimpleLocationOverlay;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.Config;
|
||||||
|
import eu.siacs.conversations.R;
|
||||||
|
import microsoft.mappoint.TileSystem;
|
||||||
|
|
||||||
|
public class MyLocation extends SimpleLocationOverlay {
|
||||||
|
private final GeoPoint position;
|
||||||
|
private final float accuracy;
|
||||||
|
private final Point mapCenterPoint;
|
||||||
|
private final Paint fill;
|
||||||
|
private final Paint outline;
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
private int getColor(final Context ctx) {
|
||||||
|
final int accent;
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
accent = ctx.getResources().getColor(R.color.accent, ctx.getTheme());
|
||||||
|
} else {
|
||||||
|
//noinspection deprecation
|
||||||
|
accent = ctx.getResources().getColor(R.color.accent);
|
||||||
|
}
|
||||||
|
return accent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MyLocation(final Context ctx, final Bitmap icon, final Location position) {
|
||||||
|
super(icon);
|
||||||
|
this.mapCenterPoint = new Point();
|
||||||
|
this.fill = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
final int accent = this.getColor(ctx);
|
||||||
|
fill.setColor(accent);
|
||||||
|
fill.setStyle(Paint.Style.FILL);
|
||||||
|
this.outline = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
outline.setColor(accent);
|
||||||
|
outline.setAlpha(50);
|
||||||
|
outline.setStyle(Paint.Style.FILL);
|
||||||
|
this.position = new GeoPoint(position);
|
||||||
|
this.accuracy = position.getAccuracy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(final Canvas c, final MapView view, final boolean shadow) {
|
||||||
|
super.draw(c, view, shadow);
|
||||||
|
|
||||||
|
view.getProjection().toPixels(position, mapCenterPoint);
|
||||||
|
c.drawCircle(mapCenterPoint.x, mapCenterPoint.y,
|
||||||
|
Math.max(Config.Map.MY_LOCATION_INDICATOR_SIZE + Config.Map.MY_LOCATION_INDICATOR_OUTLINE_SIZE,
|
||||||
|
accuracy / (float) TileSystem.GroundResolution(position.getLatitude(), view.getZoomLevel())
|
||||||
|
), this.outline);
|
||||||
|
c.drawCircle(mapCenterPoint.x, mapCenterPoint.y, Config.Map.MY_LOCATION_INDICATOR_SIZE, this.fill);
|
||||||
|
}
|
||||||
|
}
|
BIN
src/main/res/drawable-hdpi/ic_directions_black_24dp.png
Normal file
After Width: | Height: | Size: 233 B |
BIN
src/main/res/drawable-hdpi/ic_directions_white_24dp.png
Normal file
After Width: | Height: | Size: 252 B |
BIN
src/main/res/drawable-hdpi/ic_gps_fixed_black_24dp.png
Normal file
After Width: | Height: | Size: 549 B |
BIN
src/main/res/drawable-hdpi/ic_gps_fixed_white_24dp.png
Normal file
After Width: | Height: | Size: 546 B |
BIN
src/main/res/drawable-hdpi/ic_gps_not_fixed_black_24dp.png
Normal file
After Width: | Height: | Size: 472 B |
BIN
src/main/res/drawable-hdpi/ic_gps_not_fixed_white_24dp.png
Normal file
After Width: | Height: | Size: 468 B |
BIN
src/main/res/drawable-hdpi/marker.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
src/main/res/drawable-mdpi/ic_directions_black_24dp.png
Normal file
After Width: | Height: | Size: 181 B |
BIN
src/main/res/drawable-mdpi/ic_directions_white_24dp.png
Normal file
After Width: | Height: | Size: 191 B |
BIN
src/main/res/drawable-mdpi/ic_gps_fixed_black_24dp.png
Normal file
After Width: | Height: | Size: 341 B |
BIN
src/main/res/drawable-mdpi/ic_gps_fixed_white_24dp.png
Normal file
After Width: | Height: | Size: 350 B |
BIN
src/main/res/drawable-mdpi/ic_gps_not_fixed_black_24dp.png
Normal file
After Width: | Height: | Size: 295 B |
BIN
src/main/res/drawable-mdpi/ic_gps_not_fixed_white_24dp.png
Normal file
After Width: | Height: | Size: 298 B |
BIN
src/main/res/drawable-mdpi/marker.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
src/main/res/drawable-xhdpi/ic_directions_black_24dp.png
Normal file
After Width: | Height: | Size: 274 B |
BIN
src/main/res/drawable-xhdpi/ic_directions_white_24dp.png
Normal file
After Width: | Height: | Size: 307 B |
BIN
src/main/res/drawable-xhdpi/ic_gps_fixed_black_24dp.png
Normal file
After Width: | Height: | Size: 660 B |
BIN
src/main/res/drawable-xhdpi/ic_gps_fixed_white_24dp.png
Normal file
After Width: | Height: | Size: 687 B |
BIN
src/main/res/drawable-xhdpi/ic_gps_not_fixed_black_24dp.png
Normal file
After Width: | Height: | Size: 561 B |
BIN
src/main/res/drawable-xhdpi/ic_gps_not_fixed_white_24dp.png
Normal file
After Width: | Height: | Size: 577 B |
BIN
src/main/res/drawable-xhdpi/marker.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
src/main/res/drawable-xxhdpi/ic_directions_black_24dp.png
Normal file
After Width: | Height: | Size: 393 B |
BIN
src/main/res/drawable-xxhdpi/ic_directions_white_24dp.png
Normal file
After Width: | Height: | Size: 444 B |
BIN
src/main/res/drawable-xxhdpi/ic_gps_fixed_black_24dp.png
Normal file
After Width: | Height: | Size: 976 B |
BIN
src/main/res/drawable-xxhdpi/ic_gps_fixed_white_24dp.png
Normal file
After Width: | Height: | Size: 1,012 B |
BIN
src/main/res/drawable-xxhdpi/ic_gps_not_fixed_black_24dp.png
Normal file
After Width: | Height: | Size: 803 B |
BIN
src/main/res/drawable-xxhdpi/ic_gps_not_fixed_white_24dp.png
Normal file
After Width: | Height: | Size: 830 B |
BIN
src/main/res/drawable-xxhdpi/marker.png
Normal file
After Width: | Height: | Size: 7.3 KiB |
BIN
src/main/res/drawable-xxxhdpi/ic_directions_black_24dp.png
Normal file
After Width: | Height: | Size: 491 B |
BIN
src/main/res/drawable-xxxhdpi/ic_directions_white_24dp.png
Normal file
After Width: | Height: | Size: 554 B |
BIN
src/main/res/drawable-xxxhdpi/ic_gps_fixed_black_24dp.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
src/main/res/drawable-xxxhdpi/ic_gps_fixed_white_24dp.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
src/main/res/drawable-xxxhdpi/ic_gps_not_fixed_black_24dp.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/main/res/drawable-xxxhdpi/ic_gps_not_fixed_white_24dp.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/main/res/drawable-xxxhdpi/marker.png
Normal file
After Width: | Height: | Size: 9.7 KiB |
9
src/main/res/drawable/ic_directions_black_24dp.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M21.71,11.29l-9,-9c-0.39,-0.39 -1.02,-0.39 -1.41,0l-9,9c-0.39,0.39 -0.39,1.02 0,1.41l9,9c0.39,0.39 1.02,0.39 1.41,0l9,-9c0.39,-0.38 0.39,-1.01 0,-1.41zM14,14.5V12h-4v3H8v-4c0,-0.55 0.45,-1 1,-1h5V7.5l3.5,3.5 -3.5,3.5z"/>
|
||||||
|
</vector>
|
9
src/main/res/drawable/ic_gps_fixed_black_24dp.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zM20.94,11c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94L13,1h-2v2.06C6.83,3.52 3.52,6.83 3.06,11L1,11v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94L11,23h2v-2.06c4.17,-0.46 7.48,-3.77 7.94,-7.94L23,13v-2h-2.06zM12,19c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
|
||||||
|
</vector>
|
9
src/main/res/drawable/ic_gps_not_fixed_black_24dp.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M20.94,11c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94V1h-2v2.06C6.83,3.52 3.52,6.83 3.06,11H1v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94V23h2v-2.06c4.17,-0.46 7.48,-3.77 7.94,-7.94H23v-2h-2.06zM12,19c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
|
||||||
|
</vector>
|
9
src/main/res/drawable/ic_place_black_24dp.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M12,2C8.13,2 5,5.13 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.87 -3.13,-7 -7,-7zM12,11.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5z"/>
|
||||||
|
</vector>
|
66
src/main/res/layout/activity_share_location.xml
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<android.support.design.widget.CoordinatorLayout
|
||||||
|
android:id="@+id/snackbarCoordinator"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".ui.ShareLocationActivity">
|
||||||
|
|
||||||
|
<include layout="@layout/toolbar" />
|
||||||
|
|
||||||
|
<org.osmdroid.views.MapView android:id="@+id/map"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_above="@+id/button_bar"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/button_bar"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentBottom="true"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
tools:ignore="RtlHardcoded">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/cancel_button"
|
||||||
|
style="?android:attr/borderlessButtonStyle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/cancel"
|
||||||
|
android:textAppearance="@style/TextAppearance.Conversations.Body1"/>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="1dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginBottom="7dp"
|
||||||
|
android:layout_marginTop="7dp"
|
||||||
|
android:background="@color/accent"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/share_button"
|
||||||
|
style="?android:attr/borderlessButtonStyle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/share_with"
|
||||||
|
android:textAppearance="@style/TextAppearance.Conversations.Body1"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<android.support.design.widget.FloatingActionButton
|
||||||
|
android:id="@+id/fab"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end|bottom"
|
||||||
|
android:src="?attr/icon_gps_fixed"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_above="@+id/button_bar"
|
||||||
|
android:contentDescription="@string/action_unfix_from_location"
|
||||||
|
android:layout_margin="16dp" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
</android.support.design.widget.CoordinatorLayout>
|
25
src/main/res/layout/activity_show_location.xml
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".ui.ShowLocationActivity">
|
||||||
|
|
||||||
|
<include layout="@layout/toolbar" />
|
||||||
|
|
||||||
|
<org.osmdroid.views.MapView android:id="@+id/map"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"/>
|
||||||
|
|
||||||
|
<android.support.design.widget.FloatingActionButton
|
||||||
|
android:id="@+id/fab"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end|bottom"
|
||||||
|
android:src="?attr/icon_directions"
|
||||||
|
android:tint="@color/white"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:contentDescription="@string/action_unfix_from_location"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:layout_alignParentBottom="true"/>
|
||||||
|
</RelativeLayout>
|
14
src/main/res/menu/menu_show_location.xml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<menu
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item android:id="@+id/action_share_location"
|
||||||
|
app:showAsAction="ifRoom"
|
||||||
|
android:showAsAction="ifRoom"
|
||||||
|
android:title="@string/action_share_location"
|
||||||
|
android:icon="?attr/icon_share"/>
|
||||||
|
<item android:id="@+id/action_copy_location"
|
||||||
|
android:title="@string/action_copy_location"
|
||||||
|
android:icon="?attr/icon_copy_bar"
|
||||||
|
app:showAsAction="ifRoom"
|
||||||
|
android:showAsAction="ifRoom"/>
|
||||||
|
</menu>
|
|
@ -57,5 +57,8 @@
|
||||||
\n\nhttps://github.com/vinc3m1/RoundedImageView\n(Apache License, Version 2.0)
|
\n\nhttps://github.com/vinc3m1/RoundedImageView\n(Apache License, Version 2.0)
|
||||||
\n\nhttps://github.com/jdamcd/android-crop\n(Apache License, Version 2.0)
|
\n\nhttps://github.com/jdamcd/android-crop\n(Apache License, Version 2.0)
|
||||||
\n\nhttps://github.com/zxing/zxing\n(Apache License, Version 2.0)
|
\n\nhttps://github.com/zxing/zxing\n(Apache License, Version 2.0)
|
||||||
|
\n\nhttps://github.com/osmdroid/osmdroid\n(Apache License, Version 2.0)
|
||||||
|
\n\n\nMaps
|
||||||
|
\n\nMaps by Open Street Map (https://www.openstreetmap.org). Copyright restrictions may apply.
|
||||||
</string>
|
</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -66,6 +66,11 @@
|
||||||
<attr name="icon_enable_undecided_device" format="reference"/>
|
<attr name="icon_enable_undecided_device" format="reference"/>
|
||||||
<attr name="icon_scroll_down" format="reference"/>
|
<attr name="icon_scroll_down" format="reference"/>
|
||||||
|
|
||||||
|
<attr name="icon_gps_not_fixed" format="reference"/>
|
||||||
|
<attr name="icon_gps_fixed" format="reference"/>
|
||||||
|
<attr name="icon_directions" format="reference"/>
|
||||||
|
<attr name="icon_copy_bar" format="reference"/>
|
||||||
|
|
||||||
<attr name="icon_notifications" format="reference"/>
|
<attr name="icon_notifications" format="reference"/>
|
||||||
<attr name="icon_notifications_off" format="reference"/>
|
<attr name="icon_notifications_off" format="reference"/>
|
||||||
<attr name="icon_notifications_paused" format="reference"/>
|
<attr name="icon_notifications_paused" format="reference"/>
|
||||||
|
|
|
@ -690,4 +690,12 @@
|
||||||
<string name="large">Large</string>
|
<string name="large">Large</string>
|
||||||
<string name="not_encrypted_for_this_device">Message was not encrypted for this device.</string>
|
<string name="not_encrypted_for_this_device">Message was not encrypted for this device.</string>
|
||||||
<string name="undo">undo</string>
|
<string name="undo">undo</string>
|
||||||
|
<string name="location_disabled">Location sharing is disabled</string>
|
||||||
|
<string name="action_fix_to_location">Fix position</string>
|
||||||
|
<string name="action_unfix_from_location">Unfix position</string>
|
||||||
|
<string name="action_copy_location">Copy Location</string>
|
||||||
|
<string name="action_share_location">Share Location</string>
|
||||||
|
<string name="action_directions">Directions</string>
|
||||||
|
<string name="title_activity_share_location">Share location</string>
|
||||||
|
<string name="title_activity_show_location">Show location</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -79,6 +79,11 @@
|
||||||
<item type="reference" name="icon_scan_qr_code">@drawable/ic_qr_code_scan_white_24dp</item>
|
<item type="reference" name="icon_scan_qr_code">@drawable/ic_qr_code_scan_white_24dp</item>
|
||||||
<item type="reference" name="icon_scroll_down">@drawable/ic_scroll_to_end_black</item>
|
<item type="reference" name="icon_scroll_down">@drawable/ic_scroll_to_end_black</item>
|
||||||
|
|
||||||
|
<item type="reference" name="icon_gps_not_fixed">@drawable/ic_gps_not_fixed_black_24dp</item>
|
||||||
|
<item type="reference" name="icon_gps_fixed">@drawable/ic_gps_fixed_black_24dp</item>
|
||||||
|
<item type="reference" name="icon_directions">@drawable/ic_directions_black_24dp</item>
|
||||||
|
<item type="reference" name="icon_copy_bar">@drawable/ic_content_copy_white_24dp</item>
|
||||||
|
|
||||||
<item type="reference" name="icon_notifications">@drawable/ic_notifications_black_24dp</item>
|
<item type="reference" name="icon_notifications">@drawable/ic_notifications_black_24dp</item>
|
||||||
<item type="reference" name="icon_notifications_off">@drawable/ic_notifications_off_black_24dp</item>
|
<item type="reference" name="icon_notifications_off">@drawable/ic_notifications_off_black_24dp</item>
|
||||||
<item type="reference" name="icon_notifications_paused">@drawable/ic_notifications_paused_black_24dp</item>
|
<item type="reference" name="icon_notifications_paused">@drawable/ic_notifications_paused_black_24dp</item>
|
||||||
|
@ -164,6 +169,11 @@
|
||||||
<item type="reference" name="icon_scan_qr_code">@drawable/ic_qr_code_scan_white_24dp</item>
|
<item type="reference" name="icon_scan_qr_code">@drawable/ic_qr_code_scan_white_24dp</item>
|
||||||
<item type="reference" name="icon_scroll_down">@drawable/ic_scroll_to_end_white</item>
|
<item type="reference" name="icon_scroll_down">@drawable/ic_scroll_to_end_white</item>
|
||||||
|
|
||||||
|
<item type="reference" name="icon_gps_not_fixed">@drawable/ic_gps_not_fixed_white_24dp</item>
|
||||||
|
<item type="reference" name="icon_gps_fixed">@drawable/ic_gps_fixed_white_24dp</item>
|
||||||
|
<item type="reference" name="icon_directions">@drawable/ic_directions_white_24dp</item>
|
||||||
|
<item type="reference" name="icon_copy_bar">@drawable/ic_content_copy_white_24dp</item>
|
||||||
|
|
||||||
<item type="reference" name="icon_notifications">@drawable/ic_notifications_white_24dp</item>
|
<item type="reference" name="icon_notifications">@drawable/ic_notifications_white_24dp</item>
|
||||||
<item type="reference" name="icon_notifications_off">@drawable/ic_notifications_off_white_24dp</item>
|
<item type="reference" name="icon_notifications_off">@drawable/ic_notifications_off_white_24dp</item>
|
||||||
<item type="reference" name="icon_notifications_paused">@drawable/ic_notifications_paused_white_24dp</item>
|
<item type="reference" name="icon_notifications_paused">@drawable/ic_notifications_paused_white_24dp</item>
|
||||||
|
|