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:
Hans-Christoph Steiner 2018-04-06 10:00:39 +00:00
commit 4fa86f548b
15 changed files with 222 additions and 47 deletions

View File

@ -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>

View File

@ -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) {

View File

@ -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");
} }

View 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
}
}
}

View File

@ -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) {

View File

@ -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

View File

@ -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);

View File

@ -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();

View File

@ -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) {

View File

@ -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:

View File

@ -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);
}
}
}
}

View File

@ -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));

View File

@ -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) {

View File

@ -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);
} }

View File

@ -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