diff options
26 files changed, 426 insertions, 58 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/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java index c111efa4..2ce0cca8 100644 --- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java @@ -1328,9 +1328,14 @@ public class DatabaseBackend extends SQLiteOpenHelper { return count >= Config.FREQUENT_RESTARTS_THRESHOLD; } - public void clearStartTimeCounter() { - Log.d(Config.LOGTAG,"resetting start time counter"); + public void clearStartTimeCounter(boolean justOne) { SQLiteDatabase db = this.getWritableDatabase(); - db.execSQL("delete from "+START_TIMES_TABLE); + if (justOne) { + db.execSQL("delete from "+START_TIMES_TABLE+" where timestamp in (select timestamp from "+START_TIMES_TABLE+" order by timestamp desc limit 1)"); + Log.d(Config.LOGTAG,"do not count start up after being swiped away"); + } else { + Log.d(Config.LOGTAG,"resetting start time counter"); + db.execSQL("delete from " + START_TIMES_TABLE); + } } } diff --git a/src/main/java/eu/siacs/conversations/services/AvatarService.java b/src/main/java/eu/siacs/conversations/services/AvatarService.java index 45a6fd81..a6bf8875 100644 --- a/src/main/java/eu/siacs/conversations/services/AvatarService.java +++ b/src/main/java/eu/siacs/conversations/services/AvatarService.java @@ -44,6 +44,9 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { } private Bitmap get(final Contact contact, final int size, boolean cachedOnly) { + if (contact.isSelf()) { + return get(contact.getAccount(),size,cachedOnly); + } final String KEY = key(contact, size); Bitmap avatar = this.mXmppConnectionService.getBitmapCache().get(KEY); if (avatar != null || cachedOnly) { 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/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 8c691102..c69dd2db 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -93,6 +93,7 @@ import eu.siacs.conversations.parser.MessageParser; import eu.siacs.conversations.parser.PresenceParser; import eu.siacs.conversations.persistance.DatabaseBackend; import eu.siacs.conversations.persistance.FileBackend; +import eu.siacs.conversations.ui.SettingsActivity; import eu.siacs.conversations.ui.UiCallback; import eu.siacs.conversations.utils.ConversationsFileObserver; import eu.siacs.conversations.utils.CryptoHelper; @@ -567,7 +568,7 @@ public class XmppConnectionService extends Service { } break; case ACTION_DISABLE_FOREGROUND: - getPreferences().edit().putBoolean("keep_foreground_service", false).commit(); + getPreferences().edit().putBoolean(SettingsActivity.KEEP_FOREGROUND_SERVICE, false).commit(); toggleForegroundService(); break; case ACTION_DISMISS_ERROR_NOTIFICATIONS: @@ -767,15 +768,15 @@ public class XmppConnectionService extends Service { } private boolean manuallyChangePresence() { - return getPreferences().getBoolean("manually_change_presence", false); + return getPreferences().getBoolean(SettingsActivity.MANUALLY_CHANGE_PRESENCE, false); } private boolean treatVibrateAsSilent() { - return getPreferences().getBoolean("treat_vibrate_as_silent", false); + return getPreferences().getBoolean(SettingsActivity.TREAT_VIBRATE_AS_SILENT, false); } private boolean awayWhenScreenOff() { - return getPreferences().getBoolean("away_when_screen_off", false); + return getPreferences().getBoolean(SettingsActivity.AWAY_WHEN_SCREEN_IS_OFF, false); } private String getCompressPicturesPreference() { @@ -874,7 +875,7 @@ public class XmppConnectionService extends Service { this.accounts = databaseBackend.getAccounts(); if (!keepForegroundService() && databaseBackend.startTimeCountExceedsThreshold()) { - getPreferences().edit().putBoolean("keep_foreground_service",true).commit(); + getPreferences().edit().putBoolean(SettingsActivity.KEEP_FOREGROUND_SERVICE,true).commit(); Log.d(Config.LOGTAG,"number of restarts exceeds threshold. enabling foreground service"); } @@ -963,7 +964,7 @@ public class XmppConnectionService extends Service { } private boolean keepForegroundService() { - return getPreferences().getBoolean("keep_foreground_service",false); + return getPreferences().getBoolean(SettingsActivity.KEEP_FOREGROUND_SERVICE,false); } @Override @@ -978,7 +979,7 @@ public class XmppConnectionService extends Service { private void logoutAndSave(boolean stop) { int activeAccounts = 0; - databaseBackend.clearStartTimeCounter(); // regular swipes don't count towards restart counter + databaseBackend.clearStartTimeCounter(true); // regular swipes don't count towards restart counter for (final Account account : accounts) { if (account.getStatus() != Account.State.DISABLED) { activeAccounts++; @@ -3611,22 +3612,25 @@ public class XmppConnectionService extends Service { mDatabaseExecutor.execute(new Runnable() { @Override public void run() { - databaseBackend.clearStartTimeCounter(); + databaseBackend.clearStartTimeCounter(false); } }); } - public void verifyFingerprints(Contact contact, List<XmppUri.Fingerprint> fingerprints) { + public boolean verifyFingerprints(Contact contact, List<XmppUri.Fingerprint> fingerprints) { boolean needsRosterWrite = false; + boolean performedVerification = false; final AxolotlService axolotlService = contact.getAccount().getAxolotlService(); for(XmppUri.Fingerprint fp : fingerprints) { if (fp.type == XmppUri.FingerprintType.OTR) { - needsRosterWrite |= contact.addOtrFingerprint(fp.fingerprint); + performedVerification |= contact.addOtrFingerprint(fp.fingerprint); + needsRosterWrite |= performedVerification; } else if (fp.type == XmppUri.FingerprintType.OMEMO) { String fingerprint = "05"+fp.fingerprint.replaceAll("\\s",""); FingerprintStatus fingerprintStatus = axolotlService.getFingerprintTrust(fingerprint); if (fingerprintStatus != null) { if (!fingerprintStatus.isVerified()) { + performedVerification = true; axolotlService.setFingerprintTrust(fingerprint,fingerprintStatus.toVerified()); } } else { @@ -3637,6 +3641,7 @@ public class XmppConnectionService extends Service { if (needsRosterWrite) { syncRosterToDisk(contact.getAccount()); } + return performedVerification; } public boolean verifyFingerprints(Account account, List<XmppUri.Fingerprint> fingerprints) { 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/SettingsActivity.java b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java index 7fcb1e3c..e3e05d7d 100644 --- a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java @@ -35,6 +35,11 @@ import eu.siacs.conversations.xmpp.jid.Jid; public class SettingsActivity extends XmppActivity implements OnSharedPreferenceChangeListener { + public static final String KEEP_FOREGROUND_SERVICE = "keep_foreground_service"; + public static final String AWAY_WHEN_SCREEN_IS_OFF = "away_when_screen_off"; + public static final String TREAT_VIBRATE_AS_SILENT = "treat_vibrate_as_silent"; + public static final String MANUALLY_CHANGE_PRESENCE = "manually_change_presence"; + public static final int REQUEST_WRITE_LOGS = 0xbf8701; private SettingsFragment mSettingsFragment; @@ -227,10 +232,10 @@ public class SettingsActivity extends XmppActivity implements final List<String> resendPresence = Arrays.asList( "confirm_messages", "xa_on_silent_mode", - "away_when_screen_off", + AWAY_WHEN_SCREEN_IS_OFF, "allow_message_correction", - "treat_vibrate_as_silent", - "manually_change_presence", + TREAT_VIBRATE_AS_SILENT, + MANUALLY_CHANGE_PRESENCE, "last_activity"); if (name.equals("resource")) { String resource = preferences.getString("resource", "mobile") @@ -248,19 +253,18 @@ public class SettingsActivity extends XmppActivity implements } } } - } else if (name.equals("keep_foreground_service")) { - boolean foreground_service = preferences.getBoolean("keep_foreground_service",false); + } else if (name.equals(KEEP_FOREGROUND_SERVICE)) { + boolean foreground_service = preferences.getBoolean(KEEP_FOREGROUND_SERVICE,false); if (!foreground_service) { xmppConnectionService.clearStartTimeCounter(); } xmppConnectionService.toggleForegroundService(); } else if (resendPresence.contains(name)) { if (xmppConnectionServiceBound) { - if (name.equals("away_when_screen_off") - || name.equals("manually_change_presence")) { + if (name.equals(AWAY_WHEN_SCREEN_IS_OFF) || name.equals(MANUALLY_CHANGE_PRESENCE)) { xmppConnectionService.toggleScreenEventReceiver(); } - if (name.equals("manually_change_presence") && !noAccountUsesPgp()) { + if (name.equals(MANUALLY_CHANGE_PRESENCE) && !noAccountUsesPgp()) { Toast.makeText(this, R.string.republish_pgp_keys, Toast.LENGTH_LONG).show(); } xmppConnectionService.refreshAllPresences(); diff --git a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java index a55863fd..c9b5b9cc 100644 --- a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java @@ -1056,10 +1056,14 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU activity.conference_context_id = acmi.position; } else if (mResContextMenu == R.menu.contact_context) { activity.contact_context_id = acmi.position; - final Blockable contact = (Contact) activity.contacts.get(acmi.position); + final Contact contact = (Contact) activity.contacts.get(acmi.position); final MenuItem blockUnblockItem = menu.findItem(R.id.context_contact_block_unblock); + final MenuItem showContactDetailsItem = menu.findItem(R.id.context_contact_details); + if (contact.isSelf()) { + showContactDetailsItem.setVisible(false); + } XmppConnection xmpp = contact.getAccount().getXmppConnection(); - if (xmpp != null && xmpp.getFeatures().blocking()) { + if (xmpp != null && xmpp.getFeatures().blocking() && !contact.isSelf()) { if (contact.isBlocked()) { blockUnblockItem.setTitle(R.string.unblock_contact); } else { diff --git a/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java b/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java index 26836395..9af0d075 100644 --- a/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java @@ -1,7 +1,12 @@ package eu.siacs.conversations.ui; +import android.app.ActionBar; import android.content.Intent; import android.os.Bundle; +import android.util.Log; +import android.view.Gravity; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; @@ -10,6 +15,9 @@ import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; +import com.google.zxing.integration.android.IntentIntegrator; +import com.google.zxing.integration.android.IntentResult; + import org.whispersystems.libaxolotl.IdentityKey; import java.util.ArrayList; @@ -23,9 +31,9 @@ import eu.siacs.conversations.OmemoActivity; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.crypto.axolotl.FingerprintStatus; -import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Conversation; +import eu.siacs.conversations.utils.XmppUri; import eu.siacs.conversations.xmpp.OnKeyStatusUpdated; import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.Jid; @@ -64,6 +72,7 @@ public class TrustKeysActivity extends OmemoActivity implements OnKeyStatusUpdat finish(); } }; + private XmppUri mPendingFingerprintVerificationUri = null; @Override protected void refreshUiReal() { @@ -102,6 +111,64 @@ public class TrustKeysActivity extends OmemoActivity implements OnKeyStatusUpdat } } + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.trust_keys, menu); + Toast toast = Toast.makeText(this,R.string.use_camera_icon_to_scan_barcode,Toast.LENGTH_LONG); + ActionBar actionBar = getActionBar(); + toast.setGravity(Gravity.TOP | Gravity.END, 0 ,actionBar == null ? 0 : actionBar.getHeight()); + toast.show(); + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_scan_qr_code: + if (hasPendingKeyFetches()) { + Toast.makeText(this, R.string.please_wait_for_keys_to_be_fetched, Toast.LENGTH_SHORT).show(); + } else { + new IntentIntegrator(this).initiateScan(); + return true; + } + } + return super.onOptionsItemSelected(item); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent intent) { + IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent); + if (scanResult != null && scanResult.getFormatName() != null) { + String data = scanResult.getContents(); + XmppUri uri = new XmppUri(data); + if (xmppConnectionServiceBound) { + processFingerprintVerification(uri); + populateView(); + } else { + this.mPendingFingerprintVerificationUri =uri; + } + } + } + + private void processFingerprintVerification(XmppUri uri) { + if (mConversation != null + && mAccount != null + && uri.hasFingerprints() + && mAccount.getAxolotlService().getCryptoTargets(mConversation).contains(uri.getJid())) { + boolean performedVerification = xmppConnectionService.verifyFingerprints(mAccount.getRoster().getContact(uri.getJid()),uri.getFingerprints()); + boolean keys = reloadFingerprints(); + if (performedVerification && !keys && !hasNoOtherTrustedKeys() && !hasPendingKeyFetches()) { + Toast.makeText(this,R.string.all_omemo_keys_have_been_verified, Toast.LENGTH_SHORT).show(); + finishOk(); + } else if (performedVerification) { + Toast.makeText(this,R.string.verified_fingerprints,Toast.LENGTH_SHORT).show(); + } + } else { + Log.d(Config.LOGTAG,"xmpp uri was: "+uri.getJid()+" has Fingerprints: "+Boolean.toString(uri.hasFingerprints())); + Toast.makeText(this,R.string.barcode_does_not_contain_fingerprints_for_this_conversation,Toast.LENGTH_SHORT).show(); + } + } + private void populateView() { setTitle(getString(R.string.trust_omemo_fingerprints)); ownKeys.removeAllViews(); @@ -216,6 +283,10 @@ public class TrustKeysActivity extends OmemoActivity implements OnKeyStatusUpdat if (this.mAccount != null && intent != null) { String uuid = intent.getStringExtra("conversation"); this.mConversation = xmppConnectionService.findConversationByUuid(uuid); + if (this.mPendingFingerprintVerificationUri != null) { + processFingerprintVerification(this.mPendingFingerprintVerificationUri); + this.mPendingFingerprintVerificationUri = null; + } reloadFingerprints(); populateView(); } diff --git a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java index d961497c..583fab78 100644 --- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java @@ -44,7 +44,6 @@ import android.os.SystemClock; import android.preference.PreferenceManager; import android.text.InputType; import android.util.DisplayMetrics; -import android.util.Log; import android.util.Pair; import android.view.MenuItem; import android.view.View; @@ -55,10 +54,8 @@ import android.widget.Toast; import com.google.zxing.BarcodeFormat; import com.google.zxing.EncodeHintType; -import com.google.zxing.WriterException; +import com.google.zxing.aztec.AztecWriter; import com.google.zxing.common.BitMatrix; -import com.google.zxing.qrcode.QRCodeWriter; -import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import net.java.otr4j.session.SessionID; @@ -81,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; @@ -994,7 +992,7 @@ public abstract class XmppActivity extends Activity { } protected boolean manuallyChangePresence() { - return getPreferences().getBoolean("manually_change_presence", false); + return getPreferences().getBoolean(SettingsActivity.MANUALLY_CHANGE_PRESENCE, false); } protected void unregisterNdefPushMessageCallback() { @@ -1061,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); @@ -1071,31 +1069,6 @@ public abstract class XmppActivity extends Activity { } } - protected Bitmap createQrCodeBitmap(String input, int size) { - Log.d(Config.LOGTAG,"qr code requested size: "+size); - try { - final QRCodeWriter QR_CODE_WRITER = new QRCodeWriter(); - final Hashtable<EncodeHintType, Object> hints = new Hashtable<>(); - hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M); - final BitMatrix result = QR_CODE_WRITER.encode(input, BarcodeFormat.QR_CODE, 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); - Log.d(Config.LOGTAG,"output size: "+width+"x"+height); - bitmap.setPixels(pixels, 0, width, 0, 0, width, height); - return bitmap; - } catch (final WriterException 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/drawable-hdpi/ic_action_camera.png b/src/main/res/drawable-hdpi/ic_action_camera.png Binary files differnew file mode 100644 index 00000000..60f9f100 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_action_camera.png diff --git a/src/main/res/drawable-hdpi/ic_camera_alt_white_24dp.png b/src/main/res/drawable-hdpi/ic_camera_alt_white_24dp.png Binary files differnew file mode 100644 index 00000000..497c88ca --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_camera_alt_white_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_action_camera.png b/src/main/res/drawable-mdpi/ic_action_camera.png Binary files differnew file mode 100644 index 00000000..75d6b8c2 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_action_camera.png diff --git a/src/main/res/drawable-mdpi/ic_camera_alt_white_24dp.png b/src/main/res/drawable-mdpi/ic_camera_alt_white_24dp.png Binary files differnew file mode 100644 index 00000000..e8305220 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_camera_alt_white_24dp.png diff --git a/src/main/res/drawable-xhdpi/ic_action_camera.png b/src/main/res/drawable-xhdpi/ic_action_camera.png Binary files differnew file mode 100644 index 00000000..31b4b221 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_action_camera.png diff --git a/src/main/res/drawable-xhdpi/ic_camera_alt_white_24dp.png b/src/main/res/drawable-xhdpi/ic_camera_alt_white_24dp.png Binary files differnew file mode 100644 index 00000000..be9fb226 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_camera_alt_white_24dp.png diff --git a/src/main/res/drawable-xxhdpi/ic_action_camera.png b/src/main/res/drawable-xxhdpi/ic_action_camera.png Binary files differnew file mode 100644 index 00000000..e7b99102 --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_action_camera.png diff --git a/src/main/res/drawable-xxhdpi/ic_camera_alt_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_camera_alt_white_24dp.png Binary files differnew file mode 100644 index 00000000..c8e69dce --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_camera_alt_white_24dp.png diff --git a/src/main/res/drawable-xxxhdpi/ic_camera_alt_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_camera_alt_white_24dp.png Binary files differnew file mode 100644 index 00000000..777658e9 --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_camera_alt_white_24dp.png 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/menu/trust_keys.xml b/src/main/res/menu/trust_keys.xml new file mode 100644 index 00000000..90304767 --- /dev/null +++ b/src/main/res/menu/trust_keys.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + + <item + android:id="@+id/action_scan_qr_code" + android:title="@string/scan_qr_code" + android:showAsAction="always" + android:icon="?attr/icon_scan_qr_code"/> +</menu>
\ No newline at end of file diff --git a/src/main/res/values-v21/themes.xml b/src/main/res/values-v21/themes.xml index d6612552..aa5e825b 100644 --- a/src/main/res/values-v21/themes.xml +++ b/src/main/res/values-v21/themes.xml @@ -60,6 +60,7 @@ <item name="attr/icon_settings">@drawable/ic_settings_black_24dp</item> <item name="attr/icon_import_export">@drawable/ic_import_export_white_24dp</item> <item name="attr/icon_share">@drawable/ic_share_white_24dp</item> + <item name="attr/icon_scan_qr_code">@drawable/ic_camera_alt_white_24dp</item> <item name="attr/icon_notifications">@drawable/ic_notifications_black54_24dp</item> <item name="attr/icon_notifications_off">@drawable/ic_notifications_off_black54_24dp</item> @@ -126,6 +127,7 @@ <item name="attr/icon_settings">@drawable/ic_settings_white_24dp</item> <item name="attr/icon_import_export">@drawable/ic_import_export_white_24dp</item> <item name="attr/icon_share">@drawable/ic_share_white_24dp</item> + <item name="attr/icon_scan_qr_code">@drawable/ic_camera_alt_white_24dp</item> <item name="attr/icon_notifications">@drawable/ic_notifications_white_24dp</item> <item name="attr/icon_notifications_off">@drawable/ic_notifications_off_white_24dp</item> diff --git a/src/main/res/values/attrs.xml b/src/main/res/values/attrs.xml index 82f9db89..7b7d2c5a 100644 --- a/src/main/res/values/attrs.xml +++ b/src/main/res/values/attrs.xml @@ -47,6 +47,7 @@ <attr name="icon_settings" format="reference"/> <attr name="icon_share" format="reference"/> <attr name="icon_import_export" format="reference"/> + <attr name="icon_scan_qr_code" format="reference"/> <attr name="icon_notifications" format="reference"/> <attr name="icon_notifications_off" format="reference"/> diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index e9a9643c..b67aa23f 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -371,8 +371,8 @@ <string name="url_copied_to_clipboard">URL copied to clipboard</string> <string name="message_copied_to_clipboard">Message copied to clipboard</string> <string name="image_transmission_failed">Image transmission failed</string> - <string name="scan_qr_code">Scan QR code</string> - <string name="show_qr_code">Show QR code</string> + <string name="scan_qr_code">Scan 2D Barcode</string> + <string name="show_qr_code">Show 2D Barcode</string> <string name="show_block_list">Show block list</string> <string name="account_details">Account details</string> <string name="verify_otr">Verify OTR</string> @@ -703,4 +703,11 @@ <string name="this_device_has_been_verified">This device has been verified</string> <string name="copy_fingerprint">Copy fingerprint</string> <string name="all_omemo_keys_have_been_verified">All OMEMO keys have been verified</string> + <string name="barcode_does_not_contain_fingerprints_for_this_conversation">Barcode does not contain fingerprints for this conversation.</string> + <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> diff --git a/src/main/res/values/themes.xml b/src/main/res/values/themes.xml index f15822c9..69d4d39f 100644 --- a/src/main/res/values/themes.xml +++ b/src/main/res/values/themes.xml @@ -57,6 +57,7 @@ <item name="attr/icon_settings">@drawable/ic_action_settings</item> <item name="attr/icon_import_export">@drawable/ic_stat_communication_import_export</item> <item name="attr/icon_share">@drawable/ic_action_share</item> + <item name="attr/icon_scan_qr_code">@drawable/ic_action_camera</item> <item name="attr/icon_notifications">@drawable/ic_notifications_black54_24dp</item> <item name="attr/icon_notifications_off">@drawable/ic_notifications_off_black54_24dp</item> @@ -120,6 +121,7 @@ <item name="attr/icon_settings">@drawable/ic_action_settings_white</item> <item name="attr/icon_import_export">@drawable/ic_stat_communication_import_export</item> <item name="attr/icon_share">@drawable/ic_action_share</item> + <item name="attr/icon_scan_qr_code">@drawable/ic_action_camera</item> <item name="attr/icon_notifications">@drawable/ic_notifications_white80</item> <item name="attr/icon_notifications_off">@drawable/ic_notifications_off_white80</item> |