Drop support for Android 4

new minSdk is 21
This commit is contained in:
Torsten Grote
2023-02-10 11:27:26 -03:00
parent c6a284bd6d
commit 6ed55bcd7d
43 changed files with 116 additions and 476 deletions

View File

@@ -11,7 +11,7 @@ android {
} }
defaultConfig { defaultConfig {
minSdkVersion 16 minSdkVersion 21
targetSdkVersion 31 targetSdkVersion 31
versionCode 10500 versionCode 10500
versionName "1.5.0" versionName "1.5.0"

View File

@@ -20,7 +20,6 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.GuardedBy;
import javax.inject.Inject; import javax.inject.Inject;
import static android.os.Build.VERSION.SDK_INT;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static org.briarproject.bramble.util.IoUtils.deleteFileOrDir; import static org.briarproject.bramble.util.IoUtils.deleteFileOrDir;
@@ -105,15 +104,11 @@ class AndroidAccountManager extends AccountManagerImpl
} }
files.add(appContext.getFilesDir()); files.add(appContext.getFilesDir());
addIfNotNull(files, appContext.getExternalCacheDir()); addIfNotNull(files, appContext.getExternalCacheDir());
if (SDK_INT >= 19) { for (File file : appContext.getExternalCacheDirs()) {
for (File file : appContext.getExternalCacheDirs()) { addIfNotNull(files, file);
addIfNotNull(files, file);
}
} }
if (SDK_INT >= 21) { for (File file : appContext.getExternalMediaDirs()) {
for (File file : appContext.getExternalMediaDirs()) { addIfNotNull(files, file);
addIfNotNull(files, file);
}
} }
// Clear the cache directory but don't delete it // Clear the cache directory but don't delete it
File cacheDir = appContext.getCacheDir(); File cacheDir = appContext.getCacheDir();

View File

@@ -1,5 +1,6 @@
package org.briarproject.bramble.plugin.bluetooth; package org.briarproject.bramble.plugin.bluetooth;
import android.annotation.SuppressLint;
import android.app.Application; import android.app.Application;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
@@ -49,7 +50,6 @@ import static android.bluetooth.BluetoothAdapter.STATE_ON;
import static android.bluetooth.BluetoothDevice.ACTION_FOUND; import static android.bluetooth.BluetoothDevice.ACTION_FOUND;
import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_LE; import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_LE;
import static android.bluetooth.BluetoothDevice.EXTRA_DEVICE; import static android.bluetooth.BluetoothDevice.EXTRA_DEVICE;
import static android.os.Build.VERSION.SDK_INT;
import static java.util.Collections.shuffle; import static java.util.Collections.shuffle;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
@@ -60,6 +60,7 @@ import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
@SuppressLint("MissingPermission")
class AndroidBluetoothPlugin extends class AndroidBluetoothPlugin extends
AbstractBluetoothPlugin<BluetoothSocket, BluetoothServerSocket> { AbstractBluetoothPlugin<BluetoothSocket, BluetoothServerSocket> {
@@ -253,7 +254,7 @@ class AndroidBluetoothPlugin extends
} else if (ACTION_FOUND.equals(action)) { } else if (ACTION_FOUND.equals(action)) {
BluetoothDevice d = i.getParcelableExtra(EXTRA_DEVICE); BluetoothDevice d = i.getParcelableExtra(EXTRA_DEVICE);
// Ignore Bluetooth LE devices // Ignore Bluetooth LE devices
if (SDK_INT < 18 || d.getType() != DEVICE_TYPE_LE) { if (d.getType() != DEVICE_TYPE_LE) {
String address = d.getAddress(); String address = d.getAddress();
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Discovered " + LOG.info("Discovered " +

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.plugin.tcp; package org.briarproject.bramble.plugin.tcp;
import android.annotation.TargetApi;
import android.app.Application; import android.app.Application;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.LinkAddress; import android.net.LinkAddress;
@@ -37,7 +36,6 @@ import javax.net.SocketFactory;
import static android.content.Context.CONNECTIVITY_SERVICE; import static android.content.Context.CONNECTIVITY_SERVICE;
import static android.content.Context.WIFI_SERVICE; import static android.content.Context.WIFI_SERVICE;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.os.Build.VERSION.SDK_INT;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static java.util.Collections.list; import static java.util.Collections.list;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
@@ -118,7 +116,7 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
// If there's no wifi IPv4 address, we might be a client on an // If there's no wifi IPv4 address, we might be a client on an
// IPv6-only wifi network. We can only detect this on API 21+ // IPv6-only wifi network. We can only detect this on API 21+
if (wifi == null) { if (wifi == null) {
return SDK_INT >= 21 ? getWifiClientIpv6Address() : null; return getWifiClientIpv6Address();
} }
// Use the wifi IPv4 address to determine which interface's IPv6 // Use the wifi IPv4 address to determine which interface's IPv6
// address we should return (if the interface has a suitable address) // address we should return (if the interface has a suitable address)
@@ -172,7 +170,6 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
* Returns a link-local IPv6 address for the wifi client interface, or null * Returns a link-local IPv6 address for the wifi client interface, or null
* if there's no such interface or it doesn't have a suitable address. * if there's no such interface or it doesn't have a suitable address.
*/ */
@TargetApi(21)
@Nullable @Nullable
private InetAddress getWifiClientIpv6Address() { private InetAddress getWifiClientIpv6Address() {
// https://issuetracker.google.com/issues/175055271 // https://issuetracker.google.com/issues/175055271
@@ -234,7 +231,6 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
// On API 21 and later, a socket that is not created with the wifi // On API 21 and later, a socket that is not created with the wifi
// network's socket factory may try to connect via another network // network's socket factory may try to connect via another network
private SocketFactory getSocketFactory() { private SocketFactory getSocketFactory() {
if (SDK_INT < 21) return SocketFactory.getDefault();
// https://issuetracker.google.com/issues/175055271 // https://issuetracker.google.com/issues/175055271
try { try {
for (Network net : connectivityManager.getAllNetworks()) { for (Network net : connectivityManager.getAllNetworks()) {
@@ -302,7 +298,7 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
Pair<InetAddress, Boolean> wifi = getWifiIpv4Address(); Pair<InetAddress, Boolean> wifi = getWifiIpv4Address();
// If there's no wifi IPv4 address, we might be a client on an // If there's no wifi IPv4 address, we might be a client on an
// IPv6-only wifi network. We can only detect this on API 21+ // IPv6-only wifi network. We can only detect this on API 21+
if (wifi == null && SDK_INT >= 21) { if (wifi == null) {
InetAddress ipv6 = getWifiClientIpv6Address(); InetAddress ipv6 = getWifiClientIpv6Address();
if (ipv6 != null) return new Pair<>(ipv6, false); if (ipv6 != null) return new Pair<>(ipv6, false);
} }

View File

@@ -31,8 +31,6 @@ import static android.provider.Settings.Secure.ANDROID_ID;
@NotNullByDefault @NotNullByDefault
class AndroidSecureRandomProvider extends UnixSecureRandomProvider { class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
private static final int SEED_LENGTH = 32;
private final Context appContext; private final Context appContext;
@Inject @Inject
@@ -72,27 +70,6 @@ class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
// Silence strict mode // Silence strict mode
StrictMode.ThreadPolicy tp = StrictMode.allowThreadDiskWrites(); StrictMode.ThreadPolicy tp = StrictMode.allowThreadDiskWrites();
super.writeSeed(); super.writeSeed();
if (SDK_INT <= 18) applyOpenSslFix();
StrictMode.setThreadPolicy(tp); StrictMode.setThreadPolicy(tp);
} }
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
private void applyOpenSslFix() {
byte[] seed = new UnixSecureRandomSpi().engineGenerateSeed(
SEED_LENGTH);
try {
// Seed the OpenSSL PRNG
Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
.getMethod("RAND_seed", byte[].class)
.invoke(null, (Object) seed);
// Mix the output of the Linux PRNG into the OpenSSL 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();
} catch (Exception e) {
throw new SecurityException(e);
}
}
} }

View File

@@ -11,14 +11,10 @@ import org.briarproject.bramble.api.Pair;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.nullsafety.NotNullByDefault;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import java.util.Scanner;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -29,7 +25,6 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Build.VERSION.SDK_INT; import static android.os.Build.VERSION.SDK_INT;
import static android.os.Process.myPid; import static android.os.Process.myPid;
import static android.os.Process.myUid; import static android.os.Process.myUid;
import static java.lang.Runtime.getRuntime;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static org.briarproject.nullsafety.NullSafety.requireNonNull; import static org.briarproject.nullsafety.NullSafety.requireNonNull;
@@ -43,14 +38,7 @@ public class AndroidUtils {
private static final String STORED_LOGCAT = "dev-logcat"; private static final String STORED_LOGCAT = "dev-logcat";
public static Collection<String> getSupportedArchitectures() { public static Collection<String> getSupportedArchitectures() {
List<String> abis = new ArrayList<>(); return asList(Build.SUPPORTED_ABIS);
if (SDK_INT >= 21) {
abis.addAll(asList(Build.SUPPORTED_ABIS));
} else {
abis.add(Build.CPU_ABI);
if (Build.CPU_ABI2 != null) abis.add(Build.CPU_ABI2);
}
return abis;
} }
public static boolean hasBtConnectPermission(Context ctx) { public static boolean hasBtConnectPermission(Context ctx) {
@@ -136,19 +124,6 @@ public class AndroidUtils {
return new String[] {"image/jpeg", "image/png", "image/gif"}; return new String[] {"image/jpeg", "image/png", "image/gif"};
} }
@Nullable
public static String getSystemProperty(String propName) {
try {
Process p = getRuntime().exec("getprop " + propName);
Scanner s = new Scanner(p.getInputStream());
String line = s.nextLine();
s.close();
return line;
} catch (SecurityException | IOException e) {
return null;
}
}
public static boolean isUiThread() { public static boolean isUiThread() {
return Looper.myLooper() == Looper.getMainLooper(); return Looper.myLooper() == Looper.getMainLooper();
} }

View File

@@ -24,7 +24,7 @@ android {
} }
defaultConfig { defaultConfig {
minSdkVersion 16 minSdkVersion 21
targetSdkVersion 31 targetSdkVersion 31
versionCode 10500 versionCode 10500
versionName "1.5.0" versionName "1.5.0"
@@ -62,7 +62,6 @@ android {
productFlavors { productFlavors {
screenshot { screenshot {
dimension "version" dimension "version"
minSdkVersion 21
applicationIdSuffix ".screenshot" // = org.briarproject.briar.android.screenshot.debug applicationIdSuffix ".screenshot" // = org.briarproject.briar.android.screenshot.debug
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt', 'proguard-test.txt' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt', 'proguard-test.txt'
} }

View File

@@ -99,11 +99,6 @@
android:exported="false" android:exported="false"
android:label="@string/app_name" /> android:label="@string/app_name" />
<activity
android:name="org.briarproject.briar.android.splash.ExpiredOldAndroidActivity"
android:exported="false"
android:label="@string/app_name" />
<activity <activity
android:name="org.briarproject.briar.android.login.StartupActivity" android:name="org.briarproject.briar.android.login.StartupActivity"
android:exported="false" android:exported="false"

View File

@@ -294,10 +294,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
b.setOngoing(true); b.setOngoing(true);
Intent i = new Intent(appContext, SplashScreenActivity.class); Intent i = new Intent(appContext, SplashScreenActivity.class);
b.setContentIntent(getActivity(appContext, 0, i, getImmutableFlags(0))); b.setContentIntent(getActivity(appContext, 0, i, getImmutableFlags(0)));
if (SDK_INT >= 21) { b.setCategory(CATEGORY_SERVICE);
b.setCategory(CATEGORY_SERVICE); b.setVisibility(VISIBILITY_SECRET);
b.setVisibility(VISIBILITY_SECRET);
}
b.setPriority(PRIORITY_MIN); b.setPriority(PRIORITY_MIN);
return b.build(); return b.build();
} }
@@ -773,8 +771,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
i.setAction(ACTION_STOP_HOTSPOT); i.setAction(ACTION_STOP_HOTSPOT);
PendingIntent actionIntent = PendingIntent actionIntent =
getActivity(appContext, 0, i, getImmutableFlags(0)); getActivity(appContext, 0, i, getImmutableFlags(0));
int icon = SDK_INT >= 21 ? R.drawable.ic_portable_wifi_off : 0; b.addAction(R.drawable.ic_portable_wifi_off, actionTitle, actionIntent);
b.addAction(icon, actionTitle, actionIntent);
notificationManager.notify(HOTSPOT_NOTIFICATION_ID, b.build()); notificationManager.notify(HOTSPOT_NOTIFICATION_ID, b.build());
} }

View File

@@ -212,7 +212,7 @@ public class AppModule {
public Collection<SimplexPluginFactory> getSimplexFactories() { public Collection<SimplexPluginFactory> getSimplexFactories() {
List<SimplexPluginFactory> simplex = new ArrayList<>(); List<SimplexPluginFactory> simplex = new ArrayList<>();
simplex.add(mailbox); simplex.add(mailbox);
if (SDK_INT >= 19) simplex.add(drive); simplex.add(drive);
return simplex; return simplex;
} }

View File

@@ -56,16 +56,8 @@ public class Localizer {
// Get Locale from BCP-47 tag // Get Locale from BCP-47 tag
@Nullable @Nullable
public static Locale getLocaleFromTag(String tag) { public static Locale getLocaleFromTag(String tag) {
if (tag.equals("default")) if (tag.equals("default")) return null;
return null; return Locale.forLanguageTag(tag);
if (SDK_INT >= 21) {
return Locale.forLanguageTag(tag);
}
if (tag.contains("-")) {
String[] langArray = tag.split("-");
return new Locale(langArray[0], langArray[1]);
} else
return new Locale(tag);
} }
/* /*
@@ -94,12 +86,8 @@ public class Localizer {
if (locale.equals(currentLocale)) if (locale.equals(currentLocale))
return context; return context;
Locale.setDefault(locale); Locale.setDefault(locale);
if (SDK_INT >= 17) { conf.setLocale(locale);
conf.setLocale(locale); context = context.createConfigurationContext(conf);
context = context.createConfigurationContext(conf);
} else
conf.locale = locale;
//noinspection deprecation
res.updateConfiguration(conf, res.getDisplayMetrics()); res.updateConfiguration(conf, res.getDisplayMetrics());
return context; return context;
} }

View File

@@ -2,8 +2,8 @@ package org.briarproject.briar.android;
import org.briarproject.briar.BuildConfig; import org.briarproject.briar.BuildConfig;
import static android.os.Build.VERSION.SDK_INT;
import static java.util.concurrent.TimeUnit.DAYS; import static java.util.concurrent.TimeUnit.DAYS;
import static org.briarproject.briar.BuildConfig.BuildTimestamp;
public interface TestingConstants { public interface TestingConstants {
@@ -20,15 +20,9 @@ public interface TestingConstants {
*/ */
boolean PREVENT_SCREENSHOTS = !IS_DEBUG_BUILD; boolean PREVENT_SCREENSHOTS = !IS_DEBUG_BUILD;
boolean IS_OLD_ANDROID = SDK_INT <= 19;
long OLD_ANDROID_WARN_DATE = 1659225600_000L; // 2022-07-31
long OLD_ANDROID_EXPIRY_DATE = 1675123200_000L; // 2023-01-31
/** /**
* Debug builds expire after 90 days. Release builds running on Android 4 * Debug builds expire after 90 days.
* expire at a set date, otherwise they expire after 292 million years.
*/ */
long EXPIRY_DATE = IS_DEBUG_BUILD ? long EXPIRY_DATE = IS_DEBUG_BUILD ?
BuildConfig.BuildTimestamp + DAYS.toMillis(90) BuildTimestamp + DAYS.toMillis(90) : Long.MAX_VALUE;
: (IS_OLD_ANDROID ? OLD_ANDROID_EXPIRY_DATE : Long.MAX_VALUE);
} }

View File

@@ -32,7 +32,6 @@ import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_KEYGU
import static org.briarproject.briar.android.util.UiUtils.hasKeyguardLock; import static org.briarproject.briar.android.util.UiUtils.hasKeyguardLock;
import static org.briarproject.briar.android.util.UiUtils.hasUsableFingerprint; import static org.briarproject.briar.android.util.UiUtils.hasUsableFingerprint;
@RequiresApi(21)
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
public class UnlockActivity extends BaseActivity { public class UnlockActivity extends BaseActivity {

View File

@@ -81,7 +81,6 @@ import org.briarproject.briar.android.sharing.ShareBlogFragment;
import org.briarproject.briar.android.sharing.ShareForumActivity; import org.briarproject.briar.android.sharing.ShareForumActivity;
import org.briarproject.briar.android.sharing.ShareForumFragment; import org.briarproject.briar.android.sharing.ShareForumFragment;
import org.briarproject.briar.android.sharing.SharingModule; import org.briarproject.briar.android.sharing.SharingModule;
import org.briarproject.briar.android.splash.ExpiredOldAndroidActivity;
import org.briarproject.briar.android.splash.SplashScreenActivity; import org.briarproject.briar.android.splash.SplashScreenActivity;
import org.briarproject.briar.android.test.TestDataActivity; import org.briarproject.briar.android.test.TestDataActivity;
@@ -184,8 +183,6 @@ public interface ActivityComponent {
void inject(RemovableDriveActivity activity); void inject(RemovableDriveActivity activity);
void inject(ExpiredOldAndroidActivity activity);
// Fragments // Fragments
void inject(SetupFragment fragment); void inject(SetupFragment fragment);

View File

@@ -24,7 +24,6 @@ import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import androidx.annotation.RequiresApi;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
@@ -105,7 +104,7 @@ public abstract class BriarActivity extends BaseActivity {
LOG.info("Not signed in, launching StartupActivity"); LOG.info("Not signed in, launching StartupActivity");
Intent i = new Intent(this, StartupActivity.class); Intent i = new Intent(this, StartupActivity.class);
startActivityForResult(i, REQUEST_PASSWORD); startActivityForResult(i, REQUEST_PASSWORD);
} else if (SDK_INT >= 21 && lockManager.isLocked() && !isFinishing()) { } else if (lockManager.isLocked() && !isFinishing()) {
// Also check that the activity isn't finishing already. // Also check that the activity isn't finishing already.
// This is possible if we finished in onActivityResult(). // This is possible if we finished in onActivityResult().
// Launching another UnlockActivity would cause a loop. // Launching another UnlockActivity would cause a loop.
@@ -135,8 +134,7 @@ public abstract class BriarActivity extends BaseActivity {
* @param exitTransition used to move views out when starting a <b>new</b> activity. * @param exitTransition used to move views out when starting a <b>new</b> activity.
* @param returnTransition used when window is closing, because the activity is finishing. * @param returnTransition used when window is closing, because the activity is finishing.
*/ */
@RequiresApi(api = 21) protected void setSceneTransitionAnimation(
public void setSceneTransitionAnimation(
@Nullable Transition enterTransition, @Nullable Transition enterTransition,
@Nullable Transition exitTransition, @Nullable Transition exitTransition,
@Nullable Transition returnTransition) { @Nullable Transition returnTransition) {
@@ -232,8 +230,7 @@ public abstract class BriarActivity extends BaseActivity {
@Wakeful @Wakeful
private void finishAndExit() { private void finishAndExit() {
if (SDK_INT >= 21) finishAndRemoveTask(); finishAndRemoveTask();
else supportFinishAfterTransition();
LOG.info("Exiting"); LOG.info("Exiting");
BriarApplication app = (BriarApplication) getApplication(); BriarApplication app = (BriarApplication) getApplication();
if (!app.isInstrumentationTest()) System.exit(0); if (!app.isInstrumentationTest()) System.exit(0);

View File

@@ -21,7 +21,6 @@ import androidx.annotation.UiThread;
import androidx.core.view.ViewCompat; import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import static android.os.Build.VERSION.SDK_INT;
import static android.view.View.GONE; import static android.view.View.GONE;
import static android.view.View.VISIBLE; import static android.view.View.VISIBLE;
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID; import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
@@ -134,12 +133,6 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
} else { } else {
reblogger.setVisibility(GONE); reblogger.setVisibility(GONE);
} }
// Apply Android 4 padding fix after setting up author/reblogger views
if (SDK_INT < 21) {
reblogger.setPadding(padding, padding, padding, padding);
author.setPadding(padding, padding, padding, padding);
}
} }
private void onBindComment(BlogCommentItem item, boolean authorClickable) { private void onBindComment(BlogCommentItem item, boolean authorClickable) {

View File

@@ -27,11 +27,9 @@ import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.RequiresApi;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import static android.os.Build.VERSION.SDK_INT;
import static android.view.View.GONE; import static android.view.View.GONE;
import static android.view.View.VISIBLE; import static android.view.View.VISIBLE;
import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE; import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
@@ -52,7 +50,6 @@ public class RssFeedImportFragment extends BaseFragment {
private Button importButton; private Button importButton;
private ProgressBar progressBar; private ProgressBar progressBar;
@RequiresApi(19)
private final ActivityResultLauncher<String[]> docLauncher = private final ActivityResultLauncher<String[]> docLauncher =
registerForActivityResult(new OpenDocumentAdvanced(), registerForActivityResult(new OpenDocumentAdvanced(),
this::onFileChosen); this::onFileChosen);
@@ -74,7 +71,7 @@ public class RssFeedImportFragment extends BaseFragment {
@Nullable ViewGroup container, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) { @Nullable Bundle savedInstanceState) {
requireActivity().setTitle(getString(R.string.blogs_rss_feeds_import)); requireActivity().setTitle(getString(R.string.blogs_rss_feeds_import));
if (SDK_INT >= 19) setHasOptionsMenu(true); setHasOptionsMenu(true);
View v = inflater.inflate(R.layout.fragment_rss_feed_import, View v = inflater.inflate(R.layout.fragment_rss_feed_import,
container, false); container, false);
@@ -117,15 +114,13 @@ public class RssFeedImportFragment extends BaseFragment {
@Override @Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
if (SDK_INT >= 19) { inflater.inflate(R.menu.rss_feed_import_actions, menu);
inflater.inflate(R.menu.rss_feed_import_actions, menu);
}
super.onCreateOptionsMenu(menu, inflater); super.onCreateOptionsMenu(menu, inflater);
} }
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_import_file && SDK_INT >= 19) { if (item.getItemId() == R.id.action_import_file) {
launchActivityToOpenFile(requireContext(), docLauncher, launchActivityToOpenFile(requireContext(), docLauncher,
contentLauncher, "*/*"); contentLauncher, "*/*");
return true; return true;

View File

@@ -55,7 +55,6 @@ import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener
import org.briarproject.briar.android.introduction.IntroductionActivity; import org.briarproject.briar.android.introduction.IntroductionActivity;
import org.briarproject.briar.android.privategroup.conversation.GroupActivity; import org.briarproject.briar.android.privategroup.conversation.GroupActivity;
import org.briarproject.briar.android.removabledrive.RemovableDriveActivity; import org.briarproject.briar.android.removabledrive.RemovableDriveActivity;
import org.briarproject.briar.android.util.ActivityLaunchers.GetImageAdvanced;
import org.briarproject.briar.android.util.ActivityLaunchers.GetMultipleImagesAdvanced; import org.briarproject.briar.android.util.ActivityLaunchers.GetMultipleImagesAdvanced;
import org.briarproject.briar.android.util.ActivityLaunchers.OpenMultipleImageDocumentsAdvanced; import org.briarproject.briar.android.util.ActivityLaunchers.OpenMultipleImageDocumentsAdvanced;
import org.briarproject.briar.android.util.BriarSnackbarBuilder; import org.briarproject.briar.android.util.BriarSnackbarBuilder;
@@ -122,13 +121,11 @@ import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
import de.hdodenhof.circleimageview.CircleImageView; import de.hdodenhof.circleimageview.CircleImageView;
import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt; import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt;
import static android.os.Build.VERSION.SDK_INT;
import static android.view.Gravity.RIGHT; import static android.view.Gravity.RIGHT;
import static android.widget.Toast.LENGTH_SHORT; import static android.widget.Toast.LENGTH_SHORT;
import static androidx.core.app.ActivityOptionsCompat.makeSceneTransitionAnimation; import static androidx.core.app.ActivityOptionsCompat.makeSceneTransitionAnimation;
import static androidx.lifecycle.Lifecycle.State.STARTED; import static androidx.lifecycle.Lifecycle.State.STARTED;
import static androidx.recyclerview.widget.SortedList.INVALID_POSITION; import static androidx.recyclerview.widget.SortedList.INVALID_POSITION;
import static java.util.Collections.singletonList;
import static java.util.Collections.sort; import static java.util.Collections.sort;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
@@ -200,18 +197,13 @@ public class ConversationActivity extends BriarActivity
requireNonNull(name); requireNonNull(name);
loadMessages(); loadMessages();
}; };
@Nullable
private final ActivityResultLauncher<String[]> docLauncher = SDK_INT >= 19 ? private final ActivityResultLauncher<String[]> docLauncher =
registerForActivityResult(new OpenMultipleImageDocumentsAdvanced(), registerForActivityResult(new OpenMultipleImageDocumentsAdvanced(),
this::onImagesChosen) : this::onImagesChosen);
null;
private final ActivityResultLauncher<String> contentLauncher = private final ActivityResultLauncher<String> contentLauncher =
SDK_INT >= 18 ? registerForActivityResult(new GetMultipleImagesAdvanced(),
registerForActivityResult(new GetMultipleImagesAdvanced(), this::onImagesChosen);
this::onImagesChosen) :
registerForActivityResult(new GetImageAdvanced(), uri -> {
if (uri != null) onImagesChosen(singletonList(uri));
});
private AttachmentRetriever attachmentRetriever; private AttachmentRetriever attachmentRetriever;
private ConversationViewModel viewModel; private ConversationViewModel viewModel;
@@ -242,13 +234,11 @@ public class ConversationActivity extends BriarActivity
@Override @Override
public void onCreate(@Nullable Bundle state) { public void onCreate(@Nullable Bundle state) {
if (SDK_INT >= 21) { // Spurious lint warning - using END causes a crash
// Spurious lint warning - using END causes a crash @SuppressLint("RtlHardcoded")
@SuppressLint("RtlHardcoded") Transition slide = new Slide(RIGHT);
Transition slide = new Slide(RIGHT); slide.setDuration(TRANSITION_DURATION_MS);
slide.setDuration(TRANSITION_DURATION_MS); setSceneTransitionAnimation(slide, null, slide);
setSceneTransitionAnimation(slide, null, slide);
}
super.onCreate(state); super.onCreate(state);
Intent i = getIntent(); Intent i = getIntent();
@@ -389,10 +379,6 @@ public class ConversationActivity extends BriarActivity
this::showIntroductionOnboarding); this::showIntroductionOnboarding);
} }
}); });
// Transfer Data feature only supported on API 19+
if (SDK_INT >= 19) {
menu.findItem(R.id.action_transfer_data).setVisible(true);
}
// enable alias and bluetooth action once available // enable alias and bluetooth action once available
observeOnce(viewModel.getContactItem(), this, contact -> { observeOnce(viewModel.getContactItem(), this, contact -> {
menu.findItem(R.id.action_set_alias).setEnabled(true); menu.findItem(R.id.action_set_alias).setEnabled(true);
@@ -434,11 +420,9 @@ public class ConversationActivity extends BriarActivity
startActivity(intent); startActivity(intent);
return true; return true;
} else if (itemId == R.id.action_transfer_data) { } else if (itemId == R.id.action_transfer_data) {
if (SDK_INT >= 19) { Intent intent = new Intent(this, RemovableDriveActivity.class);
Intent intent = new Intent(this, RemovableDriveActivity.class); intent.putExtra(CONTACT_ID, contactId.getInt());
intent.putExtra(CONTACT_ID, contactId.getInt()); startActivity(intent);
startActivity(intent);
}
return true; return true;
} else if (itemId == R.id.action_delete_all_messages) { } else if (itemId == R.id.action_delete_all_messages) {
askToDeleteAllMessages(); askToDeleteAllMessages();
@@ -955,14 +939,10 @@ public class ConversationActivity extends BriarActivity
private void showImageOnboarding(Boolean show) { private void showImageOnboarding(Boolean show) {
if (!show) return; if (!show) return;
if (SDK_INT >= 21) { // show onboarding only after the enter transition has ended
// show onboarding only after the enter transition has ended // otherwise the tap target animation won't play
// otherwise the tap target animation won't play textInputView.postDelayed(this::showImageOnboarding,
textInputView.postDelayed(this::showImageOnboarding, TRANSITION_DURATION_MS + ONBOARDING_DELAY_MS);
TRANSITION_DURATION_MS + ONBOARDING_DELAY_MS);
} else {
showImageOnboarding();
}
} }
private void showImageOnboarding() { private void showImageOnboarding() {
@@ -973,14 +953,10 @@ public class ConversationActivity extends BriarActivity
private void showIntroductionOnboarding(@Nullable Boolean show) { private void showIntroductionOnboarding(@Nullable Boolean show) {
if (show == null || !show) return; if (show == null || !show) return;
if (SDK_INT >= 21) { // show onboarding only after the enter transition has ended
// show onboarding only after the enter transition has ended // otherwise the tap target animation won't play
// otherwise the tap target animation won't play textInputView.postDelayed(this::showIntroductionOnboarding,
textInputView.postDelayed(this::showIntroductionOnboarding, TRANSITION_DURATION_MS + ONBOARDING_DELAY_MS);
TRANSITION_DURATION_MS + ONBOARDING_DELAY_MS);
} else {
showIntroductionOnboarding();
}
} }
private void showIntroductionOnboarding() { private void showIntroductionOnboarding() {

View File

@@ -26,6 +26,7 @@ import org.briarproject.briar.android.util.BriarSnackbarBuilder;
import org.briarproject.briar.android.view.PullDownLayout; import org.briarproject.briar.android.view.PullDownLayout;
import org.briarproject.nullsafety.MethodsNotNullByDefault; import org.briarproject.nullsafety.MethodsNotNullByDefault;
import org.briarproject.nullsafety.ParametersNotNullByDefault; import org.briarproject.nullsafety.ParametersNotNullByDefault;
import org.jetbrains.annotations.NotNull;
import java.util.List; import java.util.List;
@@ -41,13 +42,11 @@ import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2; import androidx.viewpager2.widget.ViewPager2;
import static android.graphics.Color.TRANSPARENT; import static android.graphics.Color.TRANSPARENT;
import static android.os.Build.VERSION.SDK_INT;
import static android.view.View.GONE; import static android.view.View.GONE;
import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN; import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE; import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
import static android.view.View.VISIBLE; import static android.view.View.VISIBLE;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
import static com.google.android.material.snackbar.Snackbar.LENGTH_LONG; import static com.google.android.material.snackbar.Snackbar.LENGTH_LONG;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static org.briarproject.briar.android.util.UiUtils.formatDateAbsolute; import static org.briarproject.briar.android.util.UiUtils.formatDateAbsolute;
@@ -77,11 +76,9 @@ public class ImageActivity extends BriarActivity
private List<AttachmentItem> attachments; private List<AttachmentItem> attachments;
private MessageId conversationMessageId; private MessageId conversationMessageId;
@Nullable private final ActivityResultLauncher<String> launcher =
private final ActivityResultLauncher<String> launcher = SDK_INT >= 19 ?
registerForActivityResult(new CreateDocumentAdvanced(), registerForActivityResult(new CreateDocumentAdvanced(),
this::onImageUriSelected) : this::onImageUriSelected);
null;
@Override @Override
public void injectActivity(ActivityComponent component) { public void injectActivity(ActivityComponent component) {
@@ -97,10 +94,8 @@ public class ImageActivity extends BriarActivity
// Transitions // Transitions
if (state == null) supportPostponeEnterTransition(); if (state == null) supportPostponeEnterTransition();
Window window = getWindow(); Window window = getWindow();
if (SDK_INT >= 21) { Transition transition = new Fade();
Transition transition = new Fade(); setSceneTransitionAnimation(transition, null, transition);
setSceneTransitionAnimation(transition, null, transition);
}
// Intent Extras // Intent Extras
Intent i = getIntent(); Intent i = getIntent();
@@ -124,12 +119,7 @@ public class ImageActivity extends BriarActivity
layout.getViewTreeObserver().addOnGlobalLayoutListener(this); layout.getViewTreeObserver().addOnGlobalLayoutListener(this);
// Status Bar // Status Bar
if (SDK_INT >= 21) { window.setStatusBarColor(TRANSPARENT);
window.setStatusBarColor(TRANSPARENT);
} else if (SDK_INT >= 19) {
// we can't make the status bar transparent, but translucent
window.addFlags(FLAG_TRANSLUCENT_STATUS);
}
// Toolbar // Toolbar
appBarLayout = findViewById(R.id.appBarLayout); appBarLayout = findViewById(R.id.appBarLayout);
@@ -257,16 +247,12 @@ public class ImageActivity extends BriarActivity
private void showSaveImageDialog() { private void showSaveImageDialog() {
OnClickListener okListener = (dialog, which) -> { OnClickListener okListener = (dialog, which) -> {
if (SDK_INT >= 19) { String name = viewModel.getFileName() + "." +
String name = viewModel.getFileName() + "." + getVisibleAttachment().getExtension();
getVisibleAttachment().getExtension(); try {
try { launcher.launch(name);
requireNonNull(launcher).launch(name); } catch (ActivityNotFoundException e) {
} catch (ActivityNotFoundException e) { viewModel.onSaveImageError();
viewModel.onSaveImageError();
}
} else {
viewModel.saveImage(getVisibleAttachment());
} }
}; };
Builder builder = new Builder(this, R.style.BriarDialogTheme); Builder builder = new Builder(this, R.style.BriarDialogTheme);
@@ -295,7 +281,7 @@ public class ImageActivity extends BriarActivity
.show(); .show();
} }
AttachmentItem getVisibleAttachment() { private AttachmentItem getVisibleAttachment() {
return attachments.get(viewPager.getCurrentItem()); return attachments.get(viewPager.getCurrentItem());
} }
@@ -307,6 +293,7 @@ public class ImageActivity extends BriarActivity
super(ImageActivity.this); super(ImageActivity.this);
} }
@NotNull
@Override @Override
public Fragment createFragment(int position) { public Fragment createFragment(int position) {
Fragment f = ImageFragment.newInstance( Fragment f = ImageFragment.newInstance(

View File

@@ -31,7 +31,6 @@ import androidx.fragment.app.Fragment;
import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import static android.os.Build.VERSION.SDK_INT;
import static android.widget.ImageView.ScaleType.FIT_START; import static android.widget.ImageView.ScaleType.FIT_START;
import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE; import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE;
import static org.briarproject.briar.android.attachment.AttachmentItem.State.AVAILABLE; import static org.briarproject.briar.android.attachment.AttachmentItem.State.AVAILABLE;
@@ -150,7 +149,7 @@ public class ImageFragment extends Fragment
public boolean onResourceReady(Drawable resource, Object model, public boolean onResourceReady(Drawable resource, Object model,
Target<Drawable> target, DataSource dataSource, Target<Drawable> target, DataSource dataSource,
boolean isFirstResource) { boolean isFirstResource) {
if (SDK_INT >= 21 && !(resource instanceof Animatable)) { if (!(resource instanceof Animatable)) {
// set transition name only when not animatable, // set transition name only when not animatable,
// because the animation won't start otherwise // because the animation won't start otherwise
photoView.setTransitionName( photoView.setTransitionName(

View File

@@ -18,7 +18,6 @@ import androidx.annotation.DrawableRes;
import androidx.recyclerview.widget.RecyclerView.ViewHolder; import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import androidx.recyclerview.widget.StaggeredGridLayoutManager.LayoutParams; import androidx.recyclerview.widget.StaggeredGridLayoutManager.LayoutParams;
import static android.os.Build.VERSION.SDK_INT;
import static android.widget.ImageView.ScaleType.CENTER_CROP; import static android.widget.ImageView.ScaleType.CENTER_CROP;
import static android.widget.ImageView.ScaleType.FIT_CENTER; import static android.widget.ImageView.ScaleType.FIT_CENTER;
import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE; import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE;
@@ -58,10 +57,8 @@ class ImageViewHolder extends ViewHolder {
loadImage(attachment, r); loadImage(attachment, r);
imageView.setScaleType(CENTER_CROP); imageView.setScaleType(CENTER_CROP);
} }
if (SDK_INT >= 21) { imageView.setTransitionName(
imageView.setTransitionName( attachment.getTransitionName(conversationItemId));
attachment.getTransitionName(conversationItemId));
}
} }
private void setImageViewDimensions(AttachmentItem a, boolean single, private void setImageViewDimensions(AttachmentItem a, boolean single,

View File

@@ -1,8 +1,5 @@
package org.briarproject.briar.android.forum; package org.briarproject.briar.android.forum;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.briar.api.identity.AuthorInfo;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.android.threaded.ThreadItem; import org.briarproject.briar.android.threaded.ThreadItem;
import org.briarproject.briar.api.forum.ForumPostHeader; import org.briarproject.briar.api.forum.ForumPostHeader;

View File

@@ -2,7 +2,6 @@ package org.briarproject.briar.android.hotspot;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@@ -17,8 +16,6 @@ import org.briarproject.briar.android.util.ActivityLaunchers.CreateDocumentAdvan
import org.briarproject.nullsafety.MethodsNotNullByDefault; import org.briarproject.nullsafety.MethodsNotNullByDefault;
import org.briarproject.nullsafety.ParametersNotNullByDefault; import org.briarproject.nullsafety.ParametersNotNullByDefault;
import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.ActivityResultLauncher;
@@ -29,14 +26,11 @@ import androidx.lifecycle.ViewModelProvider;
import static android.content.Intent.ACTION_SEND; import static android.content.Intent.ACTION_SEND;
import static android.content.Intent.EXTRA_STREAM; import static android.content.Intent.EXTRA_STREAM;
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION; import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
import static android.os.Build.VERSION.SDK_INT;
import static android.view.View.INVISIBLE; import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE; import static android.view.View.VISIBLE;
import static androidx.transition.TransitionManager.beginDelayedTransition; import static androidx.transition.TransitionManager.beginDelayedTransition;
import static org.briarproject.briar.android.AppModule.getAndroidComponent; import static org.briarproject.briar.android.AppModule.getAndroidComponent;
import static org.briarproject.briar.android.hotspot.HotspotViewModel.getApkFileName; import static org.briarproject.briar.android.hotspot.HotspotViewModel.getApkFileName;
import static org.briarproject.nullsafety.NullSafety.requireNonNull;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
@@ -48,11 +42,9 @@ public class FallbackFragment extends BaseFragment {
ViewModelProvider.Factory viewModelFactory; ViewModelProvider.Factory viewModelFactory;
private HotspotViewModel viewModel; private HotspotViewModel viewModel;
@Nullable private final ActivityResultLauncher<String> launcher =
private final ActivityResultLauncher<String> launcher = SDK_INT >= 19 ?
registerForActivityResult(new CreateDocumentAdvanced(), registerForActivityResult(new CreateDocumentAdvanced(),
this::onDocumentCreated) : this::onDocumentCreated);
null;
private Button fallbackButton; private Button fallbackButton;
private ProgressBar progressBar; private ProgressBar progressBar;
@@ -89,12 +81,7 @@ public class FallbackFragment extends BaseFragment {
beginDelayedTransition((ViewGroup) v); beginDelayedTransition((ViewGroup) v);
fallbackButton.setVisibility(INVISIBLE); fallbackButton.setVisibility(INVISIBLE);
progressBar.setVisibility(VISIBLE); progressBar.setVisibility(VISIBLE);
launcher.launch(getApkFileName());
if (SDK_INT >= 19) {
requireNonNull(launcher).launch(getApkFileName());
} else {
viewModel.exportApk();
}
}); });
viewModel.getSavedApkToUri().observeEvent(this, this::shareUri); viewModel.getSavedApkToUri().observeEvent(this, this::shareUri);
} }
@@ -110,23 +97,11 @@ public class FallbackFragment extends BaseFragment {
progressBar.setVisibility(INVISIBLE); progressBar.setVisibility(INVISIBLE);
} }
void shareUri(Uri uri) { private void shareUri(Uri uri) {
Intent i = new Intent(ACTION_SEND); Intent i = new Intent(ACTION_SEND);
i.putExtra(EXTRA_STREAM, uri); i.putExtra(EXTRA_STREAM, uri);
i.setType("*/*"); // gives us all sharing options i.setType("*/*"); // gives us all sharing options
i.addFlags(FLAG_GRANT_READ_URI_PERMISSION); i.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
Context ctx = requireContext();
if (SDK_INT <= 19) {
// Workaround for Android bug:
// ctx.grantUriPermission also needed for Android 4
List<ResolveInfo> resInfoList = ctx.getPackageManager()
.queryIntentActivities(i, MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
ctx.grantUriPermission(packageName, uri,
FLAG_GRANT_READ_URI_PERMISSION);
}
}
startActivity(Intent.createChooser(i, null)); startActivity(Intent.createChooser(i, null));
} }

View File

@@ -25,7 +25,6 @@ import org.briarproject.nullsafety.NotNullByDefault;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@@ -38,9 +37,6 @@ import androidx.annotation.UiThread;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import static android.os.Build.VERSION.SDK_INT;
import static android.os.Environment.DIRECTORY_DOWNLOADS;
import static android.os.Environment.getExternalStoragePublicDirectory;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
@@ -168,7 +164,6 @@ class HotspotViewModel extends DbViewModel
} }
void exportApk(Uri uri) { void exportApk(Uri uri) {
if (SDK_INT < 19) throw new IllegalStateException();
try { try {
OutputStream out = getApplication().getContentResolver() OutputStream out = getApplication().getContentResolver()
.openOutputStream(uri, "wt"); .openOutputStream(uri, "wt");
@@ -178,20 +173,6 @@ class HotspotViewModel extends DbViewModel
} }
} }
void exportApk() {
if (SDK_INT >= 19) throw new IllegalStateException();
File path = getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS);
//noinspection ResultOfMethodCallIgnored
path.mkdirs();
File file = new File(path, getApkFileName());
try {
OutputStream out = new FileOutputStream(file);
writeApk(out, Uri.fromFile(file));
} catch (FileNotFoundException e) {
handleException(e);
}
}
static String getApkFileName() { static String getApkFileName() {
return "briar" + (DEBUG ? "-debug-" : "-") + VERSION_NAME + ".apk"; return "briar" + (DEBUG ? "-debug-" : "-") + VERSION_NAME + ".apk";
} }

View File

@@ -5,8 +5,6 @@ import android.os.Bundle;
import java.util.logging.Logger; import java.util.logging.Logger;
import static android.os.Build.VERSION.SDK_INT;
public class ExitActivity extends Activity { public class ExitActivity extends Activity {
private static final Logger LOG = private static final Logger LOG =
@@ -15,8 +13,7 @@ public class ExitActivity extends Activity {
@Override @Override
public void onCreate(Bundle state) { public void onCreate(Bundle state) {
super.onCreate(state); super.onCreate(state);
if (SDK_INT >= 21) finishAndRemoveTask(); finishAndRemoveTask();
else finish();
LOG.info("Exiting"); LOG.info("Exiting");
System.exit(0); System.exit(0);
} }

View File

@@ -75,15 +75,12 @@ import static org.briarproject.bramble.api.plugin.Plugin.State.ENABLING;
import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING; import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING;
import static org.briarproject.briar.android.BriarService.EXTRA_STARTUP_FAILED; import static org.briarproject.briar.android.BriarService.EXTRA_STARTUP_FAILED;
import static org.briarproject.briar.android.BriarService.EXTRA_START_RESULT; import static org.briarproject.briar.android.BriarService.EXTRA_START_RESULT;
import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE;
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD; import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PASSWORD; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PASSWORD;
import static org.briarproject.briar.android.navdrawer.IntentRouter.handleExternalIntent; import static org.briarproject.briar.android.navdrawer.IntentRouter.handleExternalIntent;
import static org.briarproject.briar.android.util.UiUtils.formatDateFull;
import static org.briarproject.briar.android.util.UiUtils.getDaysUntilExpiry; import static org.briarproject.briar.android.util.UiUtils.getDaysUntilExpiry;
import static org.briarproject.briar.android.util.UiUtils.observeOnce; import static org.briarproject.briar.android.util.UiUtils.observeOnce;
import static org.briarproject.briar.android.util.UiUtils.resolveColorAttribute; import static org.briarproject.briar.android.util.UiUtils.resolveColorAttribute;
import static org.briarproject.briar.android.util.UiUtils.shouldWarnOldAndroidExpiry;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
@@ -140,11 +137,9 @@ public class NavDrawerActivity extends BriarActivity implements
setContentView(R.layout.activity_nav_drawer); setContentView(R.layout.activity_nav_drawer);
BriarApplication app = (BriarApplication) getApplication(); BriarApplication app = (BriarApplication) getApplication();
if (!app.isInstrumentationTest()) { if (IS_DEBUG_BUILD && !app.isInstrumentationTest()) {
if (IS_DEBUG_BUILD || shouldWarnOldAndroidExpiry()) { navDrawerViewModel.showExpiryWarning()
navDrawerViewModel.showExpiryWarning() .observe(this, this::showExpiryWarning);
.observe(this, this::showExpiryWarning);
}
} }
navDrawerViewModel.shouldAskForDozeWhitelisting().observe(this, ask -> { navDrawerViewModel.shouldAskForDozeWhitelisting().observe(this, ask -> {
if (ask) showDozeDialog(R.string.dnkm_doze_intro); if (ask) showDozeDialog(R.string.dnkm_doze_intro);
@@ -212,7 +207,7 @@ public class NavDrawerActivity extends BriarActivity implements
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
lockManager.checkIfLockable(); lockManager.checkIfLockable();
if (IS_DEBUG_BUILD || shouldWarnOldAndroidExpiry()) { if (IS_DEBUG_BUILD) {
navDrawerViewModel.checkExpiryWarning(); navDrawerViewModel.checkExpiryWarning();
} }
} }
@@ -384,23 +379,14 @@ public class NavDrawerActivity extends BriarActivity implements
return; return;
} }
String text;
if (IS_DEBUG_BUILD) {
text = getResources().getQuantityString(
R.plurals.expiry_warning, (int) daysUntilExpiry,
(int) daysUntilExpiry);
} else {
text = getResources().getQuantityString(
R.plurals.old_android_expiry_warning, (int) daysUntilExpiry,
formatDateFull(this, EXPIRY_DATE),
(int) daysUntilExpiry);
}
ViewGroup expiryWarning = findViewById(R.id.expiryWarning); ViewGroup expiryWarning = findViewById(R.id.expiryWarning);
if (show) { if (show) {
// show expiry warning text // show expiry warning text
TextView expiryWarningText = TextView expiryWarningText =
expiryWarning.findViewById(R.id.expiryWarningText); expiryWarning.findViewById(R.id.expiryWarningText);
String text = getResources().getQuantityString(
R.plurals.expiry_warning, (int) daysUntilExpiry,
(int) daysUntilExpiry);
expiryWarningText.setText(text); expiryWarningText.setText(text);
// make close button functional // make close button functional
ImageView expiryWarningClose = ImageView expiryWarningClose =
@@ -432,7 +418,7 @@ public class NavDrawerActivity extends BriarActivity implements
} }
@Override @Override
public View getView(int position, View convertView, public View getView(int position, @Nullable View convertView,
ViewGroup parent) { ViewGroup parent) {
View view; View view;
if (convertView != null) { if (convertView != null) {

View File

@@ -19,7 +19,6 @@ import info.guardianproject.panic.Panic;
import info.guardianproject.panic.PanicResponder; import info.guardianproject.panic.PanicResponder;
import info.guardianproject.trustedintents.TrustedIntents; import info.guardianproject.trustedintents.TrustedIntents;
import static android.os.Build.VERSION.SDK_INT;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.briar.android.panic.PanicPreferencesFragment.KEY_LOCK; import static org.briarproject.briar.android.panic.PanicPreferencesFragment.KEY_LOCK;
import static org.briarproject.briar.android.panic.PanicPreferencesFragment.KEY_PURGE; import static org.briarproject.briar.android.panic.PanicPreferencesFragment.KEY_PURGE;
@@ -73,12 +72,7 @@ public class PanicResponderActivity extends BriarActivity {
} }
} }
} }
finishAndRemoveTask();
if (SDK_INT >= 21) {
finishAndRemoveTask();
} else {
finish();
}
} }
@Override @Override

View File

@@ -20,7 +20,6 @@ import javax.inject.Inject;
import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
@@ -31,7 +30,6 @@ import static org.briarproject.briar.android.AppModule.getAndroidComponent;
import static org.briarproject.briar.android.util.UiUtils.hideViewOnSmallScreen; import static org.briarproject.briar.android.util.UiUtils.hideViewOnSmallScreen;
import static org.briarproject.briar.android.util.UiUtils.launchActivityToOpenFile; import static org.briarproject.briar.android.util.UiUtils.launchActivityToOpenFile;
@RequiresApi(19)
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
public class ReceiveFragment extends Fragment { public class ReceiveFragment extends Fragment {

View File

@@ -19,7 +19,6 @@ import javax.inject.Inject;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
@@ -29,7 +28,6 @@ import static java.util.Objects.requireNonNull;
import static org.briarproject.briar.android.conversation.ConversationActivity.CONTACT_ID; import static org.briarproject.briar.android.conversation.ConversationActivity.CONTACT_ID;
import static org.briarproject.briar.android.util.UiUtils.showFragment; import static org.briarproject.briar.android.util.UiUtils.showFragment;
@RequiresApi(19)
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
public class RemovableDriveActivity extends BriarActivity { public class RemovableDriveActivity extends BriarActivity {

View File

@@ -24,7 +24,6 @@ import javax.inject.Inject;
import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
@@ -38,7 +37,6 @@ import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.briar.android.AppModule.getAndroidComponent; import static org.briarproject.briar.android.AppModule.getAndroidComponent;
import static org.briarproject.briar.android.util.UiUtils.hideViewOnSmallScreen; import static org.briarproject.briar.android.util.UiUtils.hideViewOnSmallScreen;
@RequiresApi(19)
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
public class SendFragment extends Fragment { public class SendFragment extends Fragment {

View File

@@ -220,10 +220,10 @@ class BriarReportCollector {
method.setAccessible(true); method.setAccessible(true);
mobileEnabled = (Boolean) requireNonNull(method.invoke(cm)); mobileEnabled = (Boolean) requireNonNull(method.invoke(cm));
} catch (ClassNotFoundException } catch (ClassNotFoundException
| NoSuchMethodException | NoSuchMethodException
| IllegalArgumentException | IllegalArgumentException
| InvocationTargetException | InvocationTargetException
| IllegalAccessException e) { | IllegalAccessException e) {
connectivityInfo connectivityInfo
.add("MobileDataReflectionException", e.toString()); .add("MobileDataReflectionException", e.toString());
} }
@@ -300,15 +300,12 @@ class BriarReportCollector {
scanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE; scanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE;
connectivityInfo.add("BluetoothDiscoverable", btDiscoverable); connectivityInfo.add("BluetoothDiscoverable", btDiscoverable);
if (SDK_INT >= 21) { // Is Bluetooth LE scanning and advertising supported?
// Is Bluetooth LE scanning and advertising supported? boolean btLeScan = bt.getBluetoothLeScanner() != null;
boolean btLeScan = bt.getBluetoothLeScanner() != null; connectivityInfo.add("BluetoothLeScanningSupported", btLeScan);
connectivityInfo.add("BluetoothLeScanningSupported", btLeScan); boolean btLeAdvertise = bt.getBluetoothLeAdvertiser() != null;
boolean btLeAdvertise = connectivityInfo.add("BluetoothLeAdvertisingSupported",
bt.getBluetoothLeAdvertiser() != null; btLeAdvertise);
connectivityInfo.add("BluetoothLeAdvertisingSupported",
btLeAdvertise);
}
Pair<String, String> p = getBluetoothAddressAndMethod(ctx, bt); Pair<String, String> p = getBluetoothAddressAndMethod(ctx, bt);
String address = p.getFirst(); String address = p.getFirst();

View File

@@ -27,7 +27,6 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.os.Build.VERSION.SDK_INT; import static android.os.Build.VERSION.SDK_INT;
import static androidx.core.view.ViewCompat.LAYOUT_DIRECTION_LTR; import static androidx.core.view.ViewCompat.LAYOUT_DIRECTION_LTR;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.briar.android.BriarApplication.ENTRY_ACTIVITY; import static org.briarproject.briar.android.BriarApplication.ENTRY_ACTIVITY;
import static org.briarproject.briar.android.navdrawer.NavDrawerActivity.SIGN_OUT_URI; import static org.briarproject.briar.android.navdrawer.NavDrawerActivity.SIGN_OUT_URI;
@@ -75,12 +74,6 @@ public class DisplayFragment extends PreferenceFragmentCompat {
Locale locale = Localizer.getLocaleFromTag(tag); Locale locale = Localizer.getLocaleFromTag(tag);
if (locale == null) if (locale == null)
throw new IllegalStateException(); throw new IllegalStateException();
// Exclude RTL locales on API < 17, they won't be laid out correctly
if (SDK_INT < 17 && !isLeftToRight(locale)) {
if (LOG.isLoggable(INFO))
LOG.info("Skipping RTL locale " + tag);
continue;
}
String nativeName = locale.getDisplayName(locale); String nativeName = locale.getDisplayName(locale);
// Fallback to English if the name is unknown in both native and // Fallback to English if the name is unknown in both native and
// current locale. // current locale.

View File

@@ -18,7 +18,6 @@ import androidx.preference.ListPreference;
import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.SwitchPreferenceCompat; import androidx.preference.SwitchPreferenceCompat;
import static android.os.Build.VERSION.SDK_INT;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static org.briarproject.briar.android.AppModule.getAndroidComponent; import static org.briarproject.briar.android.AppModule.getAndroidComponent;
import static org.briarproject.briar.android.settings.SettingsActivity.enableAndPersist; import static org.briarproject.briar.android.settings.SettingsActivity.enableAndPersist;
@@ -71,17 +70,12 @@ public class SecurityFragment extends PreferenceFragmentCompat {
@Override @Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
if (SDK_INT < 21) { // timeout depends on screenLock and gets disabled automatically
screenLock.setVisible(false); LifecycleOwner lifecycleOwner = getViewLifecycleOwner();
screenLockTimeout.setVisible(false); viewModel.getScreenLockTimeout().observe(lifecycleOwner, value -> {
} else { screenLockTimeout.setValue(value);
// timeout depends on screenLock and gets disabled automatically enableAndPersist(screenLockTimeout);
LifecycleOwner lifecycleOwner = getViewLifecycleOwner(); });
viewModel.getScreenLockTimeout().observe(lifecycleOwner, value -> {
screenLockTimeout.setValue(value);
enableAndPersist(screenLockTimeout);
});
}
} }
@Override @Override
@@ -92,7 +86,6 @@ public class SecurityFragment extends PreferenceFragmentCompat {
} }
private void checkScreenLock() { private void checkScreenLock() {
if (SDK_INT < 21) return;
LifecycleOwner lifecycleOwner = getViewLifecycleOwner(); LifecycleOwner lifecycleOwner = getViewLifecycleOwner();
viewModel.getScreenLockEnabled().removeObservers(lifecycleOwner); viewModel.getScreenLockEnabled().removeObservers(lifecycleOwner);
if (hasScreenLock(requireActivity())) { if (hasScreenLock(requireActivity())) {

View File

@@ -24,7 +24,6 @@ import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceGroup;
import static android.os.Build.VERSION.SDK_INT;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static org.briarproject.briar.android.AppModule.getAndroidComponent; import static org.briarproject.briar.android.AppModule.getAndroidComponent;
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD; import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
@@ -49,11 +48,9 @@ public class SettingsFragment extends PreferenceFragmentCompat {
private SettingsViewModel viewModel; private SettingsViewModel viewModel;
private AvatarPreference prefAvatar; private AvatarPreference prefAvatar;
@Nullable private final ActivityResultLauncher<String[]> docLauncher =
private final ActivityResultLauncher<String[]> docLauncher = SDK_INT >= 19 ?
registerForActivityResult(new OpenImageDocumentAdvanced(), registerForActivityResult(new OpenImageDocumentAdvanced(),
this::onImageSelected) : this::onImageSelected);
null;
private final ActivityResultLauncher<String> contentLauncher = private final ActivityResultLauncher<String> contentLauncher =
registerForActivityResult(new GetImageAdvanced(), registerForActivityResult(new GetImageAdvanced(),
this::onImageSelected); this::onImageSelected);

View File

@@ -1,57 +0,0 @@
package org.briarproject.briar.android.splash;
import android.content.Intent;
import android.os.Bundle;
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BaseActivity;
import org.briarproject.briar.android.controller.BriarController;
import org.briarproject.briar.android.logout.ExitActivity;
import org.briarproject.nullsafety.MethodsNotNullByDefault;
import org.briarproject.nullsafety.ParametersNotNullByDefault;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class ExpiredOldAndroidActivity extends BaseActivity {
@Inject
BriarController briarController;
@Inject
AndroidWakeLockManager wakeLockManager;
@Override
public void onCreate(@Nullable Bundle state) {
super.onCreate(state);
setContentView(R.layout.activity_expired_old_android);
findViewById(R.id.delete_account_button).setOnClickListener(v -> {
// Hold a wake lock to ensure we exit before the device goes to sleep
wakeLockManager.runWakefully(() -> {
// we're not signed in, just go ahead and delete
briarController.deleteAccount();
// remove from recent apps
Intent i = new Intent(this, ExitActivity.class);
i.addFlags(FLAG_ACTIVITY_NEW_TASK
| FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
| FLAG_ACTIVITY_NO_ANIMATION
| FLAG_ACTIVITY_CLEAR_TASK);
startActivity(i);
}, "DeleteAccount");
});
}
@Override
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
}

View File

@@ -21,7 +21,6 @@ import javax.inject.Inject;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.os.Build.VERSION.SDK_INT;
import static androidx.preference.PreferenceManager.setDefaultValues; import static androidx.preference.PreferenceManager.setDefaultValues;
import static java.lang.System.currentTimeMillis; import static java.lang.System.currentTimeMillis;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
@@ -50,12 +49,8 @@ public class SplashScreenActivity extends BaseActivity {
public void onCreate(@Nullable Bundle state) { public void onCreate(@Nullable Bundle state) {
super.onCreate(state); super.onCreate(state);
if (SDK_INT >= 21) { getWindow().setExitTransition(new Fade());
getWindow().setExitTransition(new Fade());
}
setPreferencesDefaults(); setPreferencesDefaults();
setContentView(R.layout.splash); setContentView(R.layout.splash);
if (accountManager.hasDatabaseKey()) { if (accountManager.hasDatabaseKey()) {
@@ -65,14 +60,9 @@ public class SplashScreenActivity extends BaseActivity {
int duration = int duration =
getResources().getInteger(R.integer.splashScreenDuration); getResources().getInteger(R.integer.splashScreenDuration);
new Handler().postDelayed(() -> { new Handler().postDelayed(() -> {
if (currentTimeMillis() >= EXPIRY_DATE) { if (IS_DEBUG_BUILD && currentTimeMillis() >= EXPIRY_DATE) {
if (IS_DEBUG_BUILD) { LOG.info("Expired");
LOG.info("Expired: debug build"); startNextActivity(ExpiredActivity.class);
startNextActivity(ExpiredActivity.class);
} else {
LOG.info("Expired: running on old Android");
startNextActivity(ExpiredOldAndroidActivity.class);
}
} else { } else {
startNextActivity(ENTRY_ACTIVITY); startNextActivity(ENTRY_ACTIVITY);
} }

View File

@@ -13,7 +13,6 @@ import androidx.activity.result.contract.ActivityResultContracts.OpenDocument;
import androidx.activity.result.contract.ActivityResultContracts.OpenMultipleDocuments; import androidx.activity.result.contract.ActivityResultContracts.OpenMultipleDocuments;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import static android.app.Activity.RESULT_CANCELED; import static android.app.Activity.RESULT_CANCELED;
import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE; import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE;
@@ -26,7 +25,6 @@ import static org.briarproject.bramble.util.AndroidUtils.getSupportedImageConten
@NotNullByDefault @NotNullByDefault
public class ActivityLaunchers { public class ActivityLaunchers {
@RequiresApi(19)
public static class CreateDocumentAdvanced extends CreateDocument { public static class CreateDocumentAdvanced extends CreateDocument {
@NonNull @NonNull
@Override @Override
@@ -48,7 +46,6 @@ public class ActivityLaunchers {
} }
} }
@RequiresApi(19)
public static class OpenDocumentAdvanced extends OpenDocument { public static class OpenDocumentAdvanced extends OpenDocument {
@NonNull @NonNull
@Override @Override
@@ -69,13 +66,11 @@ public class ActivityLaunchers {
putShowAdvancedExtra(i); putShowAdvancedExtra(i);
i.setType("image/*"); i.setType("image/*");
i.addFlags(FLAG_GRANT_READ_URI_PERMISSION); i.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
if (SDK_INT >= 19) i.putExtra(EXTRA_MIME_TYPES, getSupportedImageContentTypes());
i.putExtra(EXTRA_MIME_TYPES, getSupportedImageContentTypes());
return i; return i;
} }
} }
@RequiresApi(18)
public static class GetMultipleImagesAdvanced extends GetMultipleContents { public static class GetMultipleImagesAdvanced extends GetMultipleContents {
@NonNull @NonNull
@Override @Override
@@ -84,13 +79,11 @@ public class ActivityLaunchers {
putShowAdvancedExtra(i); putShowAdvancedExtra(i);
i.setType("image/*"); i.setType("image/*");
i.addFlags(FLAG_GRANT_READ_URI_PERMISSION); i.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
if (SDK_INT >= 19) i.putExtra(EXTRA_MIME_TYPES, getSupportedImageContentTypes());
i.putExtra(EXTRA_MIME_TYPES, getSupportedImageContentTypes());
return i; return i;
} }
} }
@RequiresApi(19)
public static class OpenImageDocumentAdvanced extends OpenDocument { public static class OpenImageDocumentAdvanced extends OpenDocument {
@NonNull @NonNull
@Override @Override
@@ -99,13 +92,11 @@ public class ActivityLaunchers {
putShowAdvancedExtra(i); putShowAdvancedExtra(i);
i.setType("image/*"); i.setType("image/*");
i.addFlags(FLAG_GRANT_READ_URI_PERMISSION); i.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
if (SDK_INT >= 19) i.putExtra(EXTRA_MIME_TYPES, getSupportedImageContentTypes());
i.putExtra(EXTRA_MIME_TYPES, getSupportedImageContentTypes());
return i; return i;
} }
} }
@RequiresApi(19)
public static class OpenMultipleImageDocumentsAdvanced public static class OpenMultipleImageDocumentsAdvanced
extends OpenMultipleDocuments { extends OpenMultipleDocuments {
@NonNull @NonNull
@@ -115,8 +106,7 @@ public class ActivityLaunchers {
putShowAdvancedExtra(i); putShowAdvancedExtra(i);
i.setType("image/*"); i.setType("image/*");
i.addFlags(FLAG_GRANT_READ_URI_PERMISSION); i.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
if (SDK_INT >= 19) i.putExtra(EXTRA_MIME_TYPES, getSupportedImageContentTypes());
i.putExtra(EXTRA_MIME_TYPES, getSupportedImageContentTypes());
return i; return i;
} }
} }

View File

@@ -8,7 +8,6 @@ import androidx.annotation.ColorRes;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import static android.os.Build.VERSION.SDK_INT;
import static androidx.core.app.NotificationCompat.VISIBILITY_PRIVATE; import static androidx.core.app.NotificationCompat.VISIBILITY_PRIVATE;
public class BriarNotificationBuilder extends NotificationCompat.Builder { public class BriarNotificationBuilder extends NotificationCompat.Builder {
@@ -24,7 +23,7 @@ public class BriarNotificationBuilder extends NotificationCompat.Builder {
setLights(ContextCompat.getColor(context, R.color.briar_lime_400), setLights(ContextCompat.getColor(context, R.color.briar_lime_400),
750, 500); 750, 500);
if (SDK_INT >= 21) setVisibility(VISIBILITY_PRIVATE); setVisibility(VISIBILITY_PRIVATE);
} }
public BriarNotificationBuilder setColorRes(@ColorRes int res) { public BriarNotificationBuilder setColorRes(@ColorRes int res) {
@@ -33,7 +32,7 @@ public class BriarNotificationBuilder extends NotificationCompat.Builder {
} }
public BriarNotificationBuilder setNotificationCategory(String category) { public BriarNotificationBuilder setNotificationCategory(String category) {
if (SDK_INT >= 21) setCategory(category); setCategory(category);
return this; return this;
} }

View File

@@ -4,7 +4,6 @@ import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import com.google.android.material.snackbar.Snackbar; import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.snackbar.Snackbar.Callback;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.nullsafety.NotNullByDefault;
@@ -13,11 +12,7 @@ import androidx.annotation.ColorRes;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import static android.os.Build.VERSION.SDK_INT;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static androidx.core.content.ContextCompat.getColor; import static androidx.core.content.ContextCompat.getColor;
import static com.google.android.material.snackbar.Snackbar.LENGTH_INDEFINITE;
@NotNullByDefault @NotNullByDefault
public class BriarSnackbarBuilder { public class BriarSnackbarBuilder {
@@ -37,24 +32,6 @@ public class BriarSnackbarBuilder {
R.color.briar_button_text_positive)); R.color.briar_button_text_positive));
s.setAction(actionResId, onClickListener); s.setAction(actionResId, onClickListener);
} }
// Workaround for https://issuetracker.google.com/issues/64285517
if (duration == LENGTH_INDEFINITE && SDK_INT < 21) {
// Hide snackbar while it's opening to make bouncing less noticeable
s.getView().setVisibility(INVISIBLE);
s.addCallback(new Callback() {
@Override
public void onShown(Snackbar snackbar) {
snackbar.getView().setVisibility(VISIBLE);
// Request layout again in case snackbar is in wrong place
snackbar.getView().requestLayout();
}
@Override
public void onDismissed(Snackbar snackbar, int event) {
snackbar.getView().setVisibility(INVISIBLE);
}
});
}
return s; return s;
} }

View File

@@ -51,7 +51,6 @@ import androidx.annotation.ColorInt;
import androidx.annotation.ColorRes; import androidx.annotation.ColorRes;
import androidx.annotation.DrawableRes; import androidx.annotation.DrawableRes;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.UiThread; import androidx.annotation.UiThread;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
@@ -108,8 +107,6 @@ import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE; import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE;
import static org.briarproject.briar.android.TestingConstants.IS_OLD_ANDROID;
import static org.briarproject.briar.android.TestingConstants.OLD_ANDROID_WARN_DATE;
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_APP_LOGCAT; import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_APP_LOGCAT;
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_APP_START_TIME; import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_APP_START_TIME;
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_INITIAL_COMMENT; import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_INITIAL_COMMENT;
@@ -244,11 +241,6 @@ public class UiUtils {
return (EXPIRY_DATE - now) / DAYS.toMillis(1); return (EXPIRY_DATE - now) / DAYS.toMillis(1);
} }
public static boolean shouldWarnOldAndroidExpiry() {
return IS_OLD_ANDROID &&
System.currentTimeMillis() >= OLD_ANDROID_WARN_DATE;
}
public static SpannableStringBuilder getTeaser(Context ctx, Spanned text) { public static SpannableStringBuilder getTeaser(Context ctx, Spanned text) {
if (text.length() < TEASER_LENGTH) if (text.length() < TEASER_LENGTH)
throw new IllegalArgumentException( throw new IllegalArgumentException(
@@ -376,7 +368,6 @@ public class UiUtils {
} }
public static boolean hasKeyguardLock(Context ctx) { public static boolean hasKeyguardLock(Context ctx) {
if (SDK_INT < 21) return false;
KeyguardManager keyguardManager = KeyguardManager keyguardManager =
(KeyguardManager) ctx.getSystemService(KEYGUARD_SERVICE); (KeyguardManager) ctx.getSystemService(KEYGUARD_SERVICE);
if (keyguardManager == null) return false; if (keyguardManager == null) return false;
@@ -438,7 +429,6 @@ public class UiUtils {
keyEvent.getKeyCode() == KEYCODE_ENTER; keyEvent.getKeyCode() == KEYCODE_ENTER;
} }
@RequiresApi(api = 21)
public static void excludeSystemUi(Transition transition) { public static void excludeSystemUi(Transition transition) {
transition.excludeTarget(android.R.id.statusBarBackground, true); transition.excludeTarget(android.R.id.statusBarBackground, true);
transition.excludeTarget(android.R.id.navigationBarBackground, true); transition.excludeTarget(android.R.id.navigationBarBackground, true);
@@ -480,7 +470,6 @@ public class UiUtils {
} }
public static boolean isRtl(Context ctx) { public static boolean isRtl(Context ctx) {
if (SDK_INT < 17) return false;
return ctx.getResources().getConfiguration().getLayoutDirection() == return ctx.getResources().getConfiguration().getLayoutDirection() ==
LAYOUT_DIRECTION_RTL; LAYOUT_DIRECTION_RTL;
} }
@@ -507,7 +496,7 @@ public class UiUtils {
view.setVisibility(small ? GONE : VISIBLE); view.setVisibility(small ? GONE : VISIBLE);
} }
public static boolean isSmallScreenRelativeToFontSize(Context ctx) { private static boolean isSmallScreenRelativeToFontSize(Context ctx) {
Configuration config = ctx.getResources().getConfiguration(); Configuration config = ctx.getResources().getConfiguration();
if (config.fontScale == 0f) return true; if (config.fontScale == 0f) return true;
return config.screenHeightDp / config.fontScale < 600; return config.screenHeightDp / config.fontScale < 600;
@@ -546,7 +535,7 @@ public class UiUtils {
} }
public static void launchActivityToOpenFile(Context ctx, public static void launchActivityToOpenFile(Context ctx,
@Nullable ActivityResultLauncher<String[]> docLauncher, ActivityResultLauncher<String[]> docLauncher,
ActivityResultLauncher<String> contentLauncher, ActivityResultLauncher<String> contentLauncher,
String contentType) { String contentType) {
// Try GET_CONTENT, fall back to OPEN_DOCUMENT if available // Try GET_CONTENT, fall back to OPEN_DOCUMENT if available
@@ -556,13 +545,11 @@ public class UiUtils {
} catch (ActivityNotFoundException e) { } catch (ActivityNotFoundException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
} }
if (docLauncher != null) { try {
try { docLauncher.launch(new String[] {contentType});
docLauncher.launch(new String[] {contentType}); return;
return; } catch (ActivityNotFoundException e) {
} catch (ActivityNotFoundException e) { logException(LOG, WARNING, e);
logException(LOG, WARNING, e);
}
} }
Toast.makeText(ctx, R.string.error_start_activity, LENGTH_LONG).show(); Toast.makeText(ctx, R.string.error_start_activity, LENGTH_LONG).show();
} }

View File

@@ -1,6 +1,5 @@
package org.briarproject.briar.android.widget; package org.briarproject.briar.android.widget;
import android.annotation.TargetApi;
import android.content.Context; import android.content.Context;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.MotionEvent; import android.view.MotionEvent;
@@ -27,7 +26,6 @@ public class TouchInterceptingLinearLayout extends LinearLayout {
super(context, attrs, defStyleAttr); super(context, attrs, defStyleAttr);
} }
@TargetApi(21)
public TouchInterceptingLinearLayout(Context context, AttributeSet attrs, public TouchInterceptingLinearLayout(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) { int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes); super(context, attrs, defStyleAttr, defStyleRes);

View File

@@ -35,7 +35,6 @@
<item <item
android:id="@+id/action_transfer_data" android:id="@+id/action_transfer_data"
android:title="@string/removable_drive_menu_title" android:title="@string/removable_drive_menu_title"
android:visible="false"
app:showAsAction="never" /> app:showAsAction="never" />
</menu> </menu>
</item> </item>

View File

@@ -55,10 +55,6 @@
<item quantity="one">This is a test version of Briar. Your account will expire in %d day and cannot be renewed.</item> <item quantity="one">This is a test version of Briar. Your account will expire in %d day and cannot be renewed.</item>
<item quantity="other">This is a test version of Briar. Your account will expire in %d days and cannot be renewed.</item> <item quantity="other">This is a test version of Briar. Your account will expire in %d days and cannot be renewed.</item>
</plurals> </plurals>
<plurals name="old_android_expiry_warning">
<item quantity="one">Android 4 is no longer supported. Briar will stop working on %s (in %d day). Please install Briar on a newer device and create a new account.</item>
<item quantity="other">Android 4 is no longer supported. Briar will stop working on %s (in %d days). Please install Briar on a newer device and create a new account.</item>
</plurals>
<string name="expiry_date_reached">This software has expired.\nThank you for testing!</string> <string name="expiry_date_reached">This software has expired.\nThank you for testing!</string>
<string name="download_briar">To continue using Briar, please download the latest release.</string> <string name="download_briar">To continue using Briar, please download the latest release.</string>
<string name="create_new_account">You will need to create a new account, but you can use the same nickname.</string> <string name="create_new_account">You will need to create a new account, but you can use the same nickname.</string>