Generate a QR bitmap using RxJava instead of AsyncTask.

This commit is contained in:
Isira Seneviratne 2020-10-24 11:50:50 +05:30 committed by Hans-Christoph Steiner
parent 93a160b40d
commit e1ca1552f7
4 changed files with 91 additions and 109 deletions

View File

@ -34,7 +34,18 @@ import android.widget.ProgressBar;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.SearchView;
import androidx.core.content.ContextCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.google.android.material.appbar.MaterialToolbar; import com.google.android.material.appbar.MaterialToolbar;
import com.google.android.material.switchmaterial.SwitchMaterial;
import com.google.zxing.integration.android.IntentIntegrator; import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult; import com.google.zxing.integration.android.IntentResult;
@ -54,7 +65,6 @@ import org.fdroid.fdroid.net.BluetoothDownloader;
import org.fdroid.fdroid.net.Downloader; import org.fdroid.fdroid.net.Downloader;
import org.fdroid.fdroid.net.HttpDownloader; import org.fdroid.fdroid.net.HttpDownloader;
import org.fdroid.fdroid.qr.CameraCharacteristicsChecker; import org.fdroid.fdroid.qr.CameraCharacteristicsChecker;
import org.fdroid.fdroid.qr.QrGenAsyncTask;
import org.fdroid.fdroid.views.main.MainActivity; import org.fdroid.fdroid.views.main.MainActivity;
import java.util.Date; import java.util.Date;
@ -65,17 +75,8 @@ import java.util.Set;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.SearchView;
import com.google.android.material.switchmaterial.SwitchMaterial;
import androidx.core.content.ContextCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import cc.mvdan.accesspoint.WifiApControl; import cc.mvdan.accesspoint.WifiApControl;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import static org.fdroid.fdroid.views.main.MainActivity.ACTION_REQUEST_SWAP; import static org.fdroid.fdroid.views.main.MainActivity.ACTION_REQUEST_SWAP;
@ -118,6 +119,8 @@ public class SwapWorkflowActivity extends AppCompatActivity {
@LayoutRes @LayoutRes
private int currentSwapViewLayoutRes = STEP_INTRO; private int currentSwapViewLayoutRes = STEP_INTRO;
private final CompositeDisposable compositeDisposable = new CompositeDisposable();
public static void requestSwap(Context context, String repo) { public static void requestSwap(Context context, String repo) {
requestSwap(context, Uri.parse(repo)); requestSwap(context, Uri.parse(repo));
} }
@ -235,6 +238,7 @@ public class SwapWorkflowActivity extends AppCompatActivity {
@Override @Override
protected void onDestroy() { protected void onDestroy() {
compositeDisposable.dispose();
localBroadcastManager.unregisterReceiver(downloaderInterruptedReceiver); localBroadcastManager.unregisterReceiver(downloaderInterruptedReceiver);
unbindService(serviceConnection); unbindService(serviceConnection);
super.onDestroy(); super.onDestroy();
@ -929,18 +933,23 @@ public class SwapWorkflowActivity extends AppCompatActivity {
ImageView qrImage = container.findViewById(R.id.wifi_qr_code); ImageView qrImage = container.findViewById(R.id.wifi_qr_code);
if (qrUriString != null && qrImage != null) { if (qrUriString != null && qrImage != null) {
Utils.debugLog(TAG, "Encoded swap URI in QR Code: " + qrUriString); Utils.debugLog(TAG, "Encoded swap URI in QR Code: " + qrUriString);
new QrGenAsyncTask(SwapWorkflowActivity.this, R.id.wifi_qr_code).execute(qrUriString);
// Replace all blacks with the background blue. compositeDisposable.add(Utils.generateQrBitmap(this, qrUriString)
qrImage.setColorFilter(new LightingColorFilter(0xffffffff, ContextCompat.getColor(this, .subscribe(qrBitmap -> {
R.color.swap_blue))); qrImage.setImageBitmap(qrBitmap);
final View qrWarningMessage = container.findViewById(R.id.warning_qr_scanner); // Replace all blacks with the background blue.
if (CameraCharacteristicsChecker.getInstance(this).hasAutofocus()) { qrImage.setColorFilter(new LightingColorFilter(0xffffffff,
qrWarningMessage.setVisibility(View.GONE); ContextCompat.getColor(this, R.color.swap_blue)));
} else {
qrWarningMessage.setVisibility(View.VISIBLE); final View qrWarningMessage = container.findViewById(R.id.warning_qr_scanner);
} if (CameraCharacteristicsChecker.getInstance(this).hasAutofocus()) {
qrWarningMessage.setVisibility(View.GONE);
} else {
qrWarningMessage.setVisibility(View.VISIBLE);
}
})
);
} }
} }

View File

@ -27,6 +27,7 @@ import android.content.pm.Signature;
import android.content.res.Resources; import android.content.res.Resources;
import android.database.Cursor; import android.database.Cursor;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Rect; import android.graphics.Rect;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
@ -44,11 +45,21 @@ import android.text.style.TypefaceSpan;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.Log; import android.util.Log;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.Display;
import android.view.View; import android.view.View;
import android.view.ViewTreeObserver; import android.view.ViewTreeObserver;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.encode.Contents;
import com.google.zxing.encode.QRCodeEncoder;
import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.ImageScaleType; import com.nostra13.universalimageloader.core.assist.ImageScaleType;
@ -92,10 +103,9 @@ import java.util.TimeZone;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import androidx.annotation.NonNull; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import androidx.annotation.Nullable; import io.reactivex.rxjava3.core.Single;
import androidx.annotation.RequiresApi; import io.reactivex.rxjava3.schedulers.Schedulers;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
public final class Utils { public final class Utils {
@ -986,6 +996,27 @@ public final class Utils {
} }
} }
@NonNull
public static Single<Bitmap> generateQrBitmap(@NonNull final AppCompatActivity activity,
@NonNull final String qrData) {
return Single.fromCallable(() -> {
Display display = activity.getWindowManager().getDefaultDisplay();
Point outSize = new Point();
display.getSize(outSize);
final int x = outSize.x;
final int y = outSize.y;
final int qrCodeDimension = Math.min(x, y);
debugLog(TAG, "generating QRCode Bitmap of " + qrCodeDimension + "x" + qrCodeDimension);
QRCodeEncoder qrCodeEncoder = new QRCodeEncoder(qrData, null,
Contents.Type.TEXT, BarcodeFormat.QR_CODE.toString(), qrCodeDimension);
return qrCodeEncoder.encodeAsBitmap();
})
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.doOnError(throwable -> Log.e(TAG, "Could not encode QR as bitmap", throwable));
}
/** /**
* Keep an instance of this class as an field in an AppCompatActivity for figuring out whether the on * Keep an instance of this class as an field in an AppCompatActivity for figuring out whether the on
* screen keyboard is currently visible or not. * screen keyboard is currently visible or not.

View File

@ -1,74 +0,0 @@
package org.fdroid.fdroid.qr;
import android.annotation.TargetApi;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.os.AsyncTask;
import android.util.Log;
import android.view.Display;
import android.widget.ImageView;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.WriterException;
import com.google.zxing.encode.Contents;
import com.google.zxing.encode.QRCodeEncoder;
import org.fdroid.fdroid.Utils;
import androidx.appcompat.app.AppCompatActivity;
public class QrGenAsyncTask extends AsyncTask<String, Void, Void> {
private static final String TAG = "QrGenAsyncTask";
private final AppCompatActivity activity;
private final int viewId;
private Bitmap qrBitmap;
public QrGenAsyncTask(AppCompatActivity activity, int viewId) {
this.activity = activity;
this.viewId = viewId;
}
/*
* The method for getting screen dimens changed, so this uses both the
* deprecated one and the 13+ one, and supports all Android versions.
*/
@SuppressWarnings("deprecation")
@TargetApi(13)
@Override
protected Void doInBackground(String... s) {
String qrData = s[0];
Display display = activity.getWindowManager().getDefaultDisplay();
Point outSize = new Point();
int x, y, qrCodeDimension;
display.getSize(outSize);
x = outSize.x;
y = outSize.y;
if (x < y) {
qrCodeDimension = x;
} else {
qrCodeDimension = y;
}
Utils.debugLog(TAG, "generating QRCode Bitmap of " + qrCodeDimension + "x" + qrCodeDimension);
QRCodeEncoder qrCodeEncoder = new QRCodeEncoder(qrData, null,
Contents.Type.TEXT, BarcodeFormat.QR_CODE.toString(), qrCodeDimension);
try {
qrBitmap = qrCodeEncoder.encodeAsBitmap();
} catch (WriterException e) {
Log.e(TAG, "Could not encode QR as bitmap", e);
}
return null;
}
@Override
protected void onPostExecute(Void v) {
ImageView qrCodeImageView = (ImageView) activity.findViewById(viewId);
// If the generation takes too long for whatever reason, then this view, and indeed the entire
// activity may not be around any more.
if (qrCodeImageView != null) {
qrCodeImageView.setImageBitmap(qrBitmap);
}
}
}

View File

@ -23,9 +23,19 @@ import android.view.ViewGroup;
import android.widget.Button; import android.widget.Button;
import android.widget.CompoundButton; import android.widget.CompoundButton;
import android.widget.EditText; import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.NavUtils;
import androidx.core.content.ContextCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.appbar.MaterialToolbar; import com.google.android.material.appbar.MaterialToolbar;
import com.google.android.material.textfield.TextInputLayout; import com.google.android.material.textfield.TextInputLayout;
@ -38,20 +48,12 @@ import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.data.Repo; import org.fdroid.fdroid.data.Repo;
import org.fdroid.fdroid.data.RepoProvider; import org.fdroid.fdroid.data.RepoProvider;
import org.fdroid.fdroid.data.Schema.RepoTable; import org.fdroid.fdroid.data.Schema.RepoTable;
import org.fdroid.fdroid.qr.QrGenAsyncTask;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.Locale; import java.util.Locale;
import androidx.annotation.NonNull; import io.reactivex.rxjava3.disposables.Disposable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.NavUtils;
import androidx.core.content.ContextCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
public class RepoDetailsActivity extends AppCompatActivity { public class RepoDetailsActivity extends AppCompatActivity {
private static final String TAG = "RepoDetailsActivity"; private static final String TAG = "RepoDetailsActivity";
@ -91,6 +93,8 @@ public class RepoDetailsActivity extends AppCompatActivity {
private MirrorAdapter adapterToNotify; private MirrorAdapter adapterToNotify;
private Disposable disposable;
/** /**
* Help function to make switching between two view states easier. * Help function to make switching between two view states easier.
* Perhaps there is a better way to do this. I recall that using Adobe * Perhaps there is a better way to do this. I recall that using Adobe
@ -141,7 +145,19 @@ public class RepoDetailsActivity extends AppCompatActivity {
Uri uri = Uri.parse(repo.address); Uri uri = Uri.parse(repo.address);
uri = uri.buildUpon().appendQueryParameter("fingerprint", repo.fingerprint).build(); uri = uri.buildUpon().appendQueryParameter("fingerprint", repo.fingerprint).build();
String qrUriString = uri.toString(); String qrUriString = uri.toString();
new QrGenAsyncTask(this, R.id.qr_code).execute(qrUriString); disposable = Utils.generateQrBitmap(this, qrUriString)
.subscribe(bitmap -> {
final ImageView qrCode = findViewById(R.id.qr_code);
if (qrCode != null) {
qrCode.setImageBitmap(bitmap);
}
});
}
@Override
protected void onDestroy() {
disposable.dispose();
super.onDestroy();
} }
@TargetApi(14) @TargetApi(14)