Merge branch 'handle-low-storage' into 'master'
clean cache when the device has low storage; plus 1.2 bug fixes Closes #1139, #1395, and #1400 See merge request fdroid/fdroidclient!667
This commit is contained in:
commit
4fa86f548b
@ -251,6 +251,12 @@
|
|||||||
<!-- Doesn't require an intent-filter because it is explicitly invoked via Intent.setClass() -->
|
<!-- Doesn't require an intent-filter because it is explicitly invoked via Intent.setClass() -->
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
|
<receiver android:name=".receiver.DeviceStorageReceiver">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.DEVICE_STORAGE_LOW"/>
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
|
||||||
<service android:name=".UpdateService"/>
|
<service android:name=".UpdateService"/>
|
||||||
<service
|
<service
|
||||||
android:name=".net.DownloaderService"
|
android:name=".net.DownloaderService"
|
||||||
@ -261,6 +267,9 @@
|
|||||||
<service
|
<service
|
||||||
android:name=".CleanCacheService"
|
android:name=".CleanCacheService"
|
||||||
android:exported="false"/>
|
android:exported="false"/>
|
||||||
|
<service
|
||||||
|
android:name=".DeleteCacheService"
|
||||||
|
android:exported="false"/>
|
||||||
<service android:name=".net.WifiStateChangeService"/>
|
<service android:name=".net.WifiStateChangeService"/>
|
||||||
<service android:name=".localrepo.SwapService"/>
|
<service android:name=".localrepo.SwapService"/>
|
||||||
<service
|
<service
|
||||||
@ -279,6 +288,7 @@
|
|||||||
android:name=".AppUpdateStatusService"
|
android:name=".AppUpdateStatusService"
|
||||||
android:exported="false"/>
|
android:exported="false"/>
|
||||||
|
|
||||||
|
|
||||||
<!-- Warning: Please add all new services to HidingManager -->
|
<!-- Warning: Please add all new services to HidingManager -->
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
@ -534,9 +544,11 @@
|
|||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".views.main.MainActivity"/>
|
android:value=".views.main.MainActivity"/>
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="info.guardianproject.panic.action.CONNECT"/>
|
<action android:name="info.guardianproject.panic.action.CONNECT"/>
|
||||||
<action android:name="info.guardianproject.panic.action.DISCONNECT"/>
|
<action android:name="info.guardianproject.panic.action.DISCONNECT"/>
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
@ -544,16 +556,17 @@
|
|||||||
android:name=".views.panic.PanicResponderActivity"
|
android:name=".views.panic.PanicResponderActivity"
|
||||||
android:noHistory="true"
|
android:noHistory="true"
|
||||||
android:theme="@android:style/Theme.NoDisplay">
|
android:theme="@android:style/Theme.NoDisplay">
|
||||||
|
|
||||||
<!-- this can never have launchMode singleTask or singleInstance! -->
|
<!-- this can never have launchMode singleTask or singleInstance! -->
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="info.guardianproject.panic.action.TRIGGER"/>
|
<action android:name="info.guardianproject.panic.action.TRIGGER"/>
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".views.panic.ExitActivity"
|
android:name=".views.panic.ExitActivity"
|
||||||
android:theme="@android:style/Theme.NoDisplay"/>
|
android:theme="@android:style/Theme.NoDisplay"/>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".views.hiding.CalculatorActivity"
|
android:name=".views.hiding.CalculatorActivity"
|
||||||
android:enabled="false"
|
android:enabled="false"
|
||||||
@ -562,6 +575,7 @@
|
|||||||
android:theme="@style/AppThemeLight">
|
android:theme="@style/AppThemeLight">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
@ -115,8 +115,8 @@ public final class AppUpdateStatusManager {
|
|||||||
public final Apk apk;
|
public final Apk apk;
|
||||||
public Status status;
|
public Status status;
|
||||||
public PendingIntent intent;
|
public PendingIntent intent;
|
||||||
public int progressCurrent;
|
public long progressCurrent;
|
||||||
public int progressMax;
|
public long progressMax;
|
||||||
public String errorText;
|
public String errorText;
|
||||||
|
|
||||||
AppUpdateStatus(App app, Apk apk, Status status, PendingIntent intent) {
|
AppUpdateStatus(App app, Apk apk, Status status, PendingIntent intent) {
|
||||||
@ -143,8 +143,8 @@ public final class AppUpdateStatusManager {
|
|||||||
apk = in.readParcelable(getClass().getClassLoader());
|
apk = in.readParcelable(getClass().getClassLoader());
|
||||||
intent = in.readParcelable(getClass().getClassLoader());
|
intent = in.readParcelable(getClass().getClassLoader());
|
||||||
status = (Status) in.readSerializable();
|
status = (Status) in.readSerializable();
|
||||||
progressCurrent = in.readInt();
|
progressCurrent = in.readLong();
|
||||||
progressMax = in.readInt();
|
progressMax = in.readLong();
|
||||||
errorText = in.readString();
|
errorText = in.readString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,8 +154,8 @@ public final class AppUpdateStatusManager {
|
|||||||
dest.writeParcelable(apk, 0);
|
dest.writeParcelable(apk, 0);
|
||||||
dest.writeParcelable(intent, 0);
|
dest.writeParcelable(intent, 0);
|
||||||
dest.writeSerializable(status);
|
dest.writeSerializable(status);
|
||||||
dest.writeInt(progressCurrent);
|
dest.writeLong(progressCurrent);
|
||||||
dest.writeInt(progressMax);
|
dest.writeLong(progressMax);
|
||||||
dest.writeString(errorText);
|
dest.writeString(errorText);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,7 +391,7 @@ public final class AppUpdateStatusManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateApkProgress(String key, int max, int current) {
|
public void updateApkProgress(String key, long max, long current) {
|
||||||
synchronized (appMapping) {
|
synchronized (appMapping) {
|
||||||
AppUpdateStatus entry = appMapping.get(key);
|
AppUpdateStatus entry = appMapping.get(key);
|
||||||
if (entry != null) {
|
if (entry != null) {
|
||||||
|
@ -8,7 +8,6 @@ import android.content.Intent;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.fdroid.fdroid.installer.ApkCache;
|
import org.fdroid.fdroid.installer.ApkCache;
|
||||||
|
|
||||||
@ -51,6 +50,10 @@ public class CleanCacheService extends IntentService {
|
|||||||
SystemClock.elapsedRealtime() + 5000, interval, pending);
|
SystemClock.elapsedRealtime() + 5000, interval, pending);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void start(Context context) {
|
||||||
|
context.startService(new Intent(context, CleanCacheService.class));
|
||||||
|
}
|
||||||
|
|
||||||
public CleanCacheService() {
|
public CleanCacheService() {
|
||||||
super("CleanCacheService");
|
super("CleanCacheService");
|
||||||
}
|
}
|
||||||
|
44
app/src/main/java/org/fdroid/fdroid/DeleteCacheService.java
Normal file
44
app/src/main/java/org/fdroid/fdroid/DeleteCacheService.java
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package org.fdroid.fdroid;
|
||||||
|
|
||||||
|
import android.app.IntentService;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Process;
|
||||||
|
import android.support.v4.content.ContextCompat;
|
||||||
|
import android.util.Log;
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link IntentService} subclass for deleting the full cache for this app.
|
||||||
|
*/
|
||||||
|
public class DeleteCacheService extends IntentService {
|
||||||
|
public static final String TAG = "DeleteCacheService";
|
||||||
|
|
||||||
|
public DeleteCacheService() {
|
||||||
|
super("DeleteCacheService");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void deleteAll(Context context) {
|
||||||
|
Intent intent = new Intent(context, DeleteCacheService.class);
|
||||||
|
context.startService(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onHandleIntent(Intent intent) {
|
||||||
|
if (intent == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Process.setThreadPriority(Process.THREAD_PRIORITY_LOWEST);
|
||||||
|
Log.w(TAG, "Deleting all cached contents!");
|
||||||
|
try {
|
||||||
|
FileUtils.deleteDirectory(getCacheDir());
|
||||||
|
for (File dir : ContextCompat.getExternalCacheDirs(this)) {
|
||||||
|
FileUtils.deleteDirectory(dir);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -41,6 +41,10 @@ import android.util.Log;
|
|||||||
import android.view.Display;
|
import android.view.Display;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
import com.nostra13.universalimageloader.cache.disc.DiskCache;
|
||||||
|
import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache;
|
||||||
|
import com.nostra13.universalimageloader.cache.disc.impl.ext.LruDiskCache;
|
||||||
|
import com.nostra13.universalimageloader.core.DefaultConfigurationFactory;
|
||||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||||
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
|
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
|
||||||
import com.nostra13.universalimageloader.core.process.BitmapProcessor;
|
import com.nostra13.universalimageloader.core.process.BitmapProcessor;
|
||||||
@ -399,9 +403,26 @@ public class FDroidApp extends Application {
|
|||||||
if (height > maxSize) {
|
if (height > maxSize) {
|
||||||
maxSize = height;
|
maxSize = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DiskCache diskCache;
|
||||||
|
long available = Utils.getImageCacheDirAvailableMemory(this);
|
||||||
|
int percentageFree = Utils.getPercent(available, Utils.getImageCacheDirTotalMemory(this));
|
||||||
|
if (percentageFree > 5) {
|
||||||
|
diskCache = new UnlimitedDiskCache(Utils.getImageCacheDir(this));
|
||||||
|
} else {
|
||||||
|
Log.i(TAG, "Switching to LruDiskCache(" + available / 2L + ") to save disk space!");
|
||||||
|
try {
|
||||||
|
diskCache = new LruDiskCache(Utils.getImageCacheDir(this),
|
||||||
|
DefaultConfigurationFactory.createFileNameGenerator(),
|
||||||
|
available / 2L);
|
||||||
|
} catch (IOException e) {
|
||||||
|
diskCache = new UnlimitedDiskCache(Utils.getImageCacheDir(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())
|
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())
|
||||||
.imageDownloader(new ImageLoaderForUIL(getApplicationContext()))
|
.imageDownloader(new ImageLoaderForUIL(getApplicationContext()))
|
||||||
.defaultDisplayImageOptions(Utils.getDefaultDisplayImageOptionsBuilder().build())
|
.defaultDisplayImageOptions(Utils.getDefaultDisplayImageOptionsBuilder().build())
|
||||||
|
.diskCache(diskCache)
|
||||||
.diskCacheExtraOptions(maxSize, maxSize, new BitmapProcessor() {
|
.diskCacheExtraOptions(maxSize, maxSize, new BitmapProcessor() {
|
||||||
@Override
|
@Override
|
||||||
public Bitmap process(Bitmap bitmap) {
|
public Bitmap process(Bitmap bitmap) {
|
||||||
|
@ -327,7 +327,8 @@ class NotificationHelper {
|
|||||||
if (entry.progressMax == 0) {
|
if (entry.progressMax == 0) {
|
||||||
builder.setProgress(100, 0, true);
|
builder.setProgress(100, 0, true);
|
||||||
} else {
|
} else {
|
||||||
builder.setProgress(entry.progressMax, entry.progressCurrent, false);
|
builder.setProgress(Utils.bytesToKb(entry.progressMax),
|
||||||
|
Utils.bytesToKb(entry.progressCurrent), false);
|
||||||
}
|
}
|
||||||
} else if (status == AppUpdateStatusManager.Status.Installing) {
|
} else if (status == AppUpdateStatusManager.Status.Installing) {
|
||||||
builder.setProgress(100, 0, true); // indeterminate bar
|
builder.setProgress(100, 0, true); // indeterminate bar
|
||||||
|
@ -54,7 +54,6 @@ import org.fdroid.fdroid.views.main.MainActivity;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@SuppressWarnings("LineLength")
|
|
||||||
public class UpdateService extends IntentService {
|
public class UpdateService extends IntentService {
|
||||||
|
|
||||||
private static final String TAG = "UpdateService";
|
private static final String TAG = "UpdateService";
|
||||||
@ -178,7 +177,8 @@ public class UpdateService extends IntentService {
|
|||||||
if (Build.VERSION.SDK_INT <= 10) {
|
if (Build.VERSION.SDK_INT <= 10) {
|
||||||
Intent pendingIntent = new Intent(this, MainActivity.class);
|
Intent pendingIntent = new Intent(this, MainActivity.class);
|
||||||
pendingIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
pendingIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
notificationBuilder.setContentIntent(PendingIntent.getActivity(this, 0, pendingIntent, PendingIntent.FLAG_UPDATE_CURRENT));
|
notificationBuilder.setContentIntent(
|
||||||
|
PendingIntent.getActivity(this, 0, pendingIntent, PendingIntent.FLAG_UPDATE_CURRENT));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,7 +238,7 @@ public class UpdateService extends IntentService {
|
|||||||
case STATUS_INFO:
|
case STATUS_INFO:
|
||||||
notificationBuilder.setContentText(message)
|
notificationBuilder.setContentText(message)
|
||||||
.setCategory(NotificationCompat.CATEGORY_SERVICE);
|
.setCategory(NotificationCompat.CATEGORY_SERVICE);
|
||||||
if (progress != -1) {
|
if (progress > -1) {
|
||||||
notificationBuilder.setProgress(100, progress, false);
|
notificationBuilder.setProgress(100, progress, false);
|
||||||
} else {
|
} else {
|
||||||
notificationBuilder.setProgress(100, 0, true);
|
notificationBuilder.setProgress(100, 0, true);
|
||||||
@ -448,7 +448,7 @@ public class UpdateService extends IntentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!changes) {
|
if (!changes) {
|
||||||
Utils.debugLog(TAG, "Not checking app details or compatibility, because all repos were up to date.");
|
Utils.debugLog(TAG, "Not checking app details or compatibility, because repos were up to date.");
|
||||||
} else {
|
} else {
|
||||||
notifyContentProviders();
|
notifyContentProviders();
|
||||||
|
|
||||||
@ -522,15 +522,17 @@ public class UpdateService extends IntentService {
|
|||||||
String downloadedSizeFriendly = Utils.getFriendlySize(bytesRead);
|
String downloadedSizeFriendly = Utils.getFriendlySize(bytesRead);
|
||||||
int percent = -1;
|
int percent = -1;
|
||||||
if (totalBytes > 0) {
|
if (totalBytes > 0) {
|
||||||
percent = (int) (bytesRead / (totalBytes * 100L));
|
percent = Utils.getPercent(bytesRead, totalBytes);
|
||||||
}
|
}
|
||||||
String message;
|
String message;
|
||||||
if (totalBytes == -1) {
|
if (totalBytes == -1) {
|
||||||
message = context.getString(R.string.status_download_unknown_size, updater.indexUrl, downloadedSizeFriendly);
|
message = context.getString(R.string.status_download_unknown_size,
|
||||||
|
updater.indexUrl, downloadedSizeFriendly);
|
||||||
percent = -1;
|
percent = -1;
|
||||||
} else {
|
} else {
|
||||||
String totalSizeFriendly = Utils.getFriendlySize(totalBytes);
|
String totalSizeFriendly = Utils.getFriendlySize(totalBytes);
|
||||||
message = context.getString(R.string.status_download, updater.indexUrl, downloadedSizeFriendly, totalSizeFriendly, percent);
|
message = context.getString(R.string.status_download,
|
||||||
|
updater.indexUrl, downloadedSizeFriendly, totalSizeFriendly, percent);
|
||||||
}
|
}
|
||||||
sendStatus(context, STATUS_INFO, message, percent);
|
sendStatus(context, STATUS_INFO, message, percent);
|
||||||
}
|
}
|
||||||
@ -542,23 +544,28 @@ public class UpdateService extends IntentService {
|
|||||||
String totalSize = Utils.getFriendlySize(totalBytes);
|
String totalSize = Utils.getFriendlySize(totalBytes);
|
||||||
int percent = -1;
|
int percent = -1;
|
||||||
if (totalBytes > 0) {
|
if (totalBytes > 0) {
|
||||||
percent = (int) (bytesRead / (totalBytes * 100L));
|
percent = Utils.getPercent(bytesRead, totalBytes);
|
||||||
}
|
}
|
||||||
String message = context.getString(R.string.status_processing_xml_percent, updater.indexUrl, downloadedSize, totalSize, percent);
|
String message = context.getString(R.string.status_processing_xml_percent,
|
||||||
|
updater.indexUrl, downloadedSize, totalSize, percent);
|
||||||
sendStatus(context, STATUS_INFO, message, percent);
|
sendStatus(context, STATUS_INFO, message, percent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If an updater is unable to know how many apps it has to process (i.e. it is streaming apps to the database or
|
* If an updater is unable to know how many apps it has to process (i.e. it
|
||||||
* performing a large database query which touches all apps, but is unable to report progress), then it call this
|
* is streaming apps to the database or performing a large database query
|
||||||
* listener with `totalBytes = 0`. Doing so will result in a message of "Saving app details" sent to the user. If
|
* which touches all apps, but is unable to report progress), then it call
|
||||||
* you know how many apps you have processed, then a message of "Saving app details (x/total)" is displayed.
|
* this listener with `totalBytes = 0`. Doing so will result in a message of
|
||||||
|
* "Saving app details" sent to the user. If you know how many apps you have
|
||||||
|
* processed, then a message of "Saving app details (x/total)" is displayed.
|
||||||
*/
|
*/
|
||||||
public static void reportProcessingAppsProgress(Context context, RepoUpdater updater, int appsSaved, int totalApps) {
|
public static void reportProcessingAppsProgress(Context context, RepoUpdater updater,
|
||||||
|
int appsSaved, int totalApps) {
|
||||||
Utils.debugLog(TAG, "Committing " + updater.indexUrl + "(" + appsSaved + "/" + totalApps + ")");
|
Utils.debugLog(TAG, "Committing " + updater.indexUrl + "(" + appsSaved + "/" + totalApps + ")");
|
||||||
if (totalApps > 0) {
|
if (totalApps > 0) {
|
||||||
String message = context.getString(R.string.status_inserting_x_apps, appsSaved, totalApps, updater.indexUrl);
|
String message = context.getString(R.string.status_inserting_x_apps,
|
||||||
sendStatus(context, STATUS_INFO, message, (int) ((double) appsSaved / totalApps * 100));
|
appsSaved, totalApps, updater.indexUrl);
|
||||||
|
sendStatus(context, STATUS_INFO, message, Utils.getPercent(appsSaved, totalApps));
|
||||||
} else {
|
} else {
|
||||||
String message = context.getString(R.string.status_inserting_apps);
|
String message = context.getString(R.string.status_inserting_apps);
|
||||||
sendStatus(context, STATUS_INFO, message);
|
sendStatus(context, STATUS_INFO, message);
|
||||||
|
@ -24,6 +24,8 @@ import android.content.res.Resources;
|
|||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.StatFs;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.annotation.RequiresApi;
|
import android.support.annotation.RequiresApi;
|
||||||
@ -133,6 +135,38 @@ public final class Utils {
|
|||||||
return new File(cacheDir, "icons");
|
return new File(cacheDir, "icons");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static long getImageCacheDirAvailableMemory(Context context) {
|
||||||
|
File statDir = getImageCacheDir(context);
|
||||||
|
while (statDir != null && !statDir.exists()) {
|
||||||
|
statDir = statDir.getParentFile();
|
||||||
|
}
|
||||||
|
if (statDir == null) {
|
||||||
|
return 50 * 1024 * 1024; // just return a minimal amount
|
||||||
|
}
|
||||||
|
StatFs stat = new StatFs(statDir.getPath());
|
||||||
|
if (Build.VERSION.SDK_INT < 18) {
|
||||||
|
return stat.getAvailableBlocks() * stat.getBlockSize();
|
||||||
|
} else {
|
||||||
|
return stat.getAvailableBlocksLong() * stat.getBlockSizeLong();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getImageCacheDirTotalMemory(Context context) {
|
||||||
|
File statDir = getImageCacheDir(context);
|
||||||
|
while (statDir != null && !statDir.exists()) {
|
||||||
|
statDir = statDir.getParentFile();
|
||||||
|
}
|
||||||
|
if (statDir == null) {
|
||||||
|
return 100 * 1024 * 1024; // just return a minimal amount
|
||||||
|
}
|
||||||
|
StatFs stat = new StatFs(statDir.getPath());
|
||||||
|
if (Build.VERSION.SDK_INT < 18) {
|
||||||
|
return stat.getBlockCount() * stat.getBlockSize();
|
||||||
|
} else {
|
||||||
|
return stat.getBlockCountLong() * stat.getBlockSizeLong();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void copy(InputStream input, OutputStream output) throws IOException {
|
public static void copy(InputStream input, OutputStream output) throws IOException {
|
||||||
byte[] buffer = new byte[BUFFER_SIZE];
|
byte[] buffer = new byte[BUFFER_SIZE];
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -693,6 +727,27 @@ public final class Utils {
|
|||||||
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics());
|
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a {@code long} bytes value, like from {@link File#length()}, to
|
||||||
|
* an {@code int} value that is kilobytes, suitable for things like
|
||||||
|
* {@link android.widget.ProgressBar#setMax(int)} or
|
||||||
|
* {@link android.support.v4.app.NotificationCompat.Builder#setProgress(int, int, boolean)}
|
||||||
|
*/
|
||||||
|
public static int bytesToKb(long bytes) {
|
||||||
|
return (int) (bytes / 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts two {@code long} bytes values, like from {@link File#length()}, to
|
||||||
|
* an {@code int} value that is a percentage, suitable for things like
|
||||||
|
* {@link android.widget.ProgressBar#setMax(int)} or
|
||||||
|
* {@link android.support.v4.app.NotificationCompat.Builder#setProgress(int, int, boolean)}.
|
||||||
|
* {@code total} must never be zero!
|
||||||
|
*/
|
||||||
|
public static int getPercent(long current, long total) {
|
||||||
|
return (int) ((100L * current + total / 2) / total);
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public static class Profiler {
|
public static class Profiler {
|
||||||
public final long startTime = System.currentTimeMillis();
|
public final long startTime = System.currentTimeMillis();
|
||||||
|
@ -20,6 +20,7 @@ import org.fdroid.fdroid.Utils;
|
|||||||
import org.fdroid.fdroid.data.Schema.ApkTable.Cols;
|
import org.fdroid.fdroid.data.Schema.ApkTable.Cols;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
@ -467,6 +468,9 @@ public class Apk extends ValueObject implements Comparable<Apk>, Parcelable {
|
|||||||
|
|
||||||
private void setRequestedPermissions(Object[][] permissions, int minSdk) {
|
private void setRequestedPermissions(Object[][] permissions, int minSdk) {
|
||||||
HashSet<String> set = new HashSet<>();
|
HashSet<String> set = new HashSet<>();
|
||||||
|
if (requestedPermissions != null) {
|
||||||
|
Collections.addAll(set, requestedPermissions);
|
||||||
|
}
|
||||||
for (Object[] versions : permissions) {
|
for (Object[] versions : permissions) {
|
||||||
int maxSdk = Integer.MAX_VALUE;
|
int maxSdk = Integer.MAX_VALUE;
|
||||||
if (versions[1] != null) {
|
if (versions[1] != null) {
|
||||||
|
@ -238,8 +238,8 @@ public class InstallManagerService extends Service {
|
|||||||
Utils.debugLog(TAG, action + " " + intent);
|
Utils.debugLog(TAG, action + " " + intent);
|
||||||
} else if (Downloader.ACTION_PROGRESS.equals(action)) {
|
} else if (Downloader.ACTION_PROGRESS.equals(action)) {
|
||||||
|
|
||||||
int bytesRead = intent.getIntExtra(Downloader.EXTRA_BYTES_READ, 0);
|
long bytesRead = intent.getLongExtra(Downloader.EXTRA_BYTES_READ, 0);
|
||||||
int totalBytes = intent.getIntExtra(Downloader.EXTRA_TOTAL_BYTES, 0);
|
long totalBytes = intent.getLongExtra(Downloader.EXTRA_TOTAL_BYTES, 0);
|
||||||
appUpdateStatusManager.updateApkProgress(urlString, totalBytes, bytesRead);
|
appUpdateStatusManager.updateApkProgress(urlString, totalBytes, bytesRead);
|
||||||
} else if (Downloader.ACTION_COMPLETE.equals(action)) {
|
} else if (Downloader.ACTION_COMPLETE.equals(action)) {
|
||||||
localBroadcastManager.unregisterReceiver(this);
|
localBroadcastManager.unregisterReceiver(this);
|
||||||
@ -307,8 +307,8 @@ public class InstallManagerService extends Service {
|
|||||||
appUpdateStatusManager.updateApk(urlString, AppUpdateStatusManager.Status.Downloading, action);
|
appUpdateStatusManager.updateApk(urlString, AppUpdateStatusManager.Status.Downloading, action);
|
||||||
break;
|
break;
|
||||||
case Downloader.ACTION_PROGRESS:
|
case Downloader.ACTION_PROGRESS:
|
||||||
int bytesRead = intent.getIntExtra(Downloader.EXTRA_BYTES_READ, 0);
|
long bytesRead = intent.getLongExtra(Downloader.EXTRA_BYTES_READ, 0);
|
||||||
int totalBytes = intent.getIntExtra(Downloader.EXTRA_TOTAL_BYTES, 0);
|
long totalBytes = intent.getLongExtra(Downloader.EXTRA_TOTAL_BYTES, 0);
|
||||||
appUpdateStatusManager.updateApkProgress(urlString, totalBytes, bytesRead);
|
appUpdateStatusManager.updateApkProgress(urlString, totalBytes, bytesRead);
|
||||||
break;
|
break;
|
||||||
case Downloader.ACTION_COMPLETE:
|
case Downloader.ACTION_COMPLETE:
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
public class DeviceStorageReceiver extends BroadcastReceiver {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
if (intent == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String action = intent.getAction();
|
||||||
|
if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(action)) {
|
||||||
|
int percentageFree = Utils.getPercent(Utils.getImageCacheDirAvailableMemory(context),
|
||||||
|
Utils.getImageCacheDirTotalMemory(context));
|
||||||
|
if (percentageFree > 2) {
|
||||||
|
CleanCacheService.start(context);
|
||||||
|
} else {
|
||||||
|
DeleteCacheService.deleteAll(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -46,7 +46,6 @@ import org.fdroid.fdroid.privileged.views.AppDiff;
|
|||||||
import org.fdroid.fdroid.privileged.views.AppSecurityPermissions;
|
import org.fdroid.fdroid.privileged.views.AppSecurityPermissions;
|
||||||
import org.fdroid.fdroid.views.main.MainActivity;
|
import org.fdroid.fdroid.views.main.MainActivity;
|
||||||
|
|
||||||
import java.text.NumberFormat;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@ -205,7 +204,7 @@ public class AppDetailsRecyclerViewAdapter
|
|||||||
setProgress(0, 0, 0);
|
setProgress(0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProgress(int bytesDownloaded, int totalBytes, int resIdString) {
|
public void setProgress(long bytesDownloaded, long totalBytes, int resIdString) {
|
||||||
if (headerView != null) {
|
if (headerView != null) {
|
||||||
headerView.setProgress(bytesDownloaded, totalBytes, resIdString);
|
headerView.setProgress(bytesDownloaded, totalBytes, resIdString);
|
||||||
}
|
}
|
||||||
@ -358,14 +357,14 @@ public class AppDetailsRecyclerViewAdapter
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProgress(int bytesDownloaded, int totalBytes, int resIdString) {
|
public void setProgress(long bytesDownloaded, long totalBytes, int resIdString) {
|
||||||
if (bytesDownloaded == 0 && totalBytes == 0) {
|
if (bytesDownloaded == 0 && totalBytes == 0) {
|
||||||
// Remove progress bar
|
// Remove progress bar
|
||||||
progressLayout.setVisibility(View.GONE);
|
progressLayout.setVisibility(View.GONE);
|
||||||
buttonLayout.setVisibility(View.VISIBLE);
|
buttonLayout.setVisibility(View.VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
progressBar.setMax(totalBytes);
|
progressBar.setMax(Utils.bytesToKb(totalBytes));
|
||||||
progressBar.setProgress(bytesDownloaded);
|
progressBar.setProgress(Utils.bytesToKb(bytesDownloaded));
|
||||||
progressBar.setIndeterminate(totalBytes == -1);
|
progressBar.setIndeterminate(totalBytes == -1);
|
||||||
progressLabel.setContentDescription("");
|
progressLabel.setContentDescription("");
|
||||||
if (resIdString != 0) {
|
if (resIdString != 0) {
|
||||||
@ -373,12 +372,12 @@ public class AppDetailsRecyclerViewAdapter
|
|||||||
progressLabel.setContentDescription(context.getString(R.string.downloading));
|
progressLabel.setContentDescription(context.getString(R.string.downloading));
|
||||||
progressPercent.setText("");
|
progressPercent.setText("");
|
||||||
} else if (totalBytes > 0 && bytesDownloaded >= 0) {
|
} else if (totalBytes > 0 && bytesDownloaded >= 0) {
|
||||||
float percent = (float) bytesDownloaded / totalBytes;
|
int percent = Utils.getPercent(bytesDownloaded, totalBytes);
|
||||||
progressLabel.setText(Utils.getFriendlySize(bytesDownloaded) + " / " + Utils.getFriendlySize(totalBytes));
|
progressLabel.setText(Utils.getFriendlySize(bytesDownloaded)
|
||||||
progressLabel.setContentDescription(context.getString(R.string.app__tts__downloading_progress, (int) percent));
|
+ " / " + Utils.getFriendlySize(totalBytes));
|
||||||
NumberFormat format = NumberFormat.getPercentInstance();
|
progressLabel.setContentDescription(context.getString(R.string.app__tts__downloading_progress,
|
||||||
format.setMaximumFractionDigits(0);
|
percent));
|
||||||
progressPercent.setText(format.format(percent));
|
progressPercent.setText(String.format(Locale.ENGLISH, "%d%%", percent));
|
||||||
} else if (bytesDownloaded >= 0) {
|
} else if (bytesDownloaded >= 0) {
|
||||||
progressLabel.setText(Utils.getFriendlySize(bytesDownloaded));
|
progressLabel.setText(Utils.getFriendlySize(bytesDownloaded));
|
||||||
progressLabel.setContentDescription(context.getString(R.string.downloading));
|
progressLabel.setContentDescription(context.getString(R.string.downloading));
|
||||||
|
@ -365,7 +365,8 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
|
|||||||
|
|
||||||
return new AppListItemState(app)
|
return new AppListItemState(app)
|
||||||
.setMainText(mainText)
|
.setMainText(mainText)
|
||||||
.setProgress(currentStatus.progressCurrent, currentStatus.progressMax);
|
.setProgress(Utils.bytesToKb(currentStatus.progressCurrent),
|
||||||
|
Utils.bytesToKb(currentStatus.progressMax));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AppListItemState getViewStateReadyToInstall(@NonNull App app) {
|
protected AppListItemState getViewStateReadyToInstall(@NonNull App app) {
|
||||||
|
@ -253,13 +253,12 @@ public class SwapAppsView extends ListView implements
|
|||||||
if (progressView.getVisibility() != View.VISIBLE) {
|
if (progressView.getVisibility() != View.VISIBLE) {
|
||||||
showProgress();
|
showProgress();
|
||||||
}
|
}
|
||||||
int read = intent.getIntExtra(Downloader.EXTRA_BYTES_READ, 0);
|
long read = intent.getLongExtra(Downloader.EXTRA_BYTES_READ, 0);
|
||||||
int total = intent.getIntExtra(Downloader.EXTRA_TOTAL_BYTES, 0);
|
long total = intent.getLongExtra(Downloader.EXTRA_TOTAL_BYTES, 0);
|
||||||
if (total > 0) {
|
if (total > 0) {
|
||||||
int progress = (int) ((double) read / total * 100);
|
|
||||||
progressView.setIndeterminate(false);
|
progressView.setIndeterminate(false);
|
||||||
progressView.setMax(100);
|
progressView.setMax(100);
|
||||||
progressView.setProgress(progress);
|
progressView.setProgress(Utils.getPercent(read, total));
|
||||||
} else {
|
} else {
|
||||||
progressView.setIndeterminate(true);
|
progressView.setIndeterminate(true);
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ do
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
gradle checkstyle pmd lint || exit 1
|
./gradlew checkstyle pmd lint || exit 1
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user