diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index a8e9d7557..bb851b750 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -270,7 +270,12 @@
-
+
+
= 16 && cm.isActiveNetworkMetered()) {
- return FLAG_NET_METERED;
- } else {
- return FLAG_NET_NO_LIMIT;
- }
- default:
- return FLAG_NET_METERED;
- }
- }
-
/**
* In order to send a {@link Toast} from a {@link IntentService}, we have to do these tricks.
*/
@@ -371,8 +339,8 @@ public class UpdateService extends IntentService {
try {
// See if it's time to actually do anything yet...
- int netState = getNetworkState(this);
- if (netState == FLAG_NET_UNAVAILABLE) {
+ int netState = ConnectivityMonitorService.getNetworkState(this);
+ if (netState == ConnectivityMonitorService.FLAG_NET_UNAVAILABLE) {
Utils.debugLog(TAG, "No internet, cannot update");
if (manualUpdate) {
sendNoInternetToast();
@@ -380,10 +348,10 @@ public class UpdateService extends IntentService {
return;
}
+ final Preferences fdroidPrefs = Preferences.get();
if (manualUpdate || forcedUpdate) {
Utils.debugLog(TAG, "manually requested or forced update");
- } else if (!verifyIsTimeForScheduledRun()
- || (netState == FLAG_NET_METERED && Preferences.get().isUpdateOnlyOnUnmeteredNetworks())) {
+ } else if (!verifyIsTimeForScheduledRun() || !fdroidPrefs.isBackgroundDownloadAllowed()) {
Utils.debugLog(TAG, "don't run update");
return;
}
@@ -403,7 +371,6 @@ public class UpdateService extends IntentService {
ArrayList repoErrors = new ArrayList<>();
boolean changes = false;
boolean singleRepoUpdate = !TextUtils.isEmpty(address);
- final Preferences fdroidPrefs = Preferences.get();
for (final Repo repo : repos) {
if (!repo.inuse) {
continue;
@@ -442,7 +409,7 @@ public class UpdateService extends IntentService {
}
// now that downloading the index is done, start downloading updates
- if (changes && fdroidPrefs.isAutoDownloadEnabled()) {
+ if (changes && fdroidPrefs.isAutoDownloadEnabled() && fdroidPrefs.isBackgroundDownloadAllowed()) {
autoDownloadUpdates(this);
}
}
diff --git a/app/src/main/java/org/fdroid/fdroid/Utils.java b/app/src/main/java/org/fdroid/fdroid/Utils.java
index 9ca43a347..703b169d7 100644
--- a/app/src/main/java/org/fdroid/fdroid/Utils.java
+++ b/app/src/main/java/org/fdroid/fdroid/Utils.java
@@ -406,6 +406,12 @@ public final class Utils {
return new Locale(languageTag);
}
+ /**
+ * Since there have been vulnerabilities in EXIF processing in Android, this
+ * disables all use of EXIF.
+ *
+ * @see CVE-2016-3862
+ */
public static DisplayImageOptions.Builder getDefaultDisplayImageOptionsBuilder() {
if (defaultDisplayImageOptionsBuilder == null) {
defaultDisplayImageOptionsBuilder = new DisplayImageOptions.Builder()
diff --git a/app/src/main/java/org/fdroid/fdroid/installer/Installer.java b/app/src/main/java/org/fdroid/fdroid/installer/Installer.java
index c60d53883..bb0735b4b 100644
--- a/app/src/main/java/org/fdroid/fdroid/installer/Installer.java
+++ b/app/src/main/java/org/fdroid/fdroid/installer/Installer.java
@@ -106,19 +106,23 @@ public abstract class Installer {
return intent;
}
+ /**
+ * Return if this installation process has any new permissions that the user
+ * should be aware of. Starting in {@code android-23}, all new permissions
+ * are requested when they are used, and the permissions prompt at time of
+ * install is not used. All permissions in a new install are considered new.
+ *
+ * @return the number of new permissions
+ */
private int newPermissionCount() {
boolean supportsRuntimePermissions = apk.targetSdkVersion >= 23;
if (supportsRuntimePermissions) {
return 0;
}
- AppDiff appDiff = new AppDiff(context.getPackageManager(), apk);
- if (appDiff.pkgInfo == null) {
- // could not get diff because we couldn't parse the package
- throw new RuntimeException("cannot parse!");
- }
- AppSecurityPermissions perms = new AppSecurityPermissions(context, appDiff.pkgInfo);
- if (appDiff.installedAppInfo != null) {
+ AppDiff appDiff = new AppDiff(context, apk);
+ AppSecurityPermissions perms = new AppSecurityPermissions(context, appDiff.apkPackageInfo);
+ if (appDiff.installedApplicationInfo != null) {
// update to an existing app
return perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW);
}
diff --git a/app/src/main/java/org/fdroid/fdroid/net/ConnectivityMonitorService.java b/app/src/main/java/org/fdroid/fdroid/net/ConnectivityMonitorService.java
new file mode 100644
index 000000000..13df8ee6f
--- /dev/null
+++ b/app/src/main/java/org/fdroid/fdroid/net/ConnectivityMonitorService.java
@@ -0,0 +1,86 @@
+package org.fdroid.fdroid.net;
+
+import android.app.IntentService;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Build;
+import com.nostra13.universalimageloader.core.ImageLoader;
+import org.fdroid.fdroid.FDroidApp;
+import org.fdroid.fdroid.Preferences;
+
+/**
+ * An {@link IntentService} subclass for tracking whether there is metered or
+ * unmetered internet available, based on
+ * {@link android.net.ConnectivityManager#CONNECTIVITY_ACTION}
+ */
+public class ConnectivityMonitorService extends IntentService {
+ public static final String TAG = "ConnectivityMonitorServ";
+
+ public static final int FLAG_NET_UNAVAILABLE = 0;
+ public static final int FLAG_NET_METERED = 1;
+ public static final int FLAG_NET_NO_LIMIT = 2;
+
+ private static final String ACTION_START = "org.fdroid.fdroid.net.action.CONNECTIVITY_MONITOR";
+
+ private static final BroadcastReceiver CONNECTIVITY_RECEIVER = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ start(context);
+ }
+ };
+
+ public ConnectivityMonitorService() {
+ super("ConnectivityMonitorService");
+ }
+
+ public static void registerAndStart(Context context) {
+ context.registerReceiver(CONNECTIVITY_RECEIVER, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
+ }
+
+ public static void start(Context context) {
+ Intent intent = new Intent(context, ConnectivityMonitorService.class);
+ intent.setAction(ACTION_START);
+ context.startService(intent);
+ }
+
+ /**
+ * Gets the state of internet availability, whether there is no connection at all,
+ * whether the connection has no usage limit (like most WiFi), or whether this is
+ * a metered connection like most cellular plans or hotspot WiFi connections.
+ */
+ public static int getNetworkState(Context context) {
+ ConnectivityManager cm = (ConnectivityManager) context.getSystemService(CONNECTIVITY_SERVICE);
+ if (cm == null) {
+ return FLAG_NET_UNAVAILABLE;
+ }
+ NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
+ if (activeNetwork == null || !activeNetwork.isConnected()) {
+ return FLAG_NET_UNAVAILABLE;
+ }
+
+ int networkType = activeNetwork.getType();
+ switch (networkType) {
+ case ConnectivityManager.TYPE_ETHERNET:
+ case ConnectivityManager.TYPE_WIFI:
+ if (Build.VERSION.SDK_INT >= 16 && cm.isActiveNetworkMetered()) {
+ return FLAG_NET_METERED;
+ } else {
+ return FLAG_NET_NO_LIMIT;
+ }
+ default:
+ return FLAG_NET_METERED;
+ }
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ if (intent != null && ACTION_START.equals(intent.getAction())) {
+ FDroidApp.networkState = getNetworkState(this);
+ ImageLoader.getInstance().denyNetworkDownloads(!Preferences.get().isBackgroundDownloadAllowed());
+ }
+ }
+}
diff --git a/app/src/main/java/org/fdroid/fdroid/privileged/views/AppDiff.java b/app/src/main/java/org/fdroid/fdroid/privileged/views/AppDiff.java
index 036a78533..5e080e69f 100644
--- a/app/src/main/java/org/fdroid/fdroid/privileged/views/AppDiff.java
+++ b/app/src/main/java/org/fdroid/fdroid/privileged/views/AppDiff.java
@@ -18,55 +18,51 @@
package org.fdroid.fdroid.privileged.views;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-
import org.fdroid.fdroid.data.Apk;
+/**
+ * Represents the permissions difference between the installed APK, and the
+ * update APK represented by {@link Apk}.
+ */
public class AppDiff {
- private final PackageManager pm;
- public final PackageInfo pkgInfo;
- public ApplicationInfo installedAppInfo;
+ public final PackageInfo apkPackageInfo;
+ public final ApplicationInfo installedApplicationInfo;
- /**
- * Constructor based on F-Droids Apk object
- */
- public AppDiff(PackageManager pm, Apk apk) {
- this.pm = pm;
+ public AppDiff(Context context, Apk apk) {
+ PackageManager pm = context.getPackageManager();
+ apkPackageInfo = new PackageInfo();
+ apkPackageInfo.packageName = apk.packageName;
+ apkPackageInfo.applicationInfo = new ApplicationInfo();
+ apkPackageInfo.requestedPermissions = apk.requestedPermissions;
- pkgInfo = new PackageInfo();
- pkgInfo.packageName = apk.packageName;
- pkgInfo.applicationInfo = new ApplicationInfo();
- pkgInfo.requestedPermissions = apk.requestedPermissions;
-
- init();
- }
-
- private void init() {
- String pkgName = pkgInfo.packageName;
+ String packageName = apkPackageInfo.packageName;
// Check if there is already a package on the device with this name
// but it has been renamed to something else.
- final String[] oldName = pm.canonicalToCurrentPackageNames(new String[]{pkgName});
+ final String[] oldName = pm.canonicalToCurrentPackageNames(new String[]{packageName});
if (oldName != null && oldName.length > 0 && oldName[0] != null) {
- pkgName = oldName[0];
- pkgInfo.packageName = pkgName;
- pkgInfo.applicationInfo.packageName = pkgName;
+ packageName = oldName[0];
+ apkPackageInfo.packageName = packageName;
+ apkPackageInfo.applicationInfo.packageName = packageName;
}
// Check if package is already installed
+ ApplicationInfo applicationInfo;
try {
// This is a little convoluted because we want to get all uninstalled
// apps, but this may include apps with just data, and if it is just
// data we still want to count it as "installed".
//noinspection WrongConstant (lint is actually wrong here!)
- installedAppInfo = pm.getApplicationInfo(pkgName,
- PackageManager.GET_UNINSTALLED_PACKAGES);
- if ((installedAppInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
- installedAppInfo = null;
+ applicationInfo = pm.getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES);
+ if ((applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
+ applicationInfo = null;
}
} catch (PackageManager.NameNotFoundException e) {
- installedAppInfo = null;
+ applicationInfo = null;
}
+ installedApplicationInfo = applicationInfo;
}
}
diff --git a/app/src/main/java/org/fdroid/fdroid/privileged/views/InstallConfirmActivity.java b/app/src/main/java/org/fdroid/fdroid/privileged/views/InstallConfirmActivity.java
index f9f9791b8..fb450594f 100644
--- a/app/src/main/java/org/fdroid/fdroid/privileged/views/InstallConfirmActivity.java
+++ b/app/src/main/java/org/fdroid/fdroid/privileged/views/InstallConfirmActivity.java
@@ -92,9 +92,9 @@ public class InstallConfirmActivity extends FragmentActivity implements OnCancel
scrollView = null;
okCanInstall = false;
int msg = 0;
- AppSecurityPermissions perms = new AppSecurityPermissions(this, appDiff.pkgInfo);
- if (appDiff.installedAppInfo != null) {
- msg = (appDiff.installedAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
+ AppSecurityPermissions perms = new AppSecurityPermissions(this, appDiff.apkPackageInfo);
+ if (appDiff.installedApplicationInfo != null) {
+ msg = (appDiff.installedApplicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
? R.string.install_confirm_update_system
: R.string.install_confirm_update;
scrollView = new CaffeinatedScrollView(this);
@@ -131,10 +131,10 @@ public class InstallConfirmActivity extends FragmentActivity implements OnCancel
}
if (!permVisible) {
- if (appDiff.installedAppInfo != null) {
+ if (appDiff.installedApplicationInfo != null) {
// This is an update to an application, but there are no
// permissions at all.
- msg = (appDiff.installedAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
+ msg = (appDiff.installedApplicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
? R.string.install_confirm_update_system_no_perms
: R.string.install_confirm_update_no_perms;
} else {
@@ -182,7 +182,7 @@ public class InstallConfirmActivity extends FragmentActivity implements OnCancel
app = AppProvider.Helper.findSpecificApp(getContentResolver(),
apk.packageName, apk.repoId, Schema.AppMetadataTable.Cols.ALL);
- appDiff = new AppDiff(getPackageManager(), apk);
+ appDiff = new AppDiff(this, apk);
setContentView(R.layout.install_start);
diff --git a/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java b/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java
index 110a622ba..12ffd19bc 100644
--- a/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java
+++ b/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java
@@ -789,8 +789,8 @@ public class AppDetailsRecyclerViewAdapter
headerView.setText(R.string.permissions);
updateExpandableItem(false);
contentView.removeAllViews();
- AppDiff appDiff = new AppDiff(context.getPackageManager(), versions.get(0));
- AppSecurityPermissions perms = new AppSecurityPermissions(context, appDiff.pkgInfo);
+ AppDiff appDiff = new AppDiff(context, versions.get(0));
+ AppSecurityPermissions perms = new AppSecurityPermissions(context, appDiff.apkPackageInfo);
contentView.addView(perms.getPermissionsView(AppSecurityPermissions.WHICH_ALL));
}
diff --git a/app/src/main/java/org/fdroid/fdroid/views/ScreenShotsActivity.java b/app/src/main/java/org/fdroid/fdroid/views/ScreenShotsActivity.java
index bc88526df..4e930ce06 100644
--- a/app/src/main/java/org/fdroid/fdroid/views/ScreenShotsActivity.java
+++ b/app/src/main/java/org/fdroid/fdroid/views/ScreenShotsActivity.java
@@ -18,6 +18,7 @@ import android.widget.ImageView;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import org.fdroid.fdroid.FDroidApp;
+import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.data.App;
@@ -36,6 +37,8 @@ public class ScreenShotsActivity extends AppCompatActivity {
private static final String EXTRA_PACKAGE_NAME = "EXTRA_PACKAGE_NAME";
private static final String EXTRA_START_POSITION = "EXTRA_START_POSITION";
+ private static final ImageLoader IMAGE_LOADER = ImageLoader.getInstance();
+
public static Intent getStartIntent(Context context, String packageName, int startPosition) {
Intent intent = new Intent(context, ScreenShotsActivity.class);
intent.putExtra(EXTRA_PACKAGE_NAME, packageName);
@@ -68,6 +71,18 @@ public class ScreenShotsActivity extends AppCompatActivity {
}
}
+ @Override
+ protected void onResume() {
+ super.onResume();
+ IMAGE_LOADER.denyNetworkDownloads(false);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ IMAGE_LOADER.denyNetworkDownloads(!Preferences.get().isBackgroundDownloadAllowed());
+ }
+
private static class ScreenShotPagerAdapter extends FragmentStatePagerAdapter {
private final String[] screenshots;
diff --git a/app/src/main/res/values-sc/strings.xml b/app/src/main/res/values-sc/strings.xml
index 744bd3139..4362f1ef5 100644
--- a/app/src/main/res/values-sc/strings.xml
+++ b/app/src/main/res/values-sc/strings.xml
@@ -568,4 +568,12 @@
Inclue sas aplicatziones chi impreant s\'ischermu tàtile
Ammustra sas aplicatziones chi tenent bisòngiu de s\'ischermu tàtile in cale si siat dispositivu
+ Annanghe un\'ispigru
+ %1$s est giai impostadu, custu at a annànghere informatziones de crae noas.
+ %1$s est giai impostadu, cunfirma chi lu cheres torrare a abilitare.
+ %1$s est giai impostadu e abilitadu.
+ In antis iscantzella %1$s pro annànghere custu cun una crae in cunflitu.
+ Custa est una còpia de %1$s, lu cheres annànghere comente isprigu?
+ Isprigos ufitziales
+ Isprigos impreadores