mirror of
https://github.com/f-droid/fdroidclient.git
synced 2026-04-20 06:47:06 -04:00
[app] Remove code for SDK versions that we don't support anymore
This commit is contained in:
@@ -141,10 +141,6 @@ final public class WifiApControl {
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
|
||||
private static String getDeviceName(WifiManager wifiManager) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD) {
|
||||
Log.w(TAG, "Older device - falling back to the default device name: " + FALLBACK_DEVICE);
|
||||
return FALLBACK_DEVICE;
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
Log.w(TAG, "6.0 or later, unaccessible MAC - falling back to the default device name: " + FALLBACK_DEVICE);
|
||||
return FALLBACK_DEVICE;
|
||||
|
||||
@@ -88,29 +88,21 @@ public class SDCardScannerService extends IntentService {
|
||||
Process.setThreadPriority(Process.THREAD_PRIORITY_LOWEST);
|
||||
|
||||
HashSet<File> files = new HashSet<>();
|
||||
if (Build.VERSION.SDK_INT < 21) {
|
||||
if (Environment.isExternalStorageRemovable()) {
|
||||
File sdcard = Environment.getExternalStorageDirectory();
|
||||
String state = Environment.getExternalStorageState();
|
||||
Collections.addAll(files, checkExternalStorage(sdcard, state));
|
||||
for (File f : getExternalFilesDirs(null)) {
|
||||
Log.i(TAG, "getExternalFilesDirs " + f);
|
||||
if (f == null || !f.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
for (File f : getExternalFilesDirs(null)) {
|
||||
Log.i(TAG, "getExternalFilesDirs " + f);
|
||||
if (f == null || !f.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
Log.i(TAG, "getExternalFilesDirs " + f);
|
||||
if (Environment.isExternalStorageRemovable(f)) {
|
||||
String state = Environment.getExternalStorageState(f);
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
== PackageManager.PERMISSION_GRANTED) {
|
||||
// remove Android/data/org.fdroid.fdroid/files to get root
|
||||
File sdcard = f.getParentFile().getParentFile().getParentFile().getParentFile();
|
||||
Collections.addAll(files, checkExternalStorage(sdcard, state));
|
||||
} else {
|
||||
Collections.addAll(files, checkExternalStorage(f, state));
|
||||
}
|
||||
Log.i(TAG, "getExternalFilesDirs " + f);
|
||||
if (Environment.isExternalStorageRemovable(f)) {
|
||||
String state = Environment.getExternalStorageState(f);
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
== PackageManager.PERMISSION_GRANTED) {
|
||||
// remove Android/data/org.fdroid.fdroid/files to get root
|
||||
File sdcard = f.getParentFile().getParentFile().getParentFile().getParentFile();
|
||||
Collections.addAll(files, checkExternalStorage(sdcard, state));
|
||||
} else {
|
||||
Collections.addAll(files, checkExternalStorage(f, state));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,11 +106,9 @@ public class TreeUriScannerIntentService extends IntentService {
|
||||
}
|
||||
Uri uri = intent.getData();
|
||||
if (uri != null) {
|
||||
if (Build.VERSION.SDK_INT >= 19) {
|
||||
ContentResolver contentResolver = context.getContentResolver();
|
||||
int perms = Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
|
||||
contentResolver.takePersistableUriPermission(uri, perms);
|
||||
}
|
||||
ContentResolver contentResolver = context.getContentResolver();
|
||||
int perms = Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
|
||||
contentResolver.takePersistableUriPermission(uri, perms);
|
||||
String msg = String.format(context.getString(R.string.swap_toast_using_path), uri.toString());
|
||||
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
|
||||
scan(context, uri);
|
||||
|
||||
@@ -48,9 +48,6 @@ public class UsbDeviceAttachedReceiver extends BroadcastReceiver {
|
||||
@RequiresApi(api = 19)
|
||||
@Override
|
||||
public void onReceive(final Context context, Intent intent) {
|
||||
if (Build.VERSION.SDK_INT < 19) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (intent == null || TextUtils.isEmpty(intent.getAction())
|
||||
|| !UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) {
|
||||
|
||||
@@ -48,10 +48,6 @@ public class UsbDeviceDetachedReceiver extends BroadcastReceiver {
|
||||
@RequiresApi(api = 19)
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (Build.VERSION.SDK_INT < 19) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (intent == null || TextUtils.isEmpty(intent.getAction())
|
||||
|| !UsbManager.ACTION_USB_DEVICE_DETACHED.equals(intent.getAction())) {
|
||||
Log.i(TAG, "ignoring irrelevant intent: " + intent);
|
||||
|
||||
@@ -12,11 +12,7 @@ public class ExitActivity extends AppCompatActivity {
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
finishAndRemoveTask();
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
finishAndRemoveTask();
|
||||
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
@@ -137,8 +137,6 @@ public class PanicResponderActivity extends AppCompatActivity {
|
||||
|
||||
private void exitAndClear() {
|
||||
ExitActivity.exitAndRemoveFromRecentApps(this);
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
finishAndRemoveTask();
|
||||
}
|
||||
finishAndRemoveTask();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,26 +96,19 @@ public class NearbyViewBinder {
|
||||
}
|
||||
});
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
File[] dirs = activity.getExternalFilesDirs("");
|
||||
if (dirs != null) {
|
||||
for (File dir : dirs) {
|
||||
if (dir != null && Environment.isExternalStorageRemovable(dir)) {
|
||||
String state = Environment.getExternalStorageState(dir);
|
||||
if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)
|
||||
|| Environment.MEDIA_MOUNTED.equals(state)) {
|
||||
// remove Android/data/org.fdroid.fdroid/files to get root
|
||||
externalStorage = dir.getParentFile().getParentFile().getParentFile().getParentFile();
|
||||
break;
|
||||
}
|
||||
File[] dirs = activity.getExternalFilesDirs("");
|
||||
if (dirs != null) {
|
||||
for (File dir : dirs) {
|
||||
if (dir != null && Environment.isExternalStorageRemovable(dir)) {
|
||||
String state = Environment.getExternalStorageState(dir);
|
||||
if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)
|
||||
|| Environment.MEDIA_MOUNTED.equals(state)) {
|
||||
// remove Android/data/org.fdroid.fdroid/files to get root
|
||||
externalStorage = dir.getParentFile().getParentFile().getParentFile().getParentFile();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (Environment.isExternalStorageRemovable() &&
|
||||
(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY)
|
||||
|| Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))) {
|
||||
Log.i(TAG, "<21 isExternalStorageRemovable MEDIA_MOUNTED");
|
||||
externalStorage = Environment.getExternalStorageDirectory();
|
||||
}
|
||||
|
||||
final String writeExternalStorage = Manifest.permission.WRITE_EXTERNAL_STORAGE;
|
||||
|
||||
@@ -5,7 +5,6 @@ import android.content.pm.FeatureInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
|
||||
import org.fdroid.fdroid.compat.SupportedArchitectures;
|
||||
import org.fdroid.fdroid.data.Apk;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -45,7 +44,7 @@ public class CompatibilityChecker {
|
||||
}
|
||||
}
|
||||
|
||||
cpuAbis = SupportedArchitectures.getAbis();
|
||||
cpuAbis = Build.SUPPORTED_ABIS;
|
||||
}
|
||||
|
||||
private boolean compatibleApi(@Nullable String[] nativecode) {
|
||||
|
||||
@@ -56,7 +56,6 @@ import org.fdroid.database.FDroidDatabase;
|
||||
import org.fdroid.database.Repository;
|
||||
import org.fdroid.fdroid.Preferences.ChangeListener;
|
||||
import org.fdroid.fdroid.Preferences.Theme;
|
||||
import org.fdroid.fdroid.compat.PRNGFixes;
|
||||
import org.fdroid.fdroid.data.App;
|
||||
import org.fdroid.fdroid.data.DBHelper;
|
||||
import org.fdroid.fdroid.installer.ApkFileProvider;
|
||||
@@ -327,24 +326,10 @@ public class FDroidApp extends Application implements androidx.work.Configuratio
|
||||
FDroidDatabase db = DBHelper.getDb(this);
|
||||
db.getRepositoryDao().getLiveRepositories().observeForever(repositories -> repos = repositories);
|
||||
|
||||
PRNGFixes.apply();
|
||||
|
||||
applyTheme();
|
||||
|
||||
configureProxy(preferences);
|
||||
|
||||
|
||||
// bug specific to exactly 5.0 makes it only work with the old index
|
||||
// which includes an ugly, hacky workaround
|
||||
// https://gitlab.com/fdroid/fdroidclient/issues/1014
|
||||
if (Build.VERSION.SDK_INT == 21) {
|
||||
preferences.setExpertMode(true);
|
||||
preferences.setForceOldIndex(true);
|
||||
}
|
||||
|
||||
// TODO should not be needed anymore
|
||||
//InstalledAppProviderService.compareToPackageManager(this);
|
||||
|
||||
// If the user changes the preference to do with filtering anti-feature apps,
|
||||
// it is easier to just notify a change in the app provider,
|
||||
// so that the newly updated list will correctly filter relevant apps.
|
||||
@@ -393,9 +378,7 @@ public class FDroidApp extends Application implements androidx.work.Configuratio
|
||||
if (!TextUtils.equals(packageName, unset)) {
|
||||
int modeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
|
||||
if (Build.VERSION.SDK_INT >= 19) {
|
||||
modeFlags |= Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION;
|
||||
}
|
||||
modeFlags |= Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION;
|
||||
grantUriPermission(packageName, InstallHistoryService.LOG_URI, modeFlags);
|
||||
}
|
||||
|
||||
|
||||
@@ -108,11 +108,7 @@ public final class Languages {
|
||||
|
||||
final Resources resources = contextWrapper.getBaseContext().getResources();
|
||||
Configuration config = resources.getConfiguration();
|
||||
if (Build.VERSION.SDK_INT >= 17) {
|
||||
config.setLocale(locale);
|
||||
} else {
|
||||
config.locale = locale;
|
||||
}
|
||||
config.setLocale(locale);
|
||||
resources.updateConfiguration(config, resources.getDisplayMetrics());
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import android.net.Uri;
|
||||
import android.nfc.NdefMessage;
|
||||
import android.nfc.NdefRecord;
|
||||
import android.nfc.NfcAdapter;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
@@ -34,9 +33,6 @@ public class NfcHelper {
|
||||
|
||||
@TargetApi(16)
|
||||
public static void setAndroidBeam(AppCompatActivity activity, String packageName) {
|
||||
if (Build.VERSION.SDK_INT < 16) {
|
||||
return;
|
||||
}
|
||||
PackageManager pm = activity.getPackageManager();
|
||||
NfcAdapter nfcAdapter = getAdapter(activity);
|
||||
if (nfcAdapter != null) {
|
||||
@@ -55,9 +51,6 @@ public class NfcHelper {
|
||||
|
||||
@TargetApi(16)
|
||||
public static void disableAndroidBeam(AppCompatActivity activity) {
|
||||
if (Build.VERSION.SDK_INT < 16) {
|
||||
return;
|
||||
}
|
||||
NfcAdapter nfcAdapter = getAdapter(activity);
|
||||
if (nfcAdapter != null) {
|
||||
nfcAdapter.setBeamPushUris(null, activity);
|
||||
|
||||
@@ -3,7 +3,6 @@ package org.fdroid.fdroid;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Intent;
|
||||
import android.nfc.NfcAdapter;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
|
||||
@@ -32,12 +31,6 @@ public class NfcNotEnabledActivity extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
// this API was added in 4.0 aka Ice Cream Sandwich
|
||||
@TargetApi(14)
|
||||
private void doOnIceCreamSandwich(Intent intent) {
|
||||
intent.setAction(Settings.ACTION_NFCSHARING_SETTINGS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
FDroidApp fdroidApp = (FDroidApp) getApplication();
|
||||
@@ -46,11 +39,7 @@ public class NfcNotEnabledActivity extends AppCompatActivity {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
final Intent intent = new Intent();
|
||||
if (Build.VERSION.SDK_INT >= 16) {
|
||||
doOnJellybean(intent);
|
||||
} else {
|
||||
doOnIceCreamSandwich(intent);
|
||||
}
|
||||
doOnJellybean(intent);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
@@ -18,9 +18,7 @@
|
||||
|
||||
package org.fdroid.fdroid;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.job.JobInfo;
|
||||
import android.app.job.JobScheduler;
|
||||
import android.content.BroadcastReceiver;
|
||||
@@ -32,7 +30,6 @@ import android.content.IntentFilter;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Process;
|
||||
import android.os.SystemClock;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
@@ -173,43 +170,28 @@ public class UpdateService extends JobIntentService {
|
||||
interval != Preferences.UPDATE_INTERVAL_DISABLED
|
||||
&& !(data == Preferences.OVER_NETWORK_NEVER && wifi == Preferences.OVER_NETWORK_NEVER);
|
||||
|
||||
if (Build.VERSION.SDK_INT < 21) {
|
||||
Intent intent = new Intent(context, UpdateService.class);
|
||||
PendingIntent pending = PendingIntent.getService(context, 0, intent, 0);
|
||||
|
||||
AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
||||
alarm.cancel(pending);
|
||||
if (scheduleNewJob) {
|
||||
alarm.setInexactRepeating(AlarmManager.ELAPSED_REALTIME,
|
||||
SystemClock.elapsedRealtime() + 5000, interval, pending);
|
||||
Utils.debugLog(TAG, "Update scheduler alarm set");
|
||||
} else {
|
||||
Utils.debugLog(TAG, "Update scheduler alarm not set");
|
||||
}
|
||||
Utils.debugLog(TAG, "Using android-21 JobScheduler for updates");
|
||||
JobScheduler jobScheduler = ContextCompat.getSystemService(context, JobScheduler.class);
|
||||
ComponentName componentName = new ComponentName(context, UpdateJobService.class);
|
||||
JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, componentName)
|
||||
.setRequiresDeviceIdle(true)
|
||||
.setPeriodic(interval);
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
builder.setRequiresBatteryNotLow(true)
|
||||
.setRequiresStorageNotLow(true);
|
||||
}
|
||||
if (data == Preferences.OVER_NETWORK_ALWAYS && wifi == Preferences.OVER_NETWORK_ALWAYS) {
|
||||
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
|
||||
} else {
|
||||
Utils.debugLog(TAG, "Using android-21 JobScheduler for updates");
|
||||
JobScheduler jobScheduler = ContextCompat.getSystemService(context, JobScheduler.class);
|
||||
ComponentName componentName = new ComponentName(context, UpdateJobService.class);
|
||||
JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, componentName)
|
||||
.setRequiresDeviceIdle(true)
|
||||
.setPeriodic(interval);
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
builder.setRequiresBatteryNotLow(true)
|
||||
.setRequiresStorageNotLow(true);
|
||||
}
|
||||
if (data == Preferences.OVER_NETWORK_ALWAYS && wifi == Preferences.OVER_NETWORK_ALWAYS) {
|
||||
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
|
||||
} else {
|
||||
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
|
||||
}
|
||||
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
|
||||
}
|
||||
|
||||
jobScheduler.cancel(JOB_ID);
|
||||
if (scheduleNewJob) {
|
||||
jobScheduler.schedule(builder.build());
|
||||
Utils.debugLog(TAG, "Update scheduler alarm set");
|
||||
} else {
|
||||
Utils.debugLog(TAG, "Update scheduler alarm not set");
|
||||
}
|
||||
jobScheduler.cancel(JOB_ID);
|
||||
if (scheduleNewJob) {
|
||||
jobScheduler.schedule(builder.build());
|
||||
Utils.debugLog(TAG, "Update scheduler alarm set");
|
||||
} else {
|
||||
Utils.debugLog(TAG, "Update scheduler alarm not set");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Rect;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.StatFs;
|
||||
@@ -181,11 +180,7 @@ public final class Utils {
|
||||
return 50 * 1024 * 1024; // just return a minimal amount
|
||||
}
|
||||
StatFs stat = new StatFs(statDir.getPath());
|
||||
if (Build.VERSION.SDK_INT < 18) {
|
||||
return (long) stat.getAvailableBlocks() * (long) stat.getBlockSize();
|
||||
} else {
|
||||
return stat.getAvailableBlocksLong() * stat.getBlockSizeLong();
|
||||
}
|
||||
return stat.getAvailableBlocksLong() * stat.getBlockSizeLong();
|
||||
}
|
||||
|
||||
public static long getImageCacheDirTotalMemory(Context context) {
|
||||
@@ -197,11 +192,7 @@ public final class Utils {
|
||||
return 100 * 1024 * 1024; // just return a minimal amount
|
||||
}
|
||||
StatFs stat = new StatFs(statDir.getPath());
|
||||
if (Build.VERSION.SDK_INT < 18) {
|
||||
return (long) stat.getBlockCount() * (long) stat.getBlockSize();
|
||||
} else {
|
||||
return stat.getBlockCountLong() * stat.getBlockSizeLong();
|
||||
}
|
||||
return stat.getBlockCountLong() * stat.getBlockSizeLong();
|
||||
}
|
||||
|
||||
public static void copy(InputStream input, OutputStream output) throws IOException {
|
||||
|
||||
@@ -25,15 +25,7 @@ public class FileCompat {
|
||||
private static final String TAG = "FileCompat";
|
||||
|
||||
public static boolean symlink(SanitizedFile source, SanitizedFile dest) {
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
symlinkOs(source, dest);
|
||||
} else if (Build.VERSION.SDK_INT >= 15) {
|
||||
symlinkLibcore(source, dest);
|
||||
} else {
|
||||
symlinkRuntime(source, dest);
|
||||
}
|
||||
|
||||
symlinkOs(source, dest);
|
||||
return dest.exists();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,326 +0,0 @@
|
||||
package org.fdroid.fdroid.compat;
|
||||
|
||||
/*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will Google be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, as long as the origin is not misrepresented.
|
||||
*/
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Build;
|
||||
import android.os.Process;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Provider;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.SecureRandomSpi;
|
||||
import java.security.Security;
|
||||
|
||||
/**
|
||||
* Fixes for the output of the default PRNG having low entropy.
|
||||
* <p>
|
||||
* The fixes need to be applied via {@link #apply()} before any use of Java
|
||||
* Cryptography Architecture primitives. A good place to invoke them is in the
|
||||
* application's {@code onCreate}.
|
||||
*
|
||||
* @see <a href="http://android-developers.blogspot.jp/2013/08/some-securerandom-thoughts.html">Some SecureRandom Thoughts</a>
|
||||
*/
|
||||
public final class PRNGFixes {
|
||||
|
||||
private static final byte[] BUILD_FINGERPRINT_AND_DEVICE_SERIAL =
|
||||
getBuildFingerprintAndDeviceSerial();
|
||||
|
||||
/**
|
||||
* Hidden constructor to prevent instantiation.
|
||||
*/
|
||||
private PRNGFixes() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies all fixes.
|
||||
*
|
||||
* @throws SecurityException if a fix is needed but could not be applied.
|
||||
*/
|
||||
public static void apply() {
|
||||
applyOpenSSLFix();
|
||||
installLinuxPRNGSecureRandom();
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the fix for OpenSSL PRNG having low entropy. Does nothing if the
|
||||
* fix is not needed.
|
||||
*
|
||||
* @throws SecurityException if the fix is needed but could not be applied.
|
||||
*/
|
||||
private static void applyOpenSSLFix() throws SecurityException {
|
||||
if (Build.VERSION.SDK_INT < 16 || Build.VERSION.SDK_INT > 18) {
|
||||
// No need to apply the fix
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Mix in the device- and invocation-specific seed.
|
||||
Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
||||
.getMethod("RAND_seed", byte[].class)
|
||||
.invoke(null, generateSeed());
|
||||
|
||||
// Mix output of Linux PRNG into OpenSSL's PRNG
|
||||
int bytesRead = (Integer) Class.forName(
|
||||
"org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
||||
.getMethod("RAND_load_file", String.class, long.class)
|
||||
.invoke(null, "/dev/urandom", 1024);
|
||||
if (bytesRead != 1024) {
|
||||
throw new IOException(
|
||||
"Unexpected number of bytes read from Linux PRNG: "
|
||||
+ bytesRead);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new SecurityException("Failed to seed OpenSSL PRNG", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs a Linux PRNG-backed {@code SecureRandom} implementation as the
|
||||
* default. Does nothing if the implementation is already the default or if
|
||||
* there is not need to install the implementation.
|
||||
*
|
||||
* @throws SecurityException if the fix is needed but could not be applied.
|
||||
*/
|
||||
private static void installLinuxPRNGSecureRandom()
|
||||
throws SecurityException {
|
||||
if (Build.VERSION.SDK_INT > 18) {
|
||||
// No need to apply the fix
|
||||
return;
|
||||
}
|
||||
|
||||
// Install a Linux PRNG-based SecureRandom implementation as the
|
||||
// default, if not yet installed.
|
||||
Provider[] secureRandomProviders = Security.getProviders("SecureRandom.SHA1PRNG");
|
||||
if (secureRandomProviders == null
|
||||
|| secureRandomProviders.length < 1
|
||||
|| !LinuxPRNGSecureRandomProvider.class.equals(secureRandomProviders[0].getClass())) {
|
||||
Security.insertProviderAt(new LinuxPRNGSecureRandomProvider(), 1);
|
||||
}
|
||||
|
||||
// Assert that new SecureRandom() and
|
||||
// SecureRandom.getInstance("SHA1PRNG") return a SecureRandom backed
|
||||
// by the Linux PRNG-based SecureRandom implementation.
|
||||
SecureRandom rng1 = new SecureRandom();
|
||||
if (!LinuxPRNGSecureRandomProvider.class.equals(
|
||||
rng1.getProvider().getClass())) {
|
||||
throw new SecurityException(
|
||||
"new SecureRandom() backed by wrong Provider: "
|
||||
+ rng1.getProvider().getClass());
|
||||
}
|
||||
|
||||
SecureRandom rng2;
|
||||
try {
|
||||
rng2 = SecureRandom.getInstance("SHA1PRNG");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new SecurityException("SHA1PRNG not available", e);
|
||||
}
|
||||
if (!LinuxPRNGSecureRandomProvider.class.equals(
|
||||
rng2.getProvider().getClass())) {
|
||||
throw new SecurityException(
|
||||
"SecureRandom.getInstance(\"SHA1PRNG\") backed by wrong"
|
||||
+ " Provider: " + rng2.getProvider().getClass());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code Provider} of {@code SecureRandom} engines which pass through
|
||||
* all requests to the Linux PRNG.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
private static class LinuxPRNGSecureRandomProvider extends Provider {
|
||||
|
||||
LinuxPRNGSecureRandomProvider() {
|
||||
super("LinuxPRNG",
|
||||
1.0,
|
||||
"A Linux-specific random number provider that uses"
|
||||
+ " /dev/urandom");
|
||||
// Although /dev/urandom is not a SHA-1 PRNG, some apps
|
||||
// explicitly request a SHA1PRNG SecureRandom and we thus need to
|
||||
// prevent them from getting the default implementation whose output
|
||||
// may have low entropy.
|
||||
put("SecureRandom.SHA1PRNG", LinuxPRNGSecureRandom.class.getName());
|
||||
put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link SecureRandomSpi} which passes all requests to the Linux PRNG
|
||||
* ({@code /dev/urandom}).
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public static class LinuxPRNGSecureRandom extends SecureRandomSpi {
|
||||
|
||||
private static final String TAG = "PRNGFixes";
|
||||
|
||||
/*
|
||||
* IMPLEMENTATION NOTE: Requests to generate bytes and to mix in a seed
|
||||
* are passed through to the Linux PRNG (/dev/urandom). Instances of
|
||||
* this class seed themselves by mixing in the current time, PID, UID,
|
||||
* build fingerprint, and hardware serial number (where available) into
|
||||
* Linux PRNG.
|
||||
*
|
||||
* Concurrency: Read requests to the underlying Linux PRNG are
|
||||
* serialized (on S_LOCK) to ensure that multiple threads do not get
|
||||
* duplicated PRNG output.
|
||||
*/
|
||||
|
||||
private static final File URANDOM_FILE = new File("/dev/urandom");
|
||||
|
||||
private static final Object S_LOCK = new Object();
|
||||
|
||||
/**
|
||||
* Input stream for reading from Linux PRNG or {@code null} if not yet
|
||||
* opened.
|
||||
*
|
||||
* @GuardedBy("S_LOCK")
|
||||
*/
|
||||
private static DataInputStream sUrandomIn;
|
||||
|
||||
/**
|
||||
* Output stream for writing to Linux PRNG or {@code null} if not yet
|
||||
* opened.
|
||||
*
|
||||
* @GuardedBy("S_LOCK")
|
||||
*/
|
||||
private static OutputStream sUrandomOut;
|
||||
|
||||
/**
|
||||
* Whether this engine instance has been seeded. This is needed because
|
||||
* each instance needs to seed itself if the client does not explicitly
|
||||
* seed it.
|
||||
*/
|
||||
private boolean seeded;
|
||||
|
||||
@Override
|
||||
protected void engineSetSeed(byte[] bytes) {
|
||||
try {
|
||||
OutputStream out;
|
||||
synchronized (S_LOCK) {
|
||||
out = getUrandomOutputStream();
|
||||
}
|
||||
out.write(bytes);
|
||||
out.flush();
|
||||
} catch (IOException e) {
|
||||
// On a small fraction of devices /dev/urandom is not writable.
|
||||
// Log and ignore.
|
||||
Log.w(TAG, "Failed to mix seed into " + URANDOM_FILE);
|
||||
} finally {
|
||||
seeded = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void engineNextBytes(byte[] bytes) {
|
||||
if (!seeded) {
|
||||
// Mix in the device- and invocation-specific seed.
|
||||
engineSetSeed(generateSeed());
|
||||
}
|
||||
|
||||
try {
|
||||
DataInputStream in;
|
||||
synchronized (S_LOCK) {
|
||||
in = getUrandomInputStream();
|
||||
}
|
||||
synchronized (in) {
|
||||
in.readFully(bytes);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new SecurityException(
|
||||
"Failed to read from " + URANDOM_FILE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] engineGenerateSeed(int size) {
|
||||
byte[] seed = new byte[size];
|
||||
engineNextBytes(seed);
|
||||
return seed;
|
||||
}
|
||||
|
||||
private DataInputStream getUrandomInputStream() {
|
||||
synchronized (S_LOCK) {
|
||||
if (sUrandomIn == null) {
|
||||
// NOTE: Consider inserting a BufferedInputStream between
|
||||
// DataInputStream and FileInputStream if you need higher
|
||||
// PRNG output performance and can live with future PRNG
|
||||
// output being pulled into this process prematurely.
|
||||
try {
|
||||
sUrandomIn = new DataInputStream(
|
||||
new FileInputStream(URANDOM_FILE));
|
||||
} catch (IOException e) {
|
||||
throw new SecurityException("Failed to open "
|
||||
+ URANDOM_FILE + " for reading", e);
|
||||
}
|
||||
}
|
||||
return sUrandomIn;
|
||||
}
|
||||
}
|
||||
|
||||
private OutputStream getUrandomOutputStream() throws IOException {
|
||||
synchronized (S_LOCK) {
|
||||
if (sUrandomOut == null) {
|
||||
sUrandomOut = new FileOutputStream(URANDOM_FILE);
|
||||
}
|
||||
return sUrandomOut;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a device- and invocation-specific seed to be mixed into the
|
||||
* Linux PRNG.
|
||||
*/
|
||||
private static byte[] generateSeed() {
|
||||
try {
|
||||
ByteArrayOutputStream seedBuffer = new ByteArrayOutputStream();
|
||||
DataOutputStream seedBufferOut =
|
||||
new DataOutputStream(seedBuffer);
|
||||
seedBufferOut.writeLong(System.currentTimeMillis());
|
||||
seedBufferOut.writeLong(System.nanoTime());
|
||||
seedBufferOut.writeInt(Process.myPid());
|
||||
seedBufferOut.writeInt(Process.myUid());
|
||||
seedBufferOut.write(BUILD_FINGERPRINT_AND_DEVICE_SERIAL);
|
||||
seedBufferOut.close();
|
||||
return seedBuffer.toByteArray();
|
||||
} catch (IOException e) {
|
||||
throw new SecurityException("Failed to generate seed", e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("HardwareIds")
|
||||
private static byte[] getBuildFingerprintAndDeviceSerial() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
String fingerprint = Build.FINGERPRINT;
|
||||
if (fingerprint != null) {
|
||||
result.append(fingerprint);
|
||||
}
|
||||
String serial = Build.SERIAL;
|
||||
if (serial != null) {
|
||||
result.append(serial);
|
||||
}
|
||||
try {
|
||||
return result.toString().getBytes("UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException("UTF-8 encoding not supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package org.fdroid.fdroid.compat;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.os.Build;
|
||||
|
||||
public class SupportedArchitectures {
|
||||
|
||||
/**
|
||||
* The most preferred ABI is the first element in the list.
|
||||
*/
|
||||
@TargetApi(21)
|
||||
@SuppressWarnings("deprecation")
|
||||
public static String[] getAbis() {
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
return Build.SUPPORTED_ABIS;
|
||||
}
|
||||
return new String[]{Build.CPU_ABI, Build.CPU_ABI2};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -441,17 +441,14 @@ public class Apk implements Comparable<Apk>, Parcelable {
|
||||
set.add(versions.getName());
|
||||
}
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= 16 && set.contains(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||
if (set.contains(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||
set.add(Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= 29) {
|
||||
if (set.contains(Manifest.permission.ACCESS_FINE_LOCATION)) {
|
||||
set.add(Manifest.permission.ACCESS_COARSE_LOCATION);
|
||||
}
|
||||
if (targetSdkVersion >= 29) {
|
||||
// Do nothing. The targetSdk for the below split-permissions is set to 29,
|
||||
// so we don't make any changes for apps targetting 29 or above
|
||||
} else {
|
||||
if (targetSdkVersion < 29) {
|
||||
// TODO: Change the strings below to Manifest.permission once we target SDK 29.
|
||||
if (set.contains(Manifest.permission.ACCESS_FINE_LOCATION)) {
|
||||
set.add("android.permission.ACCESS_BACKGROUND_LOCATION");
|
||||
@@ -463,12 +460,11 @@ public class Apk implements Comparable<Apk>, Parcelable {
|
||||
set.add("android.permission.ACCESS_MEDIA_LOCATION");
|
||||
}
|
||||
}
|
||||
// Else do nothing. The targetSdk for the below split-permissions is set to 29,
|
||||
// so we don't make any changes for apps targetting 29 or above
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= 31) {
|
||||
if (targetSdkVersion >= 31) {
|
||||
// Do nothing. The targetSdk for the below split-permissions is set to 31,
|
||||
// so we don't make any changes for apps targetting 31 or above
|
||||
} else {
|
||||
if (targetSdkVersion < 31) {
|
||||
// TODO: Change the strings below to Manifest.permission once we target SDK 31.
|
||||
if (set.contains(Manifest.permission.BLUETOOTH) ||
|
||||
set.contains(Manifest.permission.BLUETOOTH_ADMIN)) {
|
||||
@@ -477,6 +473,8 @@ public class Apk implements Comparable<Apk>, Parcelable {
|
||||
set.add("android.permission.BLUETOOTH_ADVERTISE");
|
||||
}
|
||||
}
|
||||
// Else do nothing. The targetSdk for the below split-permissions is set to 31,
|
||||
// so we don't make any changes for apps targetting 31 or above
|
||||
}
|
||||
|
||||
requestedPermissions = set.toArray(new String[set.size()]);
|
||||
|
||||
@@ -99,13 +99,7 @@ public class DefaultInstallerActivity extends FragmentActivity {
|
||||
// works only when being installed as system-app
|
||||
// https://code.google.com/p/android/issues/detail?id=42253
|
||||
|
||||
if (Build.VERSION.SDK_INT < 16) {
|
||||
intent.setAction(Intent.ACTION_INSTALL_PACKAGE);
|
||||
intent.setData(uri);
|
||||
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
|
||||
intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true);
|
||||
intent.putExtra(Intent.EXTRA_ALLOW_REPLACE, true);
|
||||
} else if (Build.VERSION.SDK_INT < 24) {
|
||||
if (Build.VERSION.SDK_INT < 24) {
|
||||
intent.setAction(Intent.ACTION_INSTALL_PACKAGE);
|
||||
intent.setData(uri);
|
||||
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
|
||||
|
||||
@@ -23,7 +23,6 @@ package org.fdroid.fdroid.installer;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.filefilter.WildcardFileFilter;
|
||||
@@ -150,9 +149,7 @@ public class InstallerService extends JobIntentService {
|
||||
* @param apk {@link Apk} instance of the app that will be uninstalled
|
||||
*/
|
||||
public static void uninstall(Context context, @NonNull Apk apk) {
|
||||
if (Build.VERSION.SDK_INT >= 19) {
|
||||
Objects.requireNonNull(apk);
|
||||
}
|
||||
Objects.requireNonNull(apk);
|
||||
|
||||
Installer.sendBroadcastUninstall(context, apk, Installer.ACTION_UNINSTALL_STARTED);
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -27,8 +26,7 @@ import java.util.Enumeration;
|
||||
* unmetered internet available, based on
|
||||
* {@link android.net.ConnectivityManager#CONNECTIVITY_ACTION}
|
||||
* <p>
|
||||
* {@link Build.VERSION_CODES#N Android 7.0} removed
|
||||
* {@link android.net.ConnectivityManager#CONNECTIVITY_ACTION} so this will
|
||||
* Android 7.0 removed {@link android.net.ConnectivityManager#CONNECTIVITY_ACTION} so this will
|
||||
* need to be totally changed to support that.
|
||||
*
|
||||
* @see <a href="https://developer.android.com/topic/performance/background-optimization">Background Optimizations</a>
|
||||
@@ -53,8 +51,7 @@ public class ConnectivityMonitorService extends JobIntentService {
|
||||
/**
|
||||
* Register the {@link BroadcastReceiver} which also starts this
|
||||
* {@code Service} since it is a sticky broadcast. This cannot be
|
||||
* registered in the manifest, since {@link Build.VERSION_CODES#N Android 7.0}
|
||||
* makes that not work.
|
||||
* registered in the manifest, since Android 7.0 makes that not work.
|
||||
*/
|
||||
public static void registerAndStart(Context context) {
|
||||
context.registerReceiver(CONNECTIVITY_RECEIVER, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
|
||||
@@ -85,7 +82,7 @@ public class ConnectivityMonitorService extends JobIntentService {
|
||||
}
|
||||
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
|
||||
|
||||
if (activeNetwork == null && Build.VERSION.SDK_INT >= 21 && cm.getAllNetworks().length == 0) {
|
||||
if (activeNetwork == null && cm.getAllNetworks().length == 0) {
|
||||
try {
|
||||
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
|
||||
while (networkInterfaces.hasMoreElements()) {
|
||||
|
||||
@@ -29,7 +29,6 @@ import android.content.pm.PermissionGroupInfo;
|
||||
import android.content.pm.PermissionInfo;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Parcel;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextUtils;
|
||||
@@ -120,7 +119,7 @@ public class AppSecurityPermissions {
|
||||
public Drawable loadGroupIcon(Context context, PackageManager pm) {
|
||||
Drawable iconDrawable;
|
||||
if (icon != 0) {
|
||||
iconDrawable = (Build.VERSION.SDK_INT < 22) ? loadIcon(pm) : loadUnbadgedIcon(pm);
|
||||
iconDrawable = loadUnbadgedIcon(pm);
|
||||
} else {
|
||||
iconDrawable = ContextCompat.getDrawable(context, R.drawable.ic_perm_device_info);
|
||||
}
|
||||
@@ -264,9 +263,6 @@ public class AppSecurityPermissions {
|
||||
}
|
||||
|
||||
private int[] getRequestedPermissionFlags(PackageInfo info) {
|
||||
if (Build.VERSION.SDK_INT < 16) {
|
||||
return new int[info.requestedPermissions.length];
|
||||
}
|
||||
return info.requestedPermissionsFlags;
|
||||
}
|
||||
|
||||
@@ -347,7 +343,7 @@ public class AppSecurityPermissions {
|
||||
*/
|
||||
@TargetApi(16)
|
||||
private static boolean isNewPermission(PackageInfo installedPkgInfo, int existingFlags) {
|
||||
if (installedPkgInfo == null || Build.VERSION.SDK_INT < 16) {
|
||||
if (installedPkgInfo == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -420,8 +416,7 @@ public class AppSecurityPermissions {
|
||||
private PermissionItemView getPermissionItemView(MyPermissionGroupInfo grp, MyPermissionInfo perm,
|
||||
boolean first, CharSequence newPermPrefix) {
|
||||
PermissionItemView permView = (PermissionItemView) inflater.inflate(
|
||||
Build.VERSION.SDK_INT >= 17 &&
|
||||
(perm.flags & PermissionInfo.FLAG_COSTS_MONEY) != 0
|
||||
(perm.flags & PermissionInfo.FLAG_COSTS_MONEY) != 0
|
||||
? R.layout.app_permission_item_money : R.layout.app_permission_item,
|
||||
null);
|
||||
permView.setPermission(grp, perm, first, newPermPrefix);
|
||||
|
||||
@@ -4,11 +4,7 @@ import android.content.Context;
|
||||
|
||||
public abstract class CameraCharacteristicsChecker {
|
||||
public static CameraCharacteristicsChecker getInstance(final Context context) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= 21) {
|
||||
return new CameraCharacteristicsMinApiLevel21(context);
|
||||
} else {
|
||||
return new CameraCharacteristicsMaxApiLevel20();
|
||||
}
|
||||
return new CameraCharacteristicsMinApiLevel21(context);
|
||||
}
|
||||
|
||||
public abstract boolean hasAutofocus();
|
||||
|
||||
@@ -34,7 +34,6 @@ import android.content.IntentFilter;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
@@ -782,9 +781,6 @@ public class AppDetailsActivity extends AppCompatActivity
|
||||
private BluetoothAdapter getBluetoothAdapter() {
|
||||
// to use the new, recommended way of getting the adapter
|
||||
// http://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html
|
||||
if (Build.VERSION.SDK_INT < 18) {
|
||||
return BluetoothAdapter.getDefaultAdapter();
|
||||
}
|
||||
return ContextCompat.getSystemService(this, BluetoothManager.class).getAdapter();
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import android.graphics.Color;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.text.Spannable;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
@@ -615,22 +614,15 @@ public class AppDetailsRecyclerViewAdapter
|
||||
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
|
||||
FilenameUtils.getExtension(installedFile.getName()));
|
||||
viewIntent.setDataAndType(uri, mimeType);
|
||||
if (Build.VERSION.SDK_INT < 19) {
|
||||
viewIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
} else {
|
||||
viewIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
| Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
|
||||
}
|
||||
viewIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
| Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
|
||||
if (context.getPackageManager().queryIntentActivities(viewIntent, 0).size() > 0) {
|
||||
buttonPrimaryView.setText(R.string.menu_open);
|
||||
buttonPrimaryView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
try {
|
||||
context.startActivity(viewIntent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
buttonPrimaryView.setOnClickListener(v -> {
|
||||
try {
|
||||
context.startActivity(viewIntent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -470,14 +470,12 @@ public class PreferencesFragment extends PreferenceFragmentCompat
|
||||
// by the time the user boots, opens F-Droid, and views this settings page, then there
|
||||
// is no benefit showing it to them (it will only be disabled and we can't offer any
|
||||
// way to easily install from here.
|
||||
if (Build.VERSION.SDK_INT > 19 && !installed) {
|
||||
if (pref != null) {
|
||||
otherPrefGroup.removePreference(pref);
|
||||
}
|
||||
if (!installed) {
|
||||
otherPrefGroup.removePreference(pref);
|
||||
} else {
|
||||
pref.setEnabled(installed);
|
||||
pref.setDefaultValue(installed);
|
||||
pref.setChecked(enabled && installed);
|
||||
pref.setEnabled(true);
|
||||
pref.setDefaultValue(true);
|
||||
pref.setChecked(enabled);
|
||||
|
||||
pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
@@ -581,8 +579,7 @@ public class PreferencesFragment extends PreferenceFragmentCompat
|
||||
|
||||
if (updateIntervalPrevious != updateIntervalSeekBar.getValue()) {
|
||||
UpdateService.schedule(getActivity());
|
||||
} else if (Build.VERSION.SDK_INT >= 21 &&
|
||||
(overWifiPrevious != overWifiSeekBar.getValue() || overDataPrevious != overDataSeekBar.getValue())) {
|
||||
} else if (overWifiPrevious != overWifiSeekBar.getValue() || overDataPrevious != overDataSeekBar.getValue()) {
|
||||
UpdateService.schedule(getActivity());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import android.content.IntentFilter;
|
||||
import android.net.Uri;
|
||||
import android.nfc.NdefMessage;
|
||||
import android.nfc.NfcAdapter;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.text.TextUtils;
|
||||
@@ -296,11 +295,7 @@ public class RepoDetailsActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
boolean needsEnableNfcMenuItem;
|
||||
if (Build.VERSION.SDK_INT < 16) {
|
||||
needsEnableNfcMenuItem = !nfcAdapter.isEnabled();
|
||||
} else {
|
||||
needsEnableNfcMenuItem = !nfcAdapter.isNdefPushEnabled();
|
||||
}
|
||||
needsEnableNfcMenuItem = !nfcAdapter.isNdefPushEnabled();
|
||||
|
||||
menuItem.setVisible(needsEnableNfcMenuItem);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package org.fdroid.fdroid.views;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import org.fdroid.fdroid.R;
|
||||
@@ -17,7 +16,6 @@ import androidx.core.content.ContextCompat;
|
||||
*/
|
||||
public class SeekBarForegroundThumb extends AppCompatSeekBar {
|
||||
private Drawable tickMark;
|
||||
private Context context;
|
||||
|
||||
public SeekBarForegroundThumb(Context context) {
|
||||
super(context);
|
||||
@@ -35,18 +33,9 @@ public class SeekBarForegroundThumb extends AppCompatSeekBar {
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
this.context = context;
|
||||
tickMark = ContextCompat.getDrawable(context, R.drawable.seekbar_tickmark);
|
||||
}
|
||||
|
||||
private Drawable getThumbCompat() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
return getThumb();
|
||||
} else {
|
||||
return context.getResources().getDrawable(R.drawable.seekbar_thumb);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
@@ -59,7 +48,7 @@ public class SeekBarForegroundThumb extends AppCompatSeekBar {
|
||||
if (count > 1) {
|
||||
final int w = tickMark.getIntrinsicWidth();
|
||||
final int h = tickMark.getIntrinsicHeight();
|
||||
final int halfThumbW = getThumbCompat().getIntrinsicWidth() / 2;
|
||||
final int halfThumbW = getThumb().getIntrinsicWidth() / 2;
|
||||
final int halfW = w >= 0 ? w / 2 : 1;
|
||||
final int halfH = h >= 0 ? h / 2 : 1;
|
||||
tickMark.setBounds(-halfW, -halfH, halfW, halfH);
|
||||
|
||||
@@ -21,7 +21,6 @@ package org.fdroid.fdroid.views.appdetails;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -79,11 +78,7 @@ public class AntiFeaturesListingView extends RecyclerView {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
i.setFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
|
||||
} else {
|
||||
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||
}
|
||||
i.setFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
|
||||
i.setData(Uri.parse("https://f-droid.org/docs/Anti-Features#" + antiFeatureName));
|
||||
getContext().startActivity(i);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.graphics.Outline;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
@@ -137,24 +136,22 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
|
||||
}
|
||||
});
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
installButton.setOutlineProvider(new ViewOutlineProvider() {
|
||||
@Override
|
||||
public void getOutline(View view, Outline outline) {
|
||||
float density = activity.getResources().getDisplayMetrics().density;
|
||||
installButton.setOutlineProvider(new ViewOutlineProvider() {
|
||||
@Override
|
||||
public void getOutline(View view, Outline outline) {
|
||||
float density = activity.getResources().getDisplayMetrics().density;
|
||||
|
||||
// This is a bit hacky/hardcoded/too-specific to the particular icons we're using.
|
||||
// This is because the default "download & install" and "downloaded & ready to install"
|
||||
// icons are smaller than the "downloading progress" button. Hence, we can't just use
|
||||
// the width/height of the view to calculate the outline size.
|
||||
int xPadding = (int) (8 * density);
|
||||
int yPadding = (int) (9 * density);
|
||||
int right = installButton.getWidth() - xPadding;
|
||||
int bottom = installButton.getHeight() - yPadding;
|
||||
outline.setOval(xPadding, yPadding, right, bottom);
|
||||
}
|
||||
});
|
||||
}
|
||||
// This is a bit hacky/hardcoded/too-specific to the particular icons we're using.
|
||||
// This is because the default "download & install" and "downloaded & ready to install"
|
||||
// icons are smaller than the "downloading progress" button. Hence, we can't just use
|
||||
// the width/height of the view to calculate the outline size.
|
||||
int xPadding = (int) (8 * density);
|
||||
int yPadding = (int) (9 * density);
|
||||
int right = installButton.getWidth() - xPadding;
|
||||
int bottom = installButton.getHeight() - yPadding;
|
||||
outline.setOval(xPadding, yPadding, right, bottom);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
icon = (ImageView) itemView.findViewById(R.id.icon);
|
||||
|
||||
@@ -2,7 +2,6 @@ package org.fdroid.fdroid.views.apps;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.text.Editable;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextWatcher;
|
||||
@@ -132,23 +131,19 @@ public class CategoryTextWatcher implements TextWatcher {
|
||||
}
|
||||
|
||||
removeSpans(textToSpannify, CategorySpan.class);
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
removeSpans(textToSpannify, TtsSpan.class);
|
||||
}
|
||||
removeSpans(textToSpannify, TtsSpan.class);
|
||||
|
||||
int colonIndex = textToSpannify.toString().indexOf(':');
|
||||
if (colonIndex > 0) {
|
||||
CategorySpan span = new CategorySpan(context);
|
||||
textToSpannify.setSpan(span, 0, colonIndex + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
// For accessibility reasons, make this more clear to screen readers that the
|
||||
// span we just added semantically represents a category.
|
||||
CharSequence categoryName = textToSpannify.subSequence(0, colonIndex);
|
||||
TtsSpan ttsSpan = new TtsSpan.TextBuilder(context.getString(R.string.tts_category_name,
|
||||
categoryName)).build();
|
||||
textToSpannify.setSpan(ttsSpan, 0, 0, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
// For accessibility reasons, make this more clear to screen readers that the
|
||||
// span we just added semantically represents a category.
|
||||
CharSequence categoryName = textToSpannify.subSequence(0, colonIndex);
|
||||
TtsSpan ttsSpan = new TtsSpan.TextBuilder(context.getString(R.string.tts_category_name,
|
||||
categoryName)).build();
|
||||
textToSpannify.setSpan(ttsSpan, 0, 0, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,10 +26,8 @@ import android.app.SearchManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -111,13 +109,6 @@ public class MainActivity extends AppCompatActivity {
|
||||
pager.setLayoutManager(new NonScrollingHorizontalLayoutManager(this));
|
||||
pager.setAdapter(adapter);
|
||||
|
||||
// Without this, the focus is completely busted on pre 15 devices. Trying to use them
|
||||
// without this ends up with each child view showing for a fraction of a second, then
|
||||
// reverting back to the "Latest" screen again, in completely non-deterministic ways.
|
||||
if (Build.VERSION.SDK_INT <= 15) {
|
||||
pager.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
|
||||
}
|
||||
|
||||
bottomNavigation = (BottomNavigationView) findViewById(R.id.bottom_navigation);
|
||||
bottomNavigation.setOnNavigationItemSelectedListener(item -> {
|
||||
pager.scrollToPosition(item.getOrder());
|
||||
|
||||
@@ -7,7 +7,6 @@ import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.system.StructStat;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.fdroid.fdroid.Preferences;
|
||||
import org.fdroid.fdroid.Utils;
|
||||
import org.fdroid.fdroid.installer.ApkCache;
|
||||
@@ -208,10 +207,6 @@ public class CleanCacheWorker extends Worker {
|
||||
clearOldFiles(file, millisAgo);
|
||||
}
|
||||
deleteFileAndLog(f);
|
||||
} else if (Build.VERSION.SDK_INT <= 21) {
|
||||
if (FileUtils.isFileOlder(f, olderThan)) {
|
||||
deleteFileAndLog(f);
|
||||
}
|
||||
} else {
|
||||
Impl21.deleteIfOld(f, olderThan);
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@ import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -251,9 +250,7 @@ public class FDroidMetricsWorker extends Worker {
|
||||
EVENTS.add(getDeviceEvent(weekStart, "isPrivilegedInstallerEnabled",
|
||||
Preferences.get().isPrivilegedInstallerEnabled()));
|
||||
EVENTS.add(getDeviceEvent(weekStart, "Build.VERSION.SDK_INT", Build.VERSION.SDK_INT));
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
EVENTS.add(getDeviceEvent(weekStart, "Build.SUPPORTED_ABIS", Arrays.toString(Build.SUPPORTED_ABIS)));
|
||||
}
|
||||
EVENTS.add(getDeviceEvent(weekStart, "Build.SUPPORTED_ABIS", Arrays.toString(Build.SUPPORTED_ABIS)));
|
||||
|
||||
for (PackageInfo packageInfo : packageInfoList) {
|
||||
if (isTimestampInReportingWeek(weekStart, packageInfo.firstInstallTime)) {
|
||||
@@ -425,11 +422,7 @@ public class FDroidMetricsWorker extends Worker {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (Build.VERSION.SDK_INT >= 19) {
|
||||
return Objects.hash(applicationId, versionCode, action);
|
||||
} else {
|
||||
return new Random().nextInt(); // quick kludge
|
||||
}
|
||||
return Objects.hash(applicationId, versionCode, action);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user