From b43000bdd061ce33ea06ab1186a920fe950c83ad Mon Sep 17 00:00:00 2001 From: Ryan ZHAO Date: Tue, 11 May 2021 17:05:04 +1000 Subject: [PATCH] WIP: enable contact with UI --- .../conversation/ConversationActivity.java | 446 ++++++++---------- .../loki/database/SessionContactDatabase.kt | 19 +- .../loki/dialogs/UserDetailsBottomSheet.kt | 27 +- .../loki/utilities/DatabaseUtilities.kt | 5 + .../sskenvironment/ProfileManager.kt | 12 + .../main/res/layout/conversation_activity.xml | 4 +- .../fragment_user_details_bottom_sheet.xml | 74 ++- .../libsession/utilities/SSKEnvironment.kt | 1 + 8 files changed, 316 insertions(+), 272 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java index 4c3332fac2..0755bf6a36 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -17,8 +17,6 @@ package org.thoughtcrime.securesms.conversation; import android.Manifest; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.content.ActivityNotFoundException; @@ -58,9 +56,7 @@ import android.view.View.OnFocusChangeListener; import android.view.View.OnKeyListener; import android.view.WindowManager; import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputMethodManager; import android.widget.Button; -import android.widget.EditText; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.ProgressBar; @@ -220,14 +216,14 @@ import network.loki.messenger.R; */ @SuppressLint("StaticFieldLeak") public class ConversationActivity extends PassphraseRequiredActionBarActivity - implements ConversationFragment.ConversationFragmentListener, - AttachmentManager.AttachmentListener, - RecipientModifiedListener, - OnKeyboardShownListener, - InputPanel.Listener, - InputPanel.MediaListener, - ComposeText.CursorPositionChangedListener, - ConversationSearchBottomBar.EventListener + implements ConversationFragment.ConversationFragmentListener, + AttachmentManager.AttachmentListener, + RecipientModifiedListener, + OnKeyboardShownListener, + InputPanel.Listener, + InputPanel.MediaListener, + ComposeText.CursorPositionChangedListener, + ConversationSearchBottomBar.EventListener { private static final String TAG = ConversationActivity.class.getSimpleName(); @@ -242,11 +238,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity public static final String LAST_SEEN_EXTRA = "last_seen"; public static final String STARTING_POSITION_EXTRA = "starting_position"; -// private static final int PICK_GALLERY = 1; + // private static final int PICK_GALLERY = 1; private static final int PICK_DOCUMENT = 2; private static final int PICK_AUDIO = 3; private static final int PICK_CONTACT = 4; -// private static final int GET_CONTACT_DETAILS = 5; + // private static final int GET_CONTACT_DETAILS = 5; // private static final int GROUP_EDIT = 6; private static final int TAKE_PHOTO = 7; private static final int ADD_CONTACT = 8; @@ -261,7 +257,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity private ImageButton sendButton; private ImageButton attachButton; private ProfilePictureView profilePictureView; - private EditText titleTextView; + private TextView titleTextView; private ConversationFragment fragment; private Button unblockButton; private Button makeDefaultSmsButton; @@ -274,7 +270,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity private ImageView muteIndicatorImageView; private TextView subtitleTextView; private View homeButtonContainer; - private View cancelButtonContainer; private AttachmentTypeSelector attachmentTypeSelector; private AttachmentManager attachmentManager; @@ -378,52 +373,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } }); - if (isGroupConversation()) { - titleTextView.setEnabled(false); - } else { - titleTextView.setImeOptions(EditorInfo.IME_ACTION_DONE); - titleTextView.setOnEditorActionListener((v, actionId, event) -> { - if (actionId == EditorInfo.IME_ACTION_DONE) { - String nickname = v.getText().toString().trim(); - SSKEnvironment.shared.getProfileManager().setDisplayName(this, getRecipient(), nickname); - v.clearFocus(); - return true; - } - return false; - }); - titleTextView.setOnFocusChangeListener((v, hasFocus) -> { - InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); - if (!hasFocus) { - EditText textView = (EditText) v; - if (textView.getText().toString().isEmpty()) { - textView.setText(getRecipient().getName()); - } - imm.hideSoftInputFromWindow(v.getWindowToken(),0); - inputPanel.animate().alpha(1f).setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - inputPanel.setVisibility(View.VISIBLE); - } - }); - cancelButtonContainer.setVisibility(View.GONE); - } else { - inputPanel.animate().alpha(0f).setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - inputPanel.setVisibility(View.INVISIBLE); - } - }); - String nickname = DatabaseFactory.getStorage(this).getDisplayName(getRecipient().getAddress().serialize()); - titleTextView.setText(nickname); - if (nickname != null) { - titleTextView.setSelection(nickname.length()); - } - imm.showSoftInput(v, 0); - cancelButtonContainer.setVisibility(View.VISIBLE); - } - }); - } - MentionManagerUtilities.INSTANCE.populateUserPublicKeyCacheIfNeeded(threadId, this); OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId); @@ -471,7 +420,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity protected void onNewIntent(Intent intent) { super.onNewIntent(intent); Log.i(TAG, "onNewIntent()"); - + if (isFinishing()) { Log.w(TAG, "Activity is finishing..."); return; @@ -566,85 +515,85 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity super.onActivityResult(reqCode, resultCode, data); if ((data == null && reqCode != TAKE_PHOTO && reqCode != SMS_DEFAULT) || - (resultCode != RESULT_OK && reqCode != SMS_DEFAULT)) + (resultCode != RESULT_OK && reqCode != SMS_DEFAULT)) { updateLinkPreviewState(); return; } switch (reqCode) { - case PICK_DOCUMENT: - setMedia(data.getData(), MediaType.DOCUMENT); - break; - case PICK_AUDIO: - setMedia(data.getData(), MediaType.AUDIO); - break; - case TAKE_PHOTO: - if (attachmentManager.getCaptureUri() != null) { - setMedia(attachmentManager.getCaptureUri(), MediaType.IMAGE); - } - break; - case ADD_CONTACT: - recipient = Recipient.from(this, recipient.getAddress(), true); - recipient.addListener(this); - fragment.reloadList(); - break; + case PICK_DOCUMENT: + setMedia(data.getData(), MediaType.DOCUMENT); + break; + case PICK_AUDIO: + setMedia(data.getData(), MediaType.AUDIO); + break; + case TAKE_PHOTO: + if (attachmentManager.getCaptureUri() != null) { + setMedia(attachmentManager.getCaptureUri(), MediaType.IMAGE); + } + break; + case ADD_CONTACT: + recipient = Recipient.from(this, recipient.getAddress(), true); + recipient.addListener(this); + fragment.reloadList(); + break; /* case PICK_LOCATION: SignalPlace place = new SignalPlace(PlacePicker.getPlace(data, this)); attachmentManager.setLocation(place, getCurrentMediaConstraints()); break; */ - case PICK_GIF: - setMedia(data.getData(), - MediaType.GIF, - data.getIntExtra(GiphyActivity.EXTRA_WIDTH, 0), - data.getIntExtra(GiphyActivity.EXTRA_HEIGHT, 0)); - break; - case SMS_DEFAULT: - initializeSecurity(true, isDefaultSms); - break; - case MEDIA_SENDER: - long expiresIn = recipient.getExpireMessages() * 1000L; - int subscriptionId = -1; - boolean initiating = threadId == -1; - String message = data.getStringExtra(MediaSendActivity.EXTRA_MESSAGE); - SlideDeck slideDeck = new SlideDeck(); + case PICK_GIF: + setMedia(data.getData(), + MediaType.GIF, + data.getIntExtra(GiphyActivity.EXTRA_WIDTH, 0), + data.getIntExtra(GiphyActivity.EXTRA_HEIGHT, 0)); + break; + case SMS_DEFAULT: + initializeSecurity(true, isDefaultSms); + break; + case MEDIA_SENDER: + long expiresIn = recipient.getExpireMessages() * 1000L; + int subscriptionId = -1; + boolean initiating = threadId == -1; + String message = data.getStringExtra(MediaSendActivity.EXTRA_MESSAGE); + SlideDeck slideDeck = new SlideDeck(); - List mediaList = data.getParcelableArrayListExtra(MediaSendActivity.EXTRA_MEDIA); + List mediaList = data.getParcelableArrayListExtra(MediaSendActivity.EXTRA_MEDIA); - for (Media mediaItem : mediaList) { - if (MediaUtil.isVideoType(mediaItem.getMimeType())) { - slideDeck.addSlide(new VideoSlide(this, mediaItem.getUri(), 0, mediaItem.getCaption().orNull())); - } else if (MediaUtil.isGif(mediaItem.getMimeType())) { - slideDeck.addSlide(new GifSlide(this, mediaItem.getUri(), 0, mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.getCaption().orNull())); - } else if (MediaUtil.isImageType(mediaItem.getMimeType())) { - slideDeck.addSlide(new ImageSlide(this, mediaItem.getUri(), 0, mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.getCaption().orNull())); - } else { - Log.w(TAG, "Asked to send an unexpected mimeType: '" + mediaItem.getMimeType() + "'. Skipping."); + for (Media mediaItem : mediaList) { + if (MediaUtil.isVideoType(mediaItem.getMimeType())) { + slideDeck.addSlide(new VideoSlide(this, mediaItem.getUri(), 0, mediaItem.getCaption().orNull())); + } else if (MediaUtil.isGif(mediaItem.getMimeType())) { + slideDeck.addSlide(new GifSlide(this, mediaItem.getUri(), 0, mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.getCaption().orNull())); + } else if (MediaUtil.isImageType(mediaItem.getMimeType())) { + slideDeck.addSlide(new ImageSlide(this, mediaItem.getUri(), 0, mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.getCaption().orNull())); + } else { + Log.w(TAG, "Asked to send an unexpected mimeType: '" + mediaItem.getMimeType() + "'. Skipping."); + } } - } - final Context context = ConversationActivity.this.getApplicationContext(); + final Context context = ConversationActivity.this.getApplicationContext(); - sendMediaMessage(message, - slideDeck, - inputPanel.getQuote().orNull(), - Optional.absent(), - initiating).addListener(new AssertedSuccessListener() { - @Override - public void onSuccess(Void result) { - AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> { - Stream.of(slideDeck.getSlides()) - .map(Slide::getUri) - .withoutNulls() - .filter(BlobProvider::isAuthority) - .forEach(uri -> BlobProvider.getInstance().delete(context, uri)); - }); - } - }); + sendMediaMessage(message, + slideDeck, + inputPanel.getQuote().orNull(), + Optional.absent(), + initiating).addListener(new AssertedSuccessListener() { + @Override + public void onSuccess(Void result) { + AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> { + Stream.of(slideDeck.getSlides()) + .map(Slide::getUri) + .withoutNulls() + .filter(BlobProvider::isAuthority) + .forEach(uri -> BlobProvider.getInstance().delete(context, uri)); + }); + } + }); - break; + break; } } @@ -722,14 +671,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity if (isSingleConversation() && getRecipient().getContactUri() == null) { inflater.inflate(R.menu.conversation_add_to_contacts, menu); } - - if (recipient != null && recipient.isLocalNumber()) { if (isSecureText) menu.findItem(R.id.menu_call_secure).setVisible(false); else menu.findItem(R.id.menu_call_insecure).setVisible(false); - MenuItem muteItem = menu.findItem(R.id.menu_mute_notifications); - if (muteItem != null) { muteItem.setVisible(false); } @@ -797,25 +742,25 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity switch (item.getItemId()) { // case R.id.menu_call_secure: handleDial(getRecipient(), true); return true; // case R.id.menu_call_insecure: handleDial(getRecipient(), false); return true; - case R.id.menu_unblock: handleUnblock(); return true; - case R.id.menu_block: handleBlock(); return true; - case R.id.menu_copy_session_id: handleCopySessionID(); return true; - case R.id.menu_view_media: handleViewMedia(); return true; - case R.id.menu_add_shortcut: handleAddShortcut(); return true; - case R.id.menu_search: handleSearch(); return true; + case R.id.menu_unblock: handleUnblock(); return true; + case R.id.menu_block: handleBlock(); return true; + case R.id.menu_copy_session_id: handleCopySessionID(); return true; + case R.id.menu_view_media: handleViewMedia(); return true; + case R.id.menu_add_shortcut: handleAddShortcut(); return true; + case R.id.menu_search: handleSearch(); return true; // case R.id.menu_add_to_contacts: handleAddToContacts(); return true; // case R.id.menu_reset_secure_session: handleResetSecureSession(); return true; // case R.id.menu_group_recipients: handleDisplayGroupRecipients(); return true; - case R.id.menu_distribution_broadcast: handleDistributionBroadcastEnabled(item); return true; - case R.id.menu_distribution_conversation: handleDistributionConversationEnabled(item); return true; - case R.id.menu_edit_group: handleEditPushGroup(); return true; - case R.id.menu_leave: handleLeavePushGroup(); return true; - case R.id.menu_mute_notifications: handleMuteNotifications(); return true; - case R.id.menu_unmute_notifications: handleUnmuteNotifications(); return true; + case R.id.menu_distribution_broadcast: handleDistributionBroadcastEnabled(item); return true; + case R.id.menu_distribution_conversation: handleDistributionConversationEnabled(item); return true; + case R.id.menu_edit_group: handleEditPushGroup(); return true; + case R.id.menu_leave: handleLeavePushGroup(); return true; + case R.id.menu_mute_notifications: handleMuteNotifications(); return true; + case R.id.menu_unmute_notifications: handleUnmuteNotifications(); return true; // case R.id.menu_conversation_settings: handleConversationSettings(); return true; - case R.id.menu_expiring_messages_off: - case R.id.menu_expiring_messages: handleSelectMessageExpiration(); return true; - case android.R.id.home: handleReturnToConversationList(); return true; + case R.id.menu_expiring_messages_off: + case R.id.menu_expiring_messages: handleSelectMessageExpiration(); return true; + case android.R.id.home: handleReturnToConversationList(); return true; } return false; @@ -886,7 +831,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity @Override protected Void doInBackground(Void... params) { DatabaseFactory.getRecipientDatabase(ConversationActivity.this) - .setMuted(recipient, until); + .setMuted(recipient, until); return null; } @@ -901,7 +846,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity @Override protected Void doInBackground(Void... params) { DatabaseFactory.getRecipientDatabase(ConversationActivity.this) - .setMuted(recipient, 0); + .setMuted(recipient, 0); return null; } @@ -913,20 +858,20 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity int bodyRes = R.string.ConversationActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact; new AlertDialog.Builder(this) - .setTitle(titleRes) - .setMessage(bodyRes) - .setNegativeButton(android.R.string.cancel, null) - .setPositiveButton(R.string.ConversationActivity_unblock, (dialog, which) -> { - new AsyncTask() { - @Override - protected Void doInBackground(Void... params) { - DatabaseFactory.getRecipientDatabase(ConversationActivity.this) - .setBlocked(recipient, false); + .setTitle(titleRes) + .setMessage(bodyRes) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(R.string.ConversationActivity_unblock, (dialog, which) -> { + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + DatabaseFactory.getRecipientDatabase(ConversationActivity.this) + .setBlocked(recipient, false); - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - }).show(); + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + }).show(); } @TargetApi(Build.VERSION_CODES.KITKAT) @@ -996,7 +941,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity if (icon == null) { icon = IconCompat.createWithResource(context, recipient.isGroupRecipient() ? R.mipmap.ic_group_shortcut - : R.mipmap.ic_person_shortcut); + : R.mipmap.ic_person_shortcut); } return icon; @@ -1006,14 +951,14 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity protected void onPostExecute(IconCompat icon) { Context context = getApplicationContext(); String name = Optional.fromNullable(recipient.getName()) - .or(Optional.fromNullable(recipient.getProfileName())) - .or(recipient.toShortString()); + .or(Optional.fromNullable(recipient.getProfileName())) + .or(recipient.toShortString()); ShortcutInfoCompat shortcutInfo = new ShortcutInfoCompat.Builder(context, recipient.getAddress().serialize() + '-' + System.currentTimeMillis()) - .setShortLabel(name) - .setIcon(icon) - .setIntent(ShortcutLauncherActivity.createIntent(context, recipient.getAddress())) - .build(); + .setShortLabel(name) + .setIcon(icon) + .setIntent(ShortcutLauncherActivity.createIntent(context, recipient.getAddress())) + .build(); if (ShortcutManagerCompat.requestPinShortcut(context, shortcutInfo, null)) { Toast.makeText(context, getString(R.string.ConversationActivity_added_to_home_screen), Toast.LENGTH_LONG).show(); @@ -1029,7 +974,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity private void handleLeavePushGroup() { if (getRecipient() == null) { Toast.makeText(this, getString(R.string.ConversationActivity_invalid_recipient), - Toast.LENGTH_LONG).show(); + Toast.LENGTH_LONG).show(); return; } @@ -1092,7 +1037,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity @Override protected Void doInBackground(Void... params) { DatabaseFactory.getThreadDatabase(ConversationActivity.this) - .setDistributionType(threadId, DistributionTypes.BROADCAST); + .setDistributionType(threadId, DistributionTypes.BROADCAST); return null; } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); @@ -1108,7 +1053,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity @Override protected Void doInBackground(Void... params) { DatabaseFactory.getThreadDatabase(ConversationActivity.this) - .setDistributionType(threadId, DistributionTypes.CONVERSATION); + .setDistributionType(threadId, DistributionTypes.CONVERSATION); return null; } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); @@ -1142,7 +1087,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity /* Loki - We don't support SMS if (!isSecureText && !isPushGroupConversation()) sendButton.disableTransport(Type.TEXTSECURE); if (recipient.isPushGroupRecipient()) sendButton.disableTransport(Type.SMS); - if (!recipient.isPushGroupRecipient() && recipient.isForceSmsSelection()) { sendButton.setDefaultTransport(Type.SMS); } else { @@ -1314,7 +1258,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity muteIndicatorImageView = ViewUtil.findById(this, R.id.muteIndicatorImageView); subtitleTextView = ViewUtil.findById(this, R.id.subtitleTextView); homeButtonContainer = ViewUtil.findById(this, R.id.homeButtonContainer); - cancelButtonContainer = ViewUtil.findById(this, R.id.cancelButtonContainer); ImageButton quickCameraToggle = ViewUtil.findById(this, R.id.quick_camera_toggle); ImageButton inlineAttachmentButton = ViewUtil.findById(this, R.id.inline_attachment_button); @@ -1360,12 +1303,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity inlineAttachmentButton.setOnClickListener(v -> handleAddAttachment()); homeButtonContainer.setOnClickListener(v -> onSupportNavigateUp()); - - cancelButtonContainer.setOnClickListener(v -> { - titleTextView.setText(""); - SSKEnvironment.shared.getProfileManager().setDisplayName(this, getRecipient(), ""); - titleTextView.clearFocus(); - }); } protected void initializeActionBar() { @@ -1480,35 +1417,35 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity Log.i(TAG, "Selected: " + type); switch (type) { - case AttachmentTypeSelector.ADD_GALLERY: - AttachmentManager.selectGallery(this, MEDIA_SENDER, recipient, composeText.getTextTrimmed()); break; - case AttachmentTypeSelector.ADD_DOCUMENT: - AttachmentManager.selectDocument(this, PICK_DOCUMENT); break; - case AttachmentTypeSelector.ADD_SOUND: - AttachmentManager.selectAudio(this, PICK_AUDIO); break; - case AttachmentTypeSelector.ADD_CONTACT_INFO: - AttachmentManager.selectContactInfo(this, PICK_CONTACT); break; - case AttachmentTypeSelector.ADD_LOCATION: - AttachmentManager.selectLocation(this, PICK_LOCATION); break; - case AttachmentTypeSelector.TAKE_PHOTO: - attachmentManager.capturePhoto(this, TAKE_PHOTO); break; - case AttachmentTypeSelector.ADD_GIF: - boolean hasSeenGIFMetaDataWarning = TextSecurePreferences.hasSeenGIFMetaDataWarning(this); - if (!hasSeenGIFMetaDataWarning) { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle("Search GIFs?"); - builder.setMessage("You will not have full metadata protection when sending GIFs."); - builder.setPositiveButton("OK", (dialog, which) -> { + case AttachmentTypeSelector.ADD_GALLERY: + AttachmentManager.selectGallery(this, MEDIA_SENDER, recipient, composeText.getTextTrimmed()); break; + case AttachmentTypeSelector.ADD_DOCUMENT: + AttachmentManager.selectDocument(this, PICK_DOCUMENT); break; + case AttachmentTypeSelector.ADD_SOUND: + AttachmentManager.selectAudio(this, PICK_AUDIO); break; + case AttachmentTypeSelector.ADD_CONTACT_INFO: + AttachmentManager.selectContactInfo(this, PICK_CONTACT); break; + case AttachmentTypeSelector.ADD_LOCATION: + AttachmentManager.selectLocation(this, PICK_LOCATION); break; + case AttachmentTypeSelector.TAKE_PHOTO: + attachmentManager.capturePhoto(this, TAKE_PHOTO); break; + case AttachmentTypeSelector.ADD_GIF: + boolean hasSeenGIFMetaDataWarning = TextSecurePreferences.hasSeenGIFMetaDataWarning(this); + if (!hasSeenGIFMetaDataWarning) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle("Search GIFs?"); + builder.setMessage("You will not have full metadata protection when sending GIFs."); + builder.setPositiveButton("OK", (dialog, which) -> { + AttachmentManager.selectGif(this, PICK_GIF); + dialog.dismiss(); + }); + builder.setNegativeButton("Cancel", (dialog, which) -> dialog.dismiss()); + builder.create().show(); + TextSecurePreferences.setHasSeenGIFMetaDataWarning(this); + } else { AttachmentManager.selectGif(this, PICK_GIF); - dialog.dismiss(); - }); - builder.setNegativeButton("Cancel", (dialog, which) -> dialog.dismiss()); - builder.create().show(); - TextSecurePreferences.setHasSeenGIFMetaDataWarning(this); - } else { - AttachmentManager.selectGif(this, PICK_GIF); - } - break; + } + break; } } @@ -1603,8 +1540,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity draftDatabase.insertDrafts(threadId, drafts); threadDatabase.updateSnippet(threadId, drafts.getSnippet(ConversationActivity.this), - drafts.getUriSnippet(), - System.currentTimeMillis(), Types.BASE_DRAFT_TYPE, true); + drafts.getUriSnippet(), + System.currentTimeMillis(), Types.BASE_DRAFT_TYPE, true); } else if (threadId > 0) { threadDatabase.update(threadId, false); } @@ -1707,10 +1644,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity String timestamp = new SimpleDateFormat("yyyy-MM-dd-HHmmss", Locale.US).format(new Date()); String filename = String.format("signal-%s.txt", timestamp); Uri textUri = BlobProvider.getInstance() - .forData(textData) - .withMimeType(MediaTypes.LONG_TEXT) - .withFileName(filename) - .createForSingleSessionInMemory(); + .forData(textData) + .withMimeType(MediaTypes.LONG_TEXT) + .withFileName(filename) + .createForSingleSessionInMemory(); textSlide = Optional.of(new TextSlide(this, textUri, filename, textData.length)); } @@ -1788,10 +1725,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity boolean needsSplit = message.length() > characterCalculator.calculateCharacters(message).maxPrimaryMessageSize; boolean isMediaMessage = attachmentManager.isAttachmentPresent() || // recipient.isGroupRecipient() || - inputPanel.getQuote().isPresent() || - linkPreviewViewModel.hasLinkPreview() || - LinkPreviewUtil.isValidMediaUrl(message) || // Loki - Send GIFs as media messages - needsSplit; + inputPanel.getQuote().isPresent() || + linkPreviewViewModel.hasLinkPreview() || + LinkPreviewUtil.isValidMediaUrl(message) || // Loki - Send GIFs as media messages + needsSplit; if (isMediaMessage) { sendMediaMessage(initiating); @@ -1812,7 +1749,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } private void sendMediaMessage(boolean initiating) - throws InvalidMessageException + throws InvalidMessageException { Log.i(TAG, "Sending media message..."); sendMediaMessage(getMessage(), attachmentManager.buildSlideDeck(), inputPanel.getQuote().orNull(), linkPreviewViewModel.getActiveLinkPreview(), initiating); @@ -1872,7 +1809,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } private void sendTextMessage(final boolean initiating) - throws InvalidMessageException + throws InvalidMessageException { final Context context = getApplicationContext(); final String messageBody = getMessage(); @@ -1933,10 +1870,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity @Override public void onRecorderPermissionRequired() { Permissions.with(this) - .request(Manifest.permission.RECORD_AUDIO) - .withRationaleDialog(getString(R.string.ConversationActivity_to_send_audio_messages_allow_signal_access_to_your_microphone), R.drawable.ic_baseline_mic_48) - .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_requires_the_microphone_permission_in_order_to_send_audio_messages)) - .execute(); + .request(Manifest.permission.RECORD_AUDIO) + .withRationaleDialog(getString(R.string.ConversationActivity_to_send_audio_messages_allow_signal_access_to_your_microphone), R.drawable.ic_baseline_mic_48) + .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_requires_the_microphone_permission_in_order_to_send_audio_messages)) + .execute(); } @Override @@ -2095,16 +2032,16 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity @Override public void onClick(View v) { Permissions.with(ConversationActivity.this) - .request(Manifest.permission.CAMERA) - .withRationaleDialog(getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera), R.drawable.ic_baseline_photo_camera_48) - .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video)) - .onAllGranted(() -> { - composeText.clearFocus(); - startActivityForResult(MediaSendActivity.buildCameraIntent(ConversationActivity.this, recipient), MEDIA_SENDER); - overridePendingTransition(R.anim.camera_slide_from_bottom, R.anim.stationary); - }) - .onAnyDenied(() -> Toast.makeText(ConversationActivity.this, R.string.ConversationActivity_signal_needs_camera_permissions_to_take_photos_or_video, Toast.LENGTH_LONG).show()) - .execute(); + .request(Manifest.permission.CAMERA) + .withRationaleDialog(getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera), R.drawable.ic_baseline_photo_camera_48) + .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video)) + .onAllGranted(() -> { + composeText.clearFocus(); + startActivityForResult(MediaSendActivity.buildCameraIntent(ConversationActivity.this, recipient), MEDIA_SENDER); + overridePendingTransition(R.anim.camera_slide_from_bottom, R.anim.stationary); + }) + .onAnyDenied(() -> Toast.makeText(ConversationActivity.this, R.string.ConversationActivity_signal_needs_camera_permissions_to_take_photos_or_video, Toast.LENGTH_LONG).show()) + .execute(); } } @@ -2214,7 +2151,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } if (text.length() > 0) { if (currentMentionStartIndex > text.length()) { - resetMentions(); // Should never occur + resetMentions(); // Should never occur } int lastCharacterIndex = text.length() - 1; char lastCharacter = text.charAt(lastCharacterIndex); @@ -2281,12 +2218,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } inputPanel.setQuote(GlideApp.with(this), - messageRecord.getDateSent(), - author, - body, - slideDeck, - recipient, - threadId); + messageRecord.getDateSent(), + author, + body, + slideDeck, + recipient, + threadId); } else if (messageRecord.isMms() && !((MmsMessageRecord) messageRecord).getLinkPreviews().isEmpty()) { LinkPreview linkPreview = ((MmsMessageRecord) messageRecord).getLinkPreviews().get(0); @@ -2297,26 +2234,26 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } inputPanel.setQuote(GlideApp.with(this), - messageRecord.getDateSent(), - author, - messageRecord.getBody(), - slideDeck, - recipient, - threadId); + messageRecord.getDateSent(), + author, + messageRecord.getBody(), + slideDeck, + recipient, + threadId); } else { inputPanel.setQuote(GlideApp.with(this), - messageRecord.getDateSent(), - author, - messageRecord.getBody(), - messageRecord.isMms() ? ((MmsMessageRecord) messageRecord).getSlideDeck() : new SlideDeck(), - recipient, - threadId); + messageRecord.getDateSent(), + author, + messageRecord.getBody(), + messageRecord.isMms() ? ((MmsMessageRecord) messageRecord).getSlideDeck() : new SlideDeck(), + recipient, + threadId); } } @Override public void onMessageActionToolbarOpened() { - searchViewItem.collapseActionView(); + searchViewItem.collapseActionView(); } @Override @@ -2382,8 +2319,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } else if (recipient.getAddress().toString().toLowerCase().equals(userPublicKey)) { titleTextView.setText(getResources().getString(R.string.note_to_self)); } else { - boolean hasName = (recipient.getName() != null && !recipient.getName().isEmpty()); - titleTextView.setText(hasName ? recipient.getName() : recipient.getAddress().toString()); + String displayName = SSKEnvironment.shared.getProfileManager().getDisplayName(getApplicationContext(), recipient); + boolean hasName = displayName != null; + titleTextView.setText(hasName ? displayName : recipient.getAddress().toString()); } } @@ -2434,7 +2372,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity private void updateMessageStatusProgressBar() { if (messageStatus != null) { - messageStatusProgressBar.setAlpha(1.0f); + messageStatusProgressBar.setAlpha(1.0f); switch (messageStatus) { case "calculatingPoW": setMessageStatusProgressAnimatedIfPossible(25); break; case "contactingNetwork": setMessageStatusProgressAnimatedIfPossible(50); break; @@ -2471,7 +2409,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity default: return -1; } } else { - return -1; + return -1; } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/database/SessionContactDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/database/SessionContactDatabase.kt index 5a2a4072de..4aec4b4a48 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/database/SessionContactDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/database/SessionContactDatabase.kt @@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.loki.database import android.content.ContentValues import android.content.Context +import androidx.core.database.getStringOrNull import net.sqlcipher.Cursor import org.session.libsession.messaging.contacts.Contact import org.session.libsession.messaging.jobs.Job @@ -35,7 +36,7 @@ class SessionContactDatabase(context: Context, helper: SQLCipherOpenHelper) : Da fun getContactWithSessionID(sessionID: String): Contact? { val database = databaseHelper.readableDatabase - return database.get(sessionContactTable, "$sessionID = ?", arrayOf(sessionID)) { cursor -> + return database.get(sessionContactTable, "${SessionContactDatabase.sessionID} = ?", arrayOf(sessionID)) { cursor -> contactFromCursor(cursor) } } @@ -55,7 +56,9 @@ class SessionContactDatabase(context: Context, helper: SQLCipherOpenHelper) : Da contentValues.put(nickname, contact.nickname) contentValues.put(profilePictureURL, contact.profilePictureURL) contentValues.put(profilePictureFileName, contact.profilePictureFileName) - contentValues.put(profilePictureEncryptionKey, Base64.encodeBytes(contact.profilePictureEncryptionKey)) + contact.profilePictureEncryptionKey?.let { + contentValues.put(profilePictureEncryptionKey, Base64.encodeBytes(it)) + } contentValues.put(threadID, threadID) contentValues.put(isTrusted, if (contact.isTrusted) 1 else 0) database.insertOrUpdate(sessionContactTable, contentValues, "$sessionID = ?", arrayOf(contact.sessionID)) @@ -64,11 +67,13 @@ class SessionContactDatabase(context: Context, helper: SQLCipherOpenHelper) : Da private fun contactFromCursor(cursor: Cursor): Contact { val sessionID = cursor.getString(sessionID) val contact = Contact(sessionID) - contact.name = cursor.getString(name) - contact.nickname = cursor.getString(nickname) - contact.profilePictureURL = cursor.getString(profilePictureURL) - contact.profilePictureFileName = cursor.getString(profilePictureFileName) - contact.profilePictureEncryptionKey = Base64.decode(cursor.getString(profilePictureEncryptionKey)) + contact.name = cursor.getStringOrNull(name) + contact.nickname = cursor.getStringOrNull(nickname) + contact.profilePictureURL = cursor.getStringOrNull(profilePictureURL) + contact.profilePictureFileName = cursor.getStringOrNull(profilePictureFileName) + cursor.getStringOrNull(profilePictureEncryptionKey)?.let { + contact.profilePictureEncryptionKey = Base64.decode(it) + } contact.threadID = cursor.getInt(threadID) contact.isTrusted = cursor.getInt(isTrusted) != 0 return contact diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/UserDetailsBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/UserDetailsBottomSheet.kt index 3d884f7440..7eb1e20996 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/UserDetailsBottomSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/UserDetailsBottomSheet.kt @@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.loki.dialogs import android.content.ClipData import android.content.ClipboardManager import android.content.Context +import android.opengl.Visibility import android.os.Bundle import com.google.android.material.bottomsheet.BottomSheetDialogFragment import android.view.LayoutInflater @@ -12,6 +13,9 @@ import android.widget.Toast import kotlinx.android.synthetic.main.fragment_user_details_bottom_sheet.* import kotlinx.android.synthetic.main.view_conversation.view.* import network.loki.messenger.R +import org.session.libsession.messaging.threads.Address +import org.session.libsession.messaging.threads.recipients.Recipient +import org.session.libsession.utilities.SSKEnvironment import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.mms.GlideApp @@ -24,11 +28,32 @@ public class UserDetailsBottomSheet : BottomSheetDialogFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val publicKey = arguments?.getString("publicKey") ?: return dismiss() + val recipient = Recipient.from(requireContext(), Address.fromSerialized(publicKey), false) profilePictureView.publicKey = publicKey profilePictureView.glide = GlideApp.with(this) profilePictureView.isLarge = true profilePictureView.update() - nameTextView.text = DatabaseFactory.getLokiUserDatabase(requireContext()).getDisplayName(publicKey) ?: "Anonymous" + nameTextViewContainer.visibility = View.VISIBLE + nameTextViewContainer.setOnClickListener { + nameTextViewContainer.visibility = View.INVISIBLE + nameEditContainer.visibility = View.VISIBLE + nameEditText.requestFocus() + } + btnCancelNickNameEdit.setOnClickListener { + nameEditText.clearFocus() + nameTextViewContainer.visibility = View.VISIBLE + nameEditContainer.visibility = View.INVISIBLE + nameEditText.text = null + } + btnSaveNickNameEdit.setOnClickListener { + nameEditText.clearFocus() + nameTextViewContainer.visibility = View.VISIBLE + nameEditContainer.visibility = View.INVISIBLE + var newNickName = null + SSKEnvironment.shared.profileManager.setDisplayName(requireContext(), recipient, newNickName) + nameTextView.text = SSKEnvironment.shared.profileManager.getDisplayName(requireContext(), recipient) ?: "Anonymous" + } + nameTextView.text = SSKEnvironment.shared.profileManager.getDisplayName(requireContext(), recipient) ?: "Anonymous" publicKeyTextView.text = publicKey copyButton.setOnClickListener { val clipboard = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/DatabaseUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/DatabaseUtilities.kt index 140586930f..23834fb9af 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/DatabaseUtilities.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/DatabaseUtilities.kt @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms.loki.utilities import android.content.ContentValues +import androidx.core.database.getStringOrNull import net.sqlcipher.Cursor import net.sqlcipher.database.SQLiteDatabase import org.session.libsignal.utilities.Base64 @@ -56,4 +57,8 @@ fun Cursor.getLong(columnName: String): Long { fun Cursor.getBase64EncodedData(columnName: String): ByteArray { return Base64.decode(getString(columnName)) +} + +fun Cursor.getStringOrNull(columnName: String): String? { + return getStringOrNull(getColumnIndexOrThrow(columnName)) } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/ProfileManager.kt b/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/ProfileManager.kt index 0decab0b8f..9ca7fe3c9e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/ProfileManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/ProfileManager.kt @@ -73,4 +73,16 @@ class ProfileManager: SSKEnvironment.ProfileManagerProtocol { override fun updateOpenGroupProfilePicturesIfNeeded(context: Context) { ApplicationContext.getInstance(context).updateOpenGroupProfilePicturesIfNeeded() } + + override fun getDisplayName(context: Context, recipient: Recipient): String? { + val sessionID = recipient.address.serialize() + val contactDatabase = DatabaseFactory.getSessionContactDatabase(context) + var contact = contactDatabase.getContactWithSessionID(sessionID) + if (contact == null) { + contact = Contact(sessionID) + contact.name = DatabaseFactory.getLokiUserDatabase(context).getDisplayName(sessionID) ?: recipient.profileName ?: recipient.name + contactDatabase.setContact(contact) + } + return contact.displayName(Contact.contextForRecipient(recipient)) + } } \ No newline at end of file diff --git a/app/src/main/res/layout/conversation_activity.xml b/app/src/main/res/layout/conversation_activity.xml index d7389e7c34..7504e23d49 100644 --- a/app/src/main/res/layout/conversation_activity.xml +++ b/app/src/main/res/layout/conversation_activity.xml @@ -51,7 +51,7 @@ android:layout_marginStart="@dimen/medium_spacing" android:orientation="vertical"> - + android:layout_marginTop="@dimen/large_spacing"/> - + android:gravity="center"> + + + + + + + + + + + + + + + + + +