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 0000000000..59217c52f2 Binary files /dev/null and b/res/drawable-hdpi/marker_shadow.webp differ diff --git a/res/drawable-mdpi/marker_shadow.webp b/res/drawable-mdpi/marker_shadow.webp new file mode 100644 index 0000000000..6c2916b0a1 Binary files /dev/null and b/res/drawable-mdpi/marker_shadow.webp differ diff --git a/res/drawable-xhdpi/marker_shadow.webp b/res/drawable-xhdpi/marker_shadow.webp new file mode 100644 index 0000000000..2c15136719 Binary files /dev/null and b/res/drawable-xhdpi/marker_shadow.webp differ diff --git a/res/drawable-xxhdpi/marker_shadow.webp b/res/drawable-xxhdpi/marker_shadow.webp new file mode 100644 index 0000000000..b06951c2c2 Binary files /dev/null and b/res/drawable-xxhdpi/marker_shadow.webp differ diff --git a/res/drawable-xxxhdpi/marker_shadow.webp b/res/drawable-xxxhdpi/marker_shadow.webp new file mode 100644 index 0000000000..6c7b09e533 Binary files /dev/null and b/res/drawable-xxxhdpi/marker_shadow.webp differ diff --git a/res/drawable/ic_check.xml b/res/drawable/ic_check.xml new file mode 100644 index 0000000000..17aca2af18 --- /dev/null +++ b/res/drawable/ic_check.xml @@ -0,0 +1,5 @@ + + + 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(); }