From be8a718b3fa68f0f53e5879523f1fceb400e4692 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Wed, 6 Mar 2019 17:25:01 +0100 Subject: [PATCH] implement share location button, share location timespan, rename threadId to chatId --- res/layout/attachment_type_selector.xml | 4 +- res/values/arrays.xml | 10 +++ res/values/strings.xml | 7 ++ .../securesms/ConversationActivity.java | 56 ++++++++-------- .../securesms/ConversationFragment.java | 2 +- .../securesms/ShareLocationDialog.java | 55 ++++++++++++++++ .../securesms/geolocation/DcLocation.java | 2 +- .../geolocation/DcLocationManager.java | 15 ++++- .../LocationBackgroundService.java | 64 +++++++++++-------- .../securesms/mms/AttachmentManager.java | 27 +++++--- .../securesms/service/DirectShareService.java | 2 +- 11 files changed, 176 insertions(+), 68 deletions(-) create mode 100644 src/org/thoughtcrime/securesms/ShareLocationDialog.java diff --git a/res/layout/attachment_type_selector.xml b/res/layout/attachment_type_selector.xml index 183fddfd5..ee8743be3 100644 --- a/res/layout/attachment_type_selector.xml +++ b/res/layout/attachment_type_selector.xml @@ -91,14 +91,14 @@ android:layout_height="53dp" android:src="@drawable/ic_location_on_white_24dp" android:scaleType="center" - android:visibility="gone" + android:visibility="visible" android:contentDescription="@string/location" app:circleColor="@color/location_icon"/> diff --git a/res/values/arrays.xml b/res/values/arrays.xml index e27e472cd..826b17568 100644 --- a/res/values/arrays.xml +++ b/res/values/arrays.xml @@ -129,6 +129,16 @@ @string/mute_for_one_year + + + @string/share_location_once + @string/share_location_for_5_minutes + @string/share_location_for_30_minutes + @string/share_location_for_one_hour + @string/share_location_for_two_hours + @string/share_location_for_six_hours + + @string/def @string/on diff --git a/res/values/strings.xml b/res/values/strings.xml index 83f27a780..8c2add155 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -144,6 +144,7 @@ Contact requests Press message to start chatting View profile + Share location Mute for 1 hour Mute for 2 hours @@ -151,6 +152,12 @@ Mute for 7 days Mute for 1 year + once + for 5 minutes + for 30 minutes + for 1 hour + for 2 hours + for 6 hours Are you sure you want to leave this group? diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java index f546a7cd7..9be82b202 100644 --- a/src/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationActivity.java @@ -177,7 +177,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity private Recipient recipient; private ApplicationDcContext dcContext; private DcChat dcChat = new DcChat(0); - private int threadId; + private int chatId; private boolean archived; private final boolean isSecureText = true; private boolean isDefaultSms = true; @@ -284,7 +284,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity titleView.setTitle(glideRequests, dcChat); - MessageNotifier.updateVisibleChat(this, threadId); + MessageNotifier.updateVisibleChat(this, chatId); markThreadAsRead(); } @@ -359,7 +359,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity addAttachmentContactInfo(data); break; case GROUP_EDIT: - dcChat = dcContext.getChat(threadId); + dcChat = dcContext.getChat(chatId); titleView.setTitle(glideRequests, dcChat); supportInvalidateOptionsMenu(); break; @@ -402,11 +402,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity MenuInflater inflater = this.getMenuInflater(); menu.clear(); - if(threadId==DcChat.DC_CHAT_ID_DEADDROP) { + if(chatId ==DcChat.DC_CHAT_ID_DEADDROP) { return true; } - if (recipient != null && Prefs.isChatMuted(this, threadId)) { + if (recipient != null && Prefs.isChatMuted(this, chatId)) { inflater.inflate(R.menu.conversation_muted, menu); } else { @@ -487,13 +487,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity private void handleMuteNotifications() { MuteDialog.show(this, until -> { - Prefs.setChatMutedUntil(this, threadId, until); + Prefs.setChatMutedUntil(this, chatId, until); titleView.setTitle(glideRequests, dcChat); // update title-mute-icon }); } private void handleConversationSettings() { - if(threadId!=DcChat.DC_CHAT_ID_DEADDROP) { + if(chatId !=DcChat.DC_CHAT_ID_DEADDROP) { Intent intent = new Intent(ConversationActivity.this, RecipientPreferenceActivity.class); intent.putExtra(RecipientPreferenceActivity.ADDRESS_EXTRA, recipient.getAddress()); startActivitySceneTransition(intent, titleView.findViewById(R.id.contact_photo_image), "avatar"); @@ -501,7 +501,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } private void handleUnmuteNotifications() { - Prefs.setChatMutedUntil(this, threadId, 0); + Prefs.setChatMutedUntil(this, chatId, 0); titleView.setTitle(glideRequests, dcChat); // update title-mute-icon } @@ -515,7 +515,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity new AlertDialog.Builder(this) .setMessage(getString(R.string.ask_leave_group)) .setPositiveButton(R.string.yes, (dialog, which) -> { - dcContext.removeContactFromChat(threadId, DcContact.DC_CONTACT_ID_SELF); + dcContext.removeContactFromChat(chatId, DcContact.DC_CONTACT_ID_SELF); Toast.makeText(this, getString(R.string.done), Toast.LENGTH_SHORT).show(); }) .setNegativeButton(R.string.no, null) @@ -523,8 +523,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } private void handleArchiveChat() { - int doArchive = dcContext.getChat(threadId).getArchived()==0? 1: 0; - dcContext.archiveChat(threadId, doArchive); + int doArchive = dcContext.getChat(chatId).getArchived()==0? 1: 0; + dcContext.archiveChat(chatId, doArchive); Toast.makeText(this, getString(R.string.done), Toast.LENGTH_SHORT).show(); if( doArchive == 1 ) { finish(); @@ -535,7 +535,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity new AlertDialog.Builder(this) .setMessage(getResources().getQuantityString(R.plurals.ask_delete_chat, 1, 1)) .setPositiveButton(R.string.delete, (dialog, which) -> { - dcContext.deleteChat(threadId); + dcContext.deleteChat(chatId); Toast.makeText(this, getString(R.string.done), Toast.LENGTH_SHORT).show(); finish(); }) @@ -545,7 +545,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity private void handleEditPushGroup() { Intent intent = new Intent(ConversationActivity.this, GroupCreateActivity.class); - intent.putExtra(GroupCreateActivity.EDIT_GROUP_CHAT_ID, threadId); + intent.putExtra(GroupCreateActivity.EDIT_GROUP_CHAT_ID, chatId); if (dcChat.isVerified()) { intent.putExtra(GroupCreateActivity.GROUP_CREATE_VERIFIED_EXTRA, true); } @@ -614,7 +614,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity new AsyncTask() { @Override protected DcMsg doInBackground(Void... params) { - return dcContext.getDraft(threadId); + return dcContext.getDraft(chatId); } @Override @@ -764,10 +764,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } private void initializeResources() { - threadId = getIntent().getIntExtra(THREAD_ID_EXTRA, -1); - if(threadId == DcChat.DC_CHAT_NO_CHAT) + chatId = getIntent().getIntExtra(THREAD_ID_EXTRA, -1); + if(chatId == DcChat.DC_CHAT_NO_CHAT) throw new IllegalStateException("can't display a conversation for no chat."); - dcChat = dcContext.getChat(threadId); + dcChat = dcContext.getChat(chatId); recipient = dcContext.getRecipient(dcChat); archived = getIntent().getBooleanExtra(IS_ARCHIVED_EXTRA, false); glideRequests = GlideApp.with(this); @@ -779,7 +779,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity conversationContainer.setClipToPadding(true); } - if(threadId==DcChat.DC_CHAT_ID_DEADDROP) { + if(chatId ==DcChat.DC_CHAT_ID_DEADDROP) { composePanel.setVisibility(View.GONE); titleView.hideAvatar(); } @@ -799,7 +799,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity case AttachmentTypeSelector.ADD_CONTACT_INFO: startContactChooserActivity(); break; case AttachmentTypeSelector.ADD_LOCATION: - AttachmentManager.selectLocation(this, PICK_LOCATION); break; + AttachmentManager.selectLocation(this, chatId); break; case AttachmentTypeSelector.TAKE_PHOTO: attachmentManager.capturePhoto(this, TAKE_PHOTO); break; } @@ -840,8 +840,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity return this.recipient; } - protected long getThreadId() { - return this.threadId; + protected long getChatId() { + return this.chatId; } private MediaConstraints getCurrentMediaConstraints() { @@ -852,10 +852,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity new AsyncTask() { @Override protected Void doInBackground(Integer... params) { - MessageNotifier.updateNotification(ConversationActivity.this, threadId, false); + MessageNotifier.updateNotification(ConversationActivity.this, chatId, false); return null; } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, threadId); + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, chatId); } private String getRealPathFromAttachment(Attachment attachment) { @@ -976,7 +976,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity @Override protected void onPostExecute(Void result) { - future.set(threadId); + future.set(chatId); } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, msg, recompress); @@ -984,8 +984,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } protected void sendComplete(int threadId) { - boolean refreshFragment = (threadId != this.threadId); - this.threadId = threadId; + boolean refreshFragment = (threadId != this.chatId); + this.chatId = threadId; if (fragment == null || !fragment.isVisible() || isFinishing()) { return; @@ -1279,8 +1279,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } @Override - public void setThreadId(int threadId) { - this.threadId = threadId; + public void setChatId(int chatId) { + this.chatId = chatId; } @Override diff --git a/src/org/thoughtcrime/securesms/ConversationFragment.java b/src/org/thoughtcrime/securesms/ConversationFragment.java index 4faa08858..0fedd5cf8 100644 --- a/src/org/thoughtcrime/securesms/ConversationFragment.java +++ b/src/org/thoughtcrime/securesms/ConversationFragment.java @@ -544,7 +544,7 @@ public class ConversationFragment extends Fragment } public interface ConversationFragmentListener { - void setThreadId(int threadId); + void setChatId(int threadId); void handleReplyMessage(DcMsg messageRecord); } diff --git a/src/org/thoughtcrime/securesms/ShareLocationDialog.java b/src/org/thoughtcrime/securesms/ShareLocationDialog.java new file mode 100644 index 000000000..1acde1306 --- /dev/null +++ b/src/org/thoughtcrime/securesms/ShareLocationDialog.java @@ -0,0 +1,55 @@ +package org.thoughtcrime.securesms; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.v7.app.AlertDialog; + +import java.util.concurrent.TimeUnit; + +/** + * Created by cyberta on 06.03.19. + */ + +public class ShareLocationDialog extends AlertDialog { + + + protected ShareLocationDialog(Context context) { + super(context); + } + + protected ShareLocationDialog(Context context, boolean cancelable, OnCancelListener cancelListener) { + super(context, cancelable, cancelListener); + } + + protected ShareLocationDialog(Context context, int theme) { + super(context, theme); + } + + public static void show(final Context context, final @NonNull ShareLocationDurationSelectionListener listener) { + Builder builder = new Builder(context); + builder.setTitle(R.string.title_share_location); + builder.setItems(R.array.share_location_durations, (dialog, which) -> { + final int shareLocationUnit; + + switch (which) { + default: + case 0: shareLocationUnit = 1; break; + case 1: shareLocationUnit = 5 * 60; break; + case 2: shareLocationUnit = 30 * 60; break; + case 3: shareLocationUnit = 60 * 60; break; + case 4: shareLocationUnit = 60 * 60 * 2; break; + case 5: shareLocationUnit = 60 * 60 * 6; break; + } + + listener.onSelected(shareLocationUnit); + }); + + builder.show(); + + } + + public interface ShareLocationDurationSelectionListener { + void onSelected(int durationInSeconds); + } + +} diff --git a/src/org/thoughtcrime/securesms/geolocation/DcLocation.java b/src/org/thoughtcrime/securesms/geolocation/DcLocation.java index 4fde87000..73cbd044e 100644 --- a/src/org/thoughtcrime/securesms/geolocation/DcLocation.java +++ b/src/org/thoughtcrime/securesms/geolocation/DcLocation.java @@ -25,7 +25,7 @@ public class DcLocation extends Observable { } public Location getLastLocation() { - return instance.lastLocation; + return lastLocation; } void updateLocation(Location location) { diff --git a/src/org/thoughtcrime/securesms/geolocation/DcLocationManager.java b/src/org/thoughtcrime/securesms/geolocation/DcLocationManager.java index 86786ebbd..b3cba85c6 100644 --- a/src/org/thoughtcrime/securesms/geolocation/DcLocationManager.java +++ b/src/org/thoughtcrime/securesms/geolocation/DcLocationManager.java @@ -8,6 +8,7 @@ import android.location.Location; import android.os.IBinder; import android.util.Log; +import java.util.LinkedList; import java.util.Observable; import java.util.Observer; @@ -23,11 +24,14 @@ public class DcLocationManager implements Observer { private LocationBackgroundService.LocationBackgroundServiceBinder serviceBinder; private Context context; private DcLocation dcLocation = DcLocation.getInstance(); + private LinkedList pendingShareLastLocation = new LinkedList<>(); private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { serviceBinder = (LocationBackgroundService.LocationBackgroundServiceBinder) service; - startLocationEngine(); + while (pendingShareLastLocation.size() > 0) { + shareLastLocation(pendingShareLastLocation.pop()); + } } @Override @@ -60,8 +64,14 @@ public class DcLocationManager implements Observer { serviceBinder.stop(); } - public void shareLastLocation() { + public void shareLocation(int duration, int chatId) { + startLocationEngine(); + Log.d(TAG, String.format("Share location in chat %d for %d seconds", chatId, duration)); + } + + public void shareLastLocation(int chatId) { if (serviceBinder == null) { + pendingShareLastLocation.push(chatId); initializeLocationEngine(); return; } @@ -81,5 +91,6 @@ public class DcLocationManager implements Observer { private void writeDcLocationUpdateMessage() { //TODO: implement me! + Log.d(TAG, "Share location: " + dcLocation.getLastLocation().getLatitude() + ", " + dcLocation.getLastLocation().getLongitude()); } } diff --git a/src/org/thoughtcrime/securesms/geolocation/LocationBackgroundService.java b/src/org/thoughtcrime/securesms/geolocation/LocationBackgroundService.java index 56a812ed5..8ff367679 100644 --- a/src/org/thoughtcrime/securesms/geolocation/LocationBackgroundService.java +++ b/src/org/thoughtcrime/securesms/geolocation/LocationBackgroundService.java @@ -11,6 +11,7 @@ import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.util.Log; +import android.widget.Toast; /** * Created by cyberta on 06.03.19. @@ -22,7 +23,7 @@ public class LocationBackgroundService extends Service { private static final String TAG = LocationBackgroundService.class.getSimpleName(); private LocationManager locationManager = null; private static final int LOCATION_INTERVAL = 1000; - private static final float LOCATION_DISTANCE = 20f; + private static final float LOCATION_DISTANCE = 25F; ServiceLocationListener locationListener; private final IBinder mBinder = new LocationBackgroundServiceBinder(); @@ -80,7 +81,7 @@ public class LocationBackgroundService extends Service { locationManager.requestLocationUpdates( provider, LOCATION_INTERVAL, LOCATION_DISTANCE, locationListener); - } catch (SecurityException | IllegalArgumentException ex) { + } catch (SecurityException | IllegalArgumentException ex) { Log.e(TAG, String.format("Unable to request %s provider based location updates.", provider), ex); } } @@ -109,10 +110,11 @@ public class LocationBackgroundService extends Service { } private class ServiceLocationListener implements LocationListener { + private static final int EARTH_RADIUS = 6371; @Override public void onLocationChanged(Location location) { - Log.e(TAG, "onLocationChanged: " + location); + Log.d(TAG, "onLocationChanged: " + location); if (isBetterLocation(location, DcLocation.getInstance().getLastLocation())) { DcLocation.getInstance().updateLocation(location); } @@ -147,38 +149,50 @@ public class LocationBackgroundService extends Service { // Check whether the new location fix is newer or older long timeDelta = location.getTime() - currentBestLocation.getTime(); - boolean isSignificantlyNewer = timeDelta > TIMEOUT; boolean isSignificantlyOlder = timeDelta < -TIMEOUT; - boolean isNewer = timeDelta > 0; - // If it's been more than two minutes since the current location, use the new location - // because the user has likely moved - if (isSignificantlyNewer) { - return true; - // If the new location is more than two minutes older, it must be worse - } else if (isSignificantlyOlder) { + if (isSignificantlyOlder) { return false; } // Check whether the new location fix is more or less accurate int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy()); - boolean isLessAccurate = accuracyDelta > 0; - boolean isMoreAccurate = accuracyDelta < 0; - boolean isSignificantlyLessAccurate = accuracyDelta > 200; + boolean isSignificantlyMoreAccurate = accuracyDelta < -50; + boolean isSameProvider = isSameProvider(location.getProvider(), currentBestLocation.getProvider()); - // Check if the old and new location are from the same provider - boolean isFromSameProvider = isSameProvider(location.getProvider(), - currentBestLocation.getProvider()); - - // Determine location quality using a combination of timeliness and accuracy - if (isMoreAccurate) { - return true; - } else if (isNewer && !isLessAccurate) { - return true; - } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) { + if (isSignificantlyMoreAccurate && isSameProvider) { return true; } - return false; + + boolean isSignificantlyLessAccurate = accuracyDelta > 50; + boolean hasLocationChanged = hasLocationDistanceChanged(location, currentBestLocation); + return hasLocationChanged && !isSignificantlyLessAccurate; + } + + private boolean hasLocationDistanceChanged(Location location, Location currentBestLocation) { + return distance(location.getLatitude(), location.getLongitude(), currentBestLocation.getLatitude(), currentBestLocation.getLongitude()) > LOCATION_DISTANCE; + } + + private double distance(double startLat, double startLong, + double endLat, double endLong) { + + double dLat = Math.toRadians(endLat - startLat); + double dLong = Math.toRadians(endLong - startLong); + + startLat = Math.toRadians(startLat); + endLat = Math.toRadians(endLat); + + double a = haversin(dLat) + Math.cos(startLat) * Math.cos(endLat) * haversin(dLong); + double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + + double distance = EARTH_RADIUS * c * 1000; + Log.d(TAG, "Distance between location updates: " + distance); + Toast.makeText(LocationBackgroundService.this, "Distance in m between location updates: " + distance , Toast.LENGTH_LONG).show(); + return distance; + } + + private double haversin(double val) { + return Math.pow(Math.sin(val / 2), 2); } /** Checks whether two providers are the same */ diff --git a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java index f60c21931..9f424a1b2 100644 --- a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java +++ b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java @@ -37,18 +37,23 @@ import android.util.Pair; import android.view.View; import android.widget.Toast; +import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.MediaPreviewActivity; +import org.thoughtcrime.securesms.MuteDialog; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.ShareLocationDialog; import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.audio.AudioSlidePlayer; import org.thoughtcrime.securesms.components.AudioView; import org.thoughtcrime.securesms.components.DocumentView; import org.thoughtcrime.securesms.components.RemovableEditableMediaView; import org.thoughtcrime.securesms.components.ThumbnailView; +import org.thoughtcrime.securesms.geolocation.DcLocationManager; import org.thoughtcrime.securesms.permissions.Permissions; import org.thoughtcrime.securesms.providers.PersistentBlobProvider; import org.thoughtcrime.securesms.scribbles.ScribbleActivity; import org.thoughtcrime.securesms.util.MediaUtil; +import org.thoughtcrime.securesms.util.Prefs; import org.thoughtcrime.securesms.util.ThemeUtil; import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.concurrent.ListenableFuture; @@ -385,21 +390,27 @@ public class AttachmentManager { .execute(); } - public static void selectLocation(Activity activity, int requestCode) { - /* + public static void selectLocation(Activity activity, int chatId) { + Permissions.with(activity) .request(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION) .ifNecessary() .withPermanentDenialDialog(activity.getString(R.string.perm_explain_access_to_location_denied)) .onAllGranted(() -> { - try { - activity.startActivityForResult(new PlacePicker.IntentBuilder().build(activity), requestCode); - } catch (GooglePlayServicesRepairableException | GooglePlayServicesNotAvailableException e) { - Log.w(TAG, e); - } + + + ShareLocationDialog.show(activity, durationInSeconds -> { + DcLocationManager dcLocationManager = ApplicationContext.getInstance(activity).dcLocationManager; + switch (durationInSeconds) { + case 1: dcLocationManager.shareLastLocation(chatId); break; + default: + dcLocationManager.shareLocation(durationInSeconds, chatId); + break; + } + }); }) .execute(); - */ + } private @Nullable Uri getSlideUri() { diff --git a/src/org/thoughtcrime/securesms/service/DirectShareService.java b/src/org/thoughtcrime/securesms/service/DirectShareService.java index 11ac6b217..46bbe0f94 100644 --- a/src/org/thoughtcrime/securesms/service/DirectShareService.java +++ b/src/org/thoughtcrime/securesms/service/DirectShareService.java @@ -71,7 +71,7 @@ public class DirectShareService extends ChooserTargetService { // parcel.writeParcelable(recipient.getAddress(), 0); // // Bundle bundle = new Bundle(); -// bundle.putLong(ShareActivity.EXTRA_THREAD_ID, record.getThreadId()); +// bundle.putLong(ShareActivity.EXTRA_THREAD_ID, record.getChatId()); // bundle.putByteArray(ShareActivity.EXTRA_ADDRESS_MARSHALLED, parcel.marshall()); // bundle.putInt(ShareActivity.EXTRA_DISTRIBUTION_TYPE, record.getDistributionType()); // bundle.setClassLoader(getClassLoader());