aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/main/AndroidManifest.xml5
-rw-r--r--src/main/java/eu/siacs/conversations/services/BarcodeProvider.java226
-rw-r--r--src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java31
-rw-r--r--src/main/java/eu/siacs/conversations/ui/XmppActivity.java26
-rw-r--r--src/main/java/eu/siacs/conversations/utils/CryptoHelper.java6
-rw-r--r--src/main/res/menu/editaccount.xml16
-rw-r--r--src/main/res/values/strings.xml3
7 files changed, 288 insertions, 25 deletions
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index 56ed5c76..c1baf42d 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -204,6 +204,11 @@
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
+ <provider
+ android:authorities="eu.siacs.conversations.barcodes"
+ android:name=".services.BarcodeProvider"
+ android:exported="false"
+ android:grantUriPermissions="true"/>
</application>
diff --git a/src/main/java/eu/siacs/conversations/services/BarcodeProvider.java b/src/main/java/eu/siacs/conversations/services/BarcodeProvider.java
new file mode 100644
index 00000000..ccaa65c9
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/services/BarcodeProvider.java
@@ -0,0 +1,226 @@
+package eu.siacs.conversations.services;
+
+import android.annotation.TargetApi;
+import android.content.ComponentName;
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.net.Uri;
+import android.os.Build;
+import android.os.CancellationSignal;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.support.annotation.Nullable;
+import android.util.Log;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.EncodeHintType;
+import com.google.zxing.aztec.AztecWriter;
+import com.google.zxing.common.BitMatrix;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Hashtable;
+
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.utils.CryptoHelper;
+import eu.siacs.conversations.xmpp.jid.InvalidJidException;
+import eu.siacs.conversations.xmpp.jid.Jid;
+
+public class BarcodeProvider extends ContentProvider implements ServiceConnection {
+
+ private static final String AUTHORITY = "eu.siacs.conversations.barcodes";
+
+ private final Object lock = new Object();
+
+ private XmppConnectionService mXmppConnectionService;
+
+ @Override
+ public boolean onCreate() {
+ File barcodeDirectory = new File(getContext().getCacheDir().getAbsolutePath() + "/barcodes/");
+ if (barcodeDirectory.exists() && barcodeDirectory.isDirectory()) {
+ for (File file : barcodeDirectory.listFiles()) {
+ if (file.isFile() && !file.isHidden()) {
+ Log.d(Config.LOGTAG, "deleting old barcode file " + file.getAbsolutePath());
+ file.delete();
+ }
+ }
+ }
+ return true;
+ }
+
+ @Nullable
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public String getType(Uri uri) {
+ return "image/png";
+ }
+
+ @Nullable
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ return null;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
+ return openFile(uri, mode, null);
+ }
+
+ @Override
+ public ParcelFileDescriptor openFile(Uri uri, String mode, CancellationSignal signal) throws FileNotFoundException {
+ Log.d(Config.LOGTAG, "opening file with uri (normal): " + uri.toString());
+ String path = uri.getPath();
+ if (path != null && path.endsWith(".png") && path.length() >= 5) {
+ String jid = path.substring(1).substring(0, path.length() - 4);
+ Log.d(Config.LOGTAG, "account:" + jid);
+ if (connectAndWait()) {
+ Log.d(Config.LOGTAG, "connected to background service");
+ try {
+ Account account = mXmppConnectionService.findAccountByJid(Jid.fromString(jid));
+ if (account != null) {
+ String shareableUri = account.getShareableUri();
+ String hash = CryptoHelper.getFingerprint(shareableUri);
+ File file = new File(getContext().getCacheDir().getAbsolutePath() + "/barcodes/" + hash);
+ if (!file.exists()) {
+ file.getParentFile().mkdirs();
+ file.createNewFile();
+ Bitmap bitmap = createAztecBitmap(account.getShareableUri(), 1024);
+ OutputStream outputStream = new FileOutputStream(file);
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
+ outputStream.close();
+ outputStream.flush();
+ }
+ return ParcelFileDescriptor.open(file,ParcelFileDescriptor.MODE_READ_ONLY);
+ }
+ } catch (Exception e) {
+ throw new FileNotFoundException();
+ }
+ }
+ }
+ throw new FileNotFoundException();
+ }
+
+ private boolean connectAndWait() {
+ Intent intent = new Intent(getContext(), XmppConnectionService.class);
+ intent.setAction("contact_chooser");
+ Context context = getContext();
+ if (context != null) {
+ context.startService(intent);
+ context.bindService(intent, this, Context.BIND_AUTO_CREATE);
+ try {
+ waitForService();
+ Log.d(Config.LOGTAG, "service initialized");
+ return true;
+ } catch (InterruptedException e) {
+ return false;
+ }
+ } else {
+ Log.d(Config.LOGTAG, "context was null");
+ return false;
+ }
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ XmppConnectionService.XmppConnectionBinder binder = (XmppConnectionService.XmppConnectionBinder) service;
+ mXmppConnectionService = binder.getService();
+ synchronized (this.lock) {
+ lock.notifyAll();
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mXmppConnectionService = null;
+ }
+
+ private void waitForService() throws InterruptedException {
+ if (mXmppConnectionService == null) {
+ synchronized (this.lock) {
+ lock.wait();
+ }
+ }
+ }
+
+ public static Uri getUriForAccount(Account account) {
+ return Uri.parse("content://" + AUTHORITY + "/" + account.getJid().toBareJid() + ".png");
+ }
+
+ public static Bitmap createAztecBitmap(String input, int size) {
+ try {
+ final AztecWriter AZTEC_WRITER = new AztecWriter();
+ final Hashtable<EncodeHintType, Object> hints = new Hashtable<>();
+ hints.put(EncodeHintType.ERROR_CORRECTION, 10);
+ final BitMatrix result = AZTEC_WRITER.encode(input, BarcodeFormat.AZTEC, size, size, hints);
+ final int width = result.getWidth();
+ final int height = result.getHeight();
+ final int[] pixels = new int[width * height];
+ for (int y = 0; y < height; y++) {
+ final int offset = y * width;
+ for (int x = 0; x < width; x++) {
+ pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.TRANSPARENT;
+ }
+ }
+ final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
+ return bitmap;
+ } catch (final Exception e) {
+ return null;
+ }
+ }
+
+ static class TransferThread extends Thread {
+ InputStream in;
+ OutputStream out;
+
+ TransferThread(InputStream in, OutputStream out) {
+ this.in = in;
+ this.out = out;
+ }
+
+ @Override
+ public void run() {
+ byte[] buf = new byte[1024];
+ int len;
+
+ try {
+ while ((len = in.read(buf)) >= 0) {
+ out.write(buf, 0, len);
+ }
+
+ in.close();
+ out.flush();
+ out.close();
+ } catch (IOException e) {
+ Log.e(Config.LOGTAG, "Exception transferring file", e);
+ }
+ }
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
index 693b7185..14e3d929 100644
--- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
@@ -46,6 +46,7 @@ import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.services.BarcodeProvider;
import eu.siacs.conversations.services.XmppConnectionService.OnCaptchaRequested;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
@@ -703,6 +704,15 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
case R.id.action_server_info_show_more:
changeMoreTableVisibility(!item.isChecked());
break;
+ case R.id.action_share_barcode:
+ shareBarcode();
+ break;
+ case R.id.action_share_http:
+ shareLink(true);
+ break;
+ case R.id.action_share_uri:
+ shareLink(false);
+ break;
case R.id.action_change_password_on_server:
gotoChangePassword(null);
break;
@@ -722,6 +732,27 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
return super.onOptionsItemSelected(item);
}
+ private void shareLink(boolean http) {
+ Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.setType("text/plain");
+ String text;
+ if (http) {
+ text = "https://conversations.im/i/"+mAccount.getJid().toBareJid().toString();
+ } else {
+ text = mAccount.getShareableUri();
+ }
+ intent.putExtra(Intent.EXTRA_TEXT,text);
+ startActivity(Intent.createChooser(intent, getText(R.string.share_with)));
+ }
+
+ private void shareBarcode() {
+ Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.putExtra(Intent.EXTRA_STREAM,BarcodeProvider.getUriForAccount(mAccount));
+ intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ intent.setType("image/png");
+ startActivity(Intent.createChooser(intent, getText(R.string.share_with)));
+ }
+
private void changeMoreTableVisibility(boolean visible) {
mMoreTable.setVisibility(visible ? View.VISIBLE : View.GONE);
}
diff --git a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
index 5d086807..583fab78 100644
--- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
@@ -78,6 +78,7 @@ import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.entities.Presences;
import eu.siacs.conversations.services.AvatarService;
+import eu.siacs.conversations.services.BarcodeProvider;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.services.XmppConnectionService.XmppConnectionBinder;
import eu.siacs.conversations.utils.CryptoHelper;
@@ -1058,7 +1059,7 @@ public abstract class XmppActivity extends Activity {
Point size = new Point();
getWindowManager().getDefaultDisplay().getSize(size);
final int width = (size.x < size.y ? size.x : size.y);
- Bitmap bitmap = createQrCodeBitmap(uri, width);
+ Bitmap bitmap = BarcodeProvider.createAztecBitmap(uri, width);
ImageView view = new ImageView(this);
view.setBackgroundColor(Color.WHITE);
view.setImageBitmap(bitmap);
@@ -1068,29 +1069,6 @@ public abstract class XmppActivity extends Activity {
}
}
- protected Bitmap createQrCodeBitmap(String input, int size) {
- try {
- final AztecWriter AZTEC_WRITER = new AztecWriter();
- final Hashtable<EncodeHintType, Object> hints = new Hashtable<>();
- hints.put(EncodeHintType.ERROR_CORRECTION, 10);
- final BitMatrix result = AZTEC_WRITER.encode(input, BarcodeFormat.AZTEC, size, size, hints);
- final int width = result.getWidth();
- final int height = result.getHeight();
- final int[] pixels = new int[width * height];
- for (int y = 0; y < height; y++) {
- final int offset = y * width;
- for (int x = 0; x < width; x++) {
- pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.TRANSPARENT;
- }
- }
- final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
- return bitmap;
- } catch (final Exception e) {
- return null;
- }
- }
-
protected Account extractAccount(Intent intent) {
String jid = intent != null ? intent.getStringExtra(EXTRA_ACCOUNT) : null;
try {
diff --git a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java
index 38ebced1..f1a9d8c4 100644
--- a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java
+++ b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java
@@ -206,9 +206,13 @@ public final class CryptoHelper {
}
public static String getAccountFingerprint(Account account) {
+ return getFingerprint(account.getJid().toBareJid().toString());
+ }
+
+ public static String getFingerprint(String value) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
- return bytesToHex(md.digest(account.getJid().toBareJid().toString().getBytes("UTF-8")));
+ return bytesToHex(md.digest(value.getBytes("UTF-8")));
} catch (Exception e) {
return "";
}
diff --git a/src/main/res/menu/editaccount.xml b/src/main/res/menu/editaccount.xml
index 5df7ee5b..c67e5c47 100644
--- a/src/main/res/menu/editaccount.xml
+++ b/src/main/res/menu/editaccount.xml
@@ -1,5 +1,21 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/action_share"
+ android:title="@string/share_uri_with"
+ android:icon="?attr/icon_share"
+ android:showAsAction="always">
+ <menu>
+ <item
+ android:id="@+id/action_share_barcode"
+ android:title="@string/share_as_barcode"/>
+ <item
+ android:id="@+id/action_share_uri"
+ android:title="@string/share_as_uri"/>
+ <item android:id="@+id/action_share_http"
+ android:title="@string/share_as_http"/>
+ </menu>
+ </item>
+
<item
android:id="@+id/action_change_presence"
android:showAsAction="always"
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 591d3dbc..7e829439 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -707,4 +707,7 @@
<string name="verified_fingerprints">Verified fingerprints</string>
<string name="use_camera_icon_to_scan_barcode">Use the camera to scan a contacts barcode</string>
<string name="please_wait_for_keys_to_be_fetched">Please wait for keys to be fetched</string>
+ <string name="share_as_barcode">Share as Barcode</string>
+ <string name="share_as_uri">Share as XMPP URI</string>
+ <string name="share_as_http">Share as HTTP link</string>
</resources>