mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-19 16:19:33 +01:00
Move all files to natural position.
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
package org.thoughtcrime.securesms.maps;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public final class AddressData implements Parcelable {
|
||||
|
||||
private final double latitude;
|
||||
private final double longitude;
|
||||
private final String address;
|
||||
|
||||
AddressData(double latitude, double longitude, @NonNull String address) {
|
||||
this.latitude = latitude;
|
||||
this.longitude = longitude;
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public @NonNull String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public double getLongitude() {
|
||||
return longitude;
|
||||
}
|
||||
|
||||
public double getLatitude() {
|
||||
return latitude;
|
||||
}
|
||||
|
||||
public static final Creator<AddressData> CREATOR = new Creator<AddressData>() {
|
||||
@Override
|
||||
public AddressData createFromParcel(Parcel in) {
|
||||
//noinspection ConstantConditions
|
||||
return new AddressData(in.readDouble(),
|
||||
in.readDouble(),
|
||||
in.readString());
|
||||
}
|
||||
|
||||
@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.writeString(address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package org.thoughtcrime.securesms.maps;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.location.Location;
|
||||
import android.location.LocationListener;
|
||||
import android.location.LocationManager;
|
||||
import android.location.LocationProvider;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.lifecycle.DefaultLifecycleObserver;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||
|
||||
/**
|
||||
* A lifecycle-safe way to retrieve a single location update. If a cached location is available,
|
||||
* we'll use that. Otherwise we'll listen for one.
|
||||
*/
|
||||
class LocationRetriever implements DefaultLifecycleObserver, LocationListener {
|
||||
|
||||
private static final String TAG = Log.tag(LocationRetriever.class);
|
||||
|
||||
private final Context context;
|
||||
private final LocationManager locationManager;
|
||||
private final SuccessListener successListener;
|
||||
private final FailureListener failureListener;
|
||||
|
||||
LocationRetriever(@NonNull Context context, @NonNull LifecycleOwner lifecycleOwner, @NonNull SuccessListener successListener, @NonNull FailureListener failureListener) {
|
||||
this.context = context;
|
||||
this.locationManager = ServiceUtil.getLocationManager(context);
|
||||
this.successListener = successListener;
|
||||
this.failureListener = failureListener;
|
||||
|
||||
lifecycleOwner.getLifecycle().addObserver(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart(@NonNull LifecycleOwner owner) {
|
||||
if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
|
||||
ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)
|
||||
{
|
||||
Log.w(TAG, "No location permission!");
|
||||
failureListener.onFailure();
|
||||
}
|
||||
|
||||
LocationProvider provider = locationManager.getProvider(LocationManager.GPS_PROVIDER);
|
||||
|
||||
if (provider == null) {
|
||||
Log.w(TAG, "GPS provider is null. Trying network provider.");
|
||||
provider = locationManager.getProvider(LocationManager.NETWORK_PROVIDER);
|
||||
}
|
||||
|
||||
if (provider == null) {
|
||||
Log.w(TAG, "Network provider is null. Unable to retrieve location.");
|
||||
failureListener.onFailure();
|
||||
return;
|
||||
}
|
||||
|
||||
Location lastKnown = locationManager.getLastKnownLocation(provider.getName());
|
||||
|
||||
if (lastKnown != null) {
|
||||
Log.i(TAG, "Using last known location.");
|
||||
successListener.onSuccess(lastKnown);
|
||||
} else {
|
||||
Log.i(TAG, "No last known location. Requesting a single update.");
|
||||
locationManager.requestSingleUpdate(provider.getName(), this, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop(@NonNull LifecycleOwner owner) {
|
||||
Log.i(TAG, "Removing any possible location listeners.");
|
||||
locationManager.removeUpdates(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocationChanged(@Nullable Location location) {
|
||||
if (location != null) {
|
||||
Log.w(TAG, "[onLocationChanged] Successfully retrieved location.");
|
||||
successListener.onSuccess(location);
|
||||
} else {
|
||||
Log.w(TAG, "[onLocationChanged] Null location.");
|
||||
failureListener.onFailure();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChanged(@NonNull String provider, int status, @Nullable Bundle extras) {
|
||||
Log.i(TAG, "[onStatusChanged] Provider: " + provider + " Status: " + status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderEnabled(@NonNull String provider) {
|
||||
Log.i(TAG, "[onProviderEnabled] Provider: " + provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderDisabled(@NonNull String provider) {
|
||||
Log.i(TAG, "[onProviderDisabled] Provider: " + provider);
|
||||
}
|
||||
|
||||
interface SuccessListener {
|
||||
void onSuccess(@NonNull Location location);
|
||||
}
|
||||
|
||||
interface FailureListener {
|
||||
void onFailure();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
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 androidx.core.content.ContextCompat;
|
||||
|
||||
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.
|
||||
* <p>
|
||||
* 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());
|
||||
|
||||
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED ||
|
||||
ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED)
|
||||
{
|
||||
new LocationRetriever(this, this, location -> {
|
||||
setInitialLocation(new LatLng(location.getLatitude(), location.getLongitude()));
|
||||
}, () -> {
|
||||
Log.w(TAG, "Failed to get location.");
|
||||
setInitialLocation(PRIME_MERIDIAN);
|
||||
});
|
||||
} 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();
|
||||
String address = currentAddress != null && currentAddress.getAddressLine(0) != null ? currentAddress.getAddressLine(0) : "";
|
||||
AddressData addressData = new AddressData(currentLocation.latitude, currentLocation.longitude, address);
|
||||
|
||||
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<LatLng, Void, Address> {
|
||||
|
||||
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<Address> result = geocoder.getFromLocation(latLng.latitude, latLng.longitude, 1);
|
||||
return !result.isEmpty() ? result.get(0) : null;
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Failed to get address from location", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(@Nullable Address 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();
|
||||
}
|
||||
}
|
||||
@@ -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<View> 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user