diff --git a/app/src/full/java/org/fdroid/fdroid/nearby/SwapWorkflowActivity.java b/app/src/full/java/org/fdroid/fdroid/nearby/SwapWorkflowActivity.java index e05205d86..e41c15da4 100644 --- a/app/src/full/java/org/fdroid/fdroid/nearby/SwapWorkflowActivity.java +++ b/app/src/full/java/org/fdroid/fdroid/nearby/SwapWorkflowActivity.java @@ -34,7 +34,18 @@ import android.widget.ProgressBar; import android.widget.TextView; 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.switchmaterial.SwitchMaterial; import com.google.zxing.integration.android.IntentIntegrator; 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.HttpDownloader; import org.fdroid.fdroid.qr.CameraCharacteristicsChecker; -import org.fdroid.fdroid.qr.QrGenAsyncTask; import org.fdroid.fdroid.views.main.MainActivity; import java.util.Date; @@ -65,17 +75,8 @@ import java.util.Set; import java.util.Timer; 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 io.reactivex.rxjava3.disposables.CompositeDisposable; import static org.fdroid.fdroid.views.main.MainActivity.ACTION_REQUEST_SWAP; @@ -118,6 +119,8 @@ public class SwapWorkflowActivity extends AppCompatActivity { @LayoutRes private int currentSwapViewLayoutRes = STEP_INTRO; + private final CompositeDisposable compositeDisposable = new CompositeDisposable(); + public static void requestSwap(Context context, String repo) { requestSwap(context, Uri.parse(repo)); } @@ -235,6 +238,7 @@ public class SwapWorkflowActivity extends AppCompatActivity { @Override protected void onDestroy() { + compositeDisposable.dispose(); localBroadcastManager.unregisterReceiver(downloaderInterruptedReceiver); unbindService(serviceConnection); super.onDestroy(); @@ -929,18 +933,23 @@ public class SwapWorkflowActivity extends AppCompatActivity { ImageView qrImage = container.findViewById(R.id.wifi_qr_code); if (qrUriString != null && qrImage != null) { 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. - qrImage.setColorFilter(new LightingColorFilter(0xffffffff, ContextCompat.getColor(this, - R.color.swap_blue))); + compositeDisposable.add(Utils.generateQrBitmap(this, qrUriString) + .subscribe(qrBitmap -> { + qrImage.setImageBitmap(qrBitmap); - final View qrWarningMessage = container.findViewById(R.id.warning_qr_scanner); - if (CameraCharacteristicsChecker.getInstance(this).hasAutofocus()) { - qrWarningMessage.setVisibility(View.GONE); - } else { - qrWarningMessage.setVisibility(View.VISIBLE); - } + // Replace all blacks with the background blue. + qrImage.setColorFilter(new LightingColorFilter(0xffffffff, + ContextCompat.getColor(this, R.color.swap_blue))); + + final View qrWarningMessage = container.findViewById(R.id.warning_qr_scanner); + if (CameraCharacteristicsChecker.getInstance(this).hasAutofocus()) { + qrWarningMessage.setVisibility(View.GONE); + } else { + qrWarningMessage.setVisibility(View.VISIBLE); + } + }) + ); } } diff --git a/app/src/main/java/org/fdroid/fdroid/Utils.java b/app/src/main/java/org/fdroid/fdroid/Utils.java index 142e027bd..6cbb533dd 100644 --- a/app/src/main/java/org/fdroid/fdroid/Utils.java +++ b/app/src/main/java/org/fdroid/fdroid/Utils.java @@ -27,6 +27,7 @@ import android.content.pm.Signature; import android.content.res.Resources; import android.database.Cursor; import android.graphics.Bitmap; +import android.graphics.Point; import android.graphics.Rect; import android.net.Uri; import android.os.Build; @@ -44,11 +45,21 @@ import android.text.style.TypefaceSpan; import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; +import android.view.Display; import android.view.View; import android.view.ViewTreeObserver; import android.widget.ImageView; 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.ImageLoader; import com.nostra13.universalimageloader.core.assist.ImageScaleType; @@ -92,10 +103,9 @@ import java.util.TimeZone; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.schedulers.Schedulers; public final class Utils { @@ -986,6 +996,27 @@ public final class Utils { } } + @NonNull + public static Single 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 * screen keyboard is currently visible or not. diff --git a/app/src/main/java/org/fdroid/fdroid/qr/QrGenAsyncTask.java b/app/src/main/java/org/fdroid/fdroid/qr/QrGenAsyncTask.java deleted file mode 100644 index 98cb414ab..000000000 --- a/app/src/main/java/org/fdroid/fdroid/qr/QrGenAsyncTask.java +++ /dev/null @@ -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 { - 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); - } - } -} diff --git a/app/src/main/java/org/fdroid/fdroid/views/RepoDetailsActivity.java b/app/src/main/java/org/fdroid/fdroid/views/RepoDetailsActivity.java index a86d8ff21..5657ba761 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/RepoDetailsActivity.java +++ b/app/src/main/java/org/fdroid/fdroid/views/RepoDetailsActivity.java @@ -23,9 +23,19 @@ import android.view.ViewGroup; import android.widget.Button; import android.widget.CompoundButton; import android.widget.EditText; +import android.widget.ImageView; import android.widget.TextView; 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.textfield.TextInputLayout; @@ -38,20 +48,12 @@ import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.data.Repo; import org.fdroid.fdroid.data.RepoProvider; import org.fdroid.fdroid.data.Schema.RepoTable; -import org.fdroid.fdroid.qr.QrGenAsyncTask; import java.util.Arrays; import java.util.HashSet; import java.util.Locale; -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 io.reactivex.rxjava3.disposables.Disposable; public class RepoDetailsActivity extends AppCompatActivity { private static final String TAG = "RepoDetailsActivity"; @@ -91,6 +93,8 @@ public class RepoDetailsActivity extends AppCompatActivity { private MirrorAdapter adapterToNotify; + private Disposable disposable; + /** * Help function to make switching between two view states easier. * 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.buildUpon().appendQueryParameter("fingerprint", repo.fingerprint).build(); 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)