Merge branch 'java8' into 'master'
update to Java8 and compileSdkVersion 29 See merge request fdroid/fdroidclient!974
This commit is contained in:
commit
b21dbb8646
@ -27,6 +27,7 @@ stages:
|
|||||||
artifacts:
|
artifacts:
|
||||||
name: "${CI_PROJECT_PATH}_${CI_JOB_STAGE}_${CI_COMMIT_REF_NAME}_${CI_COMMIT_SHA}"
|
name: "${CI_PROJECT_PATH}_${CI_JOB_STAGE}_${CI_COMMIT_REF_NAME}_${CI_COMMIT_SHA}"
|
||||||
paths:
|
paths:
|
||||||
|
- kernel.log
|
||||||
- logcat.txt
|
- logcat.txt
|
||||||
- app/core*
|
- app/core*
|
||||||
- app/*.log
|
- app/*.log
|
||||||
@ -70,6 +71,10 @@ errorprone:
|
|||||||
- cat config/errorprone.gradle >> app/build.gradle
|
- cat config/errorprone.gradle >> app/build.gradle
|
||||||
- ./gradlew -Dorg.gradle.dependency.verification=lenient assembleDebug
|
- ./gradlew -Dorg.gradle.dependency.verification=lenient assembleDebug
|
||||||
|
|
||||||
|
# Run the tests in the emulator. Each step is broken out to run on
|
||||||
|
# its own since the CI runner can have limited RAM, and the emulator
|
||||||
|
# can take a while to start.
|
||||||
|
#
|
||||||
# once these prove stable, the task should be switched to
|
# once these prove stable, the task should be switched to
|
||||||
# connectedCheck to test all the build flavors
|
# connectedCheck to test all the build flavors
|
||||||
.connected-template: &connected-template
|
.connected-template: &connected-template
|
||||||
@ -90,16 +95,14 @@ errorprone:
|
|||||||
- wait-for-emulator
|
- wait-for-emulator
|
||||||
- adb devices
|
- adb devices
|
||||||
- adb shell input keyevent 82 &
|
- adb shell input keyevent 82 &
|
||||||
|
- ./gradlew installFullDebug
|
||||||
|
- adb shell am start -n org.fdroid.fdroid.debug/org.fdroid.fdroid.views.main.MainActivity
|
||||||
- if [ $AVD_SDK -lt 25 ] || ! emulator -accel-check; then
|
- if [ $AVD_SDK -lt 25 ] || ! emulator -accel-check; then
|
||||||
export FLAG=-Pandroid.testInstrumentationRunnerArguments.notAnnotation=androidx.test.filters.LargeTest;
|
export FLAG=-Pandroid.testInstrumentationRunnerArguments.notAnnotation=androidx.test.filters.LargeTest;
|
||||||
fi
|
fi
|
||||||
- ./gradlew connectedFullDebugAndroidTest $FLAG
|
- ./gradlew connectedFullDebugAndroidTest $FLAG
|
||||||
|| ./gradlew connectedFullDebugAndroidTest $FLAG
|
|
||||||
|| ./gradlew connectedFullDebugAndroidTest $FLAG
|
|
||||||
|| (adb -e logcat -d > logcat.txt; exit 1)
|
|
||||||
|
|
||||||
connected 22 default armeabi-v7a:
|
no-accel 22 default x86:
|
||||||
retry: 1
|
|
||||||
<<: *test-template
|
<<: *test-template
|
||||||
<<: *connected-template
|
<<: *connected-template
|
||||||
|
|
||||||
@ -107,15 +110,14 @@ connected 22 default armeabi-v7a:
|
|||||||
tags:
|
tags:
|
||||||
- fdroid
|
- fdroid
|
||||||
- kvm
|
- kvm
|
||||||
allow_failure: true
|
only:
|
||||||
|
variables:
|
||||||
|
- $RUN_KVM_JOBS
|
||||||
<<: *test-template
|
<<: *test-template
|
||||||
<<: *connected-template
|
<<: *connected-template
|
||||||
|
|
||||||
connected 26 google_apis x86:
|
kvm 29 microg x86_64:
|
||||||
<<: *kvm-template
|
<<: *kvm-template
|
||||||
only:
|
|
||||||
- branches@fdroid/fdroidclient
|
|
||||||
- branches@eighthave/fdroidclient
|
|
||||||
|
|
||||||
deploy_nightly:
|
deploy_nightly:
|
||||||
extends: .base
|
extends: .base
|
||||||
|
@ -21,7 +21,7 @@ def basicApplicationId = "org.fdroid.basic"
|
|||||||
def privilegedExtensionApplicationId = '"org.fdroid.fdroid.privileged"'
|
def privilegedExtensionApplicationId = '"org.fdroid.fdroid.privileged"'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 28
|
compileSdkVersion 29
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
versionCode 1012000
|
versionCode 1012000
|
||||||
@ -75,6 +75,8 @@ android {
|
|||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
compileOptions.encoding = "UTF-8"
|
compileOptions.encoding = "UTF-8"
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
|
|
||||||
aaptOptions {
|
aaptOptions {
|
||||||
|
7
app/proguard-rules.pro
vendored
7
app/proguard-rules.pro
vendored
@ -4,13 +4,11 @@
|
|||||||
-keep class org.fdroid.fdroid.** {*;}
|
-keep class org.fdroid.fdroid.** {*;}
|
||||||
-dontskipnonpubliclibraryclassmembers
|
-dontskipnonpubliclibraryclassmembers
|
||||||
-dontwarn android.test.**
|
-dontwarn android.test.**
|
||||||
-dontwarn com.android.support.test.**
|
|
||||||
|
|
||||||
-dontwarn javax.naming.**
|
-dontwarn javax.naming.**
|
||||||
-dontwarn org.slf4j.**
|
-dontwarn org.slf4j.**
|
||||||
-dontnote org.apache.http.**
|
-dontnote org.apache.http.**
|
||||||
-dontnote android.net.http.**
|
-dontnote android.net.http.**
|
||||||
-dontnote android.support.**
|
|
||||||
-dontnote **ILicensingService
|
-dontnote **ILicensingService
|
||||||
|
|
||||||
# Needed for espresso https://stackoverflow.com/a/21706087
|
# Needed for espresso https://stackoverflow.com/a/21706087
|
||||||
@ -49,3 +47,8 @@ public static final org.codehaus.jackson.annotate.JsonAutoDetect$Visibility *; }
|
|||||||
-keep public class your.class.** {
|
-keep public class your.class.** {
|
||||||
*;
|
*;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# This is necessary so that RemoteWorkManager can be initialized (also marked with @Keep)
|
||||||
|
-keep class androidx.work.multiprocess.RemoteWorkManagerClient {
|
||||||
|
public <init>(...);
|
||||||
|
}
|
@ -177,6 +177,7 @@ public class MainActivityEspressoTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
|
@Test
|
||||||
public void showSettings() {
|
public void showSettings() {
|
||||||
ViewInteraction settingsBottonNavButton = onView(
|
ViewInteraction settingsBottonNavButton = onView(
|
||||||
allOf(withText(R.string.menu_settings), isDisplayed()));
|
allOf(withText(R.string.menu_settings), isDisplayed()));
|
||||||
@ -211,6 +212,7 @@ public class MainActivityEspressoTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
|
@Test
|
||||||
public void showUpdates() {
|
public void showUpdates() {
|
||||||
ViewInteraction updatesBottonNavButton = onView(allOf(withText(R.string.main_menu__updates), isDisplayed()));
|
ViewInteraction updatesBottonNavButton = onView(allOf(withText(R.string.main_menu__updates), isDisplayed()));
|
||||||
updatesBottonNavButton.perform(click());
|
updatesBottonNavButton.perform(click());
|
||||||
@ -218,6 +220,7 @@ public class MainActivityEspressoTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
|
@Test
|
||||||
public void startSwap() {
|
public void startSwap() {
|
||||||
if (!BuildConfig.FLAVOR.startsWith("full")) {
|
if (!BuildConfig.FLAVOR.startsWith("full")) {
|
||||||
return;
|
return;
|
||||||
@ -233,6 +236,7 @@ public class MainActivityEspressoTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
|
@Test
|
||||||
public void showCategories() {
|
public void showCategories() {
|
||||||
if (!BuildConfig.FLAVOR.startsWith("full")) {
|
if (!BuildConfig.FLAVOR.startsWith("full")) {
|
||||||
return;
|
return;
|
||||||
@ -258,6 +262,7 @@ public class MainActivityEspressoTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
|
@Test
|
||||||
public void showLatest() {
|
public void showLatest() {
|
||||||
if (!BuildConfig.FLAVOR.startsWith("full")) {
|
if (!BuildConfig.FLAVOR.startsWith("full")) {
|
||||||
return;
|
return;
|
||||||
@ -280,6 +285,7 @@ public class MainActivityEspressoTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
|
@Test
|
||||||
public void showSearch() {
|
public void showSearch() {
|
||||||
onView(allOf(withText(R.string.menu_settings), isDisplayed())).perform(click());
|
onView(allOf(withText(R.string.menu_settings), isDisplayed())).perform(click());
|
||||||
onView(withId(R.id.fab_search)).check(doesNotExist());
|
onView(withId(R.id.fab_search)).check(doesNotExist());
|
||||||
|
@ -17,14 +17,13 @@ import android.os.AsyncTask;
|
|||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.app.NotificationCompat;
|
import androidx.core.app.NotificationCompat;
|
||||||
import androidx.core.app.ServiceCompat;
|
import androidx.core.app.ServiceCompat;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||||
|
import cc.mvdan.accesspoint.WifiApControl;
|
||||||
import org.fdroid.fdroid.FDroidApp;
|
import org.fdroid.fdroid.FDroidApp;
|
||||||
import org.fdroid.fdroid.NotificationHelper;
|
import org.fdroid.fdroid.NotificationHelper;
|
||||||
import org.fdroid.fdroid.Preferences;
|
import org.fdroid.fdroid.Preferences;
|
||||||
@ -48,8 +47,6 @@ import java.util.Set;
|
|||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
|
|
||||||
import cc.mvdan.accesspoint.WifiApControl;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Central service which manages all of the different moving parts of swap which are required
|
* Central service which manages all of the different moving parts of swap which are required
|
||||||
* to enable p2p swapping of apps.
|
* to enable p2p swapping of apps.
|
||||||
@ -429,7 +426,9 @@ public class SwapService extends Service {
|
|||||||
localBroadcastManager.unregisterReceiver(bonjourPeerFound);
|
localBroadcastManager.unregisterReceiver(bonjourPeerFound);
|
||||||
localBroadcastManager.unregisterReceiver(bonjourPeerRemoved);
|
localBroadcastManager.unregisterReceiver(bonjourPeerRemoved);
|
||||||
|
|
||||||
unregisterReceiver(bluetoothScanModeChanged);
|
if (bluetoothAdapter != null) {
|
||||||
|
unregisterReceiver(bluetoothScanModeChanged);
|
||||||
|
}
|
||||||
|
|
||||||
BluetoothManager.stop(this);
|
BluetoothManager.stop(this);
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/both_parties_need_fdroid_text"
|
app:layout_constraintTop_toBottomOf="@+id/both_parties_need_fdroid_text"
|
||||||
android:layout_marginTop="36dp"
|
android:layout_marginTop="36dp"
|
||||||
android:tint="?attr/mainTabSwapSplashTint"
|
app:tint="?attr/mainTabSwapSplashTint"
|
||||||
android:scaleType="fitXY"
|
android:scaleType="fitXY"
|
||||||
tools:targetApi="jelly_bean"/>
|
tools:targetApi="jelly_bean"/>
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/listPreferredItemHeight"
|
android:layout_height="?attr/listPreferredItemHeight"
|
||||||
@ -20,7 +21,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:src="@drawable/circle"
|
android:src="@drawable/circle"
|
||||||
android:tint="@color/swap_light_grey_icon"/>
|
app:tint="@color/swap_light_grey_icon"/>
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/icon"
|
android:id="@+id/icon"
|
||||||
|
@ -266,8 +266,11 @@
|
|||||||
android:name=".AddRepoIntentService"
|
android:name=".AddRepoIntentService"
|
||||||
android:exported="false"/>
|
android:exported="false"/>
|
||||||
|
|
||||||
|
<provider
|
||||||
<!-- Warning: Please add all new services to HidingManager -->
|
android:name="androidx.work.impl.WorkManagerInitializer"
|
||||||
|
android:authorities="${applicationId}.workmanager-init"
|
||||||
|
android:exported="false"
|
||||||
|
tools:node="remove" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".views.main.MainActivity"
|
android:name=".views.main.MainActivity"
|
||||||
|
@ -46,11 +46,10 @@ import android.util.Log;
|
|||||||
import android.view.Display;
|
import android.view.Display;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.collection.LongSparseArray;
|
import androidx.collection.LongSparseArray;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
import com.nostra13.universalimageloader.cache.disc.DiskCache;
|
import com.nostra13.universalimageloader.cache.disc.DiskCache;
|
||||||
import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache;
|
import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache;
|
||||||
import com.nostra13.universalimageloader.cache.disc.impl.ext.LruDiskCache;
|
import com.nostra13.universalimageloader.cache.disc.impl.ext.LruDiskCache;
|
||||||
@ -58,7 +57,8 @@ import com.nostra13.universalimageloader.core.DefaultConfigurationFactory;
|
|||||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||||
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
|
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
|
||||||
import com.nostra13.universalimageloader.core.process.BitmapProcessor;
|
import com.nostra13.universalimageloader.core.process.BitmapProcessor;
|
||||||
|
import info.guardianproject.netcipher.NetCipher;
|
||||||
|
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
||||||
import org.acra.ACRA;
|
import org.acra.ACRA;
|
||||||
import org.acra.ReportField;
|
import org.acra.ReportField;
|
||||||
import org.acra.ReportingInteractionMode;
|
import org.acra.ReportingInteractionMode;
|
||||||
@ -82,17 +82,13 @@ import org.fdroid.fdroid.net.ImageLoaderForUIL;
|
|||||||
import org.fdroid.fdroid.panic.HidingManager;
|
import org.fdroid.fdroid.panic.HidingManager;
|
||||||
import org.fdroid.fdroid.work.CleanCacheWorker;
|
import org.fdroid.fdroid.work.CleanCacheWorker;
|
||||||
|
|
||||||
|
import javax.microedition.khronos.opengles.GL10;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import javax.microedition.khronos.opengles.GL10;
|
|
||||||
|
|
||||||
import info.guardianproject.netcipher.NetCipher;
|
|
||||||
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
|
||||||
|
|
||||||
@ReportsCrashes(mailTo = BuildConfig.ACRA_REPORT_EMAIL,
|
@ReportsCrashes(mailTo = BuildConfig.ACRA_REPORT_EMAIL,
|
||||||
mode = ReportingInteractionMode.DIALOG,
|
mode = ReportingInteractionMode.DIALOG,
|
||||||
reportDialogClass = org.fdroid.fdroid.acra.CrashReportActivity.class,
|
reportDialogClass = org.fdroid.fdroid.acra.CrashReportActivity.class,
|
||||||
@ -113,7 +109,7 @@ import info.guardianproject.netcipher.proxy.OrbotHelper;
|
|||||||
ReportField.STACK_TRACE,
|
ReportField.STACK_TRACE,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
public class FDroidApp extends Application {
|
public class FDroidApp extends Application implements androidx.work.Configuration.Provider {
|
||||||
|
|
||||||
private static final String TAG = "FDroidApp";
|
private static final String TAG = "FDroidApp";
|
||||||
private static final String ACRA_ID = BuildConfig.APPLICATION_ID + ":acra";
|
private static final String ACRA_ID = BuildConfig.APPLICATION_ID + ":acra";
|
||||||
@ -654,7 +650,7 @@ public class FDroidApp extends Application {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Put proxy settings (or Tor settings) globally into effect based on whats configured in Preferences.
|
* Put proxy settings (or Tor settings) globally into effect based on whats configured in Preferences.
|
||||||
*
|
* <p>
|
||||||
* Must be called on App startup and after every proxy configuration change.
|
* Must be called on App startup and after every proxy configuration change.
|
||||||
*/
|
*/
|
||||||
public static void configureProxy(Preferences preferences) {
|
public static void configureProxy(Preferences preferences) {
|
||||||
@ -676,4 +672,26 @@ public class FDroidApp extends Application {
|
|||||||
public static Context getInstance() {
|
public static Context getInstance() {
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up WorkManager on demand to avoid slowing down starts.
|
||||||
|
*
|
||||||
|
* @see CleanCacheWorker
|
||||||
|
* @see org.fdroid.fdroid.work.PopularityContestWorker
|
||||||
|
* @see org.fdroid.fdroid.work.UpdateWorker
|
||||||
|
* @see <a href="https://developer.android.com/codelabs/android-adv-workmanager#3">example</a>
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public androidx.work.Configuration getWorkManagerConfiguration() {
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
return new androidx.work.Configuration.Builder()
|
||||||
|
.setMinimumLoggingLevel(Log.DEBUG)
|
||||||
|
.build();
|
||||||
|
} else {
|
||||||
|
return new androidx.work.Configuration.Builder()
|
||||||
|
.setMinimumLoggingLevel(Log.ERROR)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,10 +33,6 @@ import android.os.Build;
|
|||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.StatFs;
|
import android.os.StatFs;
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
@ -52,6 +48,10 @@ import android.view.View;
|
|||||||
import android.view.ViewTreeObserver;
|
import android.view.ViewTreeObserver;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
import com.nostra13.universalimageloader.core.DisplayImageOptions;
|
import com.nostra13.universalimageloader.core.DisplayImageOptions;
|
||||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||||
import com.nostra13.universalimageloader.core.assist.ImageScaleType;
|
import com.nostra13.universalimageloader.core.assist.ImageScaleType;
|
||||||
@ -406,10 +406,26 @@ public final class Utils {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the fingerprint used to represent an APK signing key in F-Droid.
|
* Get the fingerprint used to represent an APK signing key in F-Droid.
|
||||||
* This is a custom fingerprint algorithm that was kind of accidentally
|
* This is a custom fingerprint algorithm that was kind of accidentally
|
||||||
* created, but is still in use.
|
* created, but is still in use.
|
||||||
|
*
|
||||||
|
* @see #getPackageSig(PackageInfo)
|
||||||
|
* @see org.fdroid.fdroid.data.Apk#sig
|
||||||
|
*/
|
||||||
|
public static String getsig(byte[] rawCertBytes) {
|
||||||
|
return Utils.hashBytes(toHexString(rawCertBytes).getBytes(), "md5");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the fingerprint used to represent an APK signing key in F-Droid.
|
||||||
|
* This is a custom fingerprint algorithm that was kind of accidentally
|
||||||
|
* created, but is still in use.
|
||||||
|
*
|
||||||
|
* @see #getsig(byte[])
|
||||||
|
* @see org.fdroid.fdroid.data.Apk#sig
|
||||||
*/
|
*/
|
||||||
public static String getPackageSig(PackageInfo info) {
|
public static String getPackageSig(PackageInfo info) {
|
||||||
if (info == null || info.signatures == null || info.signatures.length < 1) {
|
if (info == null || info.signatures == null || info.signatures.length < 1) {
|
||||||
@ -556,7 +572,7 @@ public final class Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
byte[] mdbytes = md.digest();
|
byte[] mdbytes = md.digest();
|
||||||
return toHexString(mdbytes).toLowerCase(Locale.ENGLISH);
|
return toHexString(mdbytes);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
String message = e.getMessage();
|
String message = e.getMessage();
|
||||||
if (message.contains("read failed: EIO (I/O error)")) {
|
if (message.contains("read failed: EIO (I/O error)")) {
|
||||||
@ -576,7 +592,7 @@ public final class Utils {
|
|||||||
* Computes the base 16 representation of the byte array argument.
|
* Computes the base 16 representation of the byte array argument.
|
||||||
*
|
*
|
||||||
* @param bytes an array of bytes.
|
* @param bytes an array of bytes.
|
||||||
* @return the bytes represented as a string of hexadecimal digits.
|
* @return the bytes represented as a string of lowercase hexadecimal digits.
|
||||||
* @see <a href="https://stackoverflow.com/a/9855338">source</a>
|
* @see <a href="https://stackoverflow.com/a/9855338">source</a>
|
||||||
*/
|
*/
|
||||||
public static String toHexString(byte[] bytes) {
|
public static String toHexString(byte[] bytes) {
|
||||||
@ -589,7 +605,7 @@ public final class Utils {
|
|||||||
return new String(hexChars);
|
return new String(hexChars);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final char[] HEX_LOOKUP_ARRAY = "0123456789ABCDEF".toCharArray();
|
private static final char[] HEX_LOOKUP_ARRAY = "0123456789abcdef".toCharArray();
|
||||||
|
|
||||||
public static int parseInt(String str, int fallback) {
|
public static int parseInt(String str, int fallback) {
|
||||||
if (str == null || str.length() == 0) {
|
if (str == null || str.length() == 0) {
|
||||||
|
@ -949,24 +949,7 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
|
|||||||
}
|
}
|
||||||
apkJar.close();
|
apkJar.close();
|
||||||
|
|
||||||
/*
|
apk.sig = Utils.getsig(rawCertBytes);
|
||||||
* I don't fully understand the loop used here. I've copied it verbatim
|
|
||||||
* from getsig.java bundled with FDroidServer. I *believe* it is taking
|
|
||||||
* the raw byte encoding of the certificate & converting it to a byte
|
|
||||||
* array of the hex representation of the original certificate byte
|
|
||||||
* array. This is then MD5 sum'd. It's a really bad way to be doing this
|
|
||||||
* if I'm right... If I'm not right, I really don't know! see lines
|
|
||||||
* 67->75 in getsig.java bundled with Fdroidserver
|
|
||||||
*/
|
|
||||||
final byte[] fdroidSig = new byte[rawCertBytes.length * 2];
|
|
||||||
for (int j = 0; j < rawCertBytes.length; j++) {
|
|
||||||
byte v = rawCertBytes[j];
|
|
||||||
int d = (v >> 4) & 0xF;
|
|
||||||
fdroidSig[j * 2] = (byte) (d >= 10 ? ('a' + d - 10) : ('0' + d));
|
|
||||||
d = v & 0xF;
|
|
||||||
fdroidSig[j * 2 + 1] = (byte) (d >= 10 ? ('a' + d - 10) : ('0' + d));
|
|
||||||
}
|
|
||||||
apk.sig = Utils.hashBytes(fdroidSig, "md5");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,7 +37,6 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
@ -45,11 +44,9 @@ import androidx.core.content.ContextCompat;
|
|||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import com.ashokvarma.bottomnavigation.BottomNavigationBar;
|
import com.ashokvarma.bottomnavigation.BottomNavigationBar;
|
||||||
import com.ashokvarma.bottomnavigation.BottomNavigationItem;
|
import com.ashokvarma.bottomnavigation.BottomNavigationItem;
|
||||||
import com.ashokvarma.bottomnavigation.TextBadgeItem;
|
import com.ashokvarma.bottomnavigation.TextBadgeItem;
|
||||||
|
|
||||||
import org.fdroid.fdroid.AppUpdateStatusManager;
|
import org.fdroid.fdroid.AppUpdateStatusManager;
|
||||||
import org.fdroid.fdroid.AppUpdateStatusManager.AppUpdateStatus;
|
import org.fdroid.fdroid.AppUpdateStatusManager.AppUpdateStatus;
|
||||||
import org.fdroid.fdroid.BuildConfig;
|
import org.fdroid.fdroid.BuildConfig;
|
||||||
@ -305,6 +302,11 @@ public class MainActivity extends AppCompatActivity implements BottomNavigationB
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Since any app could send this {@link Intent}, and the search terms are
|
||||||
|
* fed into a SQL query, the data must be strictly sanitized to avoid
|
||||||
|
* SQL injection attacks.
|
||||||
|
*/
|
||||||
private void handleSearchOrAppViewIntent(Intent intent) {
|
private void handleSearchOrAppViewIntent(Intent intent) {
|
||||||
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
|
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
|
||||||
String query = intent.getStringExtra(SearchManager.QUERY);
|
String query = intent.getStringExtra(SearchManager.QUERY);
|
||||||
@ -391,6 +393,8 @@ public class MainActivity extends AppCompatActivity implements BottomNavigationB
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(packageName)) {
|
if (!TextUtils.isEmpty(packageName)) {
|
||||||
|
// sanitize packageName to be a valid Java packageName and prevent exploits
|
||||||
|
packageName = packageName.replaceAll("[^A-Za-z\\d_.]", "");
|
||||||
Utils.debugLog(TAG, "FDroid launched via app link for '" + packageName + "'");
|
Utils.debugLog(TAG, "FDroid launched via app link for '" + packageName + "'");
|
||||||
Intent intentToInvoke = new Intent(this, AppDetailsActivity.class);
|
Intent intentToInvoke = new Intent(this, AppDetailsActivity.class);
|
||||||
intentToInvoke.putExtra(AppDetailsActivity.EXTRA_APPID, packageName);
|
intentToInvoke.putExtra(AppDetailsActivity.EXTRA_APPID, packageName);
|
||||||
@ -402,12 +406,19 @@ public class MainActivity extends AppCompatActivity implements BottomNavigationB
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These strings might end up in a SQL query, so strip all non-alpha-num
|
||||||
|
*/
|
||||||
|
static String sanitizeSearchTerms(String query) {
|
||||||
|
return query.replaceAll("[^\\p{L}\\d_ -]", " ");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initiates the {@link AppListActivity} with the relevant search terms passed in via the query arg.
|
* Initiates the {@link AppListActivity} with the relevant search terms passed in via the query arg.
|
||||||
*/
|
*/
|
||||||
private void performSearch(String query) {
|
private void performSearch(String query) {
|
||||||
Intent searchIntent = new Intent(this, AppListActivity.class);
|
Intent searchIntent = new Intent(this, AppListActivity.class);
|
||||||
searchIntent.putExtra(AppListActivity.EXTRA_SEARCH_TERMS, query);
|
searchIntent.putExtra(AppListActivity.EXTRA_SEARCH_TERMS, sanitizeSearchTerms(query));
|
||||||
startActivity(searchIntent);
|
startActivity(searchIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
<view class="org.fdroid.fdroid.privileged.views.AppSecurityPermissions$PermissionItemView"
|
<view class="org.fdroid.fdroid.privileged.views.AppSecurityPermissions$PermissionItemView"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
@ -34,7 +35,7 @@
|
|||||||
android:layout_marginRight="8dp"
|
android:layout_marginRight="8dp"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
android:tint="@android:color/black" />
|
app:tint="@android:color/black" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
<view class="org.fdroid.fdroid.privileged.views.AppSecurityPermissions$PermissionItemView"
|
<view class="org.fdroid.fdroid.privileged.views.AppSecurityPermissions$PermissionItemView"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
@ -58,7 +59,7 @@
|
|||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_alignBottom="@+id/perm_money_label"
|
android:layout_alignBottom="@+id/perm_money_label"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
android:tint="@color/perms_costs_money"
|
app:tint="@color/perms_costs_money"
|
||||||
android:tintMode="src_in"
|
android:tintMode="src_in"
|
||||||
android:src="@drawable/ic_coins_s" />
|
android:src="@drawable/ic_coins_s" />
|
||||||
<TextView
|
<TextView
|
||||||
|
@ -55,7 +55,7 @@
|
|||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/empty_state"
|
app:layout_constraintTop_toBottomOf="@+id/empty_state"
|
||||||
android:tint="?attr/mainTabSwapSplashTint"
|
app:tint="?attr/mainTabSwapSplashTint"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
android:visibility="gone" />
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
package org.fdroid.fdroid;
|
package org.fdroid.fdroid;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.content.pm.Signature;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
import org.fdroid.fdroid.views.AppDetailsRecyclerViewAdapter;
|
import org.fdroid.fdroid.views.AppDetailsRecyclerViewAdapter;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@ -12,6 +12,7 @@ import org.robolectric.RobolectricTestRunner;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.Random;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
@ -215,4 +216,40 @@ public class UtilsTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the replacement for the ancient fingerprint algorithm.
|
||||||
|
*
|
||||||
|
* @see org.fdroid.fdroid.data.Apk#sig
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGetsig() {
|
||||||
|
/*
|
||||||
|
* I don't fully understand the loop used here. I've copied it verbatim
|
||||||
|
* from getsig.java bundled with FDroidServer. I *believe* it is taking
|
||||||
|
* the raw byte encoding of the certificate & converting it to a byte
|
||||||
|
* array of the hex representation of the original certificate byte
|
||||||
|
* array. This is then MD5 sum'd. It's a really bad way to be doing this
|
||||||
|
* if I'm right... If I'm not right, I really don't know! see lines
|
||||||
|
* 67->75 in getsig.java bundled with Fdroidserver
|
||||||
|
*/
|
||||||
|
for (int length : new int[]{256, 345, 1233, 4032, 12092}) {
|
||||||
|
byte[] rawCertBytes = new byte[length];
|
||||||
|
new Random().nextBytes(rawCertBytes);
|
||||||
|
final byte[] fdroidSig = new byte[rawCertBytes.length * 2];
|
||||||
|
for (int j = 0; j < rawCertBytes.length; j++) {
|
||||||
|
byte v = rawCertBytes[j];
|
||||||
|
int d = (v >> 4) & 0xF;
|
||||||
|
fdroidSig[j * 2] = (byte) (d >= 10 ? ('a' + d - 10) : ('0' + d));
|
||||||
|
d = v & 0xF;
|
||||||
|
fdroidSig[j * 2 + 1] = (byte) (d >= 10 ? ('a' + d - 10) : ('0' + d));
|
||||||
|
}
|
||||||
|
String sig = Utils.hashBytes(fdroidSig, "md5");
|
||||||
|
assertEquals(sig, Utils.getsig(rawCertBytes));
|
||||||
|
|
||||||
|
PackageInfo packageInfo = new PackageInfo();
|
||||||
|
packageInfo.signatures = new Signature[]{new Signature(rawCertBytes)};
|
||||||
|
assertEquals(sig, Utils.getPackageSig(packageInfo));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import org.junit.Test;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotEquals;
|
||||||
import static org.junit.Assume.assumeTrue;
|
import static org.junit.Assume.assumeTrue;
|
||||||
|
|
||||||
public class SanitizedFileTest {
|
public class SanitizedFileTest {
|
||||||
@ -45,4 +46,13 @@ public class SanitizedFileTest {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSanitizeFileName() {
|
||||||
|
for (String valid : new String[]{"An.stop", "a.0", "packageName", "com.this-and-that", "A_.o"}) {
|
||||||
|
assertEquals(valid, SanitizedFile.sanitizeFileName(valid));
|
||||||
|
}
|
||||||
|
for (String invalid : new String[]{"'--;DROP", "a.0)", "packageName\n"}) {
|
||||||
|
assertNotEquals(invalid, SanitizedFile.sanitizeFileName(invalid));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
package org.fdroid.fdroid.views.main;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotEquals;
|
||||||
|
|
||||||
|
public class MainActivityTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSanitizeSearchTerms() {
|
||||||
|
for (String valid : new String[]{"private browser", "πÇÇ", "现代 通用字", "български", "عربي"}) {
|
||||||
|
assertEquals(valid, MainActivity.sanitizeSearchTerms(valid));
|
||||||
|
}
|
||||||
|
for (String invalid : new String[]{
|
||||||
|
"Robert'); DROP TABLE Students; --",
|
||||||
|
"xxx') OR 1 = 1 -- ]",
|
||||||
|
"105 OR 1=1;",
|
||||||
|
"\" OR \"=\"",
|
||||||
|
}) {
|
||||||
|
assertNotEquals(invalid, MainActivity.sanitizeSearchTerms(invalid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -115,7 +115,6 @@
|
|||||||
<module name="FinalClass" />
|
<module name="FinalClass" />
|
||||||
|
|
||||||
<module name="ArrayTypeStyle" />
|
<module name="ArrayTypeStyle" />
|
||||||
<module name="ArrayTrailingComma" />
|
|
||||||
<module name="UpperEll" />
|
<module name="UpperEll" />
|
||||||
|
|
||||||
<module name="StringLiteralEquality" />
|
<module name="StringLiteralEquality" />
|
||||||
|
Loading…
x
Reference in New Issue
Block a user