This commit is contained in:
Florian Haar
2018-10-18 10:56:46 +02:00
34 changed files with 893 additions and 91 deletions
+1 -1
View File
@@ -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 ();
@@ -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);
}
}
}
@@ -38,6 +38,7 @@ import android.widget.Toast;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.request.target.SimpleTarget;
import com.bumptech.glide.request.transition.Transition;
import com.dd.CircularProgressButton;
import com.soundcloud.android.crop.Crop;
import org.thoughtcrime.securesms.connect.ApplicationDcContext;
@@ -97,6 +98,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
private ImageView avatar;
private TextView creatingText;
private Bitmap avatarBmp;
private CircularProgressButton verifyButton;
@NonNull private Optional<GroupData> groupToUpdate = Optional.absent();
@@ -200,11 +202,16 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
avatar = ViewUtil.findById(this, R.id.avatar);
groupName = ViewUtil.findById(this, R.id.group_name);
creatingText = ViewUtil.findById(this, R.id.creating_group_text);
verifyButton = ViewUtil.findById(this, R.id.verify_button);
SelectedRecipientsAdapter adapter = new SelectedRecipientsAdapter(this);
adapter.setOnRecipientDeletedListener(this);
lv.setAdapter(adapter);
findViewById(R.id.add_member_button).setOnClickListener(new AddRecipientButtonListener());
if (createVerified) {
verifyButton.setOnClickListener(new ShowQrButtonListener());
verifyButton.setVisibility(View.VISIBLE);
}
avatar.setImageDrawable(new ResourceContactPhoto(R.drawable.ic_group_white_24dp).asDrawable(this, ContactColors.UNKNOWN_COLOR.toConversationColor(this)));
avatar.setOnClickListener(view -> Crop.pickImage(GroupCreateActivity.this));
}
@@ -363,6 +370,15 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
}
}
private class ShowQrButtonListener implements View.OnClickListener {
@Override
public void onClick(View view) {
Intent qrIntent = new Intent(GroupCreateActivity.this, QrShowActivity.class);
startActivity(qrIntent);
}
}
private static class FillExistingGroupInfoAsyncTask extends ProgressDialogAsyncTask<String,Void,Optional<GroupData>> {
private GroupCreateActivity activity;
@@ -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);
}
}
@@ -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();
}
}
}
}
}
@@ -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;
}
@@ -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()));
}
}
@@ -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<Boolean> signal, Slide slide) {
signal.addListener(new ListenableFuture.Listener<Boolean>() {
@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) {
@@ -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) {
@@ -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.getBytes(), dcMsg.getType()==DcMsg.DC_MSG_VOICE);
super(context, new DcAttachment(dcMsg));
dcMsgId = dcMsg.getId();
}
@@ -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.getBytes(), dcMsg.getFilename());
this(context, new DcAttachment(dcMsg));
dcMsgId = dcMsg.getId();
}
@@ -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);
}
@@ -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);
}
@@ -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);
}
@@ -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);
}
@@ -2,32 +2,16 @@ package org.thoughtcrime.securesms.preferences;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.preference.ListPreference;
import android.support.v7.preference.Preference;
import android.view.Display;
import android.widget.Toast;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.Arrays;
import java.util.concurrent.ExecutionException;
import static android.app.Activity.RESULT_OK;
public class AppearancePreferenceFragment extends ListSummaryPreferenceFragment {
@@ -85,7 +69,7 @@ public class AppearancePreferenceFragment extends ListSummaryPreferenceFragment
private class BackgroundClickListener implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {
Intent intent = new Intent(getContext(), ChatBackgroundDialogFragment.class);
Intent intent = new Intent(getContext(), ChatBackgroundActivity.class);
getActivity().startActivity(intent);
return true;
}
@@ -8,21 +8,14 @@ import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.Toast;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
@@ -38,12 +31,12 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.concurrent.ExecutionException;
public class ChatBackgroundDialogFragment extends BaseActionBarActivity {
public class ChatBackgroundActivity extends BaseActionBarActivity {
Button galleryButton;
Button defaultButton;
FrameLayout layoutContainer;
MenuItem acceptMenuItem;
ImageView preview;
String tempDestinationPath;
Uri imageUri;
@@ -51,11 +44,11 @@ public class ChatBackgroundDialogFragment extends BaseActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.select_chat_background_dialog);
setContentView(R.layout.activity_select_chat_background);
defaultButton = findViewById(R.id.set_default_button);
galleryButton = findViewById(R.id.from_gallery_button);
layoutContainer = findViewById(R.id.layout_container);
preview = findViewById(R.id.preview);
defaultButton.setOnClickListener(new DefaultClickListener());
galleryButton.setOnClickListener(new GalleryClickListener());
@@ -64,8 +57,10 @@ public class ChatBackgroundDialogFragment extends BaseActionBarActivity {
setLayoutBackgroundImage(backgroundImagePath);
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeAsUpIndicator(R.drawable.ic_close_white_24dp);
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeAsUpIndicator(R.drawable.ic_close_white_24dp);
}
}
@Override
@@ -140,7 +135,7 @@ public class ChatBackgroundDialogFragment extends BaseActionBarActivity {
private void setLayoutBackgroundImage(String backgroundImagePath) {
Drawable image = Drawable.createFromPath(backgroundImagePath);
layoutContainer.setBackground(image);
preview.setImageDrawable(image);
}
@Override
@@ -0,0 +1,221 @@
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.DcEventCenter;
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.database.Address;
import org.thoughtcrime.securesms.util.IntentUtils;
import org.thoughtcrime.securesms.util.Util;
public class QrScanHandler implements DcEventCenter.DcEventDelegate {
private Activity activity;
ProgressDialog progressDialog;
private ApplicationDcContext 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() + "<br><br><c#808080>" + String.format(activity.getString(R.string.qr_scan_handler_Scan_contains_text), scannedText) + "</c>";
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) + "<br><br><c#808080>" + activity.getString(R.string.qr_scan_handler_fingerprint) + ":<br>" + qrParsed.getText1() + "</c>"));
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) {
if (progressDialog != null) {
progressDialog.dismiss();
progressDialog = null;
}
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 = 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();
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) {
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();
});
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);
}
}
}
}
@@ -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);
}
}
@@ -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())) {