From cfcb9a8cdbb7013290b000c964c3781d1cdc9b15 Mon Sep 17 00:00:00 2001 From: Alan Evans Date: Thu, 27 Jun 2019 12:14:54 -0400 Subject: [PATCH] Custom place picker to replace places SDK. --- AndroidManifest.xml | 5 + build.gradle | 8 +- res/drawable-hdpi/marker_shadow.webp | Bin 0 -> 360 bytes res/drawable-mdpi/marker_shadow.webp | Bin 0 -> 244 bytes res/drawable-xhdpi/marker_shadow.webp | Bin 0 -> 506 bytes res/drawable-xxhdpi/marker_shadow.webp | Bin 0 -> 844 bytes res/drawable-xxxhdpi/marker_shadow.webp | Bin 0 -> 1414 bytes res/drawable/ic_check.xml | 5 + res/drawable/ic_map_marker.xml | 9 + res/layout/activity_map_bottom_sheet_view.xml | 91 +++++++ res/layout/activity_place_picker.xml | 56 ++++ res/values/strings.xml | 7 + .../components/location/SignalMapView.java | 2 - .../components/location/SignalPlace.java | 18 +- .../conversation/ConversationActivity.java | 4 +- .../securesms/maps/AddressData.java | 57 ++++ .../securesms/maps/PlacePickerActivity.java | 243 ++++++++++++++++++ .../maps/SingleAddressBottomSheet.java | 81 ++++++ .../securesms/mms/AttachmentManager.java | 23 +- 19 files changed, 579 insertions(+), 30 deletions(-) create mode 100644 res/drawable-hdpi/marker_shadow.webp create mode 100644 res/drawable-mdpi/marker_shadow.webp create mode 100644 res/drawable-xhdpi/marker_shadow.webp create mode 100644 res/drawable-xxhdpi/marker_shadow.webp create mode 100644 res/drawable-xxxhdpi/marker_shadow.webp create mode 100644 res/drawable/ic_check.xml create mode 100644 res/drawable/ic_map_marker.xml create mode 100755 res/layout/activity_map_bottom_sheet_view.xml create mode 100644 res/layout/activity_place_picker.xml create mode 100644 src/org/thoughtcrime/securesms/maps/AddressData.java create mode 100644 src/org/thoughtcrime/securesms/maps/PlacePickerActivity.java create mode 100644 src/org/thoughtcrime/securesms/maps/SingleAddressBottomSheet.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 3edaa4afd5..52a16b345d 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -457,6 +457,11 @@ android:exported="true" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/> + + diff --git a/build.gradle b/build.gradle index 1a841c2f34..e0f76085d0 100644 --- a/build.gradle +++ b/build.gradle @@ -76,8 +76,8 @@ dependencies { exclude group: 'com.google.firebase', module: 'firebase-measurement-connector' } - implementation 'com.google.android.gms:play-services-maps:16.0.0' - implementation 'com.google.android.gms:play-services-places:16.0.0' + implementation 'com.google.android.gms:play-services-maps:16.1.0' + implementation 'com.google.android.gms:play-services-location:16.0.0' implementation 'com.google.android.gms:play-services-auth:16.0.1' implementation 'com.google.android.exoplayer:exoplayer-core:2.9.1' @@ -189,8 +189,8 @@ dependencyVerification { 'androidx.lifecycle:lifecycle-extensions:8d4072201b6231d67e4192d608d46b1f5c920845106c9831632c2e3ffe706117', 'androidx.lifecycle:lifecycle-common-java8:9edc2d4f589656d470ef03b9c6ece62d335971294b033ec7d9ceb6e361e9aafa', 'com.google.firebase:firebase-messaging:e42288e7950d7d3b033d3395a5ac9365d230da3e439a2794ec13e2ef0fbaf078', - 'com.google.android.gms:play-services-places:2d5c4e4ac3ee5be21b4ec544411bc51d11457b5ae2fa2a5d4539019f87c233c6', - 'com.google.android.gms:play-services-maps:07f59c5955b759ce7b80ceaeb8261643c5b79acc9f180df2b7c3987658eed2e8', + 'com.google.android.gms:play-services-maps:ff50cae9e4059416202375597d99cdc8ddefd9cea3f1dc2ff53779a3a12eb480', + 'com.google.android.gms:play-services-location:240a0fcb9e8e58586e38ea43b69c09ed6e89ea9a0c69770b7634d81dabf5f3a0', 'com.google.android.gms:play-services-auth:aec9e1c584d442cb9f59481a50b2c66dc191872607c04d97ecb82dd0eb5149ec', 'com.google.android.exoplayer:exoplayer-ui:7a942afcc402ff01e9bf48e8d3942850986710f06562d50a1408aaf04a683151', 'com.google.android.exoplayer:exoplayer-core:b6ab34abac36bc2bc6934b7a50008162feca2c0fde91aaf1e8c1c22f2c16e2c0', diff --git a/res/drawable-hdpi/marker_shadow.webp b/res/drawable-hdpi/marker_shadow.webp new file mode 100644 index 0000000000000000000000000000000000000000..59217c52f23d971cc2832e4cf8f3b465f05e8528 GIT binary patch literal 360 zcmV-u0hj(#Nk&Fs0RRA3MM6+kP&il$0000G0000)004^s06|PpNc;c*00oC&rfr*f z{PFHwXhcK|EUx+rklaWTq(trZWKui5S^WRI+H8$f7IGUmA)Wk~ z>~ogfmL&^S7eg|cgqi!8r+wM7uw**PcV&{vWFNC+`LZR;dfu5#CYc;2%a)LRRVNeX zJ(I~nPpd9lmg=gm4w+;!HFF=sl5DkALe!8>n9O@N2emCBqk@KzA(Qld5DQrdcL@SR zey}-v37IxfO<+Pc$3IILxQalBu=&UZf@(fcW!X}IV|6+WP6w&_);Q#ipf`WX-h~BOuwzBfrhyhVR zYf*h=~5Dk!Oy<5eOvhkG(`hWC&4)<4BUAC`+ui3hVy=({8k=jKu^0B4Ppzq;0dq z{X0Gv5CMyrfCzj^{+A;>KqN-sTG8ymX941YAHUx98 zmhpO%^?I43q)B6fYZVm@)^^7pFH0fRG>|5oY8chZo=b)-4p|06++fmYCu0v@c|ndW z?kT9TP7PUvye%~Im;$KcE>xjXl_IHpb9oi2PEfnUb{Q2kw4h-qM{sa(8WS)J!)|xk zVHWmLj8lg+7*x=7S$5db>{Cz>XJm|JKK8|SG@9+QhXG;PSSeOf(ZFb#(Xdy@C79I6 zeAJXzx34bq#*#^cfmB6%?8R?se{B4&yup7T4OTc6Kvs89-jjQberm8#^V`Rkm|` zGPq$*(-?z|W>;-JC%g&zXX0d-QKT$UXHtAii;^&!s%*NHTnNz?lge<}(y0c$Ve8hh z$vKooBDom3Z)pJa4^YZ{eTwTY22Rw8YSp!KBE171NqYb!nWYM8Iv?s~I|~qW#4pfq z-^g9$Vxdxvw0%1(K#~Sv`v`rMiCR@Esb;>JL9#v~JY3Ivz{aYi9X@E)>e>Ooe-D5B z7-Cgvrq!r4-^@ya^_!sM$@y)gky?KkwNl%;y*SA)kTuRH=9(5PnZoZ#UAC(vH^ zNE3yVnvvul?f8>hkWA!B2h36^`ax}`HB?p-5;vu6Hz&i4FRCFqe9^hBRh zEQ>^~V&o+nKFwFBq08LeO4}~Q>3$U!t-F0oWi_BrbhCVl(#=w?cf9^);+r}dm7=nh z=Q%?##uymY-l6RilRd3{CWpb5MPx2o_Go={nYioL^}v+FIEP5_&6U-3D^_{{X`C_b zOgvV2%Pea6xpb2h@mS@<>{fQ?mu^T`W+VoYz41yY}O?KKY z5M&V5Qfs!+aDyO*s*_!|i-Z{jHB_4Iv|J#_A*!U;ZKB}@K@C+VyKNT;G6-s^HQQ*o WL6AdLNrV6Z{{OrY%76d>0000*Y=yc2 literal 0 HcmV?d00001 diff --git a/res/drawable-xxxhdpi/marker_shadow.webp b/res/drawable-xxxhdpi/marker_shadow.webp new file mode 100644 index 0000000000000000000000000000000000000000..6c7b09e533431a6f394509261f70aac057da2573 GIT binary patch literal 1414 zcmcJP`7_%I0L8y@s9-BAbY0a{sXAhYVuGeA#YV)@)X_!VSF6;br7}&F)>3qWMvryG zCZU#8gHqXT(<9=lEta}NY*S}8j*3;scg^hIuy5x5^4<^c{q!MwxVux~01)Wr;v3+L zy`TyJ0CL}L0FbH#IFoz{bua*c2QtxsZth;uSXh1@VP$yg8^H)DEsmb}=(EK#S?Mx5 zaLmkeIMw?IXTGJ|D0gS;ase?G?!cweRv%tfx5h+kOZr+r@8IXYXkCwU1x` zfME8j#$f_Lf49-j&;$Naok2D1`b$E-xIR6E=2k=_M=VlMwqZctZHA{Z!u*MZSg6@O z3y=RtfXI9t%?=BNpT2QRe(F}gM|-A*mY-rZM8JozDs5qr9OeGXxU9u=U(4a$-oK_^ z#4Ns>f~b^+I1zCD>VDGu3&}0|#wX~fXstk=7%De2?$+@yG`BZM2!SaZ(PtoX$FX#B zY?U$Vt~If@GYUrv3P%&(!sanusyi`rg#(1BZ5xg6XN3z04;5FKg_08by6&vQV1_sa z2WGHq5vwgKuA0dZp34E8S4kaVtW4WtJGuOvlUF|pVm*n=G>VX+|7Pm2g73AkdjmV$ zD@<)l!!0${T`7CY^L%JbVUojOjg`~kgX>6YR0pNN-G|WsR7hbyY93W&f{ns)!(iHd zYwXe?TYGFh{IZ%RTDtM-6Whux>ACU7pAnyHe^ASDpY9u1uD;bsKXXwbB;LjI%B&Wo zCysc}3rPl!nDW-hPbw{IYjQzT$*AZtzhB0|cTv*jg$CZ^@v|+|pl~mb>kQ zVg%rCo)SIr!d9z^M#uOaq;>ZBoowMJe9NTmf^HB0twQ{iMt~pQ0 zWtaTSS9jZ$q9c!={L$TMsnRDt@9g%STsOwM>d?#D zkj1iz&_U_%JQvTcjKXsu^-=z}sr#$B?@Jj?HS(~%)UXSzJ9a@t^DslGVWGWZq_lZm zi-g4lK-rIN5_z05Vj{&8lxeI5m*>AhIm#>@f7@9oKFg)`O*fYiYuqlzvmUS}v1u8Z z_QJ8a^)H0u`ABfQZI=?TxNAZs-?4Kvh)yfu#b@*48xFdz8)oE=k~zV#MP+owN0I_FM^vF-S&P;HMvu zdXpb&m||j$VsCDe=%Zp(_%kTWwoL0%ez1S1?(=5CWX2{*B`6=z%%V}KIcp}%yb@+73h0ZEGV)~Y9IU#yOvW) zM+#Tw>P*Ew*EW&qd-kG}MH8DhutQJa$}YY$v$fBIo#?|OR^*OZhorE#ysI7h#(6OA zeWk3;9c}|s{wQ*}Avfm@cObyX+hzqGE^C^zRAM>!+r1v>bo0p)_H-V!P1F5Y5y!NY ze$_Uh4TUlaTNqwcLafT?KfvII(sLrW$$zEOn z0#la}G=&&4l(Z4YJR + + diff --git a/res/drawable/ic_map_marker.xml b/res/drawable/ic_map_marker.xml new file mode 100644 index 0000000000..def118b481 --- /dev/null +++ b/res/drawable/ic_map_marker.xml @@ -0,0 +1,9 @@ + + + diff --git a/res/layout/activity_map_bottom_sheet_view.xml b/res/layout/activity_map_bottom_sheet_view.xml new file mode 100755 index 0000000000..b4c25a9f43 --- /dev/null +++ b/res/layout/activity_map_bottom_sheet_view.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/activity_place_picker.xml b/res/layout/activity_place_picker.xml new file mode 100644 index 0000000000..f9665bfe41 --- /dev/null +++ b/res/layout/activity_place_picker.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index 4f69236b8e..eced3b8ac1 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -547,6 +547,13 @@ Submit passphrase Invalid passphrase! + + Map + + Not a valid Address + Drop pin + Accept address + The version of Google Play Services you have installed is not functioning correctly. Please reinstall Google Play Services and try again. diff --git a/src/org/thoughtcrime/securesms/components/location/SignalMapView.java b/src/org/thoughtcrime/securesms/components/location/SignalMapView.java index 067af7bb85..cc305c538a 100644 --- a/src/org/thoughtcrime/securesms/components/location/SignalMapView.java +++ b/src/org/thoughtcrime/securesms/components/location/SignalMapView.java @@ -7,12 +7,10 @@ import android.os.Build; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; -import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; -import com.google.android.gms.location.places.Place; import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.MapView; diff --git a/src/org/thoughtcrime/securesms/components/location/SignalPlace.java b/src/org/thoughtcrime/securesms/components/location/SignalPlace.java index dabfbe639a..7e8b7ac85b 100644 --- a/src/org/thoughtcrime/securesms/components/location/SignalPlace.java +++ b/src/org/thoughtcrime/securesms/components/location/SignalPlace.java @@ -1,16 +1,18 @@ package org.thoughtcrime.securesms.components.location; +import android.location.Address; import android.net.Uri; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import android.text.TextUtils; +import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.android.gms.location.places.Place; import com.google.android.gms.maps.model.LatLng; import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.maps.AddressData; import org.thoughtcrime.securesms.util.JsonUtils; import java.io.IOException; @@ -32,13 +34,17 @@ public class SignalPlace { @JsonProperty private double longitude; - public SignalPlace(Place place) { - this.name = place.getName(); - this.address = place.getAddress(); - this.latitude = place.getLatLng().latitude; - this.longitude = place.getLatLng().longitude; + public SignalPlace(@NonNull AddressData place) { + Address address = place.getAddress(); + + this.name = ""; + this.address = address!= null ? address.getAddressLine(0) : ""; + this.latitude = place.getLatitude(); + this.longitude = place.getLongitude(); } + @JsonCreator + @SuppressWarnings("unused") public SignalPlace() {} @JsonIgnore diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java index 2dcdd1a76c..099c2a30af 100644 --- a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -72,7 +72,6 @@ import android.widget.TextView; import android.widget.Toast; import com.annimon.stream.Stream; -import com.google.android.gms.location.places.ui.PlacePicker; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; @@ -153,6 +152,7 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreview; import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository; import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel; import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.maps.PlacePickerActivity; import org.thoughtcrime.securesms.mediasend.Media; import org.thoughtcrime.securesms.mediasend.MediaSendActivity; import org.thoughtcrime.securesms.mms.AttachmentManager; @@ -521,7 +521,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity fragment.reloadList(); break; case PICK_LOCATION: - SignalPlace place = new SignalPlace(PlacePicker.getPlace(data, this)); + SignalPlace place = new SignalPlace(PlacePickerActivity.addressFromData(data)); attachmentManager.setLocation(place, getCurrentMediaConstraints()); break; case PICK_GIF: diff --git a/src/org/thoughtcrime/securesms/maps/AddressData.java b/src/org/thoughtcrime/securesms/maps/AddressData.java new file mode 100644 index 0000000000..cf8eec8660 --- /dev/null +++ b/src/org/thoughtcrime/securesms/maps/AddressData.java @@ -0,0 +1,57 @@ +package org.thoughtcrime.securesms.maps; + +import android.location.Address; +import android.os.Parcel; +import android.os.Parcelable; +import androidx.annotation.Nullable; + +public final class AddressData implements Parcelable { + + private final double latitude; + private final double longitude; + private final @Nullable Address address; + + AddressData(double latitude, double longitude, @Nullable Address address) { + this.latitude = latitude; + this.longitude = longitude; + this.address = address; + } + + public @Nullable Address getAddress() { + return address; + } + + public double getLongitude() { + return longitude; + } + + public double getLatitude() { + return latitude; + } + + public static final Creator CREATOR = new Creator() { + @Override + public AddressData createFromParcel(Parcel in) { + return new AddressData(in.readDouble(), + in.readDouble(), + Address.CREATOR.createFromParcel(in)); + } + + @Override + public AddressData[] newArray(int size) { + return new AddressData[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeDouble(latitude); + dest.writeDouble(longitude); + dest.writeParcelable(address, flags); + } + + @Override + public int describeContents() { + return 0; + } +} diff --git a/src/org/thoughtcrime/securesms/maps/PlacePickerActivity.java b/src/org/thoughtcrime/securesms/maps/PlacePickerActivity.java new file mode 100644 index 0000000000..c5d14e26da --- /dev/null +++ b/src/org/thoughtcrime/securesms/maps/PlacePickerActivity.java @@ -0,0 +1,243 @@ +package org.thoughtcrime.securesms.maps; + +import android.Manifest; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.location.Address; +import android.location.Geocoder; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.view.View; +import android.view.animation.OvershootInterpolator; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; + +import com.google.android.gms.location.FusedLocationProviderClient; +import com.google.android.gms.location.LocationServices; +import com.google.android.gms.maps.CameraUpdateFactory; +import com.google.android.gms.maps.GoogleMap; +import com.google.android.gms.maps.SupportMapFragment; +import com.google.android.gms.maps.model.LatLng; + +import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.logging.Log; + +import java.io.IOException; +import java.util.List; +import java.util.Locale; + +/** + * Allows selection of an address from a google map. + *

+ * Based on https://github.com/suchoX/PlacePicker + */ +public final class PlacePickerActivity extends AppCompatActivity { + + private static final String TAG = Log.tag(PlacePickerActivity.class); + + // If it cannot load location for any reason, it defaults to the prime meridian. + private static final LatLng PRIME_MERIDIAN = new LatLng(51.4779, -0.0015); + private static final String ADDRESS_INTENT = "ADDRESS"; + private static final float ZOOM = 17.0f; + + private static final int ANIMATION_DURATION = 250; + private static final OvershootInterpolator OVERSHOOT_INTERPOLATOR = new OvershootInterpolator(); + + private SingleAddressBottomSheet bottomSheet; + private Address currentAddress; + private LatLng initialLocation; + private LatLng currentLocation = new LatLng(0, 0); + private AddressLookup addressLookup; + private GoogleMap googleMap; + + public static void startActivityForResultAtCurrentLocation(@NonNull Activity activity, int requestCode) { + activity.startActivityForResult(new Intent(activity, PlacePickerActivity.class), requestCode); + } + + public static AddressData addressFromData(@NonNull Intent data) { + return data.getParcelableExtra(ADDRESS_INTENT); + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_place_picker); + + bottomSheet = findViewById(R.id.bottom_sheet); + View markerImage = findViewById(R.id.marker_image_view); + View fab = findViewById(R.id.place_chosen_button); + + fab.setOnClickListener(v -> finishWithAddress()); + + FusedLocationProviderClient fusedLocationClient = LocationServices.getFusedLocationProviderClient(this); + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || + checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED || + checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED + ) { + fusedLocationClient.getLastLocation() + .addOnFailureListener(e -> { + Log.e(TAG, "Failed to get location", e); + setInitialLocation(PRIME_MERIDIAN); + }) + .addOnSuccessListener(location -> { + if (location == null) { + Log.w(TAG, "Failed to get location"); + setInitialLocation(PRIME_MERIDIAN); + } else { + setInitialLocation(new LatLng(location.getLatitude(), location.getLongitude())); + } + }); + } else { + Log.w(TAG, "No location permissions"); + setInitialLocation(PRIME_MERIDIAN); + } + + SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map); + if (mapFragment == null) throw new AssertionError("No map fragment"); + + mapFragment.getMapAsync(googleMap -> { + + setMap(googleMap); + + enableMyLocationButtonIfHaveThePermission(googleMap); + + googleMap.setOnCameraMoveStartedListener(i -> { + markerImage.animate() + .translationY(-75f) + .setInterpolator(OVERSHOOT_INTERPOLATOR) + .setDuration(ANIMATION_DURATION) + .start(); + + bottomSheet.hide(); + }); + + googleMap.setOnCameraIdleListener(() -> { + markerImage.animate() + .translationY(0f) + .setInterpolator(OVERSHOOT_INTERPOLATOR) + .setDuration(ANIMATION_DURATION) + .start(); + + setCurrentLocation(googleMap.getCameraPosition().target); + }); + }); + } + + private void setInitialLocation(@NonNull LatLng latLng) { + initialLocation = latLng; + + moveMapToInitialIfPossible(); + } + + private void setMap(GoogleMap googleMap) { + this.googleMap = googleMap; + + moveMapToInitialIfPossible(); + } + + private void moveMapToInitialIfPossible() { + if (initialLocation != null && googleMap != null) { + Log.d(TAG, "Moving map to initial location"); + googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(initialLocation, ZOOM)); + setCurrentLocation(initialLocation); + } + } + + private void setCurrentLocation(LatLng location) { + currentLocation = location; + bottomSheet.showLoading(); + lookupAddress(location); + } + + private void finishWithAddress() { + Intent returnIntent = new Intent(); + AddressData addressData = new AddressData(currentLocation.latitude, currentLocation.longitude, currentAddress); + returnIntent.putExtra(ADDRESS_INTENT, addressData); + setResult(RESULT_OK, returnIntent); + finish(); + } + + private void enableMyLocationButtonIfHaveThePermission(GoogleMap googleMap) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || + checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED || + checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) + { + googleMap.setMyLocationEnabled(true); + } + } + + private void lookupAddress(@Nullable LatLng target) { + if (addressLookup != null) { + addressLookup.cancel(true); + } + addressLookup = new AddressLookup(); + addressLookup.execute(target); + } + + @Override + protected void onPause() { + super.onPause(); + if (addressLookup != null) { + addressLookup.cancel(true); + } + } + + @SuppressLint("StaticFieldLeak") + private class AddressLookup extends AsyncTask { + + private final String TAG = Log.tag(AddressLookup.class); + private final Geocoder geocoder; + + AddressLookup() { + geocoder = new Geocoder(getApplicationContext(), Locale.getDefault()); + } + + @Override + protected Address doInBackground(LatLng... latLngs) { + if (latLngs.length == 0) return null; + LatLng latLng = latLngs[0]; + if (latLng == null) return null; + try { + List

result = geocoder.getFromLocation(latLng.latitude, latLng.longitude, 1); + return !result.isEmpty() ? result.get(0) : null; + } catch (IOException e) { + Log.e(TAG, "Failed to get address from location", e); + return null; + } + } + + @Override + protected void onPostExecute(@Nullable Address address) { + Log.d(TAG, String.format("%s", addressToString(address))); + currentAddress = address; + if (address != null) { + bottomSheet.showResult(address.getLatitude(), address.getLongitude(), addressToShortString(address), addressToString(address)); + } else { + bottomSheet.hide(); + } + } + } + + private static @NonNull String addressToString(@Nullable Address address) { + return address != null ? address.getAddressLine(0) : ""; + } + + private static @NonNull String addressToShortString(@Nullable Address address) { + if (address == null) return ""; + + String addressLine = address.getAddressLine(0); + String[] split = addressLine.split(","); + + if (split.length >= 3) { + return split[1].trim() + ", " + split[2].trim(); + } else if (split.length == 2) { + return split[1].trim(); + } else return split[0].trim(); + } +} diff --git a/src/org/thoughtcrime/securesms/maps/SingleAddressBottomSheet.java b/src/org/thoughtcrime/securesms/maps/SingleAddressBottomSheet.java new file mode 100644 index 0000000000..d1de0512e2 --- /dev/null +++ b/src/org/thoughtcrime/securesms/maps/SingleAddressBottomSheet.java @@ -0,0 +1,81 @@ +package org.thoughtcrime.securesms.maps; + +import android.content.Context; +import android.location.Location; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.View; +import android.widget.ProgressBar; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.coordinatorlayout.widget.CoordinatorLayout; +import com.google.android.material.bottomsheet.BottomSheetBehavior; +import org.thoughtcrime.securesms.R; + +import java.util.Locale; + +final class SingleAddressBottomSheet extends CoordinatorLayout { + + private TextView placeNameTextView; + private TextView placeAddressTextView; + private ProgressBar placeProgressBar; + private BottomSheetBehavior bottomSheetBehavior; + + public SingleAddressBottomSheet(@NonNull Context context) { + super(context); + init(); + } + + public SingleAddressBottomSheet(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(); + } + + public SingleAddressBottomSheet(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + private void init() { + CoordinatorLayout rootView = (CoordinatorLayout) inflate(getContext(), R.layout.activity_map_bottom_sheet_view, this); + + bottomSheetBehavior = BottomSheetBehavior.from(rootView.findViewById(R.id.root_bottom_sheet)); + bottomSheetBehavior.setHideable(true); + bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); + + bindViews(); + } + + private void bindViews() { + placeNameTextView = findViewById(R.id.text_view_place_name); + placeAddressTextView = findViewById(R.id.text_view_place_address); + placeProgressBar = findViewById(R.id.progress_bar_place); + } + + public void showLoading() { + bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); + placeNameTextView.setText(""); + placeAddressTextView.setText(""); + placeProgressBar.setVisibility(View.VISIBLE); + } + + public void showResult(double latitude, double longitude, String addressToShortString, String addressToString) { + bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); + placeProgressBar.setVisibility(View.GONE); + + if (TextUtils.isEmpty(addressToString)) { + String longString = Location.convert(longitude, Location.FORMAT_DEGREES); + String latString = Location.convert(latitude, Location.FORMAT_DEGREES); + + placeNameTextView.setText(String.format(Locale.getDefault(), "%s %s", latString, longString)); + } else { + placeNameTextView.setText(addressToShortString); + placeAddressTextView.setText(addressToString); + } + } + + public void hide() { + bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); + } +} diff --git a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java index 64478d595f..53bd324999 100644 --- a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java +++ b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java @@ -30,23 +30,17 @@ import android.os.AsyncTask; import android.provider.ContactsContract; import android.provider.MediaStore; import android.provider.OpenableColumns; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import android.text.TextUtils; - -import org.thoughtcrime.securesms.TransportOption; -import org.thoughtcrime.securesms.mediasend.MediaSendActivity; -import org.thoughtcrime.securesms.logging.Log; import android.util.Pair; import android.view.View; import android.widget.Toast; -import com.google.android.gms.common.GooglePlayServicesNotAvailableException; -import com.google.android.gms.common.GooglePlayServicesRepairableException; -import com.google.android.gms.location.places.ui.PlacePicker; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import org.thoughtcrime.securesms.MediaPreviewActivity; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.TransportOption; import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.components.AudioView; import org.thoughtcrime.securesms.components.DocumentView; @@ -55,6 +49,9 @@ import org.thoughtcrime.securesms.components.ThumbnailView; import org.thoughtcrime.securesms.components.location.SignalMapView; import org.thoughtcrime.securesms.components.location.SignalPlace; import org.thoughtcrime.securesms.giph.ui.GiphyActivity; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.maps.PlacePickerActivity; +import org.thoughtcrime.securesms.mediasend.MediaSendActivity; import org.thoughtcrime.securesms.permissions.Permissions; import org.thoughtcrime.securesms.providers.BlobProvider; import org.thoughtcrime.securesms.providers.DeprecatedPersistentBlobProvider; @@ -414,13 +411,7 @@ public class AttachmentManager { .request(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION) .ifNecessary() .withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_location_information_in_order_to_attach_a_location)) - .onAllGranted(() -> { - try { - activity.startActivityForResult(new PlacePicker.IntentBuilder().build(activity), requestCode); - } catch (GooglePlayServicesRepairableException | GooglePlayServicesNotAvailableException e) { - Log.w(TAG, e); - } - }) + .onAllGranted(() -> PlacePickerActivity.startActivityForResultAtCurrentLocation(activity, requestCode)) .execute(); }