Detailed changes
@@ -236,13 +236,11 @@ public abstract class LocationActivity extends ActionBarActivity implements Loca
}
}
- @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(
@@ -9,6 +9,7 @@ import android.location.Location;
import android.location.LocationListener;
import android.net.Uri;
import android.os.Bundle;
+import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -17,11 +18,8 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
-import org.osmdroid.util.GeoPoint;
-
-import java.util.HashMap;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import com.google.common.base.Strings;
+import com.google.common.primitives.Doubles;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
@@ -30,8 +28,13 @@ 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;
+import eu.siacs.conversations.utils.GeoHelper;
import eu.siacs.conversations.utils.LocationProvider;
+import org.osmdroid.util.GeoPoint;
+
+import java.util.Map;
+
public class ShowLocationActivity extends LocationActivity implements LocationListener {
private GeoPoint loc = LocationProvider.FALLBACK;
@@ -56,73 +59,41 @@ public class ShowLocationActivity extends LocationActivity implements LocationLi
this.binding.fab.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);
- }
+ if (intent == null) {
+ return;
+ }
+ final String action = intent.getAction();
+ switch (Strings.nullToEmpty(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 uri = intent.getData();
+ if (uri == null) {
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) {
- }
- }
- }
-
+ }
+ final GeoPoint point;
+ try {
+ point = GeoHelper.parseGeoPoint(uri);
+ } catch (final Exception e) {
break;
- }
- updateLocationMarkers();
+ }
+ this.loc = point;
+ final Map<String, String> query = UriHelper.parseQueryString(uri.getQuery());
+ final String z = query.get("z");
+ final Double zoom = Strings.isNullOrEmpty(z) ? null : Doubles.tryParse(z);
+ if (zoom != null) {
+ Log.d(Config.LOGTAG, "inferring zoom level " + zoom + " from geo uri");
+ mapController.setZoom(zoom);
+ gotoLoc(false);
+ }
+ break;
}
+ updateLocationMarkers();
}
@Override
@@ -173,37 +144,43 @@ public class ShowLocationActivity extends LocationActivity implements LocationLi
@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);
- Toast.makeText(this, R.string.url_copied_to_clipboard, Toast.LENGTH_SHORT)
- .show();
- }
- 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;
+ final var itemId = item.getItemId();
+ if (itemId == R.id.action_copy_location) {
+ final ClipboardManager clipboard = getSystemService(ClipboardManager.class);
+ final ClipData clip = ClipData.newPlainText("location", createGeoUri().toString());
+ clipboard.setPrimaryClip(clip);
+ Toast.makeText(this, R.string.url_copied_to_clipboard, Toast.LENGTH_SHORT).show();
+ return true;
+ } else if (itemId == 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;
+ } else if (itemId == R.id.action_open_with) {
+ final Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(createGeoUri());
+ try {
+ startActivity(Intent.createChooser(intent, getText(R.string.open_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;
+
+ } else {
+ return super.onOptionsItemSelected(item);
}
- return super.onOptionsItemSelected(item);
}
private void startNavigation() {
@@ -9,6 +9,7 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.ImageView;
@@ -20,16 +21,19 @@ import androidx.core.widget.ImageViewCompat;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.RecyclerView;
+import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ItemMediaPreviewBinding;
import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.ui.ConversationFragment;
+import eu.siacs.conversations.ui.ShowLocationActivity;
import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.ui.util.Attachment;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
import java.util.concurrent.RejectedExecutionException;
public class MediaPreviewAdapter
@@ -73,11 +77,16 @@ public class MediaPreviewAdapter
holder.binding.mediaPreview.setOnClickListener(v -> view(context, attachment));
}
- private static void view(final Context context, Attachment attachment) {
+ private static void view(final Context context, final Attachment attachment) {
final Intent view = new Intent(Intent.ACTION_VIEW);
- final Uri uri = FileBackend.getUriForUri(context, attachment.getUri());
- view.setDataAndType(uri, attachment.getMime());
- view.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ if (attachment.getType() == Attachment.Type.LOCATION) {
+ view.setClass(context, ShowLocationActivity.class);
+ view.setData(attachment.getUri());
+ } else {
+ final Uri uri = FileBackend.getUriForUri(context, attachment.getUri());
+ view.setDataAndType(uri, attachment.getMime());
+ view.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
try {
context.startActivity(view);
} catch (final ActivityNotFoundException e) {
@@ -38,6 +38,7 @@ import android.os.Build;
import android.text.Editable;
import android.text.Spanned;
import android.text.style.URLSpan;
+import android.view.SoundEffectConstants;
import android.view.View;
import android.widget.Toast;
@@ -45,43 +46,56 @@ import java.util.Arrays;
import eu.siacs.conversations.R;
import eu.siacs.conversations.ui.ConversationsActivity;
-
+import eu.siacs.conversations.ui.ShowLocationActivity;
@SuppressLint("ParcelCreator")
public class FixedURLSpan extends URLSpan {
- private FixedURLSpan(String url) {
- super(url);
- }
+ private FixedURLSpan(String url) {
+ super(url);
+ }
- public static void fix(final Editable editable) {
- for (final URLSpan urlspan : editable.getSpans(0, editable.length() - 1, URLSpan.class)) {
- final int start = editable.getSpanStart(urlspan);
- final int end = editable.getSpanEnd(urlspan);
- editable.removeSpan(urlspan);
- editable.setSpan(new FixedURLSpan(urlspan.getURL()), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- }
+ public static void fix(final Editable editable) {
+ for (final URLSpan urlspan : editable.getSpans(0, editable.length() - 1, URLSpan.class)) {
+ final int start = editable.getSpanStart(urlspan);
+ final int end = editable.getSpanEnd(urlspan);
+ editable.removeSpan(urlspan);
+ editable.setSpan(
+ new FixedURLSpan(urlspan.getURL()),
+ start,
+ end,
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
- @Override
- public void onClick(View widget) {
- final Uri uri = Uri.parse(getURL());
- final Context context = widget.getContext();
- final boolean candidateToProcessDirectly = "xmpp".equals(uri.getScheme()) || ("https".equals(uri.getScheme()) && "conversations.im".equals(uri.getHost()) && uri.getPathSegments().size() > 1 && Arrays.asList("j","i").contains(uri.getPathSegments().get(0)));
- if (candidateToProcessDirectly && context instanceof ConversationsActivity) {
- if (((ConversationsActivity) context).onXmppUriClicked(uri)) {
- widget.playSoundEffect(0);
- return;
- }
- }
- final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
- //intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
- try {
- context.startActivity(intent);
- widget.playSoundEffect(0);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(context, R.string.no_application_found_to_open_link, Toast.LENGTH_SHORT).show();
- }
- }
+ @Override
+ public void onClick(final View widget) {
+ final Uri uri = Uri.parse(getURL());
+ final Context context = widget.getContext();
+ final boolean candidateToProcessDirectly =
+ "xmpp".equals(uri.getScheme())
+ || ("https".equals(uri.getScheme())
+ && "conversations.im".equals(uri.getHost())
+ && uri.getPathSegments().size() > 1
+ && Arrays.asList("j", "i").contains(uri.getPathSegments().get(0)));
+ if (candidateToProcessDirectly && context instanceof ConversationsActivity) {
+ if (((ConversationsActivity) context).onXmppUriClicked(uri)) {
+ widget.playSoundEffect(SoundEffectConstants.CLICK);
+ return;
+ }
+ }
+ final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ if ("geo".equalsIgnoreCase(uri.getScheme())) {
+ intent.setClass(context, ShowLocationActivity.class);
+ } else {
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
+ }
+ try {
+ context.startActivity(intent);
+ widget.playSoundEffect(SoundEffectConstants.CLICK);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(context, R.string.no_application_found_to_open_link, Toast.LENGTH_SHORT)
+ .show();
+ }
+ }
}
@@ -1,6 +1,8 @@
package eu.siacs.conversations.ui.util;
-import java.util.HashMap;
+import com.google.common.collect.ImmutableMap;
+
+import java.util.Map;
/**
* Helper methods for parsing URI's.
@@ -12,19 +14,18 @@ public final class UriHelper {
* @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) {
+ public static Map<String, String> parseQueryString(final String q) {
if (q == null || q.isEmpty()) {
- return null;
+ return ImmutableMap.of();
}
+ final ImmutableMap.Builder<String,String> queryMapBuilder = new ImmutableMap.Builder<>();
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);
+ queryMapBuilder.put(pair[0], pair.length == 2 && !pair[1].isEmpty() ? pair[1] : null);
}
- return queryMap;
+ return queryMapBuilder.build();
}
}
@@ -46,13 +46,17 @@ public class GeoHelper {
}
}
+ public static GeoPoint parseGeoPoint(final Uri uri) {
+ return parseGeoPoint(uri.toString());
+ }
+
private static GeoPoint parseGeoPoint(String body) throws IllegalArgumentException {
- Matcher matcher = GEO_URI.matcher(body);
+ final Matcher matcher = GEO_URI.matcher(body);
if (!matcher.matches()) {
throw new IllegalArgumentException("Invalid geo uri");
}
- double latitude;
- double longitude;
+ final double latitude;
+ final double longitude;
try {
latitude = Double.parseDouble(matcher.group(1));
if (latitude > 90.0 || latitude < -90.0) {
@@ -62,7 +66,7 @@ public class GeoHelper {
if (longitude > 180.0 || longitude < -180.0) {
throw new IllegalArgumentException("Invalid geo uri");
}
- } catch (NumberFormatException e) {
+ } catch (final NumberFormatException e) {
throw new IllegalArgumentException("Invalid geo uri",e);
}
return new GeoPoint(latitude, longitude);
@@ -7,6 +7,6 @@
<path
android:fillColor="@android:color/white"
- android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z" />
+ android:pathData="M10,9h4L14,6h3l-5,-5 -5,5h3v3zM9,10L6,10L6,7l-5,5 5,5v-3h3v-4zM23,12l-5,-5v3h-3v4h3v3l5,-5zM14,15h-4v3L7,18l5,5 5,-5h-3v-3z" />
</vector>
@@ -6,9 +6,13 @@
android:showAsAction="ifRoom"
android:title="@string/action_share_location"
android:icon="@drawable/ic_share_24dp"/>
+ <item android:id="@+id/action_open_with"
+ app:showAsAction="ifRoom"
+ android:showAsAction="ifRoom"
+ android:title="@string/open_with"
+ android:icon="@drawable/ic_open_with_24dp"/>
<item android:id="@+id/action_copy_location"
android:title="@string/action_copy_location"
- android:icon="@drawable/ic_content_copy_24dp"
- app:showAsAction="ifRoom"
+ app:showAsAction="never"
android:showAsAction="ifRoom"/>
</menu>