mirror of
				https://github.com/oxen-io/session-android.git
				synced 2025-10-30 23:09:20 +00:00 
			
		
		
		
	Custom place picker to replace places SDK.
This commit is contained in:
		| @@ -457,6 +457,11 @@ | ||||
|               android:exported="true" | ||||
|               android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/> | ||||
|  | ||||
|     <activity | ||||
|             android:name=".maps.PlacePickerActivity" | ||||
|             android:label="@string/PlacePickerActivity_title" | ||||
|             android:theme="@style/TextSecure.LightNoActionBar" /> | ||||
|  | ||||
|     <service android:enabled="true" android:name="org.thoughtcrime.securesms.service.WebRtcCallService"/> | ||||
|     <service android:enabled="true" android:name=".service.ApplicationMigrationService"/> | ||||
|     <service android:enabled="true" android:exported="false" android:name=".service.KeyCachingService"/> | ||||
|   | ||||
| @@ -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', | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								res/drawable-hdpi/marker_shadow.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								res/drawable-hdpi/marker_shadow.webp
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 360 B | 
							
								
								
									
										
											BIN
										
									
								
								res/drawable-mdpi/marker_shadow.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								res/drawable-mdpi/marker_shadow.webp
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 244 B | 
							
								
								
									
										
											BIN
										
									
								
								res/drawable-xhdpi/marker_shadow.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								res/drawable-xhdpi/marker_shadow.webp
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 506 B | 
							
								
								
									
										
											BIN
										
									
								
								res/drawable-xxhdpi/marker_shadow.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								res/drawable-xxhdpi/marker_shadow.webp
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 844 B | 
							
								
								
									
										
											BIN
										
									
								
								res/drawable-xxxhdpi/marker_shadow.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								res/drawable-xxxhdpi/marker_shadow.webp
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.4 KiB | 
							
								
								
									
										5
									
								
								res/drawable/ic_check.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								res/drawable/ic_check.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| <vector android:height="24dp" android:tint="#FFFFFF" | ||||
|     android:viewportHeight="24.0" android:viewportWidth="24.0" | ||||
|     android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
|     <path android:fillColor="#FF000000" android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/> | ||||
| </vector> | ||||
							
								
								
									
										9
									
								
								res/drawable/ic_map_marker.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								res/drawable/ic_map_marker.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:width="512dp" | ||||
|     android:height="512dp" | ||||
|     android:viewportWidth="438.536" | ||||
|     android:viewportHeight="438.536"> | ||||
|   <path | ||||
|       android:pathData="M322.621,42.825C294.073,14.272 259.619,0 219.268,0c-40.353,0 -74.803,14.275 -103.353,42.825c-28.549,28.549 -42.825,63 -42.825,103.353c0,20.749 3.14,37.782 9.419,51.106l104.21,220.986c2.856,6.276 7.283,11.225 13.278,14.838c5.996,3.617 12.419,5.428 19.273,5.428c6.852,0 13.278,-1.811 19.273,-5.428c5.996,-3.613 10.513,-8.562 13.559,-14.838l103.918,-220.986c6.282,-13.324 9.424,-30.358 9.424,-51.106C365.449,105.825 351.176,71.378 322.621,42.825zM270.942,197.855c-14.273,14.272 -31.497,21.411 -51.674,21.411s-37.401,-7.139 -51.678,-21.411c-14.275,-14.277 -21.414,-31.501 -21.414,-51.678c0,-20.175 7.139,-37.402 21.414,-51.675c14.277,-14.275 31.504,-21.414 51.678,-21.414c20.177,0 37.401,7.139 51.674,21.414c14.274,14.272 21.413,31.5 21.413,51.675C292.355,166.352 285.217,183.575 270.942,197.855z" | ||||
|       android:fillColor="#2090ea"/> | ||||
| </vector> | ||||
							
								
								
									
										91
									
								
								res/layout/activity_map_bottom_sheet_view.xml
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										91
									
								
								res/layout/activity_map_bottom_sheet_view.xml
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,91 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="match_parent" | ||||
|     android:layout_gravity="bottom"> | ||||
|  | ||||
|     <com.google.android.material.floatingactionbutton.FloatingActionButton | ||||
|         android:id="@+id/place_chosen_button" | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:layout_margin="16dp" | ||||
|         android:contentDescription="@string/PlacePickerActivity_accept_address" | ||||
|         android:scaleType="center" | ||||
|         android:tint="@color/white" | ||||
|         app:backgroundTint="?attr/colorPrimary" | ||||
|         app:elevation="3dp" | ||||
|         app:fabSize="normal" | ||||
|         app:layout_anchor="@id/root_bottom_sheet" | ||||
|         app:layout_anchorGravity="top|end" | ||||
|         app:srcCompat="@drawable/ic_check" /> | ||||
|  | ||||
|     <LinearLayout | ||||
|         android:id="@+id/root_bottom_sheet" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:orientation="vertical" | ||||
|         app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"> | ||||
|  | ||||
|         <ImageView | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="3dp" | ||||
|             android:scaleType="fitXY" | ||||
|             android:background="@drawable/compose_divider_background"/> | ||||
|  | ||||
|         <FrameLayout | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:background="@color/white"> | ||||
|  | ||||
|             <LinearLayout | ||||
|                 android:layout_width="match_parent" | ||||
|                 android:layout_height="wrap_content" | ||||
|                 android:layout_gravity="center" | ||||
|                 android:layout_marginBottom="12dp" | ||||
|                 android:orientation="vertical"> | ||||
|  | ||||
|                 <TextView | ||||
|                     android:id="@+id/text_view_place_name" | ||||
|                     android:layout_width="match_parent" | ||||
|                     android:layout_height="wrap_content" | ||||
|                     android:layout_marginStart="16dp" | ||||
|                     android:layout_marginTop="18dp" | ||||
|                     android:layout_marginEnd="16dp" | ||||
|                     style="@style/Signal.Text.Body" | ||||
|                     android:fontFamily="sans-serif-medium" | ||||
|                     android:ellipsize="end" | ||||
|                     android:maxLines="1" | ||||
|                     android:textColor="?conversation_item_sent_text_primary_color" | ||||
|                     tools:text="Short address" /> | ||||
|  | ||||
|                 <TextView | ||||
|                     android:id="@+id/text_view_place_address" | ||||
|                     android:layout_width="match_parent" | ||||
|                     android:layout_height="wrap_content" | ||||
|                     android:layout_marginStart="16dp" | ||||
|                     android:layout_marginTop="8dp" | ||||
|                     android:layout_marginEnd="16dp" | ||||
|                     style="@style/Signal.Text.Preview" | ||||
|                     android:ellipsize="end" | ||||
|                     android:maxLines="1" | ||||
|                     android:textColor="?conversation_item_sent_text_secondary_color" | ||||
|                     tools:text="Precise address" /> | ||||
|             </LinearLayout> | ||||
|  | ||||
|             <ProgressBar | ||||
|                 android:id="@+id/progress_bar_place" | ||||
|                 style="?android:attr/progressBarStyle" | ||||
|                 android:layout_width="wrap_content" | ||||
|                 android:layout_height="wrap_content" | ||||
|                 android:layout_gravity="center" | ||||
|                 android:indeterminate="true" | ||||
|                 android:visibility="invisible" | ||||
|                 tools:visibility="visible" /> | ||||
|  | ||||
|         </FrameLayout> | ||||
|  | ||||
|     </LinearLayout> | ||||
|  | ||||
| </androidx.coordinatorlayout.widget.CoordinatorLayout> | ||||
							
								
								
									
										56
									
								
								res/layout/activity_place_picker.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								res/layout/activity_place_picker.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <androidx.constraintlayout.widget.ConstraintLayout | ||||
|     xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="match_parent" | ||||
|     tools:context="org.thoughtcrime.securesms.maps.PlacePickerActivity"> | ||||
|  | ||||
|     <fragment | ||||
|         android:id="@+id/map" | ||||
|         android:name="com.google.android.gms.maps.SupportMapFragment" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="match_parent" | ||||
|         app:layout_constraintBottom_toBottomOf="parent" | ||||
|         app:layout_constraintEnd_toEndOf="parent" | ||||
|         app:layout_constraintHorizontal_bias="0.0" | ||||
|         app:layout_constraintStart_toStartOf="parent" | ||||
|         app:layout_constraintTop_toTopOf="parent" | ||||
|         app:layout_constraintVertical_bias="0.0" /> | ||||
|  | ||||
|     <ImageView | ||||
|         android:id="@+id/marker_shadow_image_view" | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:importantForAccessibility="no" | ||||
|         app:layout_constraintBottom_toBottomOf="@+id/map" | ||||
|         app:layout_constraintEnd_toEndOf="parent" | ||||
|         app:layout_constraintRight_toRightOf="parent" | ||||
|         app:layout_constraintStart_toStartOf="parent" | ||||
|         app:layout_constraintTop_toTopOf="parent" | ||||
|         app:srcCompat="@drawable/marker_shadow" /> | ||||
|  | ||||
|     <ImageView | ||||
|         android:id="@+id/marker_image_view" | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="47dp" | ||||
|         android:adjustViewBounds="true" | ||||
|         android:contentDescription="@string/PlacePickerActivity_drop_pin" | ||||
|         app:layout_constraintBottom_toBottomOf="parent" | ||||
|         app:layout_constraintEnd_toEndOf="parent" | ||||
|         app:layout_constraintStart_toStartOf="parent" | ||||
|         app:layout_constraintTop_toTopOf="@id/marker_shadow_image_view" | ||||
|         app:layout_constraintVertical_bias="0.0" | ||||
|         app:srcCompat="@drawable/ic_map_marker" /> | ||||
|  | ||||
|     <org.thoughtcrime.securesms.maps.SingleAddressBottomSheet | ||||
|         android:id="@+id/bottom_sheet" | ||||
|         android:layout_width="0dp" | ||||
|         android:layout_height="0dp" | ||||
|         app:layout_constraintBottom_toBottomOf="parent" | ||||
|         app:layout_constraintEnd_toEndOf="parent" | ||||
|         app:layout_constraintStart_toStartOf="parent" | ||||
|         app:layout_constraintTop_toTopOf="parent" /> | ||||
|  | ||||
| </androidx.constraintlayout.widget.ConstraintLayout> | ||||
| @@ -547,6 +547,13 @@ | ||||
|     <string name="PassphrasePromptActivity_ok_button_content_description">Submit passphrase</string> | ||||
|     <string name="PassphrasePromptActivity_invalid_passphrase_exclamation">Invalid passphrase!</string> | ||||
|  | ||||
|     <!-- PlacePickerActivity --> | ||||
|     <string name="PlacePickerActivity_title">Map</string> | ||||
|  | ||||
|     <string name="PlacePickerActivity_not_a_valid_address">Not a valid Address</string> | ||||
|     <string name="PlacePickerActivity_drop_pin">Drop pin</string> | ||||
|     <string name="PlacePickerActivity_accept_address">Accept address</string> | ||||
|  | ||||
|     <!-- PlayServicesProblemFragment --> | ||||
|     <string name="PlayServicesProblemFragment_the_version_of_google_play_services_you_have_installed_is_not_functioning">The version of Google Play Services you have installed is not functioning correctly.  Please reinstall Google Play Services and try again.</string> | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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: | ||||
|   | ||||
							
								
								
									
										57
									
								
								src/org/thoughtcrime/securesms/maps/AddressData.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/org/thoughtcrime/securesms/maps/AddressData.java
									
									
									
									
									
										Normal file
									
								
							| @@ -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<AddressData> CREATOR = new Creator<AddressData>() { | ||||
|     @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; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										243
									
								
								src/org/thoughtcrime/securesms/maps/PlacePickerActivity.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										243
									
								
								src/org/thoughtcrime/securesms/maps/PlacePickerActivity.java
									
									
									
									
									
										Normal file
									
								
							| @@ -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. | ||||
|  * <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()); | ||||
|  | ||||
|     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<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.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(); | ||||
|   } | ||||
| } | ||||
| @@ -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); | ||||
|   } | ||||
| } | ||||
| @@ -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(); | ||||
|   } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Alan Evans
					Alan Evans