From 9b6dcf955f96ab966dfe45f91c12f25f019c479e Mon Sep 17 00:00:00 2001 From: "B. Petersen" Date: Wed, 17 Oct 2018 14:06:36 +0200 Subject: [PATCH 1/8] adapt name and return value of dc_msg_get_filebytes() wrapper function --- jni/dc_wrapper.c | 4 ++-- src/com/b44t/messenger/DcMsg.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/jni/dc_wrapper.c b/jni/dc_wrapper.c index 389ff4c3e..b5ce78b15 100644 --- a/jni/dc_wrapper.c +++ b/jni/dc_wrapper.c @@ -982,9 +982,9 @@ JNIEXPORT void Java_com_b44t_messenger_DcMsg_lateFilingMediaSize(JNIEnv *env, jo } -JNIEXPORT jint Java_com_b44t_messenger_DcMsg_getBytes(JNIEnv *env, jobject obj) +JNIEXPORT jlong Java_com_b44t_messenger_DcMsg_getFilebytes(JNIEnv *env, jobject obj) { - return (jint)dc_msg_get_filebytes(get_dc_msg(env, obj)); + return (jlong)dc_msg_get_filebytes(get_dc_msg(env, obj)); } diff --git a/src/com/b44t/messenger/DcMsg.java b/src/com/b44t/messenger/DcMsg.java index cfa2b6e63..012c6584f 100644 --- a/src/com/b44t/messenger/DcMsg.java +++ b/src/com/b44t/messenger/DcMsg.java @@ -93,7 +93,6 @@ public class DcMsg { public native int getHeight (int def); public native int getDuration (); public native void lateFilingMediaSize(int width, int height, int duration); - public native int getBytes (); public @NonNull DcLot getSummary (DcChat chat) { return new DcLot(getSummaryCPtr(chat.getChatCPtr())); } public native String getSummarytext (int approx_characters); public native int showPadlock (); @@ -101,6 +100,7 @@ public class DcMsg { public native String getFile (); public native String getFilemime (); public native String getFilename (); + public native long getFilebytes (); public native boolean isForwarded (); public native boolean isInfo (); public native boolean isSetupMessage (); From 264765917f417e796681c68fc3d2ad4725187550 Mon Sep 17 00:00:00 2001 From: "B. Petersen" Date: Wed, 17 Oct 2018 14:08:55 +0200 Subject: [PATCH 2/8] adapt to new api --- src/org/thoughtcrime/securesms/mms/AudioSlide.java | 2 +- src/org/thoughtcrime/securesms/mms/DocumentSlide.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/thoughtcrime/securesms/mms/AudioSlide.java b/src/org/thoughtcrime/securesms/mms/AudioSlide.java index 822630019..e13205d3b 100644 --- a/src/org/thoughtcrime/securesms/mms/AudioSlide.java +++ b/src/org/thoughtcrime/securesms/mms/AudioSlide.java @@ -38,7 +38,7 @@ import java.io.File; public class AudioSlide extends Slide { public AudioSlide(Context context, DcMsg dcMsg) { - this(context, Uri.fromFile(new File(dcMsg.getFile())), dcMsg.getBytes(), dcMsg.getType()==DcMsg.DC_MSG_VOICE); + this(context, Uri.fromFile(new File(dcMsg.getFile())), dcMsg.getFilebytes(), dcMsg.getType()==DcMsg.DC_MSG_VOICE); dcMsgId = dcMsg.getId(); } diff --git a/src/org/thoughtcrime/securesms/mms/DocumentSlide.java b/src/org/thoughtcrime/securesms/mms/DocumentSlide.java index 525b45c5e..356d6ff22 100644 --- a/src/org/thoughtcrime/securesms/mms/DocumentSlide.java +++ b/src/org/thoughtcrime/securesms/mms/DocumentSlide.java @@ -20,7 +20,7 @@ public class DocumentSlide extends Slide { } public DocumentSlide(Context context, DcMsg dcMsg) { - this(context, Uri.fromFile(new File(dcMsg.getFile())), dcMsg.getFilemime(), dcMsg.getBytes(), dcMsg.getFilename()); + this(context, Uri.fromFile(new File(dcMsg.getFile())), dcMsg.getFilemime(), dcMsg.getFilebytes(), dcMsg.getFilename()); dcMsgId = dcMsg.getId(); } From e72f2b4cb6e786c9711d850f41387d35783526fc Mon Sep 17 00:00:00 2001 From: Angelo Fuchs Date: Wed, 17 Oct 2018 12:54:52 +0200 Subject: [PATCH 3/8] for #78 setting the attachment state to done when its displayed. --- .../securesms/attachments/Attachment.java | 6 ++++- .../components/ConversationItemThumbnail.java | 23 +++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/org/thoughtcrime/securesms/attachments/Attachment.java b/src/org/thoughtcrime/securesms/attachments/Attachment.java index 7cc795d85..a070ca659 100644 --- a/src/org/thoughtcrime/securesms/attachments/Attachment.java +++ b/src/org/thoughtcrime/securesms/attachments/Attachment.java @@ -10,7 +10,7 @@ public abstract class Attachment { @NonNull private final String contentType; - private final int transferState; + private int transferState; private final long size; @Nullable @@ -63,6 +63,10 @@ public abstract class Attachment { @Nullable public abstract Uri getThumbnailUri(); + public void setTransferState(int transferState) { + this.transferState = transferState; + } + public int getTransferState() { return transferState; } diff --git a/src/org/thoughtcrime/securesms/components/ConversationItemThumbnail.java b/src/org/thoughtcrime/securesms/components/ConversationItemThumbnail.java index 1bbfb106d..9cedc26bd 100644 --- a/src/org/thoughtcrime/securesms/components/ConversationItemThumbnail.java +++ b/src/org/thoughtcrime/securesms/components/ConversationItemThumbnail.java @@ -16,10 +16,14 @@ import android.widget.FrameLayout; import android.widget.ImageView; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.mms.SlideClickListener; import org.thoughtcrime.securesms.util.ThemeUtil; +import org.thoughtcrime.securesms.util.concurrent.ListenableFuture; + +import java.util.concurrent.ExecutionException; public class ConversationItemThumbnail extends FrameLayout { @@ -145,11 +149,25 @@ public class ConversationItemThumbnail extends FrameLayout { return footer; } + private void refreshSlideAttachmentState(ListenableFuture signal, Slide slide) { + signal.addListener(new ListenableFuture.Listener() { + @Override + public void onSuccess(Boolean result) { + slide.asAttachment().setTransferState(AttachmentDatabase.TRANSFER_PROGRESS_DONE); + } + + @Override + public void onFailure(ExecutionException e) { + slide.asAttachment().setTransferState(AttachmentDatabase.TRANSFER_PROGRESS_FAILED); + } + }); + } + @UiThread public void setImageResource(@NonNull GlideRequests glideRequests, @NonNull Slide slide, boolean showControls, boolean isPreview) { - thumbnail.setImageResource(glideRequests, slide, showControls, isPreview); + refreshSlideAttachmentState(thumbnail.setImageResource(glideRequests, slide, showControls, isPreview), slide); } @UiThread @@ -157,7 +175,8 @@ public class ConversationItemThumbnail extends FrameLayout { boolean showControls, boolean isPreview, int naturalWidth, int naturalHeight) { - thumbnail.setImageResource(glideRequests, slide, showControls, isPreview, naturalWidth, naturalHeight); + refreshSlideAttachmentState(thumbnail.setImageResource(glideRequests, slide, showControls, isPreview, naturalWidth, naturalHeight), slide); + } public void setImageResource(@NonNull GlideRequests glideRequests, @NonNull Uri uri) { From 7d4add1b5e32d2130184d3c5abfe7839308caa99 Mon Sep 17 00:00:00 2001 From: Angelo Fuchs Date: Wed, 17 Oct 2018 15:17:39 +0200 Subject: [PATCH 4/8] introduced DcAttachment and introduced its use where applicable. --- .../securesms/attachments/DcAttachment.java | 36 +++++++++++++++++++ .../securesms/mms/AudioSlide.java | 3 +- .../securesms/mms/DocumentSlide.java | 3 +- .../thoughtcrime/securesms/mms/GifSlide.java | 6 ++++ .../securesms/mms/ImageSlide.java | 7 ++++ .../thoughtcrime/securesms/mms/MmsSlide.java | 6 ++++ .../securesms/mms/VideoSlide.java | 7 ++++ .../securesms/util/MediaUtil.java | 20 +++++++++++ 8 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 src/org/thoughtcrime/securesms/attachments/DcAttachment.java diff --git a/src/org/thoughtcrime/securesms/attachments/DcAttachment.java b/src/org/thoughtcrime/securesms/attachments/DcAttachment.java new file mode 100644 index 000000000..8a42aad0e --- /dev/null +++ b/src/org/thoughtcrime/securesms/attachments/DcAttachment.java @@ -0,0 +1,36 @@ +package org.thoughtcrime.securesms.attachments; + +import java.io.File; +import android.net.Uri; +import android.support.annotation.Nullable; + +import com.b44t.messenger.DcMsg; + +import org.thoughtcrime.securesms.database.AttachmentDatabase; + +public class DcAttachment extends Attachment { + + private final DcMsg dcMsg; + + public DcAttachment(DcMsg dcMsg) { + // todo: width, height, quote + super(dcMsg.getFilemime(), AttachmentDatabase.TRANSFER_PROGRESS_DONE, dcMsg.getFilebytes(), + dcMsg.getFilename(), + Uri.fromFile(new File(dcMsg.getFile())).toString(), String.valueOf(dcMsg.getId()), null, null, + null, dcMsg.getType() == DcMsg.DC_MSG_VOICE, + 0, 0, false); + this.dcMsg = dcMsg; + } + + @Nullable + @Override + public Uri getDataUri() { + return Uri.fromFile(new File(dcMsg.getFile())); + } + + @Nullable + @Override + public Uri getThumbnailUri() { + return Uri.fromFile(new File(dcMsg.getFile())); + } +} diff --git a/src/org/thoughtcrime/securesms/mms/AudioSlide.java b/src/org/thoughtcrime/securesms/mms/AudioSlide.java index e13205d3b..2c0d40ffc 100644 --- a/src/org/thoughtcrime/securesms/mms/AudioSlide.java +++ b/src/org/thoughtcrime/securesms/mms/AudioSlide.java @@ -27,6 +27,7 @@ import com.b44t.messenger.DcMsg; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.attachments.Attachment; +import org.thoughtcrime.securesms.attachments.DcAttachment; import org.thoughtcrime.securesms.attachments.UriAttachment; import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.util.MediaUtil; @@ -38,7 +39,7 @@ import java.io.File; public class AudioSlide extends Slide { public AudioSlide(Context context, DcMsg dcMsg) { - this(context, Uri.fromFile(new File(dcMsg.getFile())), dcMsg.getFilebytes(), dcMsg.getType()==DcMsg.DC_MSG_VOICE); + super(context, new DcAttachment(dcMsg)); dcMsgId = dcMsg.getId(); } diff --git a/src/org/thoughtcrime/securesms/mms/DocumentSlide.java b/src/org/thoughtcrime/securesms/mms/DocumentSlide.java index 356d6ff22..3131f5e81 100644 --- a/src/org/thoughtcrime/securesms/mms/DocumentSlide.java +++ b/src/org/thoughtcrime/securesms/mms/DocumentSlide.java @@ -9,6 +9,7 @@ import android.support.annotation.Nullable; import com.b44t.messenger.DcMsg; import org.thoughtcrime.securesms.attachments.Attachment; +import org.thoughtcrime.securesms.attachments.DcAttachment; import org.thoughtcrime.securesms.util.StorageUtil; import java.io.File; @@ -20,7 +21,7 @@ public class DocumentSlide extends Slide { } public DocumentSlide(Context context, DcMsg dcMsg) { - this(context, Uri.fromFile(new File(dcMsg.getFile())), dcMsg.getFilemime(), dcMsg.getFilebytes(), dcMsg.getFilename()); + this(context, new DcAttachment(dcMsg)); dcMsgId = dcMsg.getId(); } diff --git a/src/org/thoughtcrime/securesms/mms/GifSlide.java b/src/org/thoughtcrime/securesms/mms/GifSlide.java index bb8161a5d..d3c7029aa 100644 --- a/src/org/thoughtcrime/securesms/mms/GifSlide.java +++ b/src/org/thoughtcrime/securesms/mms/GifSlide.java @@ -4,11 +4,17 @@ import android.content.Context; import android.net.Uri; import android.support.annotation.Nullable; +import com.b44t.messenger.DcMsg; + import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.util.MediaUtil; public class GifSlide extends ImageSlide { + public GifSlide(Context context, DcMsg dcMsg) { + super(context, dcMsg); + } + public GifSlide(Context context, Attachment attachment) { super(context, attachment); } diff --git a/src/org/thoughtcrime/securesms/mms/ImageSlide.java b/src/org/thoughtcrime/securesms/mms/ImageSlide.java index ffe1fdbd7..149253b26 100644 --- a/src/org/thoughtcrime/securesms/mms/ImageSlide.java +++ b/src/org/thoughtcrime/securesms/mms/ImageSlide.java @@ -23,8 +23,11 @@ import android.support.annotation.DrawableRes; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import com.b44t.messenger.DcMsg; + import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.attachments.Attachment; +import org.thoughtcrime.securesms.attachments.DcAttachment; import org.thoughtcrime.securesms.util.MediaUtil; public class ImageSlide extends Slide { @@ -32,6 +35,10 @@ public class ImageSlide extends Slide { @SuppressWarnings("unused") private static final String TAG = ImageSlide.class.getSimpleName(); + public ImageSlide(@NonNull Context context, @NonNull DcMsg dcMsg) { + super(context, new DcAttachment(dcMsg)); + } + public ImageSlide(@NonNull Context context, @NonNull Attachment attachment) { super(context, attachment); } diff --git a/src/org/thoughtcrime/securesms/mms/MmsSlide.java b/src/org/thoughtcrime/securesms/mms/MmsSlide.java index bed6a6741..c9f8885c1 100644 --- a/src/org/thoughtcrime/securesms/mms/MmsSlide.java +++ b/src/org/thoughtcrime/securesms/mms/MmsSlide.java @@ -4,10 +4,16 @@ package org.thoughtcrime.securesms.mms; import android.content.Context; import android.support.annotation.NonNull; +import com.b44t.messenger.DcMsg; + import org.thoughtcrime.securesms.attachments.Attachment; public class MmsSlide extends ImageSlide { + public MmsSlide(@NonNull Context context, @NonNull DcMsg dcMsg) { + super(context, dcMsg); + } + public MmsSlide(@NonNull Context context, @NonNull Attachment attachment) { super(context, attachment); } diff --git a/src/org/thoughtcrime/securesms/mms/VideoSlide.java b/src/org/thoughtcrime/securesms/mms/VideoSlide.java index 84750dd01..ce5ffd3a0 100644 --- a/src/org/thoughtcrime/securesms/mms/VideoSlide.java +++ b/src/org/thoughtcrime/securesms/mms/VideoSlide.java @@ -22,8 +22,11 @@ import android.net.Uri; import android.support.annotation.DrawableRes; import android.support.annotation.NonNull; +import com.b44t.messenger.DcMsg; + import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.attachments.Attachment; +import org.thoughtcrime.securesms.attachments.DcAttachment; import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.ResUtil; @@ -33,6 +36,10 @@ public class VideoSlide extends Slide { super(context, constructAttachmentFromUri(context, uri, MediaUtil.VIDEO_UNSPECIFIED, dataSize, 0, 0, MediaUtil.hasVideoThumbnail(uri), null, false, false)); } + public VideoSlide(Context context, DcMsg dcMsg) { + super(context, new DcAttachment(dcMsg)); + } + public VideoSlide(Context context, Attachment attachment) { super(context, attachment); } diff --git a/src/org/thoughtcrime/securesms/util/MediaUtil.java b/src/org/thoughtcrime/securesms/util/MediaUtil.java index 0b1cf1361..43d39823e 100644 --- a/src/org/thoughtcrime/securesms/util/MediaUtil.java +++ b/src/org/thoughtcrime/securesms/util/MediaUtil.java @@ -13,6 +13,7 @@ import android.util.Log; import android.util.Pair; import android.webkit.MimeTypeMap; +import com.b44t.messenger.DcMsg; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.load.resource.gif.GifDrawable; @@ -47,6 +48,25 @@ public class MediaUtil { public static final String VCARD = "text/x-vcard"; + public static Slide getSlideForMsg(Context context, DcMsg dcMsg) { + Slide slide = null; + if (isGif(dcMsg.getFilemime())) { + slide = new GifSlide(context, dcMsg); + } else if (isImageType(dcMsg.getFilemime())) { + slide = new ImageSlide(context, dcMsg); + } else if (isVideoType(dcMsg.getFilemime())) { + slide = new VideoSlide(context, dcMsg); + } else if (isAudioType(dcMsg.getFilemime())) { + slide = new AudioSlide(context, dcMsg); + } else if (isMms(dcMsg.getFilemime())) { + slide = new MmsSlide(context, dcMsg); + } else if (dcMsg.getFilemime() != null) { + slide = new DocumentSlide(context, dcMsg); + } + + return slide; + } + public static Slide getSlideForAttachment(Context context, Attachment attachment) { Slide slide = null; if (isGif(attachment.getContentType())) { From 77d7f545d43cf517e3a63d51f189e278277edb82 Mon Sep 17 00:00:00 2001 From: "daniel.boehrs" Date: Mon, 15 Oct 2018 15:28:46 +0200 Subject: [PATCH 5/8] add qr show+scan activities #79 --- AndroidManifest.xml | 8 + build.gradle | 4 +- res/drawable-xhdpi/ic_ab_qr.png | Bin 0 -> 314 bytes res/drawable-xhdpi/qr_overlay.png | Bin 0 -> 4974 bytes res/layout/activity_qr_scan.xml | 13 ++ res/layout/activity_qr_show.xml | 26 +++ res/menu/text_secure_normal.xml | 11 +- res/values/strings.xml | 28 +++ .../securesms/ConversationListActivity.java | 28 ++- .../securesms/QrScanActivity.java | 118 +++++++++++ .../securesms/QrShowActivity.java | 188 +++++++++++++++++ .../securesms/connect/DcHelper.java | 14 +- .../securesms/qr/QrScanHandler.java | 193 ++++++++++++++++++ .../securesms/util/IntentUtils.java | 6 + 14 files changed, 628 insertions(+), 9 deletions(-) create mode 100644 res/drawable-xhdpi/ic_ab_qr.png create mode 100644 res/drawable-xhdpi/qr_overlay.png create mode 100644 res/layout/activity_qr_scan.xml create mode 100644 res/layout/activity_qr_show.xml create mode 100644 src/org/thoughtcrime/securesms/QrScanActivity.java create mode 100644 src/org/thoughtcrime/securesms/QrShowActivity.java create mode 100644 src/org/thoughtcrime/securesms/qr/QrScanHandler.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 2ba93bc8b..4408a91ec 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -300,6 +300,14 @@ android:windowSoftInputMode="stateVisible" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/> + + + + $B>A_Z^I7q9aZ4ieEQ%2={FPK zZ_vp2*lT&VOgM{U>jlT4H`|xGb1AH25L=k>jH$Y8ojqsFib4@i(Z>O-uIb$h>xx!P z)N1;cQgy#5MOq-EjqBH4^t6DUu0zQame7^`f44}TMVAAelF{r G5}E*v=XV1D literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/qr_overlay.png b/res/drawable-xhdpi/qr_overlay.png new file mode 100644 index 0000000000000000000000000000000000000000..5ba3a7b9b237bc68aa4cb74de4d7f17bbcc99c8f GIT binary patch literal 4974 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 + + + + + \ No newline at end of file diff --git a/res/layout/activity_qr_show.xml b/res/layout/activity_qr_show.xml new file mode 100644 index 000000000..0eefb4714 --- /dev/null +++ b/res/layout/activity_qr_show.xml @@ -0,0 +1,26 @@ + + + + + + + + \ No newline at end of file diff --git a/res/menu/text_secure_normal.xml b/res/menu/text_secure_normal.xml index c7d2a6adf..4ff04be7b 100644 --- a/res/menu/text_secure_normal.xml +++ b/res/menu/text_secure_normal.xml @@ -1,5 +1,14 @@ - + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 51a071ea8..183b7cf39 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1312,6 +1312,31 @@ Refresh + + Scan QR code + Place your camera over the QR code + + + Do you want to join the verified group ]]>%1$s]]>? + Verify contact and start chatting with ]]>%1$s]]>? + Contact ]]>%1$s]]> verified. Start chatting? + The scanned fingerprint does ]]>not match]]> the fingerprint last seen for ]]>%1$s]]>. + This QR code contains a fingerprint but no email address.\n\nFor an out-of-band-verification, please establish an encrpyted connection to the recipient first. + Scanned QR code text:\n\n%1$s + Scanned QR code URL:\n\n%1$s + Fingerprint + Chat with ]]>%1$s]]>? + + + Show QR verify code + %1$s joins. + %1$s verified. + %1$s securely joined the group. + Scan this to join the verified group ]]>%1$s]]>. + QR invite code + QR verify code + ]]>%1$s
]]>Scan this to set up a verified contact.
+ Deleting Deleting old messages... @@ -1487,5 +1512,8 @@ Status text Login Chat-Background + Copy to clipboard + Open + Done diff --git a/src/org/thoughtcrime/securesms/ConversationListActivity.java b/src/org/thoughtcrime/securesms/ConversationListActivity.java index ab4d976aa..efbbcf654 100644 --- a/src/org/thoughtcrime/securesms/ConversationListActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationListActivity.java @@ -17,12 +17,9 @@ package org.thoughtcrime.securesms; import android.Manifest; -import android.annotation.SuppressLint; import android.content.ActivityNotFoundException; -import android.content.Context; import android.content.Intent; import android.net.Uri; -import android.os.AsyncTask; import android.os.Bundle; import android.support.v7.widget.Toolbar; import android.support.v7.widget.TooltipCompat; @@ -33,10 +30,13 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.Toast; +import com.google.zxing.integration.android.IntentIntegrator; +import com.google.zxing.integration.android.IntentResult; + import org.thoughtcrime.securesms.components.SearchToolbar; -import org.thoughtcrime.securesms.connect.DcHelper; import org.thoughtcrime.securesms.lock.RegistrationLockDialog; import org.thoughtcrime.securesms.permissions.Permissions; +import org.thoughtcrime.securesms.qr.QrScanHandler; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.search.SearchFragment; import org.thoughtcrime.securesms.service.KeyCachingService; @@ -163,11 +163,22 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit case R.id.menu_clear_passphrase: handleClearPassphrase(); return true; case R.id.menu_invite: handleInvite(); return true; case R.id.menu_help: handleHelp(); return true; + case R.id.menu_qr_scan: handleQrScan(); return true; + case R.id.menu_qr_show: handleQrShow(); return true; } return false; } + private void handleQrScan() { + new IntentIntegrator(this).setCaptureActivity(QrScanActivity.class).initiateScan(); + } + + private void handleQrShow() { + Intent qrIntent = new Intent(this, QrShowActivity.class); + startActivity(qrIntent); + } + @Override public void onCreateConversation(long threadId, Recipient recipient, int distributionType, long lastSeen) { openConversation(threadId, recipient, distributionType, lastSeen, -1); @@ -227,4 +238,13 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit Toast.makeText(this, R.string.ConversationListActivity_there_is_no_browser_installed_on_your_device, Toast.LENGTH_LONG).show(); } } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == IntentIntegrator.REQUEST_CODE) { + IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data); + QrScanHandler qrScanHandler = new QrScanHandler(this); + qrScanHandler.onScanPerformed(scanResult); + } + } } diff --git a/src/org/thoughtcrime/securesms/QrScanActivity.java b/src/org/thoughtcrime/securesms/QrScanActivity.java new file mode 100644 index 000000000..b8ad88a09 --- /dev/null +++ b/src/org/thoughtcrime/securesms/QrScanActivity.java @@ -0,0 +1,118 @@ +package org.thoughtcrime.securesms; + +import android.Manifest; +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v7.app.AppCompatActivity; +import android.view.KeyEvent; +import android.view.MenuItem; + +import com.journeyapps.barcodescanner.CaptureManager; +import com.journeyapps.barcodescanner.CompoundBarcodeView; + +import org.thoughtcrime.securesms.permissions.Permissions; + +public class QrScanActivity extends AppCompatActivity { + + private CaptureManager capture; + + private CompoundBarcodeView barcodeScannerView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_qr_scan); + + assert getSupportActionBar() != null; + getSupportActionBar().setTitle(R.string.qr_scan_activity__title); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + barcodeScannerView = findViewById(R.id.zxing_barcode_scanner); + barcodeScannerView.setStatusText(getString(R.string.qr_scan_activity__hint) + "\n "); + + if (savedInstanceState != null) { + init(barcodeScannerView, getIntent(), savedInstanceState); + } + + Permissions.with(this) + .request(Manifest.permission.CAMERA) + .ifNecessary() + .onAnyResult(this::handleQrScanWithPermissions) + .onAnyDenied(this::handleQrScanWithDeniedPermission) + .execute(); + } + + private void handleQrScanWithPermissions() { + init(barcodeScannerView, getIntent(), null); + } + + private void handleQrScanWithDeniedPermission() { + setResult(Activity.RESULT_CANCELED); + finish(); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + super.onOptionsItemSelected(item); + + switch (item.getItemId()) { + case android.R.id.home: + finish(); + return true; + } + + return false; + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { + Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults); + } + + private void init(CompoundBarcodeView barcodeScannerView, Intent intent, Bundle savedInstanceState) { + capture = new CaptureManager(this, barcodeScannerView); + capture.initializeFromIntent(intent, savedInstanceState); + capture.decode(); + } + + @Override + protected void onResume() { + super.onResume(); + if (capture != null) { + capture.onResume(); + } + } + + @Override + protected void onPause() { + super.onPause(); + if (capture != null) { + capture.onPause(); + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (capture != null) { + capture.onDestroy(); + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + if (capture != null) { + capture.onSaveInstanceState(outState); + } + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + return barcodeScannerView.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event); + } + +} diff --git a/src/org/thoughtcrime/securesms/QrShowActivity.java b/src/org/thoughtcrime/securesms/QrShowActivity.java new file mode 100644 index 000000000..31ce6f842 --- /dev/null +++ b/src/org/thoughtcrime/securesms/QrShowActivity.java @@ -0,0 +1,188 @@ +package org.thoughtcrime.securesms; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.os.Bundle; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.text.Html; +import android.view.MenuItem; +import android.view.WindowManager; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +import com.b44t.messenger.DcContext; +import com.b44t.messenger.DcEventCenter; +import com.google.zxing.BarcodeFormat; +import com.google.zxing.MultiFormatWriter; +import com.google.zxing.WriterException; +import com.google.zxing.common.BitMatrix; + +import org.thoughtcrime.securesms.connect.ApplicationDcContext; +import org.thoughtcrime.securesms.connect.DcHelper; + +public class QrShowActivity extends AppCompatActivity implements DcEventCenter.DcEventDelegate { + + public final static int WHITE = 0xFFFFFFFF; + public final static int BLACK = 0xFF000000; + public final static int WIDTH = 400; + public final static int HEIGHT = 400; + + public int numJoiners; + + DcEventCenter dcEventCenter; + + ApplicationDcContext dcContext; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_qr_show); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); // keeping the screen on also avoids falling back from IDLE to POLL + + dcContext = DcHelper.getContext(this); + dcEventCenter = dcContext.eventCenter; + + Bundle extras = getIntent().getExtras(); + int chatId = 0; + if (extras != null) { + chatId = extras.getInt("chat_id"); + } + + ActionBar supportActionBar = getSupportActionBar(); + assert supportActionBar != null; + supportActionBar.setDisplayHomeAsUpEnabled(true); + + String hint; + if (chatId != 0) { + // verified-group + String groupName = dcContext.getChat(chatId).getName(); + hint = String.format(this.getString(R.string.qr_show_activity__join_verified_group_hint), groupName); + supportActionBar.setTitle(groupName); + supportActionBar.setSubtitle(R.string.qr_show_activity__join_verified_group_title); + } else { + // verify-contact + String selfName = DcHelper.get(this, DcHelper.CONFIG_DISPLAY_NAME); // we cannot use MrContact.getDisplayName() as this would result in "Me" instead of + String nameAndAddress; + if (selfName.isEmpty()) { + selfName = DcHelper.get(this, DcHelper.CONFIG_ADDRESS, "unknown"); + nameAndAddress = selfName; + } else { + nameAndAddress = String.format("%s (%s)", selfName, DcHelper.get(this, DcHelper.CONFIG_ADDRESS)); + } + hint = String.format(this.getString(R.string.qr_show_activity__join_contact_hint), nameAndAddress); + supportActionBar.setTitle(selfName); + supportActionBar.setSubtitle(R.string.qr_show_activity__join_contact_title); + } + TextView hintBelowQr = findViewById(R.id.qrShowHint); + hintBelowQr.setText(Html.fromHtml(hint)); + + numJoiners = 0; + + ImageView imageView = findViewById(R.id.qrImage); + try { + Bitmap bitmap = encodeAsBitmap(dcContext.getSecurejoinQr(chatId)); + imageView.setImageBitmap(bitmap); + } catch (WriterException e) { + e.printStackTrace(); + } + + dcEventCenter.addObserver(this, DcContext.DC_EVENT_SECUREJOIN_INVITER_PROGRESS); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + dcEventCenter.removeObserver(this, DcContext.DC_EVENT_SECUREJOIN_INVITER_PROGRESS); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + super.onOptionsItemSelected(item); + + switch (item.getItemId()) { + case android.R.id.home: + finish(); + return true; + } + + return false; + } + + Bitmap encodeAsBitmap(String str) throws WriterException { + BitMatrix result; + try { + result = new MultiFormatWriter().encode(str, + BarcodeFormat.QR_CODE, WIDTH, HEIGHT, null); + } catch (IllegalArgumentException iae) { + // Unsupported format + return null; + } + + int w = result.getWidth(); + int h = result.getHeight(); + int[] pixels = new int[w * h]; + for (int y = 0; y < h; y++) { + int offset = y * w; + for (int x = 0; x < w; x++) { + pixels[offset + x] = result.get(x, y) ? BLACK : WHITE; + } + } + + Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); + bitmap.setPixels(pixels, 0, w, 0, 0, w, h); + + int configQrOverlayLogo = DcHelper.getInt(this, DcHelper.CONFIG_QR_OVERLAY_LOGO, 1); + if (configQrOverlayLogo != 0) { + Bitmap overlay = BitmapFactory.decodeResource(this.getResources(), R.drawable.qr_overlay); + putOverlay(bitmap, overlay); + } + + return bitmap; + } + + public void putOverlay(Bitmap bitmap, Bitmap overlay) { + int bw = bitmap.getWidth(); + int bh = bitmap.getHeight(); + int ow = bw / 6; + int oh = bh / 6; + + Canvas canvas = new Canvas(bitmap); + Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG); + canvas.drawBitmap(overlay, null, new Rect(bw / 2 - ow / 2, bh / 2 - oh / 2, bw / 2 + ow / 2, bh / 2 + oh / 2), paint); + } + + @Override + public void handleEvent(int eventId, Object data1, Object data2) { + if (eventId == DcContext.DC_EVENT_SECUREJOIN_INVITER_PROGRESS) { + DcContext dcContext = DcHelper.getContext(this); + int contact_id = ((Long) data1).intValue(); + long progress = (Long) data2; + String msg = null; + if (progress == 300) { + msg = String.format(getString(R.string.qr_show_activity__joining), dcContext.getContact(contact_id).getNameNAddr()); + numJoiners++; + } else if (progress == 600) { + msg = String.format(getString(R.string.qr_show_activity__verified), dcContext.getContact(contact_id).getNameNAddr()); + } else if (progress == 800) { + msg = String.format(getString(R.string.qr_show_activity__group_joined), dcContext.getContact(contact_id).getNameNAddr()); + } + + if (msg != null) { + Toast.makeText(this, msg, Toast.LENGTH_SHORT).show(); + } + + if (progress == 1000) { + numJoiners--; + if (numJoiners <= 0) { + finish(); + } + } + } + + } +} diff --git a/src/org/thoughtcrime/securesms/connect/DcHelper.java b/src/org/thoughtcrime/securesms/connect/DcHelper.java index 4bc68cf3a..76bb2d563 100644 --- a/src/org/thoughtcrime/securesms/connect/DcHelper.java +++ b/src/org/thoughtcrime/securesms/connect/DcHelper.java @@ -22,6 +22,7 @@ public class DcHelper { public static final String CONFIG_SELF_STATUS = "selfstatus"; public static final String CONFIG_SELF_AVATAR = "selfavatar"; public static final String CONFIG_E2EE_ENABLED = "e2ee_enabled"; + public static final String CONFIG_QR_OVERLAY_LOGO = "qr_overlay_logo"; public static ApplicationDcContext getContext(Context context) { return ApplicationContext.getInstance(context).dcContext; @@ -32,9 +33,18 @@ public class DcHelper { return dcContext.isConfigured() == 1; } - public static String get(Context context, String key) { + public static int getInt(Context context, String key, int defaultValue) { DcContext dcContext = getContext(context); - return dcContext.getConfig(key, ""); + return dcContext.getConfigInt(key, defaultValue); + } + + public static String get(Context context, String key, String defaultValue) { + DcContext dcContext = getContext(context); + return dcContext.getConfig(key, defaultValue); + } + + public static String get(Context context, String key) { + return get(context, key, ""); } public static void set(Context context, String key, String value) { diff --git a/src/org/thoughtcrime/securesms/qr/QrScanHandler.java b/src/org/thoughtcrime/securesms/qr/QrScanHandler.java new file mode 100644 index 000000000..c96019a45 --- /dev/null +++ b/src/org/thoughtcrime/securesms/qr/QrScanHandler.java @@ -0,0 +1,193 @@ +package org.thoughtcrime.securesms.qr; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.ProgressDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.StringRes; +import android.text.Html; +import android.widget.Toast; + +import com.b44t.messenger.DcContext; +import com.b44t.messenger.DcLot; +import com.google.zxing.integration.android.IntentResult; + +import org.thoughtcrime.securesms.ConversationActivity; +import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.connect.ApplicationDcContext; +import org.thoughtcrime.securesms.connect.DcHelper; +import org.thoughtcrime.securesms.util.IntentUtils; +import org.thoughtcrime.securesms.util.Util; + +public class QrScanHandler { + + private Activity activity; + + private DcContext dcContext; + + public QrScanHandler(Activity activity) { + this.activity = activity; + dcContext = DcHelper.getContext(activity); + } + + public void onScanPerformed(IntentResult scanResult) { + if (scanResult == null || scanResult.getFormatName() == null) { + return; // Should not happen! + } + + AlertDialog.Builder builder = new AlertDialog.Builder(activity); + final String qrRawString = scanResult.getContents(); + final DcLot qrParsed = dcContext.checkQr(qrRawString); + String nameAndAddress = dcContext.getContact(qrParsed.getId()).getNameNAddr(); + switch (qrParsed.getState()) { + case DcContext.DC_QR_ASK_VERIFYCONTACT: + case DcContext.DC_QR_ASK_VERIFYGROUP: + showVerifyContactOrGroup(activity, builder, qrRawString, qrParsed, nameAndAddress); + break; + + case DcContext.DC_QR_FPR_WITHOUT_ADDR: + showVerifyFingerprintWithoutAddress(builder, qrParsed); + break; + + case DcContext.DC_QR_FPR_MISMATCH: + showFingerPrintError(builder, nameAndAddress); + break; + + case DcContext.DC_QR_FPR_OK: + case DcContext.DC_QR_ADDR: + showFingerprintOrQrSuccess(builder, qrParsed, nameAndAddress); + break; + + case DcContext.DC_QR_URL: + showQrUrl(builder, qrParsed); + break; + + default: + handleDefault(builder, qrRawString, qrParsed); + break; + } + builder.create().show(); + } + + private void handleDefault(AlertDialog.Builder builder, String qrRawString, DcLot qrParsed) { + String msg; + final String scannedText; + switch (qrParsed.getState()) { + case DcContext.DC_QR_ERROR: + scannedText = qrRawString; + msg = qrParsed.getText1() + "

" + String.format(activity.getString(R.string.qr_scan_handler_Scan_contains_text), scannedText) + ""; + break; + case DcContext.DC_QR_TEXT: + scannedText = qrParsed.getText1(); + msg = String.format(activity.getString(R.string.qr_scan_handler_Scan_contains_text), scannedText); + break; + default: + scannedText = qrRawString; + msg = String.format(activity.getString(R.string.qr_scan_handler_Scan_contains_text), scannedText); + break; + } + builder.setMessage(Html.fromHtml(msg)); + builder.setPositiveButton(android.R.string.ok, null); + builder.setNeutralButton(R.string.copy_to_clipboard, (dialog, which) -> { + Util.writeTextToClipboard(activity, scannedText); + showDoneToast(activity); + }); + } + + private void showQrUrl(AlertDialog.Builder builder, DcLot qrParsed) { + final String url = qrParsed.getText1(); + String msg = String.format(activity.getString(R.string.qr_scan_handler_qr_scan_contains_url), url); + builder.setMessage(msg); + builder.setPositiveButton(R.string.open, (dialog, which) -> IntentUtils.showBrowserIntent(activity, url)); + builder.setNegativeButton(android.R.string.cancel, null); + builder.setNeutralButton(R.string.copy_to_clipboard, (dialog, which) -> { + Util.writeTextToClipboard(activity, url); + showDoneToast(activity); + }); + } + + private void showDoneToast(Activity activity) { + Toast.makeText(activity, activity.getString(R.string.done), Toast.LENGTH_SHORT).show(); + } + + private void showFingerprintOrQrSuccess(AlertDialog.Builder builder, DcLot qrParsed, String nameAndAddress) { + @StringRes int resId = qrParsed.getState() == DcContext.DC_QR_ADDR ? R.string.qr_scan_handler_ask_start_chat_with : R.string.qr_scan_handler_fingerprint_ok; + builder.setMessage(Html.fromHtml(String.format(activity.getString(resId, nameAndAddress)))); + builder.setPositiveButton(android.R.string.ok, (dialogInterface, i) -> { + Bundle bundle = new Bundle(); + bundle.putInt(ConversationActivity.THREAD_ID_EXTRA, dcContext.createChatByContactId(qrParsed.getId())); + Intent intent = new Intent(activity, ConversationActivity.class); + //TODO how to we get the contact ID here? We need it to avoid crashing when starting the ConversationActivity + activity.startActivity(intent, bundle); + }); + builder.setNegativeButton(android.R.string.cancel, null); + } + + private void showFingerPrintError(AlertDialog.Builder builder, String nameAndAddress) { + builder.setMessage(Html.fromHtml(String.format(activity.getString(R.string.qr_scan_handler_fingerprint_mismatch), nameAndAddress))); + builder.setPositiveButton(android.R.string.ok, null); + } + + private void showVerifyFingerprintWithoutAddress(AlertDialog.Builder builder, DcLot qrParsed) { + builder.setMessage(Html.fromHtml(activity.getString(R.string.qr_scan_handler_fingerprint_without_address) + "

" + activity.getString(R.string.qr_scan_handler_fingerprint) + ":
" + qrParsed.getText1() + "")); + builder.setPositiveButton(android.R.string.ok, null); + builder.setNeutralButton(R.string.copy_to_clipboard, (dialog, which) -> { + Util.writeTextToClipboard(activity, qrParsed.getText1()); + showDoneToast(activity); + }); + } + + private void showVerifyContactOrGroup(Activity activity, AlertDialog.Builder builder, String qrRawString, DcLot qrParsed, String nameNAddr) { + String msg; + switch (qrParsed.getState()) { + case DcContext.DC_QR_ASK_VERIFYGROUP: + msg = String.format(activity.getString(R.string.qr_scan_handler_join_verified_group), qrParsed.getText1()); + break; + default: + msg = String.format(activity.getString(R.string.qr_scan_handler_fingerprint_ask_oob), nameNAddr); + break; + } + builder.setMessage(Html.fromHtml(msg)); + builder.setPositiveButton(android.R.string.ok, (dialogInterface, i) -> { + ProgressDialog progressDialog = new ProgressDialog(activity); + progressDialog.setMessage(activity.getString(R.string.one_moment)); + progressDialog.setCanceledOnTouchOutside(false); + progressDialog.setCancelable(false); + progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, activity.getString(android.R.string.cancel), (dialog, which) -> dcContext.stopOngoingProcess()); + progressDialog.show(); + + new Thread() { + @Override + public void run() { + ApplicationDcContext applicationDcContext = DcHelper.getContext(activity); + applicationDcContext.captureNextError(); + + final int oobDone = dcContext.joinSecurejoin(qrRawString); // oobJoin() runs until all needed messages are sent+received! + String errorString = applicationDcContext.getCapturedError(); + applicationDcContext.endCaptureNextError(); + + if (oobDone != 0) { + Bundle bundle = new Bundle(); + bundle.putInt(ConversationActivity.THREAD_ID_EXTRA, oobDone); + //TODO how to we get the contact ID here? We need it to avoid crashing when starting the ConversationActivity + Intent intent = new Intent(activity, ConversationActivity.class); + activity.startActivity(intent, bundle); + } else if (!errorString.isEmpty()) { + activity.runOnUiThread(() -> { + AlertDialog.Builder builder1 = new AlertDialog.Builder(activity); + builder1.setMessage(errorString); + builder1.setPositiveButton(android.R.string.ok, null); + builder1.create().show(); + }); + } + } + }.start(); + + + }); + builder.setNegativeButton(android.R.string.cancel, null); + } + +} diff --git a/src/org/thoughtcrime/securesms/util/IntentUtils.java b/src/org/thoughtcrime/securesms/util/IntentUtils.java index 31295d240..777f8d037 100644 --- a/src/org/thoughtcrime/securesms/util/IntentUtils.java +++ b/src/org/thoughtcrime/securesms/util/IntentUtils.java @@ -4,6 +4,7 @@ package org.thoughtcrime.securesms.util; import android.content.Context; import android.content.Intent; import android.content.pm.ResolveInfo; +import android.net.Uri; import android.support.annotation.NonNull; import java.util.List; @@ -15,4 +16,9 @@ public class IntentUtils { return resolveInfoList != null && resolveInfoList.size() > 1; } + public static void showBrowserIntent(Context context, String url) { + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + context.startActivity(browserIntent); + } + } From c441c116010b09202ea1d4dde6db524b81a29ebd Mon Sep 17 00:00:00 2001 From: "B. Petersen" Date: Wed, 17 Oct 2018 19:10:23 +0200 Subject: [PATCH 6/8] start activity after QR scanning from ui thread --- .../securesms/qr/QrScanHandler.java | 50 +++++++++++-------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/src/org/thoughtcrime/securesms/qr/QrScanHandler.java b/src/org/thoughtcrime/securesms/qr/QrScanHandler.java index c96019a45..2f0f4c754 100644 --- a/src/org/thoughtcrime/securesms/qr/QrScanHandler.java +++ b/src/org/thoughtcrime/securesms/qr/QrScanHandler.java @@ -18,12 +18,14 @@ import org.thoughtcrime.securesms.ConversationActivity; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.connect.ApplicationDcContext; import org.thoughtcrime.securesms.connect.DcHelper; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.util.IntentUtils; import org.thoughtcrime.securesms.util.Util; public class QrScanHandler { private Activity activity; + ProgressDialog progressDialog; private DcContext dcContext; @@ -140,6 +142,11 @@ public class QrScanHandler { } private void showVerifyContactOrGroup(Activity activity, AlertDialog.Builder builder, String qrRawString, DcLot qrParsed, String nameNAddr) { + if (progressDialog != null) { + progressDialog.dismiss(); + progressDialog = null; + } + String msg; switch (qrParsed.getState()) { case DcContext.DC_QR_ASK_VERIFYGROUP: @@ -151,39 +158,42 @@ public class QrScanHandler { } builder.setMessage(Html.fromHtml(msg)); builder.setPositiveButton(android.R.string.ok, (dialogInterface, i) -> { - ProgressDialog progressDialog = new ProgressDialog(activity); + progressDialog = new ProgressDialog(activity); progressDialog.setMessage(activity.getString(R.string.one_moment)); progressDialog.setCanceledOnTouchOutside(false); progressDialog.setCancelable(false); progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, activity.getString(android.R.string.cancel), (dialog, which) -> dcContext.stopOngoingProcess()); progressDialog.show(); - new Thread() { - @Override - public void run() { - ApplicationDcContext applicationDcContext = DcHelper.getContext(activity); - applicationDcContext.captureNextError(); + ApplicationDcContext dcContext = DcHelper.getContext(activity); + dcContext.captureNextError(); - final int oobDone = dcContext.joinSecurejoin(qrRawString); // oobJoin() runs until all needed messages are sent+received! - String errorString = applicationDcContext.getCapturedError(); - applicationDcContext.endCaptureNextError(); + new Thread(() -> { - if (oobDone != 0) { - Bundle bundle = new Bundle(); - bundle.putInt(ConversationActivity.THREAD_ID_EXTRA, oobDone); - //TODO how to we get the contact ID here? We need it to avoid crashing when starting the ConversationActivity - Intent intent = new Intent(activity, ConversationActivity.class); - activity.startActivity(intent, bundle); - } else if (!errorString.isEmpty()) { - activity.runOnUiThread(() -> { + int newChatId = dcContext.joinSecurejoin(qrRawString); // joinSecurejoin() runs until all needed messages are sent+received! + + Util.runOnMain(() -> { + if (progressDialog != null) { + progressDialog.dismiss(); + progressDialog = null; + } + + String errorString = dcContext.getCapturedError(); + dcContext.endCaptureNextError(); + if (newChatId != 0) { + Intent intent = new Intent(activity, ConversationActivity.class); + intent.putExtra(ConversationActivity.ADDRESS_EXTRA, Address.fromChat(newChatId)); + intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, (long)newChatId); + activity.startActivity(intent); + } else if (!errorString.isEmpty()) { AlertDialog.Builder builder1 = new AlertDialog.Builder(activity); builder1.setMessage(errorString); builder1.setPositiveButton(android.R.string.ok, null); builder1.create().show(); - }); - } + } + }); } - }.start(); + ).start(); }); From f27f0b430559f818ac6133d27dc8c1bd8151ecac Mon Sep 17 00:00:00 2001 From: "B. Petersen" Date: Wed, 17 Oct 2018 23:26:58 +0200 Subject: [PATCH 7/8] show secure-join-events on joiner side --- res/values/strings.xml | 1 + .../securesms/qr/QrScanHandler.java | 24 ++++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 183b7cf39..34f7a77e0 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1326,6 +1326,7 @@ Scanned QR code URL:\n\n%1$s Fingerprint Chat with ]]>%1$s]]>? + %1$s verified, introduce myself … Show QR verify code diff --git a/src/org/thoughtcrime/securesms/qr/QrScanHandler.java b/src/org/thoughtcrime/securesms/qr/QrScanHandler.java index 2f0f4c754..56fc85b36 100644 --- a/src/org/thoughtcrime/securesms/qr/QrScanHandler.java +++ b/src/org/thoughtcrime/securesms/qr/QrScanHandler.java @@ -11,6 +11,7 @@ import android.text.Html; import android.widget.Toast; import com.b44t.messenger.DcContext; +import com.b44t.messenger.DcEventCenter; import com.b44t.messenger.DcLot; import com.google.zxing.integration.android.IntentResult; @@ -22,12 +23,12 @@ import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.util.IntentUtils; import org.thoughtcrime.securesms.util.Util; -public class QrScanHandler { +public class QrScanHandler implements DcEventCenter.DcEventDelegate { private Activity activity; ProgressDialog progressDialog; - private DcContext dcContext; + private ApplicationDcContext dcContext; public QrScanHandler(Activity activity) { this.activity = activity; @@ -165,12 +166,13 @@ public class QrScanHandler { progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, activity.getString(android.R.string.cancel), (dialog, which) -> dcContext.stopOngoingProcess()); progressDialog.show(); - ApplicationDcContext dcContext = DcHelper.getContext(activity); dcContext.captureNextError(); new Thread(() -> { + dcContext.eventCenter.addObserver(this, DcContext.DC_EVENT_SECUREJOIN_JOINER_PROGRESS); int newChatId = dcContext.joinSecurejoin(qrRawString); // joinSecurejoin() runs until all needed messages are sent+received! + dcContext.eventCenter.removeObservers(this); Util.runOnMain(() -> { if (progressDialog != null) { @@ -200,4 +202,20 @@ public class QrScanHandler { builder.setNegativeButton(android.R.string.cancel, null); } + @Override + public void handleEvent(int eventId, Object data1, Object data2) { + if (eventId == DcContext.DC_EVENT_SECUREJOIN_JOINER_PROGRESS) { + long contact_id = (Long)data1; + long progress = (Long)data2; + String msg = null; + if( progress == 400) { + msg = activity.getString(R.string.qr_scan_handler_verfied_introduce_myself, dcContext.getContact((int)contact_id).getNameNAddr()); + } + + if( progressDialog != null && msg != null ) { + progressDialog.setMessage(msg); + } + + } + } } From bf85918ed2d7694b2967262e081fdc64ff521ccb Mon Sep 17 00:00:00 2001 From: "daniel.boehrs" Date: Thu, 18 Oct 2018 09:58:17 +0200 Subject: [PATCH 8/8] add qr show+scan activities #79 and adjust UI --- AndroidManifest.xml | 2 +- ...ml => activity_select_chat_background.xml} | 22 +++++-- res/layout/conversation_list_item_view.xml | 39 +++++++----- res/layout/conversation_title_view.xml | 12 +++- res/layout/group_create_activity.xml | 60 +++++++++++++------ res/values/styles.xml | 1 + .../securesms/GroupCreateActivity.java | 16 +++++ .../AppearancePreferenceFragment.java | 18 +----- ...gment.java => ChatBackgroundActivity.java} | 25 ++++---- 9 files changed, 121 insertions(+), 74 deletions(-) rename res/layout/{select_chat_background_dialog.xml => activity_select_chat_background.xml} (66%) rename src/org/thoughtcrime/securesms/preferences/{ChatBackgroundDialogFragment.java => ChatBackgroundActivity.java} (89%) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 4408a91ec..8bb7ff96b 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -332,7 +332,7 @@ android:theme="@style/TextSecure.LightNoActionBar" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/> - diff --git a/res/layout/select_chat_background_dialog.xml b/res/layout/activity_select_chat_background.xml similarity index 66% rename from res/layout/select_chat_background_dialog.xml rename to res/layout/activity_select_chat_background.xml index c344c19d2..ae4383ffa 100644 --- a/res/layout/select_chat_background_dialog.xml +++ b/res/layout/activity_select_chat_background.xml @@ -3,25 +3,35 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/layout_container" android:layout_width="match_parent" - android:layout_height="match_parent" - > + android:layout_height="match_parent"> + + + +