From 5fad229dbeb9caf1bd12df85892346fc5faabf85 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sat, 24 Oct 2020 11:04:55 +0530 Subject: [PATCH 01/11] Update RxJava to version 3. --- app/build.gradle | 9 ++++++++- app/proguard-rules.pro | 7 ------- gradle.properties | 3 ++- gradle/verification-keyring.gpg | Bin 220017 -> 222274 bytes gradle/verification-metadata.xml | 10 +++++++++- 5 files changed, 19 insertions(+), 10 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 4d1be9151..224cf0e6c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -139,6 +139,11 @@ android { exclude 'META-INF/INDEX.LIST' exclude '.readme' } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } } dependencies { @@ -162,9 +167,11 @@ dependencies { implementation 'commons-io:commons-io:2.6' implementation 'commons-net:commons-net:3.6' implementation 'ch.acra:acra:4.9.1' - implementation 'io.reactivex:rxjava:1.1.0' implementation 'com.hannesdorfmann:adapterdelegates3:3.0.1' + implementation 'io.reactivex.rxjava3:rxandroid:3.0.0' + implementation 'io.reactivex.rxjava3:rxjava:3.0.7' + implementation 'com.fasterxml.jackson.core:jackson-core:2.11.1' implementation 'com.fasterxml.jackson.core:jackson-annotations:2.11.1' implementation 'com.fasterxml.jackson.core:jackson-databind:2.11.1' diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 045bbd72a..a00690fad 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -31,13 +31,6 @@ public *; } -# The rxjava library depends on sun.misc.Unsafe, which is unavailable on Android -# The rxjava team is aware of this, and mention in the docs that they only use -# the unsafe functionality if the platform supports it. -# - https://github.com/ReactiveX/RxJava/issues/1415#issuecomment-48390883 -# - https://github.com/ReactiveX/RxJava/blob/1.x/src/main/java/rx/internal/util/unsafe/UnsafeAccess.java#L23 --dontwarn rx.internal.util.** - -keepattributes *Annotation*,EnclosingMethod,Signature -keepnames class com.fasterxml.jackson.** { *; } -dontwarn com.fasterxml.jackson.databind.ext.** diff --git a/gradle.properties b/gradle.properties index 5465fec0e..3c43fbad5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,3 @@ android.enableJetifier=true -android.useAndroidX=true \ No newline at end of file +android.useAndroidX=true +org.gradle.jvmargs=-Xmx4096m \ No newline at end of file diff --git a/gradle/verification-keyring.gpg b/gradle/verification-keyring.gpg index 6349fbeee46b66c0d845e4fe5e1c1fc0b90a422d..809bc70201a76e8a7a87eaba3c6354724a7b1257 100644 GIT binary patch delta 2266 zcmV<02qpLNwhh9_4S<9JgaWh!aa(_x0u2ORRi94*5CGtO>#1H6aml^y)=a}Su6^dp zNv7Dvg zmE-}kxzBW|<(6Z%j}NJ zr5=wbhSLshoYA1)Fg-C82f=@h)yQt-W13&J?8~oHLmSAml$Y$-dB89Nll4*2I`Zj~ z@TQny$>aBFf$wAoe&_5i3piXJln@SgvxXL6d1*iGJ(T|It?aQT3+$0L944J+pf4v2 zCv$jo6m?aD(J^G}Y=04{e$l3*nh9CGpsZH3hy$?|zMR-QE4<9Hpn`w;lkHP|W4rf? zM={mI{eq>+*{QbBl@SI+_s_q4(&qO6tN}Cm1VAaux`?u`M|VGlE>kI(HY0{t@XR!7 zgmQvQyBEkQBjtpe@!)ry4tS3ylwp9NOcH(sD)!m=5{zjs+{5wUxi*m^VwDXv#bV~@ zSagqOVcK|E@S%3cIwE94?=yGlxA%vk^r~_#YZU+y0RRECC01!`bZ>GXOKf3oXCORw zX=`+EaxQCZVQyzYXKi6=Y%XJOZ9a(tP6QJH2mm-1Aq40;tGkuTH$xsELLfsRJLQi_ zN1X)%T~(h?0viL9A%+}(Lm)flk4Zd{0Inl>aTz{) zH9mvxEVSGLz(S-M>RvG9(eQquYsR|6>mkr*0@hzG?w?p1qIw#Q#C1ByTIbN>v`x^| z3sjB2Wnn(0m!z%4<%TFMz*xkK7|yeJeZA(QNZN8BB=PmkQNqfhHxFL29P*)Fdl_HP zkTNbENDk(YY@eWicx?RGL?t@W6A$vnvwx-pm^Ts8C{dbx-5ln+7u1(^p9hdCS!WgD zP9B94$=>PVV~d}R3N7q1ho|Al)qfR{Ra4h}#xn*`kg)~xS9*VWer*?uE2Nd|a@&8l z!;7LsC4=*=Q|xiYicNxWw-kdw>B!!wj784SI!A1E<|M*@30A?@mGhfgY?B{t^sMV= zJS))01%OSSc^bm|pn9TFWjc5)kpmY!P#ILYeH}*pS{S0|+rOy;3i%F8`FnzqM+GCc zDAz~@ffLI9$NUuh64$`!(_3b-M*z^Uy62y_c^msX_+{T)Xf-xdIIYT~(h?0T2Miv_YRqve``TA}Ffh*wllX za4PlzqqMou^T_GU#u4jKbg3k+YTqMJ7s}X?E|0}?5;5dqX)p+8YhOSr*Vb)hQ8A(r zFx9(ciEBrX!g318EiRc2`Quq1o_TiCj;lEjMyo4-R~u%m9#yb@_?%@XvSh~Y<9m1^ z+oSAOUitgZ2_i9Kg3r%cdyCxSM05zD&|=S~C0Q0zC#uwBI!|K>~3C{VUp z0Z@bTFhQylrZP7E5So`_F?WX{d=f5S`bM{Zzshp8TQK6=4V21JCUge)CF+HfKX*~U z<~Y78s?Ju-n5-yEn+r5j-o|_#5fYKjVp2y9>FoCg*XjY|zVr&2FH=nBzA;-}*+l-n zB!HbCr>akAkwSs2ZgG|hczy2mC1!p`a4Ycs6kiRSQVC$*UFJFVsAenT63rWUh* zoz|?DVRpP_T;9|y@yFDMBw$o@CMO4trwFP7R1CcU5di=Ji2^nR7y$?XAQmA6=sK&r zmC83m9w0&>Lm)flk4Zf>EQC>HX4SxJ2@ur(V2 z1%D|gvFm};S#i-ylVHsghb5LEoEJe=UcJ0Zf_pN?LSP*{0HwIXo1X_C<9)Y(Vl!@t z%HA>l-Kx%m(g`JxjEqg1`9qd9Y*8LFcuIn{ULON)bkuO6Zx){}Wyvk1Ug3ssv(&II zK}I`@pjl=g1=d1V7c28dv4Av%^%Eg4NZ}_Ut!l>)tm1#xKGnO6PB9E&&b3hdEYw@z z^xl*2^tBDAYVvr7Wop#JGcMs^@?adRNXFBVXVLNPtzI;?Q_faDRAK8aqA5L*s0N5< oIs$Ua{Mz5(#?2aP?j>GxjWgM|8%NvUieD0O4%uaFINH!?;;dCXNdN!< delta 15 WcmX?fg7@P#-i8*&7N#xCh0y>yg$7yx diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index c09689f64..9e93bb9f8 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -16,7 +16,10 @@ - + + + + @@ -2702,6 +2705,11 @@ + + + + + From 93a160b40df7fd353d3a6e95a047ffa069d7d426 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sat, 24 Oct 2020 11:19:54 +0530 Subject: [PATCH 02/11] Use RxJava 3 types in InstalledAppProviderService. --- .../data/InstalledAppProviderService.java | 67 ++++++++++--------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java b/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java index ffcbd2a11..715c0a40f 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java +++ b/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java @@ -14,6 +14,10 @@ import android.os.Process; import android.os.RemoteException; import android.util.Log; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.app.JobIntentService; + import org.acra.ACRA; import org.fdroid.fdroid.AppUpdateStatusManager; import org.fdroid.fdroid.Utils; @@ -28,12 +32,9 @@ import java.util.Map; import java.util.TreeSet; import java.util.concurrent.TimeUnit; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.app.JobIntentService; -import rx.functions.Action1; -import rx.schedulers.Schedulers; -import rx.subjects.PublishSubject; +import io.reactivex.rxjava3.disposables.CompositeDisposable; +import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.subjects.PublishSubject; /** * Handles all updates to {@link InstalledAppProvider}, whether checking the contents @@ -65,13 +66,15 @@ public class InstalledAppProviderService extends JobIntentService { private static final String EXTRA_PACKAGE_INFO = "org.fdroid.fdroid.data.extra.PACKAGE_INFO"; /** - * This is for notifing the users of this {@link android.content.ContentProvider} - * that the contents has changed. Since {@link Intent}s can come in slow + * This is for notifying the users of this {@link android.content.ContentProvider} + * that the contents have changed. Since {@link Intent}s can come in slow * or fast, and this can trigger a lot of UI updates, the actual * notifications are rate limited to one per second. */ private PublishSubject packageChangeNotifier; + private final CompositeDisposable compositeDisposable = new CompositeDisposable(); + @Override public void onCreate() { super.onCreate(); @@ -81,17 +84,16 @@ public class InstalledAppProviderService extends JobIntentService { // only emit an event to the subscriber after it has not received any new events for one second. // This ensures that we don't constantly ask our lists of apps to update as we iterate over // the list of installed apps and insert them to the database... - packageChangeNotifier - .subscribeOn(Schedulers.newThread()) - .debounce(3, TimeUnit.SECONDS) - .subscribe(new Action1() { - @Override - public void call(String packageName) { - Utils.debugLog(TAG, "Notifying content providers (so they can update the relevant views)."); - getContentResolver().notifyChange(AppProvider.getContentUri(), null); - getContentResolver().notifyChange(ApkProvider.getContentUri(), null); - } - }); + compositeDisposable.add( + packageChangeNotifier + .subscribeOn(Schedulers.newThread()) + .debounce(3, TimeUnit.SECONDS) + .subscribe(packageName -> { + Utils.debugLog(TAG, "Notifying content providers (so they can update the relevant views)."); + getContentResolver().notifyChange(AppProvider.getContentUri(), null); + getContentResolver().notifyChange(ApkProvider.getContentUri(), null); + }) + ); // ...alternatively, this non-debounced version will instantly emit an event about the // particular package being updated. This is required so that our AppDetails view can update @@ -100,14 +102,18 @@ public class InstalledAppProviderService extends JobIntentService { // only for changes to specific URIs in the AppProvider. These are triggered when a more // general notification (e.g. to AppProvider.getContentUri()) is fired, but not when a // sibling such as AppProvider.getHighestPriorityMetadataUri() is fired. - packageChangeNotifier.subscribeOn(Schedulers.newThread()) - .subscribe(new Action1() { - @Override - public void call(String packageName) { - getContentResolver() - .notifyChange(AppProvider.getHighestPriorityMetadataUri(packageName), null); - } - }); + compositeDisposable.add( + packageChangeNotifier + .subscribeOn(Schedulers.newThread()) + .subscribe(packageName -> getContentResolver() + .notifyChange(AppProvider.getHighestPriorityMetadataUri(packageName), null)) + ); + } + + @Override + public void onDestroy() { + compositeDisposable.dispose(); + super.onDestroy(); } /** @@ -243,12 +249,7 @@ public class InstalledAppProviderService extends JobIntentService { public static File getPathToInstalledApk(PackageInfo packageInfo) { File apk = new File(packageInfo.applicationInfo.publicSourceDir); if (apk.isDirectory()) { - FilenameFilter filter = new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - return name.endsWith(".apk"); - } - }; + FilenameFilter filter = (dir, name) -> name.endsWith(".apk"); File[] files = apk.listFiles(filter); if (files == null) { String msg = packageInfo.packageName + " sourceDir has no APKs: " + apk.getAbsolutePath(); From e1ca1552f72f275f32f0f7faa8c428df9ad94b95 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sat, 24 Oct 2020 11:50:50 +0530 Subject: [PATCH 03/11] Generate a QR bitmap using RxJava instead of AsyncTask. --- .../fdroid/nearby/SwapWorkflowActivity.java | 51 +++++++------ .../main/java/org/fdroid/fdroid/Utils.java | 39 +++++++++- .../org/fdroid/fdroid/qr/QrGenAsyncTask.java | 74 ------------------- .../fdroid/views/RepoDetailsActivity.java | 36 ++++++--- 4 files changed, 91 insertions(+), 109 deletions(-) delete mode 100644 app/src/main/java/org/fdroid/fdroid/qr/QrGenAsyncTask.java 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) From 242662d02a8a5069eb7f3592e7c7b10021f3754f Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sun, 25 Oct 2020 08:38:58 +0530 Subject: [PATCH 04/11] Create new repos using RxJava instead of AsyncTask. --- .../fdroid/views/ManageReposActivity.java | 236 ++++++++---------- 1 file changed, 104 insertions(+), 132 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/views/ManageReposActivity.java b/app/src/main/java/org/fdroid/fdroid/views/ManageReposActivity.java index 0036b224d..a57bcdac7 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/ManageReposActivity.java +++ b/app/src/main/java/org/fdroid/fdroid/views/ManageReposActivity.java @@ -19,7 +19,6 @@ package org.fdroid.fdroid.views; -import android.annotation.SuppressLint; import android.content.ClipData; import android.content.ClipboardManager; import android.content.ContentResolver; @@ -32,13 +31,13 @@ import android.database.Cursor; import android.net.Uri; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; -import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.util.Log; +import android.util.Pair; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; @@ -87,6 +86,12 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Locale; +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.disposables.CompositeDisposable; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.schedulers.Schedulers; + public class ManageReposActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks, RepoAdapter.EnabledListener { private static final String TAG = "ManageReposActivity"; @@ -107,6 +112,8 @@ public class ManageReposActivity extends AppCompatActivity */ private boolean finishAfterAddingRepo; + private final CompositeDisposable compositeDisposable = new CompositeDisposable(); + @Override protected void onCreate(Bundle savedInstanceState) { FDroidApp fdroidApp = (FDroidApp) getApplication(); @@ -154,6 +161,12 @@ public class ManageReposActivity extends AppCompatActivity }); } + @Override + protected void onDestroy() { + compositeDisposable.dispose(); + super.onDestroy(); + } + @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater menuInflater = getMenuInflater(); @@ -571,10 +584,8 @@ public class ManageReposActivity extends AppCompatActivity /** * Adds a new repo to the database. */ - @SuppressLint("StaticFieldLeak") private void prepareToCreateNewRepo(final String originalAddress, final String fingerprint, final String username, final String password) { - final View addRepoForm = addRepoDialog.findViewById(R.id.add_repo_form); addRepoForm.setVisibility(View.GONE); final View positiveButton = addRepoDialog.getButton(AlertDialog.BUTTON_POSITIVE); @@ -586,153 +597,114 @@ public class ManageReposActivity extends AppCompatActivity final Button skip = addRepoDialog.getButton(AlertDialog.BUTTON_NEGATIVE); skip.setText(R.string.skip); - final AsyncTask checker = new AsyncTask() { + final int REFRESH_DIALOG = Integer.MAX_VALUE; + final Disposable disposable = Single.fromCallable(() -> { + int statusCode = -1; - private int statusCode = -1; - private static final int REFRESH_DIALOG = Integer.MAX_VALUE; + if (fingerprintRepoMap.containsKey(fingerprint)) { + statusCode = REFRESH_DIALOG; + return Pair.create(statusCode, originalAddress); + } - @Override - protected String doInBackground(String... params) { - final String originalAddress = params[0]; + if (originalAddress.startsWith(ContentResolver.SCHEME_CONTENT) + || originalAddress.startsWith(ContentResolver.SCHEME_FILE)) { + // TODO check whether there is read access + return Pair.create(statusCode, originalAddress); + } - if (fingerprintRepoMap.containsKey(fingerprint)) { + final String[] pathsToCheck = {"", "fdroid/repo", "repo"}; + for (final String path : pathsToCheck) { + Utils.debugLog(TAG, "Check for repo at " + originalAddress + " with suffix '" + path + "'"); + Uri.Builder builder = Uri.parse(originalAddress).buildUpon().appendEncodedPath(path); + final String addressWithoutIndex = builder.build().toString(); + runOnUiThread(() -> textSearching.setText(getString(R.string.repo_searching_address, addressWithoutIndex))); + + if (urlRepoMap.containsKey(addressWithoutIndex)) { statusCode = REFRESH_DIALOG; - return originalAddress; + return Pair.create(statusCode, addressWithoutIndex); } - if (originalAddress.startsWith(ContentResolver.SCHEME_CONTENT) - || originalAddress.startsWith(ContentResolver.SCHEME_FILE)) { - // TODO check whether there is read access - return originalAddress; + final Uri uri = builder.appendPath(IndexUpdater.SIGNED_FILE_NAME).build(); + + try { + final URL url = new URL(uri.toString()); + final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("HEAD"); + + statusCode = connection.getResponseCode(); + + if (statusCode == HttpURLConnection.HTTP_UNAUTHORIZED || statusCode == HttpURLConnection.HTTP_OK) { + Utils.debugLog(TAG, "Found F-Droid repo at " + addressWithoutIndex); + return Pair.create(statusCode, addressWithoutIndex); + } + } catch (IOException e) { + Log.e(TAG, "Error while searching for repo at " + addressWithoutIndex, e); + return Pair.create(statusCode, originalAddress); } - - final String[] pathsToCheck = {"", "fdroid/repo", "repo"}; - for (final String path : pathsToCheck) { - - Utils.debugLog(TAG, "Check for repo at " + originalAddress + " with suffix '" + path + "'"); - Uri.Builder builder = Uri.parse(originalAddress).buildUpon().appendEncodedPath(path); - final String addressWithoutIndex = builder.build().toString(); - publishProgress(addressWithoutIndex); - - if (urlRepoMap.containsKey(addressWithoutIndex)) { - statusCode = REFRESH_DIALOG; - return addressWithoutIndex; - } - - final Uri uri = builder.appendPath(IndexUpdater.SIGNED_FILE_NAME).build(); - - try { - if (checkForRepository(uri)) { - Utils.debugLog(TAG, "Found F-Droid repo at " + addressWithoutIndex); - return addressWithoutIndex; - } - } catch (IOException e) { - Log.e(TAG, "Error while searching for repo at " + addressWithoutIndex, e); - return originalAddress; - } - - if (isCancelled()) { - Utils.debugLog(TAG, "Not checking more repo addresses, because process was skipped."); - break; - } - } - return originalAddress; - } + return Pair.create(statusCode, originalAddress); + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .doOnDispose(() -> Utils.debugLog(TAG, "Not checking more repo addresses, because process was skipped.")) + .subscribe(codeAddressPair -> { + final int statusCode = codeAddressPair.first; + final String newAddress = codeAddressPair.second; - private boolean checkForRepository(Uri indexUri) throws IOException { - final URL url = new URL(indexUri.toString()); - final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.setRequestMethod("HEAD"); + if (addRepoDialog.isShowing()) { + if (statusCode == HttpURLConnection.HTTP_UNAUTHORIZED) { + final View view = getLayoutInflater().inflate(R.layout.login, null); + final AlertDialog credentialsDialog = new AlertDialog.Builder(context) + .setView(view).create(); + final EditText nameInput = (EditText) view.findViewById(R.id.edit_name); + final EditText passwordInput = (EditText) view.findViewById(R.id.edit_password); - statusCode = connection.getResponseCode(); + if (username != null) { + nameInput.setText(username); + } + if (password != null) { + passwordInput.setText(password); + } - return statusCode == HttpURLConnection.HTTP_UNAUTHORIZED - || statusCode == HttpURLConnection.HTTP_OK; - } - - @Override - protected void onProgressUpdate(String... values) { - String address = values[0]; - textSearching.setText(getString(R.string.repo_searching_address, address)); - } - - @Override - protected void onPostExecute(final String newAddress) { - - if (addRepoDialog.isShowing()) { - - if (statusCode == HttpURLConnection.HTTP_UNAUTHORIZED) { - - final View view = getLayoutInflater().inflate(R.layout.login, null); - final AlertDialog credentialsDialog = new AlertDialog.Builder(context) - .setView(view).create(); - final EditText nameInput = (EditText) view.findViewById(R.id.edit_name); - final EditText passwordInput = (EditText) view.findViewById(R.id.edit_password); - - if (username != null) { - nameInput.setText(username); - } - if (password != null) { - passwordInput.setText(password); - } - - credentialsDialog.setTitle(R.string.login_title); - credentialsDialog.setButton(DialogInterface.BUTTON_NEGATIVE, - getString(R.string.cancel), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { + credentialsDialog.setTitle(R.string.login_title); + credentialsDialog.setButton(DialogInterface.BUTTON_NEGATIVE, + getString(R.string.cancel), (dialog, which) -> { dialog.dismiss(); // cancel parent dialog, don't add repo addRepoDialog.cancel(); - } - }); + }); - credentialsDialog.setButton(DialogInterface.BUTTON_POSITIVE, - getString(R.string.ok), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - createNewRepo(newAddress, fingerprint, - nameInput.getText().toString(), - passwordInput.getText().toString()); - } - }); + credentialsDialog.setButton(DialogInterface.BUTTON_POSITIVE, + getString(R.string.ok), + (dialog, which) -> createNewRepo(newAddress, fingerprint, + nameInput.getText().toString(), + passwordInput.getText().toString())); - credentialsDialog.show(); - - } else if (statusCode == REFRESH_DIALOG) { - addRepoForm.setVisibility(View.VISIBLE); - positiveButton.setVisibility(View.VISIBLE); - textSearching.setText(""); - skip.setText(R.string.cancel); - skip.setOnClickListener(null); - validateRepoDetails(newAddress, fingerprint); - } else { - - // create repo without username/password - createNewRepo(newAddress, fingerprint); + credentialsDialog.show(); + } else if (statusCode == REFRESH_DIALOG) { + addRepoForm.setVisibility(View.VISIBLE); + positiveButton.setVisibility(View.VISIBLE); + textSearching.setText(""); + skip.setText(R.string.cancel); + skip.setOnClickListener(null); + validateRepoDetails(newAddress, fingerprint); + } else { + // create repo without username/password + createNewRepo(newAddress, fingerprint); + } } - } - } - }; + }); + compositeDisposable.add(disposable); - skip.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - // Still proceed with adding the repo, just don't bother searching for - // a better alternative than the one provided. - // The reason for this is that if they are not connected to the internet, - // or their internet is playing up, then you'd have to wait for several - // connection timeouts before being able to proceed. - - createNewRepo(originalAddress, fingerprint); - checker.cancel(false); - } + skip.setOnClickListener(v -> { + // Still proceed with adding the repo, just don't bother searching for + // a better alternative than the one provided. + // The reason for this is that if they are not connected to the internet, + // or their internet is playing up, then you'd have to wait for several + // connection timeouts before being able to proceed. + createNewRepo(originalAddress, fingerprint); + disposable.dispose(); }); - - checker.execute(originalAddress); } /** From c0a699e21e3f67441a86c4d22afafad1f0d665df Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sun, 25 Oct 2020 14:58:57 +0530 Subject: [PATCH 05/11] Handle server swapping using RxJava instead of AsyncTask. --- .../org/fdroid/fdroid/nearby/SwapService.java | 104 +++++++++--------- 1 file changed, 55 insertions(+), 49 deletions(-) diff --git a/app/src/full/java/org/fdroid/fdroid/nearby/SwapService.java b/app/src/full/java/org/fdroid/fdroid/nearby/SwapService.java index 32a7e2a6c..25ecb4223 100644 --- a/app/src/full/java/org/fdroid/fdroid/nearby/SwapService.java +++ b/app/src/full/java/org/fdroid/fdroid/nearby/SwapService.java @@ -1,6 +1,5 @@ package org.fdroid.fdroid.nearby; -import android.annotation.SuppressLint; import android.app.Notification; import android.app.PendingIntent; import android.app.Service; @@ -13,11 +12,17 @@ import android.content.IntentFilter; import android.content.SharedPreferences; import android.net.Uri; import android.net.wifi.WifiManager; -import android.os.AsyncTask; import android.os.IBinder; import android.text.TextUtils; import android.util.Log; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.app.NotificationCompat; +import androidx.core.app.ServiceCompat; +import androidx.core.content.ContextCompat; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + import org.fdroid.fdroid.FDroidApp; import org.fdroid.fdroid.NotificationHelper; import org.fdroid.fdroid.Preferences; @@ -30,7 +35,6 @@ import org.fdroid.fdroid.data.Schema; import org.fdroid.fdroid.nearby.peers.Peer; import org.fdroid.fdroid.net.Downloader; -import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.net.HttpURLConnection; @@ -41,13 +45,11 @@ import java.util.Set; import java.util.Timer; import java.util.TimerTask; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.app.NotificationCompat; -import androidx.core.app.ServiceCompat; -import androidx.core.content.ContextCompat; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; import cc.mvdan.accesspoint.WifiApControl; +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Completable; +import io.reactivex.rxjava3.disposables.CompositeDisposable; +import io.reactivex.rxjava3.schedulers.Schedulers; /** * Central service which manages all of the different moving parts of swap which are required @@ -109,46 +111,6 @@ public class SwapService extends Service { UpdateService.updateRepoNow(this, peer.getRepoAddress()); } - @SuppressLint("StaticFieldLeak") - private void askServerToSwapWithUs(final Repo repo) { - new AsyncTask() { - @Override - protected Void doInBackground(Void... args) { - String swapBackUri = Utils.getLocalRepoUri(FDroidApp.repo).toString(); - HttpURLConnection conn = null; - try { - URL url = new URL(repo.address.replace("/fdroid/repo", "/request-swap")); - conn = (HttpURLConnection) url.openConnection(); - conn.setRequestMethod("POST"); - conn.setDoInput(true); - conn.setDoOutput(true); - - OutputStream outputStream = conn.getOutputStream(); - OutputStreamWriter writer = new OutputStreamWriter(outputStream); - writer.write("repo=" + swapBackUri); - writer.flush(); - writer.close(); - outputStream.close(); - - int responseCode = conn.getResponseCode(); - Utils.debugLog(TAG, "Asking server at " + repo.address + " to swap with us in return (by " + - "POSTing to \"/request-swap\" with repo \"" + swapBackUri + "\"): " + responseCode); - } catch (IOException e) { - Log.e(TAG, "Error while asking server to swap with us", e); - Intent intent = new Intent(Downloader.ACTION_INTERRUPTED); - intent.setData(Uri.parse(repo.address)); - intent.putExtra(Downloader.EXTRA_ERROR_MESSAGE, e.getLocalizedMessage()); - LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent); - } finally { - if (conn != null) { - conn.disconnect(); - } - } - return null; - } - }.execute(); - } - private Repo ensureRepoExists(@NonNull Peer peer) { // TODO: newRepoConfig.getParsedUri() will include a fingerprint, which may not match with // the repos address in the database. Not sure on best behaviour in this situation. @@ -342,12 +304,15 @@ public class SwapService extends Service { @Nullable private Timer timer; + private final CompositeDisposable compositeDisposable = new CompositeDisposable(); + public class Binder extends android.os.Binder { public SwapService getService() { return SwapService.this; } } + @Override public void onCreate() { super.onCreate(); startForeground(NOTIFICATION, createNotification()); @@ -397,6 +362,45 @@ public class SwapService extends Service { BonjourManager.setVisible(this, getWifiVisibleUserPreference() || getHotspotActivatedUserPreference()); } + private void askServerToSwapWithUs(final Repo repo) { + compositeDisposable.add( + Completable.fromAction(() -> { + String swapBackUri = Utils.getLocalRepoUri(FDroidApp.repo).toString(); + HttpURLConnection conn = null; + try { + URL url = new URL(repo.address.replace("/fdroid/repo", "/request-swap")); + conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("POST"); + conn.setDoInput(true); + conn.setDoOutput(true); + + try (OutputStream outputStream = conn.getOutputStream(); + OutputStreamWriter writer = new OutputStreamWriter(outputStream)) { + writer.write("repo=" + swapBackUri); + writer.flush(); + } + + int responseCode = conn.getResponseCode(); + Utils.debugLog(TAG, "Asking server at " + repo.address + " to swap with us in return (by " + + "POSTing to \"/request-swap\" with repo \"" + swapBackUri + "\"): " + responseCode); + } finally { + if (conn != null) { + conn.disconnect(); + } + } + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .doOnError(e -> { + Intent intent = new Intent(Downloader.ACTION_INTERRUPTED); + intent.setData(Uri.parse(repo.address)); + intent.putExtra(Downloader.EXTRA_ERROR_MESSAGE, e.getLocalizedMessage()); + LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent); + }) + .subscribe() + ); + } + /** * This is for setting things up for when the {@code SwapService} was * started by the user clicking on the initial start button. The things @@ -420,6 +424,8 @@ public class SwapService extends Service { @Override public void onDestroy() { + compositeDisposable.dispose(); + Utils.debugLog(TAG, "Destroying service, will disable swapping if required, and unregister listeners."); Preferences.get().unregisterLocalRepoHttpsListeners(httpsEnabledListener); localBroadcastManager.unregisterReceiver(onWifiChange); From eab5ef59b9f60dc24732ad978ac57a9f584a0bc2 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sun, 25 Oct 2020 15:17:36 +0530 Subject: [PATCH 06/11] Use RxJava instead of AsyncTask to handle updates. --- .../fdroid/nearby/WifiStateChangeService.java | 18 ++++-- .../java/org/fdroid/fdroid/UpdateService.java | 61 ++++++++----------- 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/app/src/full/java/org/fdroid/fdroid/nearby/WifiStateChangeService.java b/app/src/full/java/org/fdroid/fdroid/nearby/WifiStateChangeService.java index 2cbb14a2f..c3ddc3879 100644 --- a/app/src/full/java/org/fdroid/fdroid/nearby/WifiStateChangeService.java +++ b/app/src/full/java/org/fdroid/fdroid/nearby/WifiStateChangeService.java @@ -13,6 +13,10 @@ import android.os.Build; import android.text.TextUtils; import android.util.Log; +import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + import org.apache.commons.net.util.SubnetUtils; import org.fdroid.fdroid.BuildConfig; import org.fdroid.fdroid.FDroidApp; @@ -31,10 +35,8 @@ import java.security.cert.Certificate; import java.util.Enumeration; import java.util.Locale; -import androidx.annotation.Nullable; -import androidx.core.content.ContextCompat; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; import cc.mvdan.accesspoint.WifiApControl; +import io.reactivex.rxjava3.disposables.CompositeDisposable; /** * Handle state changes to the device's wifi, storing the required bits. @@ -70,6 +72,8 @@ public class WifiStateChangeService extends IntentService { private static int previousWifiState = Integer.MIN_VALUE; private static int wifiState; + private final CompositeDisposable compositeDisposable = new CompositeDisposable(); + public WifiStateChangeService() { super("WifiStateChangeService"); } @@ -82,6 +86,12 @@ public class WifiStateChangeService extends IntentService { context.startService(intent); } + @Override + public void onDestroy() { + compositeDisposable.dispose(); + super.onDestroy(); + } + @Override protected void onHandleIntent(Intent intent) { android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_LOWEST); @@ -109,7 +119,7 @@ public class WifiStateChangeService extends IntentService { } if (Build.VERSION.SDK_INT < 21 && wifiState == WifiManager.WIFI_STATE_ENABLED) { - UpdateService.scheduleIfStillOnWifi(this); + compositeDisposable.add(UpdateService.scheduleIfStillOnWifi(this).subscribe()); } } } diff --git a/app/src/main/java/org/fdroid/fdroid/UpdateService.java b/app/src/main/java/org/fdroid/fdroid/UpdateService.java index 1d0631289..78a2440b2 100644 --- a/app/src/main/java/org/fdroid/fdroid/UpdateService.java +++ b/app/src/main/java/org/fdroid/fdroid/UpdateService.java @@ -30,7 +30,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; -import android.os.AsyncTask; import android.os.Build; import android.os.Process; import android.os.SystemClock; @@ -38,6 +37,12 @@ import android.text.TextUtils; import android.util.Log; import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.core.app.JobIntentService; +import androidx.core.app.NotificationCompat; +import androidx.core.content.ContextCompat; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.ApkProvider; import org.fdroid.fdroid.data.App; @@ -51,15 +56,13 @@ import org.fdroid.fdroid.installer.InstallManagerService; import org.fdroid.fdroid.net.BluetoothDownloader; import org.fdroid.fdroid.net.ConnectivityMonitorService; -import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.TimeUnit; -import androidx.annotation.NonNull; -import androidx.core.app.JobIntentService; -import androidx.core.app.NotificationCompat; -import androidx.core.content.ContextCompat; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Completable; +import io.reactivex.rxjava3.schedulers.Schedulers; public class UpdateService extends JobIntentService { @@ -215,42 +218,30 @@ public class UpdateService extends JobIntentService { * unlimited networks over metered networks for index updates and auto * downloads of app updates. Starting with {@code android-21}, this uses * {@link android.app.job.JobScheduler} instead. + * + * @return a {@link Completable} that schedules the update. If this process is already running, + * a {@code Completable} that completes immediately is returned. */ - public static void scheduleIfStillOnWifi(Context context) { + @NonNull + public static Completable scheduleIfStillOnWifi(Context context) { if (Build.VERSION.SDK_INT >= 21) { throw new IllegalStateException("This should never be used on android-21 or newer!"); } if (isScheduleIfStillOnWifiRunning || !Preferences.get().isBackgroundDownloadAllowed()) { - return; + return Completable.complete(); } isScheduleIfStillOnWifiRunning = true; - new StillOnWifiAsyncTask(context).execute(); - } - - private static final class StillOnWifiAsyncTask extends AsyncTask { - - private final WeakReference contextWeakReference; - - private StillOnWifiAsyncTask(Context context) { - this.contextWeakReference = new WeakReference<>(context); - } - - @Override - protected Void doInBackground(Void... voids) { - Context context = contextWeakReference.get(); - try { - Thread.sleep(120000); - if (Preferences.get().isBackgroundDownloadAllowed()) { - Utils.debugLog(TAG, "scheduling update because there is good internet"); - schedule(context); - } - } catch (Throwable e) { // NOPMD - Utils.debugLog(TAG, e.getMessage()); - } - isScheduleIfStillOnWifiRunning = false; - return null; - } + return Completable.timer(2, TimeUnit.MINUTES) + .andThen(Completable.fromAction(() -> { + if (Preferences.get().isBackgroundDownloadAllowed()) { + Utils.debugLog(TAG, "scheduling update because there is good internet"); + schedule(context); + } + isScheduleIfStillOnWifiRunning = false; + })) + .subscribeOn(Schedulers.computation()) + .observeOn(AndroidSchedulers.mainThread()); } public static void stopNow(Context context) { From c758cb60d968a811552c97ea394f2d7cdc9f1856 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Tue, 15 Jun 2021 08:17:21 +0530 Subject: [PATCH 07/11] Fix checkstyle issues. --- .../data/InstalledAppProviderService.java | 3 ++- .../fdroid/views/ManageReposActivity.java | 17 ++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java b/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java index 715c0a40f..5adfa2ea8 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java +++ b/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java @@ -89,7 +89,8 @@ public class InstalledAppProviderService extends JobIntentService { .subscribeOn(Schedulers.newThread()) .debounce(3, TimeUnit.SECONDS) .subscribe(packageName -> { - Utils.debugLog(TAG, "Notifying content providers (so they can update the relevant views)."); + Utils.debugLog(TAG, "Notifying content providers (so they can update" + + " the relevant views)."); getContentResolver().notifyChange(AppProvider.getContentUri(), null); getContentResolver().notifyChange(ApkProvider.getContentUri(), null); }) diff --git a/app/src/main/java/org/fdroid/fdroid/views/ManageReposActivity.java b/app/src/main/java/org/fdroid/fdroid/views/ManageReposActivity.java index a57bcdac7..6d922e6e4 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/ManageReposActivity.java +++ b/app/src/main/java/org/fdroid/fdroid/views/ManageReposActivity.java @@ -597,12 +597,12 @@ public class ManageReposActivity extends AppCompatActivity final Button skip = addRepoDialog.getButton(AlertDialog.BUTTON_NEGATIVE); skip.setText(R.string.skip); - final int REFRESH_DIALOG = Integer.MAX_VALUE; + final int refreshDialog = Integer.MAX_VALUE; final Disposable disposable = Single.fromCallable(() -> { int statusCode = -1; if (fingerprintRepoMap.containsKey(fingerprint)) { - statusCode = REFRESH_DIALOG; + statusCode = refreshDialog; return Pair.create(statusCode, originalAddress); } @@ -617,10 +617,11 @@ public class ManageReposActivity extends AppCompatActivity Utils.debugLog(TAG, "Check for repo at " + originalAddress + " with suffix '" + path + "'"); Uri.Builder builder = Uri.parse(originalAddress).buildUpon().appendEncodedPath(path); final String addressWithoutIndex = builder.build().toString(); - runOnUiThread(() -> textSearching.setText(getString(R.string.repo_searching_address, addressWithoutIndex))); + runOnUiThread(() -> textSearching.setText(getString(R.string.repo_searching_address, + addressWithoutIndex))); if (urlRepoMap.containsKey(addressWithoutIndex)) { - statusCode = REFRESH_DIALOG; + statusCode = refreshDialog; return Pair.create(statusCode, addressWithoutIndex); } @@ -633,7 +634,8 @@ public class ManageReposActivity extends AppCompatActivity statusCode = connection.getResponseCode(); - if (statusCode == HttpURLConnection.HTTP_UNAUTHORIZED || statusCode == HttpURLConnection.HTTP_OK) { + if (statusCode == HttpURLConnection.HTTP_UNAUTHORIZED + || statusCode == HttpURLConnection.HTTP_OK) { Utils.debugLog(TAG, "Found F-Droid repo at " + addressWithoutIndex); return Pair.create(statusCode, addressWithoutIndex); } @@ -646,7 +648,8 @@ public class ManageReposActivity extends AppCompatActivity }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .doOnDispose(() -> Utils.debugLog(TAG, "Not checking more repo addresses, because process was skipped.")) + .doOnDispose(() -> Utils.debugLog(TAG, "Not checking more repo addresses," + + " because process was skipped.")) .subscribe(codeAddressPair -> { final int statusCode = codeAddressPair.first; final String newAddress = codeAddressPair.second; @@ -681,7 +684,7 @@ public class ManageReposActivity extends AppCompatActivity passwordInput.getText().toString())); credentialsDialog.show(); - } else if (statusCode == REFRESH_DIALOG) { + } else if (statusCode == refreshDialog) { addRepoForm.setVisibility(View.VISIBLE); positiveButton.setVisibility(View.VISIBLE); textSearching.setText(""); From d549fb905d454bae8357aaa49d0de450713eabf8 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sat, 24 Oct 2020 11:19:54 +0530 Subject: [PATCH 08/11] fix checkstyle LineLength --- .../org/fdroid/fdroid/data/InstalledAppProviderService.java | 3 +-- .../java/org/fdroid/fdroid/views/ManageReposActivity.java | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java b/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java index 5adfa2ea8..69763437a 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java +++ b/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java @@ -89,8 +89,7 @@ public class InstalledAppProviderService extends JobIntentService { .subscribeOn(Schedulers.newThread()) .debounce(3, TimeUnit.SECONDS) .subscribe(packageName -> { - Utils.debugLog(TAG, "Notifying content providers (so they can update" + - " the relevant views)."); + Utils.debugLog(TAG, "Notifying content providers to update relevant views."); getContentResolver().notifyChange(AppProvider.getContentUri(), null); getContentResolver().notifyChange(ApkProvider.getContentUri(), null); }) diff --git a/app/src/main/java/org/fdroid/fdroid/views/ManageReposActivity.java b/app/src/main/java/org/fdroid/fdroid/views/ManageReposActivity.java index 6d922e6e4..a7da39a0d 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/ManageReposActivity.java +++ b/app/src/main/java/org/fdroid/fdroid/views/ManageReposActivity.java @@ -648,8 +648,8 @@ public class ManageReposActivity extends AppCompatActivity }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .doOnDispose(() -> Utils.debugLog(TAG, "Not checking more repo addresses," + - " because process was skipped.")) + .doOnDispose(() -> Utils.debugLog(TAG, + "Not checking more repo addresses, because process was skipped.")) .subscribe(codeAddressPair -> { final int statusCode = codeAddressPair.first; final String newAddress = codeAddressPair.second; From c27e1a697e2a606b3403eb33888b098095f04f1f Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Tue, 15 Jun 2021 10:48:59 +0200 Subject: [PATCH 09/11] format imports using default Android Studio 4.1.2 settings --- .../java/org/fdroid/fdroid/UpdateService.java | 11 +++++----- .../main/java/org/fdroid/fdroid/Utils.java | 11 +++++----- .../data/InstalledAppProviderService.java | 7 +++---- .../fdroid/views/ManageReposActivity.java | 21 +++++++++---------- .../fdroid/views/RepoDetailsActivity.java | 17 +++++++-------- 5 files changed, 31 insertions(+), 36 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/UpdateService.java b/app/src/main/java/org/fdroid/fdroid/UpdateService.java index 78a2440b2..16146e9c0 100644 --- a/app/src/main/java/org/fdroid/fdroid/UpdateService.java +++ b/app/src/main/java/org/fdroid/fdroid/UpdateService.java @@ -37,12 +37,6 @@ import android.text.TextUtils; import android.util.Log; import android.widget.Toast; -import androidx.annotation.NonNull; -import androidx.core.app.JobIntentService; -import androidx.core.app.NotificationCompat; -import androidx.core.content.ContextCompat; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; - import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.ApkProvider; import org.fdroid.fdroid.data.App; @@ -60,6 +54,11 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; +import androidx.annotation.NonNull; +import androidx.core.app.JobIntentService; +import androidx.core.app.NotificationCompat; +import androidx.core.content.ContextCompat; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.core.Completable; import io.reactivex.rxjava3.schedulers.Schedulers; diff --git a/app/src/main/java/org/fdroid/fdroid/Utils.java b/app/src/main/java/org/fdroid/fdroid/Utils.java index 6cbb533dd..c8401be15 100644 --- a/app/src/main/java/org/fdroid/fdroid/Utils.java +++ b/app/src/main/java/org/fdroid/fdroid/Utils.java @@ -51,12 +51,6 @@ 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; @@ -103,6 +97,11 @@ 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.appcompat.app.AppCompatActivity; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.core.Single; import io.reactivex.rxjava3.schedulers.Schedulers; diff --git a/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java b/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java index 69763437a..7deee2545 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java +++ b/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java @@ -14,10 +14,6 @@ import android.os.Process; import android.os.RemoteException; import android.util.Log; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.app.JobIntentService; - import org.acra.ACRA; import org.fdroid.fdroid.AppUpdateStatusManager; import org.fdroid.fdroid.Utils; @@ -32,6 +28,9 @@ import java.util.Map; import java.util.TreeSet; import java.util.concurrent.TimeUnit; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.app.JobIntentService; import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.subjects.PublishSubject; diff --git a/app/src/main/java/org/fdroid/fdroid/views/ManageReposActivity.java b/app/src/main/java/org/fdroid/fdroid/views/ManageReposActivity.java index a7da39a0d..0db904c8a 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/ManageReposActivity.java +++ b/app/src/main/java/org/fdroid/fdroid/views/ManageReposActivity.java @@ -49,17 +49,6 @@ import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; -import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; -import androidx.core.app.NavUtils; -import androidx.core.app.TaskStackBuilder; -import androidx.core.content.ContextCompat; -import androidx.loader.app.LoaderManager; -import androidx.loader.content.CursorLoader; -import androidx.loader.content.Loader; - import com.google.android.material.appbar.MaterialToolbar; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.textfield.TextInputLayout; @@ -86,6 +75,16 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Locale; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; +import androidx.core.app.NavUtils; +import androidx.core.app.TaskStackBuilder; +import androidx.core.content.ContextCompat; +import androidx.loader.app.LoaderManager; +import androidx.loader.content.CursorLoader; +import androidx.loader.content.Loader; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.core.Single; import io.reactivex.rxjava3.disposables.CompositeDisposable; 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 5657ba761..38e6ff9e3 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/RepoDetailsActivity.java +++ b/app/src/main/java/org/fdroid/fdroid/views/RepoDetailsActivity.java @@ -27,15 +27,6 @@ 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; @@ -53,6 +44,14 @@ 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 { From e698f4f8a3cd8a1d5dd802ee91a6ada8508d357d Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Tue, 15 Jun 2021 10:52:37 +0200 Subject: [PATCH 10/11] revert unneeded/unrelated changes --- app/build.gradle | 5 ----- gradle.properties | 3 +-- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 224cf0e6c..4c12ce7be 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -139,11 +139,6 @@ android { exclude 'META-INF/INDEX.LIST' exclude '.readme' } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } } dependencies { diff --git a/gradle.properties b/gradle.properties index 3c43fbad5..5465fec0e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,2 @@ android.enableJetifier=true -android.useAndroidX=true -org.gradle.jvmargs=-Xmx4096m \ No newline at end of file +android.useAndroidX=true \ No newline at end of file From d9b443429d04b15598120d45851f6661f63c4e7b Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Tue, 15 Jun 2021 11:04:22 +0200 Subject: [PATCH 11/11] bump to latest io.reactivex.rxjava3:rxjava --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 4c12ce7be..d85643a41 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -165,7 +165,7 @@ dependencies { implementation 'com.hannesdorfmann:adapterdelegates3:3.0.1' implementation 'io.reactivex.rxjava3:rxandroid:3.0.0' - implementation 'io.reactivex.rxjava3:rxjava:3.0.7' + implementation 'io.reactivex.rxjava3:rxjava:3.0.9' implementation 'com.fasterxml.jackson.core:jackson-core:2.11.1' implementation 'com.fasterxml.jackson.core:jackson-annotations:2.11.1'