Merge 'Use_WorkManager' into 'master'
* origin/master: gitlab-ci: fix excluding @LargeTest from emulator jobs use TAG to identify CleanCacheWorker to WorkManager add WorkManagerTestRule to CleanCacheWorkerTest move static helper method into its class: CleanCacheWorker fdroidclient does not use variables for gradle dependencies Add WorkManagerTestRule. Use WorkManager to clean the cache. Add AndroidX WorkManager. fdroid/fdroidclient!959
This commit is contained in:
commit
e6819e7f12
@ -82,7 +82,7 @@ errorprone:
|
||||
- adb devices
|
||||
- adb shell input keyevent 82 &
|
||||
- if [ $AVD_SDK -lt 25 ] || ! emulator -accel-check; then
|
||||
export FLAG=-Pandroid.testInstrumentationRunnerArguments.notAnnotation=android.support.test.filters.LargeTest;
|
||||
export FLAG=-Pandroid.testInstrumentationRunnerArguments.notAnnotation=androidx.test.filters.LargeTest;
|
||||
fi
|
||||
- ./gradlew connectedFullDebugAndroidTest $FLAG
|
||||
|| ./gradlew connectedFullDebugAndroidTest $FLAG
|
||||
|
@ -145,6 +145,7 @@ dependencies {
|
||||
implementation 'androidx.vectordrawable:vectordrawable:1.1.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
implementation 'androidx.palette:palette:1.0.0'
|
||||
implementation 'androidx.work:work-runtime:2.4.0'
|
||||
|
||||
implementation 'com.google.android.material:material:1.1.0'
|
||||
|
||||
@ -176,6 +177,7 @@ dependencies {
|
||||
testImplementation 'org.hamcrest:hamcrest:2.2'
|
||||
testImplementation 'org.bouncycastle:bcprov-jdk15on:1.65'
|
||||
|
||||
androidTestImplementation 'androidx.arch.core:core-testing:2.1.0'
|
||||
androidTestImplementation 'androidx.test:core:1.3.0'
|
||||
androidTestImplementation 'androidx.test:runner:1.3.0'
|
||||
androidTestImplementation 'androidx.test:rules:1.3.0'
|
||||
@ -183,6 +185,7 @@ dependencies {
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
||||
androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
|
||||
androidTestImplementation 'androidx.work:work-testing:2.4.0'
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
|
@ -1,65 +0,0 @@
|
||||
package org.fdroid.fdroid;
|
||||
|
||||
import android.app.Instrumentation;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.fdroid.fdroid.compat.FileCompatTest;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class CleanCacheServiceTest {
|
||||
|
||||
public static final String TAG = "CleanCacheServiceTest";
|
||||
|
||||
@Test
|
||||
public void testClearOldFiles() throws IOException, InterruptedException {
|
||||
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
|
||||
File tempDir = FileCompatTest.getWriteableDir(instrumentation);
|
||||
assertTrue(tempDir.isDirectory());
|
||||
assertTrue(tempDir.canWrite());
|
||||
|
||||
File dir = new File(tempDir, "F-Droid-test.clearOldFiles");
|
||||
FileUtils.deleteQuietly(dir);
|
||||
assertTrue(dir.mkdirs());
|
||||
assertTrue(dir.isDirectory());
|
||||
|
||||
File first = new File(dir, "first");
|
||||
first.deleteOnExit();
|
||||
|
||||
File second = new File(dir, "second");
|
||||
second.deleteOnExit();
|
||||
|
||||
assertFalse(first.exists());
|
||||
assertFalse(second.exists());
|
||||
|
||||
assertTrue(first.createNewFile());
|
||||
assertTrue(first.exists());
|
||||
|
||||
Thread.sleep(7000);
|
||||
assertTrue(second.createNewFile());
|
||||
assertTrue(second.exists());
|
||||
|
||||
CleanCacheService.clearOldFiles(dir, 3000); // check all in dir
|
||||
assertFalse(first.exists());
|
||||
assertTrue(second.exists());
|
||||
|
||||
Thread.sleep(7000);
|
||||
CleanCacheService.clearOldFiles(second, 3000); // check just second file
|
||||
assertFalse(first.exists());
|
||||
assertFalse(second.exists());
|
||||
|
||||
// make sure it doesn't freak out on a non-existent file
|
||||
File nonexistent = new File(tempDir, "nonexistent");
|
||||
CleanCacheService.clearOldFiles(nonexistent, 1);
|
||||
CleanCacheService.clearOldFiles(null, 1);
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
package org.fdroid.fdroid.work;
|
||||
|
||||
import android.app.Instrumentation;
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule;
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.work.OneTimeWorkRequest;
|
||||
import androidx.work.WorkInfo;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.fdroid.fdroid.compat.FileCompatTest;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* This test cannot run on Robolectric unfortunately since it does not support
|
||||
* <p>
|
||||
* This is marked with {@link LargeTest} because it always fails on the emulator
|
||||
* tests on GitLab CI. That excludes it from the test run there.
|
||||
*/
|
||||
@LargeTest
|
||||
public class CleanCacheWorkerTest {
|
||||
public static final String TAG = "CleanCacheWorkerEmulatorTest";
|
||||
|
||||
@Rule
|
||||
public InstantTaskExecutorRule instantTaskExecutorRule = new InstantTaskExecutorRule();
|
||||
|
||||
@Rule
|
||||
public WorkManagerTestRule workManagerTestRule = new WorkManagerTestRule();
|
||||
|
||||
@Test
|
||||
public void testWorkRequest() throws ExecutionException, InterruptedException {
|
||||
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(CleanCacheWorker.class).build();
|
||||
workManagerTestRule.workManager.enqueue(request).getResult();
|
||||
ListenableFuture<WorkInfo> workInfo = workManagerTestRule.workManager.getWorkInfoById(request.getId());
|
||||
assertEquals(WorkInfo.State.SUCCEEDED, workInfo.get().getState());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearOldFiles() throws IOException, InterruptedException {
|
||||
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
|
||||
File tempDir = FileCompatTest.getWriteableDir(instrumentation);
|
||||
assertTrue(tempDir.isDirectory());
|
||||
assertTrue(tempDir.canWrite());
|
||||
|
||||
File dir = new File(tempDir, "F-Droid-test.clearOldFiles");
|
||||
FileUtils.deleteQuietly(dir);
|
||||
assertTrue(dir.mkdirs());
|
||||
assertTrue(dir.isDirectory());
|
||||
|
||||
File first = new File(dir, "first");
|
||||
first.deleteOnExit();
|
||||
|
||||
File second = new File(dir, "second");
|
||||
second.deleteOnExit();
|
||||
|
||||
assertFalse(first.exists());
|
||||
assertFalse(second.exists());
|
||||
|
||||
assertTrue(first.createNewFile());
|
||||
assertTrue(first.exists());
|
||||
|
||||
Thread.sleep(7000);
|
||||
assertTrue(second.createNewFile());
|
||||
assertTrue(second.exists());
|
||||
|
||||
CleanCacheWorker.clearOldFiles(dir, 3000); // check all in dir
|
||||
assertFalse(first.exists());
|
||||
assertTrue(second.exists());
|
||||
|
||||
Thread.sleep(7000);
|
||||
CleanCacheWorker.clearOldFiles(second, 3000); // check just second file
|
||||
assertFalse(first.exists());
|
||||
assertFalse(second.exists());
|
||||
|
||||
// make sure it doesn't freak out on a non-existent file
|
||||
File nonexistent = new File(tempDir, "nonexistent");
|
||||
CleanCacheWorker.clearOldFiles(nonexistent, 1);
|
||||
CleanCacheWorker.clearOldFiles(null, 1);
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package org.fdroid.fdroid.work;
|
||||
|
||||
import android.app.Instrumentation;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.work.Configuration;
|
||||
import androidx.work.WorkManager;
|
||||
import androidx.work.testing.SynchronousExecutor;
|
||||
import androidx.work.testing.WorkManagerTestInitHelper;
|
||||
import org.junit.rules.TestWatcher;
|
||||
import org.junit.runner.Description;
|
||||
|
||||
public class WorkManagerTestRule extends TestWatcher {
|
||||
Context targetContext;
|
||||
Context testContext;
|
||||
Configuration configuration;
|
||||
WorkManager workManager;
|
||||
|
||||
@Override
|
||||
protected void starting(Description description) {
|
||||
final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
|
||||
targetContext = instrumentation.getTargetContext();
|
||||
testContext = instrumentation.getContext();
|
||||
configuration = new Configuration.Builder()
|
||||
.setMinimumLoggingLevel(Log.DEBUG)
|
||||
.setExecutor(new SynchronousExecutor())
|
||||
.build();
|
||||
|
||||
WorkManagerTestInitHelper.initializeTestWorkManager(targetContext, configuration);
|
||||
workManager = WorkManager.getInstance(targetContext);
|
||||
}
|
||||
}
|
@ -241,14 +241,6 @@
|
||||
android:name=".installer.InstallerService"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||
android:exported="false"/>
|
||||
<service
|
||||
android:name=".CleanCacheService"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||
android:exported="false"/>
|
||||
<service
|
||||
android:name=".CleanCacheJobService"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||
android:exported="false"/>
|
||||
<service
|
||||
android:name=".DeleteCacheService"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||
|
@ -1,22 +0,0 @@
|
||||
package org.fdroid.fdroid;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.job.JobParameters;
|
||||
import android.app.job.JobService;
|
||||
|
||||
/**
|
||||
* Shim to run {@link CleanCacheService} with {@link android.app.job.JobScheduler}
|
||||
*/
|
||||
@TargetApi(21)
|
||||
public class CleanCacheJobService extends JobService {
|
||||
@Override
|
||||
public boolean onStartJob(JobParameters jobParameters) {
|
||||
CleanCacheService.start(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onStopJob(JobParameters jobParameters) {
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,193 +0,0 @@
|
||||
package org.fdroid.fdroid;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.job.JobInfo;
|
||||
import android.app.job.JobScheduler;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Process;
|
||||
import android.os.SystemClock;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.JobIntentService;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.fdroid.fdroid.installer.ApkCache;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Handles cleaning up caches files that are not going to be used, and do not
|
||||
* block the operation of the app itself. For things that must happen before
|
||||
* F-Droid starts normal operation, that should go into
|
||||
* {@link FDroidApp#onCreate()}.
|
||||
* <p>
|
||||
* These files should only be deleted when they are at least an hour-ish old,
|
||||
* in case they are actively in use while {@code CleanCacheService} is running.
|
||||
* {@link #clearOldFiles(File, long)} checks the file age using access time from
|
||||
* {@link android.system.StructStat#st_atime} on {@link android.os.Build.VERSION_CODES#LOLLIPOP}
|
||||
* and newer. On older Android, last modified time from {@link File#lastModified()}
|
||||
* is used.
|
||||
*/
|
||||
public class CleanCacheService extends JobIntentService {
|
||||
public static final String TAG = "CleanCacheService";
|
||||
|
||||
private static final int JOB_ID = 0x982374;
|
||||
|
||||
/**
|
||||
* Schedule or cancel this service to update the app index, according to the
|
||||
* current preferences. Should be called a) at boot, b) if the preference
|
||||
* is changed, or c) on startup, in case we get upgraded.
|
||||
*/
|
||||
public static void schedule(Context context) {
|
||||
long keepTime = Preferences.get().getKeepCacheTime();
|
||||
long interval = TimeUnit.DAYS.toMillis(1);
|
||||
if (keepTime < interval) {
|
||||
interval = keepTime;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < 21) {
|
||||
Intent intent = new Intent(context, CleanCacheService.class);
|
||||
PendingIntent pending = PendingIntent.getService(context, 0, intent, 0);
|
||||
|
||||
AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
||||
alarm.cancel(pending);
|
||||
alarm.setInexactRepeating(AlarmManager.ELAPSED_REALTIME,
|
||||
SystemClock.elapsedRealtime() + 5000, interval, pending);
|
||||
} else {
|
||||
Utils.debugLog(TAG, "Using android-21 JobScheduler for updates");
|
||||
JobScheduler jobScheduler = ContextCompat.getSystemService(context, JobScheduler.class);
|
||||
ComponentName componentName = new ComponentName(context, CleanCacheJobService.class);
|
||||
JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, componentName)
|
||||
.setRequiresDeviceIdle(true)
|
||||
.setRequiresCharging(true)
|
||||
.setPeriodic(interval);
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
builder.setRequiresBatteryNotLow(true);
|
||||
}
|
||||
jobScheduler.schedule(builder.build());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static void start(Context context) {
|
||||
enqueueWork(context, CleanCacheService.class, JOB_ID, new Intent(context, CleanCacheService.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleWork(@NonNull Intent intent) {
|
||||
Process.setThreadPriority(Process.THREAD_PRIORITY_LOWEST);
|
||||
deleteExpiredApksFromCache();
|
||||
deleteStrayIndexFiles();
|
||||
deleteOldInstallerFiles();
|
||||
deleteOldIcons();
|
||||
}
|
||||
|
||||
/**
|
||||
* All downloaded APKs will be cached for a certain amount of time, which is
|
||||
* specified by the user in the "Keep Cache Time" preference. This removes
|
||||
* any APK in the cache that is older than that preference specifies.
|
||||
*/
|
||||
private void deleteExpiredApksFromCache() {
|
||||
File cacheDir = ApkCache.getApkCacheDir(getBaseContext());
|
||||
clearOldFiles(cacheDir, Preferences.get().getKeepCacheTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link org.fdroid.fdroid.installer.Installer} instances copy the APK into
|
||||
* a safe place before installing. It doesn't clean up them reliably yet.
|
||||
*/
|
||||
private void deleteOldInstallerFiles() {
|
||||
File filesDir = getFilesDir();
|
||||
if (filesDir == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final File[] files = filesDir.listFiles();
|
||||
if (files == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (File f : files) {
|
||||
if (f.getName().endsWith(".apk")) {
|
||||
clearOldFiles(f, TimeUnit.HOURS.toMillis(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete index files which were downloaded, but not removed (e.g. due to F-Droid being
|
||||
* force closed during processing of the file, before getting a chance to delete). This
|
||||
* may include both "index-*-downloaded" and "index-*-extracted.xml" files.
|
||||
* <p>
|
||||
* Note that if the SD card is not ready, then the cache directory will probably not be
|
||||
* available. In this situation no files will be deleted (and thus they may still exist
|
||||
* after the SD card becomes available).
|
||||
* <p>
|
||||
* This also deletes temp files that are created by
|
||||
* {@link org.fdroid.fdroid.net.DownloaderFactory#create(Context, String)}, e.g. "dl-*"
|
||||
*/
|
||||
private void deleteStrayIndexFiles() {
|
||||
File cacheDir = getCacheDir();
|
||||
if (cacheDir == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final File[] files = cacheDir.listFiles();
|
||||
if (files == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (File f : files) {
|
||||
if (f.getName().startsWith("index-")) {
|
||||
clearOldFiles(f, TimeUnit.HOURS.toMillis(1));
|
||||
}
|
||||
if (f.getName().startsWith("dl-")) {
|
||||
clearOldFiles(f, TimeUnit.HOURS.toMillis(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete cached icons that have not been accessed in over a year.
|
||||
*/
|
||||
private void deleteOldIcons() {
|
||||
clearOldFiles(Utils.getImageCacheDir(this), TimeUnit.DAYS.toMillis(365));
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively delete files in {@code f} that were last used
|
||||
* {@code millisAgo} milliseconds ago. On {@code android-21} and newer, this
|
||||
* is based on the last access of the file, on older Android versions, it is
|
||||
* based on the last time the file was modified, e.g. downloaded.
|
||||
*
|
||||
* @param f The file or directory to clean
|
||||
* @param millisAgo The number of milliseconds old that marks a file for deletion.
|
||||
*/
|
||||
public static void clearOldFiles(File f, long millisAgo) {
|
||||
if (f == null) {
|
||||
return;
|
||||
}
|
||||
long olderThan = System.currentTimeMillis() - millisAgo;
|
||||
if (f.isDirectory()) {
|
||||
File[] files = f.listFiles();
|
||||
if (files == null) {
|
||||
return;
|
||||
}
|
||||
for (File file : files) {
|
||||
clearOldFiles(file, millisAgo);
|
||||
}
|
||||
f.delete();
|
||||
} else if (Build.VERSION.SDK_INT < 21) {
|
||||
if (FileUtils.isFileOlder(f, olderThan)) {
|
||||
f.delete();
|
||||
}
|
||||
} else {
|
||||
CleanCacheService21.deleteIfOld(f, olderThan);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
package org.fdroid.fdroid;
|
||||
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.system.StructStat;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Helper class to prevent {@link VerifyError}s from occurring in {@link CleanCacheService#clearOldFiles(File, long)}
|
||||
* due to the fact that {@link Os} was only introduced in API 21.
|
||||
*/
|
||||
@RequiresApi(21)
|
||||
class CleanCacheService21 {
|
||||
static void deleteIfOld(File file, long olderThan) {
|
||||
if (file == null || !file.exists()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
StructStat stat = Os.lstat(file.getAbsolutePath());
|
||||
if ((stat.st_atime * 1000L) < olderThan) {
|
||||
file.delete();
|
||||
}
|
||||
} catch (ErrnoException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
@ -40,16 +40,17 @@ import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.os.StrictMode;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.collection.LongSparseArray;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.collection.LongSparseArray;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.nostra13.universalimageloader.cache.disc.DiskCache;
|
||||
import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache;
|
||||
import com.nostra13.universalimageloader.cache.disc.impl.ext.LruDiskCache;
|
||||
@ -57,8 +58,7 @@ import com.nostra13.universalimageloader.core.DefaultConfigurationFactory;
|
||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
|
||||
import com.nostra13.universalimageloader.core.process.BitmapProcessor;
|
||||
import info.guardianproject.netcipher.NetCipher;
|
||||
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
||||
|
||||
import org.acra.ACRA;
|
||||
import org.acra.ReportField;
|
||||
import org.acra.ReportingInteractionMode;
|
||||
@ -73,20 +73,25 @@ import org.fdroid.fdroid.data.Repo;
|
||||
import org.fdroid.fdroid.installer.ApkFileProvider;
|
||||
import org.fdroid.fdroid.installer.InstallHistoryService;
|
||||
import org.fdroid.fdroid.nearby.SDCardScannerService;
|
||||
import org.fdroid.fdroid.nearby.WifiStateChangeService;
|
||||
import org.fdroid.fdroid.net.ConnectivityMonitorService;
|
||||
import org.fdroid.fdroid.net.Downloader;
|
||||
import org.fdroid.fdroid.net.HttpDownloader;
|
||||
import org.fdroid.fdroid.net.ImageLoaderForUIL;
|
||||
import org.fdroid.fdroid.nearby.WifiStateChangeService;
|
||||
import org.fdroid.fdroid.panic.HidingManager;
|
||||
import org.fdroid.fdroid.work.CleanCacheWorker;
|
||||
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.Security;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
|
||||
import info.guardianproject.netcipher.NetCipher;
|
||||
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
||||
|
||||
@ReportsCrashes(mailTo = BuildConfig.ACRA_REPORT_EMAIL,
|
||||
mode = ReportingInteractionMode.DIALOG,
|
||||
reportDialogClass = org.fdroid.fdroid.acra.CrashReportActivity.class,
|
||||
@ -421,7 +426,7 @@ public class FDroidApp extends Application {
|
||||
}
|
||||
});
|
||||
|
||||
CleanCacheService.schedule(this);
|
||||
CleanCacheWorker.schedule(this);
|
||||
|
||||
notificationHelper = new NotificationHelper(getApplicationContext());
|
||||
|
||||
@ -551,7 +556,7 @@ public class FDroidApp extends Application {
|
||||
* problems that arise from executing the code twice. This happens due to the `android:process`
|
||||
* statement in AndroidManifest.xml causes another process to be created to run
|
||||
* {@link org.fdroid.fdroid.acra.CrashReportActivity}. This was causing lots of things to be
|
||||
* started/run twice including {@link CleanCacheService} and {@link WifiStateChangeService}.
|
||||
* started/run twice including {@link CleanCacheWorker} and {@link WifiStateChangeService}.
|
||||
* <p>
|
||||
* Note that it is not perfect, because some devices seem to not provide a list of running app
|
||||
* processes when asked. In such situations, F-Droid may regress to the behaviour where some
|
||||
|
@ -3,9 +3,10 @@ package org.fdroid.fdroid.receiver;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import org.fdroid.fdroid.CleanCacheService;
|
||||
|
||||
import org.fdroid.fdroid.DeleteCacheService;
|
||||
import org.fdroid.fdroid.Utils;
|
||||
import org.fdroid.fdroid.work.CleanCacheWorker;
|
||||
|
||||
public class DeviceStorageReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
@ -18,7 +19,7 @@ public class DeviceStorageReceiver extends BroadcastReceiver {
|
||||
int percentageFree = Utils.getPercent(Utils.getImageCacheDirAvailableMemory(context),
|
||||
Utils.getImageCacheDirTotalMemory(context));
|
||||
if (percentageFree > 2) {
|
||||
CleanCacheService.start(context);
|
||||
CleanCacheWorker.schedule(context);
|
||||
} else {
|
||||
DeleteCacheService.deleteAll(context);
|
||||
}
|
||||
|
@ -48,7 +48,6 @@ import androidx.preference.SwitchPreference;
|
||||
import androidx.recyclerview.widget.LinearSmoothScroller;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.fdroid.fdroid.CleanCacheService;
|
||||
import org.fdroid.fdroid.FDroidApp;
|
||||
import org.fdroid.fdroid.Languages;
|
||||
import org.fdroid.fdroid.Preferences;
|
||||
@ -58,6 +57,7 @@ import org.fdroid.fdroid.Utils;
|
||||
import org.fdroid.fdroid.data.RepoProvider;
|
||||
import org.fdroid.fdroid.installer.InstallHistoryService;
|
||||
import org.fdroid.fdroid.installer.PrivilegedInstaller;
|
||||
import org.fdroid.fdroid.work.CleanCacheWorker;
|
||||
|
||||
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
||||
|
||||
@ -304,7 +304,7 @@ public class PreferencesFragment extends PreferenceFragmentCompat
|
||||
entrySummary(key);
|
||||
if (changing
|
||||
&& currentKeepCacheTime != Preferences.get().getKeepCacheTime()) {
|
||||
CleanCacheService.schedule(getActivity());
|
||||
CleanCacheWorker.schedule(requireContext());
|
||||
}
|
||||
break;
|
||||
|
||||
|
215
app/src/main/java/org/fdroid/fdroid/work/CleanCacheWorker.java
Normal file
215
app/src/main/java/org/fdroid/fdroid/work/CleanCacheWorker.java
Normal file
@ -0,0 +1,215 @@
|
||||
package org.fdroid.fdroid.work;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Process;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.system.StructStat;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.work.Constraints;
|
||||
import androidx.work.ExistingPeriodicWorkPolicy;
|
||||
import androidx.work.PeriodicWorkRequest;
|
||||
import androidx.work.WorkManager;
|
||||
import androidx.work.Worker;
|
||||
import androidx.work.WorkerParameters;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.fdroid.fdroid.Preferences;
|
||||
import org.fdroid.fdroid.Utils;
|
||||
import org.fdroid.fdroid.installer.ApkCache;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class CleanCacheWorker extends Worker {
|
||||
public static final String TAG = "CleanCacheWorker";
|
||||
|
||||
public CleanCacheWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
|
||||
super(context, workerParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule or cancel a work request to update the app index, according to the
|
||||
* current preferences. Should be called a) at boot, b) if the preference
|
||||
* is changed, or c) on startup, in case we get upgraded.
|
||||
*/
|
||||
public static void schedule(@NonNull final Context context) {
|
||||
final WorkManager workManager = WorkManager.getInstance(context);
|
||||
final long keepTime = Preferences.get().getKeepCacheTime();
|
||||
long interval = TimeUnit.DAYS.toMillis(1);
|
||||
if (keepTime < interval) {
|
||||
interval = keepTime;
|
||||
}
|
||||
|
||||
final Constraints.Builder constraintsBuilder = new Constraints.Builder()
|
||||
.setRequiresCharging(true)
|
||||
.setRequiresBatteryNotLow(true);
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
constraintsBuilder.setRequiresDeviceIdle(true);
|
||||
}
|
||||
final PeriodicWorkRequest cleanCache =
|
||||
new PeriodicWorkRequest.Builder(CleanCacheWorker.class, interval, TimeUnit.MILLISECONDS)
|
||||
.setConstraints(constraintsBuilder.build())
|
||||
.build();
|
||||
workManager.enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.REPLACE, cleanCache);
|
||||
Utils.debugLog(TAG, "Scheduled periodic work for cleaning the cache.");
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Result doWork() {
|
||||
Process.setThreadPriority(Process.THREAD_PRIORITY_LOWEST);
|
||||
try {
|
||||
deleteExpiredApksFromCache();
|
||||
deleteStrayIndexFiles();
|
||||
deleteOldInstallerFiles();
|
||||
deleteOldIcons();
|
||||
return Result.success();
|
||||
} catch (Exception e) {
|
||||
return Result.failure();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* All downloaded APKs will be cached for a certain amount of time, which is
|
||||
* specified by the user in the "Keep Cache Time" preference. This removes
|
||||
* any APK in the cache that is older than that preference specifies.
|
||||
*/
|
||||
private void deleteExpiredApksFromCache() {
|
||||
File cacheDir = ApkCache.getApkCacheDir(getApplicationContext());
|
||||
clearOldFiles(cacheDir, Preferences.get().getKeepCacheTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link org.fdroid.fdroid.installer.Installer} instances copy the APK into
|
||||
* a safe place before installing. It doesn't clean up them reliably yet.
|
||||
*/
|
||||
private void deleteOldInstallerFiles() {
|
||||
File filesDir = getApplicationContext().getFilesDir();
|
||||
if (filesDir == null) {
|
||||
Utils.debugLog(TAG, "The files directory doesn't exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
final File[] files = filesDir.listFiles();
|
||||
if (files == null) {
|
||||
Utils.debugLog(TAG, "The files directory doesn't have any files.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (File f : files) {
|
||||
if (f.getName().endsWith(".apk")) {
|
||||
clearOldFiles(f, TimeUnit.HOURS.toMillis(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete index files which were downloaded, but not removed (e.g. due to F-Droid being
|
||||
* force closed during processing of the file, before getting a chance to delete). This
|
||||
* may include both "index-*-downloaded" and "index-*-extracted.xml" files.
|
||||
* <p>
|
||||
* Note that if the SD card is not ready, then the cache directory will probably not be
|
||||
* available. In this situation no files will be deleted (and thus they may still exist
|
||||
* after the SD card becomes available).
|
||||
* <p>
|
||||
* This also deletes temp files that are created by
|
||||
* {@link org.fdroid.fdroid.net.DownloaderFactory#create(Context, String)}, e.g. "dl-*"
|
||||
*/
|
||||
private void deleteStrayIndexFiles() {
|
||||
File cacheDir = getApplicationContext().getCacheDir();
|
||||
if (cacheDir == null) {
|
||||
Utils.debugLog(TAG, "The cache directory doesn't exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
final File[] files = cacheDir.listFiles();
|
||||
if (files == null) {
|
||||
Utils.debugLog(TAG, "The cache directory doesn't have files.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (File f : files) {
|
||||
if (f.getName().startsWith("index-")) {
|
||||
clearOldFiles(f, TimeUnit.HOURS.toMillis(1));
|
||||
}
|
||||
if (f.getName().startsWith("dl-")) {
|
||||
clearOldFiles(f, TimeUnit.HOURS.toMillis(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete cached icons that have not been accessed in over a year.
|
||||
*/
|
||||
private void deleteOldIcons() {
|
||||
clearOldFiles(Utils.getImageCacheDir(getApplicationContext()), TimeUnit.DAYS.toMillis(365));
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively delete files in {@code f} that were last used
|
||||
* {@code millisAgo} milliseconds ago. On {@code android-21} and newer, this
|
||||
* is based on the last access of the file, on older Android versions, it is
|
||||
* based on the last time the file was modified, e.g. downloaded.
|
||||
*
|
||||
* @param f The file or directory to clean
|
||||
* @param millisAgo The number of milliseconds old that marks a file for deletion.
|
||||
*/
|
||||
public static void clearOldFiles(File f, long millisAgo) {
|
||||
if (f == null) {
|
||||
Utils.debugLog(TAG, "No files to be cleared.");
|
||||
return;
|
||||
}
|
||||
long olderThan = System.currentTimeMillis() - millisAgo;
|
||||
if (f.isDirectory()) {
|
||||
File[] files = f.listFiles();
|
||||
if (files == null) {
|
||||
Utils.debugLog(TAG, "No more files to be cleared.");
|
||||
return;
|
||||
}
|
||||
for (File file : files) {
|
||||
clearOldFiles(file, millisAgo);
|
||||
}
|
||||
deleteFileAndLog(f);
|
||||
} else if (Build.VERSION.SDK_INT <= 21) {
|
||||
if (FileUtils.isFileOlder(f, olderThan)) {
|
||||
deleteFileAndLog(f);
|
||||
}
|
||||
} else {
|
||||
Impl21.deleteIfOld(f, olderThan);
|
||||
}
|
||||
}
|
||||
|
||||
private static void deleteFileAndLog(final File file) {
|
||||
file.delete();
|
||||
Utils.debugLog(TAG, "Deleted file: " + file);
|
||||
}
|
||||
|
||||
@RequiresApi(api = 21)
|
||||
private static class Impl21 {
|
||||
/**
|
||||
* Recursively delete files in {@code f} that were last used
|
||||
* {@code millisAgo} milliseconds ago. On {@code android-21} and newer, this
|
||||
* is based on the last access of the file, on older Android versions, it is
|
||||
* based on the last time the file was modified, e.g. downloaded.
|
||||
*
|
||||
* @param file The file or directory to clean
|
||||
* @param olderThan The number of milliseconds old that marks a file for deletion.
|
||||
*/
|
||||
public static void deleteIfOld(File file, long olderThan) {
|
||||
if (file == null || !file.exists()) {
|
||||
Utils.debugLog(TAG, "No files to be cleared.");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
StructStat stat = Os.lstat(file.getAbsolutePath());
|
||||
if ((stat.st_atime * 1000L) < olderThan) {
|
||||
deleteFileAndLog(file);
|
||||
}
|
||||
} catch (ErrnoException e) {
|
||||
Utils.debugLog(TAG, "An exception occurred while deleting: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
@ -63,6 +63,7 @@
|
||||
<trusted-key id="7f36e793ae3252e5d9e9b98fee9e7dc9d92fc896" group="com.google.errorprone"/>
|
||||
<trusted-key id="7faa0f2206de228f0db01ad741321490758aad6f" group="org.codehaus.groovy" name="groovy-all" version="2.4.15"/>
|
||||
<trusted-key id="8254180bfc943b816e0b5e2e5e2f2b3d474efe6b" group="it.unimi.dsi" name="fastutil" version="7.2.0"/>
|
||||
<trusted-key id="8756c4f765c9ac3cb6b85d62379ce192d401ab61" group="org.jetbrains.kotlinx"/>
|
||||
<trusted-key id="90ee19787a7bcf6fd37a1e9180c08b1c29100955">
|
||||
<trusting group="com.squareup" name="javawriter"/>
|
||||
<trusting group="com.squareup" name="javawriter" version="2.1.1"/>
|
||||
@ -79,11 +80,17 @@
|
||||
<trusted-key id="afcc4c7594d09e2182c60e0f7a01b0f236e5430f" group="com.google.code.gson"/>
|
||||
<trusted-key id="b6e73d84ea4fcc47166087253faad2cd5ecbb314" group="org.apache.commons"/>
|
||||
<trusted-key id="b801e2f8ef035068ec1139cc29579f18fa8fd93b" group="com.google.j2objc" name="j2objc-annotations" version="1.1"/>
|
||||
<trusted-key id="bdb5fa4fe719d787fb3d3197f6d4a1d411e9d1ae" group="com.google.guava" name="listenablefuture" version="9999.0-empty-to-avoid-conflict-with-guava"/>
|
||||
<trusted-key id="bdb5fa4fe719d787fb3d3197f6d4a1d411e9d1ae">
|
||||
<trusting group="com.google.guava" name="listenablefuture" version="9999.0-empty-to-avoid-conflict-with-guava"/>
|
||||
<trusting group="com.google.guava" name="listenablefuture"/>
|
||||
</trusted-key>
|
||||
<trusted-key id="c7be5bcc9fec15518cfda882b0f3710fa64900e7" group="com.google.auto.value"/>
|
||||
<trusted-key id="cacfbd4755a2fc78709bdd92be096e29edb8d141" group="net.sf.proguard"/>
|
||||
<trusted-key id="cb3190ca7842439e57f3712e44ce7bf2825ea2cd" group="com.ibm.icu" name="icu4j" version="53.1"/>
|
||||
<trusted-key id="cc4483cd6a3eb2939b948667a1b4460d8ba7b9af" group="org.mockito" name="mockito-core" version="3.3.3"/>
|
||||
<trusted-key id="cc4483cd6a3eb2939b948667a1b4460d8ba7b9af">
|
||||
<trusting group="org.mockito" name="mockito-core" version="3.3.3"/>
|
||||
<trusting group="org.mockito" name="mockito-core"/>
|
||||
</trusted-key>
|
||||
<trusted-key id="cd5464315f0b98c77e6e8ecd9daadc1c9fcc82d0">
|
||||
<trusting group="commons-io"/>
|
||||
<trusting group="commons-cli"/>
|
||||
@ -163,6 +170,11 @@
|
||||
<sha256 value="c0c4ed0160cd2ca18390015de8b392b697c173327c6b2c0947d4e62f6958c05d" origin="Generated by Gradle because artifact wasn't signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.arch.core" name="core-testing" version="2.1.0">
|
||||
<artifact name="core-testing-2.1.0.aar">
|
||||
<sha256 value="c57ffade2a9a844bd62b4f4c3916fad5e09e9f24cceba27e932c25bb7a6d1d8f" origin="Generated by Gradle because artifact wasn't signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.asynclayoutinflater" name="asynclayoutinflater" version="1.0.0">
|
||||
<artifact name="asynclayoutinflater-1.0.0.aar">
|
||||
<sha256 value="f7eab60c57addd94bb06275832fe7600611beaaae1a1ec597c231956faf96c8b" origin="Generated by Gradle because artifact wasn't signed"/>
|
||||
@ -346,6 +358,11 @@
|
||||
<sha256 value="a84842ffc0f14e518db75c05cc112680a8a4a164fa78395be32d88304a439423" origin="Generated by Gradle because artifact wasn't signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.lifecycle" name="lifecycle-livedata" version="2.1.0">
|
||||
<artifact name="lifecycle-livedata-2.1.0.aar">
|
||||
<sha256 value="242e446bed3db36f0df0aab0cb7f91060bd2dab7adcad1117adf54e724cd1d26" origin="Generated by Gradle because artifact wasn't signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.lifecycle" name="lifecycle-livedata-core" version="2.2.0">
|
||||
<artifact name="lifecycle-livedata-core-2.2.0.aar">
|
||||
<sha256 value="556c1f3af90aa9d7d0d330565adbf6da71b2429148bac91e07c485f4f9abf614" origin="Generated by Gradle because artifact wasn't signed"/>
|
||||
@ -367,6 +384,11 @@
|
||||
<sha256 value="7f154066fed1c9162870f728b208352831b3d1f8f23f5d388958a50ca4e9f441" origin="Generated by Gradle because artifact wasn't signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.lifecycle" name="lifecycle-service" version="2.1.0">
|
||||
<artifact name="lifecycle-service-2.1.0.aar">
|
||||
<sha256 value="23516745f34f16ff7850bb1eadd55cf193dd789cba428de4bca120433e3bfd69" origin="Generated by Gradle because artifact wasn't signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.lifecycle" name="lifecycle-viewmodel" version="2.2.0">
|
||||
<artifact name="lifecycle-viewmodel-2.2.0.aar">
|
||||
<sha256 value="967efab24d6c49dd414a8c0ac4a1cd09b018f0b8bb43b739ad360c4158ebde27" origin="Generated by Gradle because artifact wasn't signed"/>
|
||||
@ -439,6 +461,16 @@
|
||||
<sha256 value="fe321062a6e4e168b9c2b39a1137564c8784a2e5849402729768d5d7c4d972ef" origin="Generated by Gradle because artifact wasn't signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.room" name="room-common" version="2.2.5">
|
||||
<artifact name="room-common-2.2.5.jar">
|
||||
<sha256 value="2b130dd4a1d3d91b6701ed33096d389f01c4fc1197a7acd6b91724ddc5acfc06" origin="Generated by Gradle because artifact wasn't signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.room" name="room-runtime" version="2.2.5">
|
||||
<artifact name="room-runtime-2.2.5.aar">
|
||||
<sha256 value="24a5549b796e43e337513d2908adac67f45350d9a90bca7e2e6120692140bb14" origin="Generated by Gradle because artifact wasn't signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.savedstate" name="savedstate" version="1.0.0">
|
||||
<artifact name="savedstate-1.0.0.aar">
|
||||
<sha256 value="2510a5619c37579c9ce1a04574faaf323cd0ffe2fc4e20fa8f8f01e5bb402e83" origin="Generated by Gradle because artifact wasn't signed"/>
|
||||
@ -455,6 +487,16 @@
|
||||
<sha256 value="40e90f96838c2a8156ab51b181400767049f387cec8c695e412d3d9205b0745b" origin="Generated by Gradle because artifact wasn't signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.sqlite" name="sqlite" version="2.1.0">
|
||||
<artifact name="sqlite-2.1.0.aar">
|
||||
<sha256 value="8341ff092d6060d62a07227f29237155fff36fb16f96c95fbd9a884e375db912" origin="Generated by Gradle because artifact wasn't signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.sqlite" name="sqlite-framework" version="2.1.0">
|
||||
<artifact name="sqlite-framework-2.1.0.aar">
|
||||
<sha256 value="8673737fdb2efbad91aeaeed1927ebb29212d36a867d93b9639c8069019f8a1e" origin="Generated by Gradle because artifact wasn't signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.swiperefreshlayout" name="swiperefreshlayout" version="1.0.0">
|
||||
<artifact name="swiperefreshlayout-1.0.0.aar">
|
||||
<sha256 value="9761b3a809c9b093fd06a3c4bbc645756dec0e95b5c9da419bc9f2a3f3026e8d" origin="Generated by Gradle because artifact wasn't signed"/>
|
||||
@ -610,6 +652,21 @@
|
||||
<sha256 value="4063bca7fe94fe65c98f4168f97ae10e02da0248598ad3ac21c432c7f608a17a" origin="Generated by Gradle because artifact wasn't signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.work" name="work-runtime" version="2.4.0">
|
||||
<artifact name="work-runtime-2.4.0.aar">
|
||||
<sha256 value="7801441cb973fb007ef311d3db35c3b3c9d011cef890357e7ca33890833354e5" origin="Generated by Gradle because artifact wasn't signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.work" name="work-runtime-ktx" version="2.4.0">
|
||||
<artifact name="work-runtime-ktx-2.4.0.aar">
|
||||
<sha256 value="290d2e9c1a22388b3f0c3dbc204a6176f8c49174126de884820befa83899f129" origin="Generated by Gradle because artifact wasn't signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.work" name="work-testing" version="2.4.0">
|
||||
<artifact name="work-testing-2.4.0.aar">
|
||||
<sha256 value="dc1d9f743c59ee08c605aa924fc20819b6a0b734338e0a95af1943c3cd806e23" origin="Generated by Gradle because artifact wasn't signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="antlr" name="antlr" version="2.7.7">
|
||||
<artifact name="antlr-2.7.7.jar">
|
||||
<sha256 value="88fbda4b912596b9f56e8e12e580cc954bacfb51776ecfddd3e18fc1cf56dc4c" origin="Generated by Gradle because artifact wasn't signed"/>
|
||||
@ -768,6 +825,9 @@
|
||||
<artifact name="aapt2-3.6.3-6040484-linux.jar">
|
||||
<sha256 value="1e2fcdbe75c2c74c6f9ae841e032fd1c803d30f59505d657b3dbcec1cecf961b" origin="Generated by Gradle because artifact wasn't signed"/>
|
||||
</artifact>
|
||||
<artifact name="aapt2-3.6.3-6040484-windows.jar">
|
||||
<sha256 value="82ea931921dede9d6a5e6a6735c99b4f289fd9e6951da54453c3b32c3684d82a" origin="Generated by Gradle because artifact wasn't signed"/>
|
||||
</artifact>
|
||||
<artifact name="aapt2-3.6.3-6040484.pom">
|
||||
<sha256 value="cc6e7d9c35677382afdbce47d5e81e683bee37a9af8a595fcab3b8948f7d3d51" origin="Generated by Gradle because artifact wasn't signed"/>
|
||||
</artifact>
|
||||
@ -854,6 +914,7 @@
|
||||
</component>
|
||||
<component group="com.android.tools.build" name="transform-api" version="2.0.0-deprecated-use-gradle-api">
|
||||
<artifact name="transform-api-2.0.0-deprecated-use-gradle-api.jar">
|
||||
<pgp value="3872ed7d5904493d23d78fa2c4c8cb73b1435348"/>
|
||||
<sha256 value="e8b4151ae1679f1abe7a14ee371ac9b3c651ae7b63290d1f586bdd0f78face9a" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="transform-api-2.0.0-deprecated-use-gradle-api.pom">
|
||||
@ -1143,6 +1204,7 @@
|
||||
</component>
|
||||
<component group="com.google.code.findbugs" name="jsr305" version="3.0.2">
|
||||
<artifact name="jsr305-3.0.2.jar">
|
||||
<pgp value="7616eb882daf57a11477aaf559a252fb1199d873"/>
|
||||
<sha256 value="766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="jsr305-3.0.2.pom">
|
||||
@ -1173,6 +1235,7 @@
|
||||
</component>
|
||||
<component group="com.google.errorprone" name="error_prone_annotations" version="2.2.0">
|
||||
<artifact name="error_prone_annotations-2.2.0.jar">
|
||||
<pgp value="e77417ac194160a3fabd04969a259c7ee636c5ed"/>
|
||||
<sha256 value="6ebd22ca1b9d8ec06d41de8d64e0596981d9607b42035f9ed374f9de271a481a" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="error_prone_annotations-2.2.0.pom">
|
||||
@ -1181,6 +1244,7 @@
|
||||
</component>
|
||||
<component group="com.google.errorprone" name="error_prone_annotations" version="2.3.1">
|
||||
<artifact name="error_prone_annotations-2.3.1.jar">
|
||||
<pgp value="7f36e793ae3252e5d9e9b98fee9e7dc9d92fc896"/>
|
||||
<sha256 value="10a5949aa0f95c8de4fd47edfe20534d2acefd8c224f8afea1f607e112816120" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="error_prone_annotations-2.3.1.pom">
|
||||
@ -1306,6 +1370,7 @@
|
||||
</component>
|
||||
<component group="com.google.j2objc" name="j2objc-annotations" version="1.1">
|
||||
<artifact name="j2objc-annotations-1.1.jar">
|
||||
<pgp value="b801e2f8ef035068ec1139cc29579f18fa8fd93b"/>
|
||||
<sha256 value="2994a7eb78f2710bd3d3bfb639b2c94e219cedac0d4d084d516e78c16dddecf6" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="j2objc-annotations-1.1.pom">
|
||||
@ -1387,6 +1452,7 @@
|
||||
</component>
|
||||
<component group="com.googlecode.juniversalchardet" name="juniversalchardet" version="1.0.3">
|
||||
<artifact name="juniversalchardet-1.0.3.jar">
|
||||
<pgp value="31bae2e51d95e0f8ad9b7bcc40a3c4432bd7308c"/>
|
||||
<sha256 value="757bfe906193b8b651e79dc26cd67d6b55d0770a2cdfb0381591504f779d4a76" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="juniversalchardet-1.0.3.pom">
|
||||
@ -1463,6 +1529,7 @@
|
||||
</component>
|
||||
<component group="com.sun.activation" name="javax.activation" version="1.2.0">
|
||||
<artifact name="javax.activation-1.2.0.jar">
|
||||
<pgp value="4f7e32d440ef90a83011a8fc6425559c47cc79c4"/>
|
||||
<sha256 value="993302b16cd7056f21e779cc577d175a810bb4900ef73cd8fbf2b50f928ba9ce" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="javax.activation-1.2.0.pom">
|
||||
@ -1535,6 +1602,7 @@
|
||||
</component>
|
||||
<component group="commons-codec" name="commons-codec" version="1.10">
|
||||
<artifact name="commons-codec-1.10.jar">
|
||||
<pgp value="2db4f1ef0fa761ecc4ea935c86fdc7e2a11262cb"/>
|
||||
<sha256 value="4241dfa94e711d435f29a4604a3e2de5c4aa3c165e23bd066be6fc1fc4309569" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="commons-codec-1.10.pom">
|
||||
@ -1552,6 +1620,7 @@
|
||||
</component>
|
||||
<component group="commons-io" name="commons-io" version="2.4">
|
||||
<artifact name="commons-io-2.4.jar">
|
||||
<pgp value="2db4f1ef0fa761ecc4ea935c86fdc7e2a11262cb"/>
|
||||
<sha256 value="cc6a41dc3eaacc9e440a6bd0d2890b20d36b4ee408fe2d67122f328bb6e01581" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="commons-io-2.4.pom">
|
||||
@ -1569,6 +1638,7 @@
|
||||
</component>
|
||||
<component group="commons-logging" name="commons-logging" version="1.2">
|
||||
<artifact name="commons-logging-1.2.jar">
|
||||
<pgp value="0cc641c3a62453ab390066c4a41f13c999945293"/>
|
||||
<sha256 value="daddea1ea0be0f56978ab3006b8ac92834afeefbd9b7e4e6316fca57df0fa636" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="commons-logging-1.2.pom">
|
||||
@ -1611,6 +1681,7 @@
|
||||
</component>
|
||||
<component group="it.unimi.dsi" name="fastutil" version="7.2.0">
|
||||
<artifact name="fastutil-7.2.0.jar">
|
||||
<pgp value="8254180bfc943b816e0b5e2e5e2f2b3d474efe6b"/>
|
||||
<sha256 value="74fa208043740642f7e6eb09faba15965218ad2f50ce3020efb100136e4b591c" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="fastutil-7.2.0.pom">
|
||||
@ -1752,6 +1823,7 @@
|
||||
</component>
|
||||
<component group="net.sf.jopt-simple" name="jopt-simple" version="4.9">
|
||||
<artifact name="jopt-simple-4.9.jar">
|
||||
<pgp value="517b94f8d0a46317a28d8ab30da8a5ec02d11ead"/>
|
||||
<sha256 value="26c5856e954b5f864db76f13b86919b59c6eecf9fd930b96baa8884626baf2f5" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="jopt-simple-4.9.pom">
|
||||
@ -1916,6 +1988,7 @@
|
||||
</component>
|
||||
<component group="org.apache.commons" name="commons-compress" version="1.12">
|
||||
<artifact name="commons-compress-1.12.jar">
|
||||
<pgp value="ce8075a251547bee249bc151a2115ae15f6b8b72"/>
|
||||
<sha256 value="2c1542faf343185b7cab9c3d55c8ae5471d6d095d3887a4adefdbdf2984dc0b6" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="commons-compress-1.12.pom">
|
||||
@ -2195,6 +2268,7 @@
|
||||
</component>
|
||||
<component group="org.checkerframework" name="checker-qual" version="2.5.2">
|
||||
<artifact name="checker-qual-2.5.2.jar">
|
||||
<pgp value="19beab2d799c020f17c69126b16698a4adf4d638"/>
|
||||
<sha256 value="64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="checker-qual-2.5.2.pom">
|
||||
@ -2224,6 +2298,7 @@
|
||||
</component>
|
||||
<component group="org.codehaus.groovy" name="groovy-all" version="2.4.15">
|
||||
<artifact name="groovy-all-2.4.15.jar">
|
||||
<pgp value="7faa0f2206de228f0db01ad741321490758aad6f"/>
|
||||
<sha256 value="51d6c4e71782e85674239189499854359d380fb75e1a703756e3aaa5b98a5af0" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="groovy-all-2.4.15.pom">
|
||||
@ -2242,6 +2317,7 @@
|
||||
</component>
|
||||
<component group="org.codehaus.mojo" name="animal-sniffer-annotations" version="1.17">
|
||||
<artifact name="animal-sniffer-annotations-1.17.jar">
|
||||
<pgp value="f254b35617dc255d9344bcfa873a8e86b4372146"/>
|
||||
<sha256 value="92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="animal-sniffer-annotations-1.17.pom">
|
||||
@ -2374,6 +2450,7 @@
|
||||
</component>
|
||||
<component group="org.jdom" name="jdom2" version="2.0.6">
|
||||
<artifact name="jdom2-2.0.6.jar">
|
||||
<pgp value="5897253bea3046aeea95a067e93671c7272b7b3f"/>
|
||||
<sha256 value="1345f11ba606d15603d6740551a8c21947c0215640770ec67271fe78bea97cf5" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="jdom2-2.0.6.pom">
|
||||
@ -2382,6 +2459,7 @@
|
||||
</component>
|
||||
<component group="org.jetbrains" name="annotations" version="13.0">
|
||||
<artifact name="annotations-13.0.jar">
|
||||
<pgp value="2e3a1affe42b5f53af19f780bcf4173966770193"/>
|
||||
<sha256 value="ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="annotations-13.0.pom">
|
||||
|
Loading…
x
Reference in New Issue
Block a user