diff --git a/app/build.gradle b/app/build.gradle index 441e32e..27215aa 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -35,6 +35,7 @@ dependencies { compile ('com.mcxiaoke.viewpagerindicator:library:2.4.1') { exclude group: 'com.android.support', module: 'support-v4' } + compile 'com.google.zxing:core:3.1.0' compile 'com.google.zxing:android-integration:3.1.0' } @@ -45,6 +46,7 @@ dependencyVerification { 'com.android.support:appcompat-v7:45e999dda55fe81d9cc1c7342b7b70480ff3f307baa8da0df767f92fc5c52cd1', 'net.i2p.android.ext:floatingactionbutton:84cf5b67a66337bef59b46f57468c730387ca766b5a5f06ca852ba46cabbc0fb', 'com.mcxiaoke.viewpagerindicator:library:470bbd2bec1ede64ad21efd6f02676807d22f1b526c4ac6a5b41a428c6e47e67', + 'com.google.zxing:core:f00b32f7a1b0edc914a8f74301e8dc34f189afc4698e9c8cc54e5d46772734a5', 'com.google.zxing:android-integration:89e56aadf1164bd71e57949163c53abf90af368b51669c0d4a47a163335f95c4', ] } diff --git a/app/src/main/java/i2p/bote/android/Constants.java b/app/src/main/java/i2p/bote/android/Constants.java new file mode 100644 index 0000000..8b4fac5 --- /dev/null +++ b/app/src/main/java/i2p/bote/android/Constants.java @@ -0,0 +1,5 @@ +package i2p.bote.android; + +public class Constants { + public static final String EMAILDEST_SCHEME = "bote"; +} diff --git a/app/src/main/java/i2p/bote/android/config/ViewIdentityFragment.java b/app/src/main/java/i2p/bote/android/config/ViewIdentityFragment.java index e84f68b..e9a615c 100644 --- a/app/src/main/java/i2p/bote/android/config/ViewIdentityFragment.java +++ b/app/src/main/java/i2p/bote/android/config/ViewIdentityFragment.java @@ -8,6 +8,7 @@ import android.content.Intent; import android.graphics.Bitmap; import android.nfc.NdefMessage; import android.nfc.NdefRecord; +import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.support.v4.app.DialogFragment; @@ -20,7 +21,7 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.Button; +import android.view.animation.AlphaAnimation; import android.widget.ImageView; import android.widget.TextView; @@ -29,8 +30,10 @@ import com.google.zxing.integration.android.IntentIntegrator; import java.io.IOException; import java.security.GeneralSecurityException; +import i2p.bote.android.Constants; import i2p.bote.android.R; import i2p.bote.android.util.BoteHelper; +import i2p.bote.android.util.QrCodeUtils; import i2p.bote.email.EmailIdentity; import i2p.bote.fileencryption.PasswordException; @@ -47,8 +50,7 @@ public class ViewIdentityFragment extends Fragment { TextView mFingerprintField; TextView mCryptoField; TextView mKeyField; - - Button mGenQRCode; + ImageView mKeyQrCode; public static ViewIdentityFragment newInstance(String key) { ViewIdentityFragment f = new ViewIdentityFragment(); @@ -82,7 +84,7 @@ public class ViewIdentityFragment extends Fragment { mFingerprintField = (TextView) view.findViewById(R.id.fingerprint); mCryptoField = (TextView) view.findViewById(R.id.crypto_impl); mKeyField = (TextView) view.findViewById(R.id.key); - mGenQRCode = (Button) view.findViewById(R.id.generate_qr); + mKeyQrCode = (ImageView) view.findViewById(R.id.key_qr_code); if (mKey != null) { try { @@ -142,13 +144,15 @@ public class ViewIdentityFragment extends Fragment { mCryptoField.setText(mIdentity.getCryptoImpl().getName()); mKeyField.setText(mKey); - mGenQRCode.setOnClickListener(new View.OnClickListener() { + mKeyQrCode.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { IntentIntegrator i = new IntentIntegrator(getActivity()); i.shareText("bote:" + mKey); } }); + + loadQrCode(); } } @@ -241,4 +245,34 @@ public class ViewIdentityFragment extends Fragment { "i2p.bote", "contactDestination", mIdentity.getKey().getBytes() ); } + + /** + * Load QR Code asynchronously and with a fade in animation + */ + private void loadQrCode() { + AsyncTask loadTask = + new AsyncTask() { + protected Bitmap doInBackground(Void... unused) { + String qrCodeContent = Constants.EMAILDEST_SCHEME + ":" + mKey; + // render with minimal size + return QrCodeUtils.getQRCodeBitmap(qrCodeContent, 0); + } + protected void onPostExecute(Bitmap qrCode) { + // only change view, if fragment is attached to activity + if (ViewIdentityFragment.this.isAdded()) { + // scale the image up to our actual size. we do this in code rather + // than let the ImageView do this because we don't require filtering. + Bitmap scaled = Bitmap.createScaledBitmap(qrCode, + mKeyQrCode.getHeight(), mKeyQrCode.getHeight(), + false); + mKeyQrCode.setImageBitmap(scaled); + // simple fade-in animation + AlphaAnimation anim = new AlphaAnimation(0.0f, 1.0f); + anim.setDuration(200); + mKeyQrCode.startAnimation(anim); + } + } + }; + loadTask.execute(); + } } diff --git a/app/src/main/java/i2p/bote/android/util/QrCodeUtils.java b/app/src/main/java/i2p/bote/android/util/QrCodeUtils.java new file mode 100644 index 0000000..6a8c61a --- /dev/null +++ b/app/src/main/java/i2p/bote/android/util/QrCodeUtils.java @@ -0,0 +1,78 @@ +/** + * Copyright (C) 2014 str4d + * Copyright (C) 2013-2014 Dominik Schürmann + * Copyright (C) 2011 Andreas Schildbach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package i2p.bote.android.util; + +import android.graphics.Bitmap; +import android.graphics.Color; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.EncodeHintType; +import com.google.zxing.WriterException; +import com.google.zxing.common.BitMatrix; +import com.google.zxing.qrcode.QRCodeWriter; +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; + +import net.i2p.I2PAppContext; +import net.i2p.util.Log; + +import java.util.Hashtable; + +/** + * Copied from OpenKeychain + * Originally copied from Bitcoin Wallet + */ +public class QrCodeUtils { + + /** + * Generate Bitmap with QR Code based on input. + * + * @param input + * @param size + * @return QR Code as Bitmap + */ + public static Bitmap getQRCodeBitmap(final String input, final int size) { + try { + final Hashtable hints = new Hashtable(); + hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M); + final BitMatrix result = new QRCodeWriter().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.WHITE; + } + } + + final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + bitmap.setPixels(pixels, 0, width, 0, 0, width, height); + return bitmap; + } catch (final WriterException e) { + Log log = I2PAppContext.getGlobalContext().logManager().getLog(QrCodeUtils.class); + if (log.shouldLog(Log.ERROR)) + log.error("QrCodeUtils", e); + return null; + } + } +} diff --git a/app/src/main/res/layout/fragment_view_identity.xml b/app/src/main/res/layout/fragment_view_identity.xml index 900d2aa..0570c64 100644 --- a/app/src/main/res/layout/fragment_view_identity.xml +++ b/app/src/main/res/layout/fragment_view_identity.xml @@ -68,9 +68,10 @@ android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin"> - + android:layout_height="wrap_content" + android:orientation="vertical"> + + @@ -101,7 +107,6 @@ android:id="@+id/title_fingerprint" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_below="@id/key" android:layout_marginTop="8dp" android:text="Fingerprint" android:textAppearance="@style/TextAppearance.AppCompat.Subject" @@ -111,20 +116,11 @@ android:id="@+id/fingerprint" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_below="@+id/title_fingerprint" android:layout_marginTop="8dp" android:text="Fingerprint" android:textAppearance="@style/TextAppearance.AppCompat.Primary" android:visibility="gone"/> - -