Move all files to natural position.

This commit is contained in:
Alan Evans
2020-01-06 10:52:48 -05:00
parent 0df36047e7
commit 9ebe920195
3016 changed files with 6 additions and 36 deletions

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}