Merge branch 'minSdkVersion-to-android14' into 'master'

The Great Upgrade to minSdkVersion 14!

Closes #1379, #1383, and #248

See merge request fdroid/fdroidclient!676
This commit is contained in:
Hans-Christoph Steiner 2018-04-23 08:16:46 +00:00
commit 8903a089ba
122 changed files with 1868 additions and 4750 deletions

View File

@ -12,7 +12,7 @@ stages:
before_script: before_script:
- export GRADLE_USER_HOME=$PWD/.gradle - export GRADLE_USER_HOME=$PWD/.gradle
- export ANDROID_COMPILE_SDK=`sed -n 's,.*compileSdkVersion\s*\([0-9][0-9]*\).*,\1,p' app/build.gradle` - export ANDROID_COMPILE_SDK=`sed -n 's,.*compileSdkVersion\s*\([0-9][0-9]*\).*,\1,p' app/build.gradle`
- echo y | android --silent update sdk --no-ui --filter android-${ANDROID_COMPILE_SDK} - echo y | $ANDROID_HOME/tools/bin/sdkmanager "platforms;android-${ANDROID_COMPILE_SDK}"
.test-template: &test-template .test-template: &test-template
artifacts: artifacts:
@ -54,13 +54,16 @@ errorprone:
- ./gradlew assembleDebug - ./gradlew assembleDebug
allow_failure: true allow_failure: true
connected10: connected14:
stage: test stage: test
<<: *test-template <<: *test-template
variables: variables:
AVD_SDK: "10" AVD_SDK: "14"
script: script:
- ./gradlew assembleDebug - ./gradlew assembleDebug
- echo y | $ANDROID_HOME/tools/bin/sdkmanager "platforms;android-$AVD_SDK"
- echo y | $ANDROID_HOME/tools/bin/sdkmanager "system-images;android-${AVD_SDK};default;armeabi-v7a"
- echo no | android --verbose create avd --name fcl-test-$AVD_SDK --target android-$AVD_SDK
- emulator64-arm -avd fcl-test-$AVD_SDK -no-skin -no-audio -no-window & - emulator64-arm -avd fcl-test-$AVD_SDK -no-skin -no-audio -no-window &
- ./tools/wait-for-emulator - ./tools/wait-for-emulator
- adb shell input keyevent 82 & - adb shell input keyevent 82 &

View File

@ -13,24 +13,18 @@ def getVersionName = { ->
return stdout.toString().trim() return stdout.toString().trim()
} }
repositories {
jcenter()
maven {
url "https://jitpack.io"
}
}
dependencies { dependencies {
compile "com.android.support:support-v4:25.3.1" compile "com.android.support:support-v4:27.1.1"
compile "com.android.support:appcompat-v7:25.3.1" compile "com.android.support:appcompat-v7:27.1.1"
compile "com.android.support:gridlayout-v7:25.3.1" compile "com.android.support:gridlayout-v7:27.1.1"
compile "com.android.support:support-annotations:25.3.1" compile "com.android.support:support-annotations:27.1.1"
compile "com.android.support:recyclerview-v7:25.3.1" compile "com.android.support:recyclerview-v7:27.1.1"
compile "com.android.support:cardview-v7:25.3.1" compile "com.android.support:cardview-v7:27.1.1"
compile "com.android.support:design:25.3.1" compile "com.android.support:design:27.1.1"
compile "com.android.support:support-vector-drawable:25.3.1" compile "com.android.support:support-vector-drawable:27.1.1"
compile 'com.android.support.constraint:constraint-layout:1.0.2' compile 'com.android.support.constraint:constraint-layout:1.1.0'
compile "com.android.support:palette-v7:25.3.1" compile "com.android.support:palette-v7:27.1.1"
compile "com.android.support:preference-v7:27.1.1"
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5' compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
compile 'com.google.zxing:core:3.3.2' compile 'com.google.zxing:core:3.3.2'
@ -41,147 +35,87 @@ dependencies {
compile 'commons-io:commons-io:2.5' compile 'commons-io:commons-io:2.5'
compile 'commons-net:commons-net:3.5' compile 'commons-net:commons-net:3.5'
compile 'org.jmdns:jmdns:3.5.3' compile 'org.jmdns:jmdns:3.5.3'
compile 'org.nanohttpd:nanohttpd:2.3.1'
compile 'ch.acra:acra:4.9.1' compile 'ch.acra:acra:4.9.1'
compile 'io.reactivex:rxjava:1.1.0' compile 'io.reactivex:rxjava:1.1.0'
compile 'io.reactivex:rxandroid:0.23.0' compile 'io.reactivex:rxandroid:0.23.0'
compile 'com.hannesdorfmann:adapterdelegates3:3.0.1' compile 'com.hannesdorfmann:adapterdelegates3:3.0.1'
compile 'com.ashokvarma.android:bottom-navigation-bar:2.0.4'
// Migrate this to upstream https://github.com/Ashok-Varma/BottomNavigation if PR #110 gets
// accepted to drop the minSdk to 10.
compile('com.github.pserwylo:BottomNavigation:1.5.0')
compile 'com.fasterxml.jackson.core:jackson-core:2.8.7' compile 'com.fasterxml.jackson.core:jackson-core:2.8.7'
compile 'com.fasterxml.jackson.core:jackson-annotations:2.8.7' compile 'com.fasterxml.jackson.core:jackson-annotations:2.8.7'
compile 'com.fasterxml.jackson.core:jackson-databind:2.8.7' compile 'com.fasterxml.jackson.core:jackson-databind:2.8.7'
testCompile "org.robolectric:robolectric:3.3.2" compile 'org.bouncycastle:bcpkix-jdk15on:1.59'
compile 'org.bouncycastle:bcprov-jdk15on:1.59'
testCompile "org.robolectric:robolectric:3.8"
testCompile 'junit:junit:4.12' testCompile 'junit:junit:4.12'
// As per https://github.com/robolectric/robolectric/issues/1932#issuecomment-219796474 testCompile "org.mockito:mockito-core:2.7.22"
testCompile 'org.khronos:opengl-api:gl1.1-android-2.1_r1'
testCompile "org.mockito:mockito-core:1.10.19"
androidTestCompile "com.android.support:support-annotations:25.3.1" androidTestCompile "com.android.support:support-annotations:25.3.1"
androidTestCompile 'com.android.support.test:runner:0.5' androidTestCompile 'com.android.support.test:runner:0.5'
androidTestCompile 'com.android.support.test:rules:0.5' androidTestCompile 'com.android.support.test:rules:0.5'
} }
if (!hasProperty('sourceDeps')) {
repositories {
// This is here until we sort out all dependencies from mavenCentral/jcenter. Once all of
// the dependencies below have been sorted out, this can be removed.
flatDir {
dirs 'libs/binaryDeps'
}
}
dependencies {
compile 'com.madgag.spongycastle:pkix:1.54.0.0'
compile 'com.madgag.spongycastle:prov:1.54.0.0'
compile 'com.madgag.spongycastle:core:1.54.0.0'
// Upstream doesn't have a binary on mavenCentral/jcenter yet:
// https://github.com/kolavar/android-support-v4-preferencefragment/issues/13
compile(name: 'support-v4-preferencefragment-release', ext: 'aar')
// Fork for F-Droid, including support for https. Not merged into upstream
// yet (seems to be a little unsupported as of late), so not using mavenCentral/jcenter.
compile(name: 'nanohttpd-2.1.0')
// Upstream doesn't have a binary on mavenCentral, and it is an SVN repo on
// Google Code. We include this code directly in this repo, and have made
// modifications that should be pushed to anyone who wants to maintain it.
compile(name: 'zipsigner')
}
// Only do the libraries imported from maven repositories. Our own libraries
// (like privileged-api-lib) and the prebuilt jars already checked into the
// source code don't need to be here.
// generate using: `gradle -q calculateChecksums | sort -V` // generate using: `gradle -q calculateChecksums | sort -V`
dependencyVerification { dependencyVerification {
verify = [ verify = [
'android.arch.core:common:d34824b794bc92ff8f647a9bb13a7c73de920de5b47075b5d2c4f0770e9b8bfd',
'android.arch.core:runtime:83400f7575bcfb8a2eeec64e05590f037bfaed1e56aa3a4214d20e55878445e3',
'android.arch.lifecycle:common:614e31cfd33255dc4d5f5d8e62cfa6be2fbbc2a35643a79dc3ed008004c30807',
'android.arch.lifecycle:livedata-core:14e57ff8ffb65a80c7e72d91f2076acccdaf2970f234c6261e03a6127eb5206b',
'android.arch.lifecycle:runtime:094fd793924dd6a5136753e599ac8174a8147f4a401386b694ba7d818c223e2e',
'android.arch.lifecycle:viewmodel:6407c93a5ea9850661dca42a0068d6f3deccefd7228ee69bae1c35d70cbc2557',
'cc.mvdan.accesspoint:library:0837b38adb48b66bb1385adb6ade8ecce7002ad815c55abf13517c82193458ea', 'cc.mvdan.accesspoint:library:0837b38adb48b66bb1385adb6ade8ecce7002ad815c55abf13517c82193458ea',
'ch.acra:acra:d2762968c448757a7d6acc9f141881d9632f664988e9723ece33b5f7c79f3bc9', 'ch.acra:acra:d2762968c448757a7d6acc9f141881d9632f664988e9723ece33b5f7c79f3bc9',
'commons-io:commons-io:a10418348d234968600ccb1d988efcbbd08716e1d96936ccc1880e7d22513474', 'commons-io:commons-io:a10418348d234968600ccb1d988efcbbd08716e1d96936ccc1880e7d22513474',
'commons-net:commons-net:c25b0da668b3c5649f002d504def22d1b4cb30d206f05428d2fe168fa1a901c2', 'commons-net:commons-net:c25b0da668b3c5649f002d504def22d1b4cb30d206f05428d2fe168fa1a901c2',
'com.android.support.constraint:constraint-layout-solver:8c62525a9bc5cff5633a96cb9b32fffeccaf41b8841aa87fc22607070dea9b8d', 'com.android.support.constraint:constraint-layout-solver:fcb4c7d705754ca3d69b1b2c3caf445a425599fda8caabbcf855d98ea0663e4e',
'com.android.support.constraint:constraint-layout:b0c688cc2b7172608f8153a689d746da40f71e52d7e2fe2bfd9df2f92db77085', 'com.android.support.constraint:constraint-layout:d490188709b7bb2f11609beadd7e5eb7538892f308828ec3ff261a74e6ecf47e',
'com.android.support:animated-vector-drawable:4bc46edf1946b32d518b41416d1734e915e0cbb28021de3b340527419b070691', 'com.android.support:animated-vector-drawable:59670473f6e98fda792f7bef25dd7292b0a3106031c7a5e30eb020bf26f077bd',
'com.android.support:appcompat-v7:ac1ebbc46589195dda3e0b1becfe410bafd75bdf3edd1cd9acf04850f3895830', 'com.android.support:appcompat-v7:0c7808fbbc5838d831e32e3c0a6f84e1f2c981deb8f11e010650f2b57923a335',
'com.android.support:cardview-v7:defc17032ffa600a82e1c7d96bb574aa5ed64e2b57e28414a245da7d6db0c666', 'com.android.support:cardview-v7:8ed955dd037d82a7b4bbcaedb4f896523c3e4c1bf3ca698ce807c350767a2886',
'com.android.support:design:a3e83064fe99d0a4369f9b46d8bfbe77d0c3022fffdee4be3ac3857b87cc89e3', 'com.android.support:design:7225973f7ee03765008a9c2f17a40b154c6885169fef022276e811c926a2202c',
'com.android.support:gridlayout-v7:de87a59472f19eb05429faf6b2683a09dd6995f5db562d3daf6033297c312388', 'com.android.support:gridlayout-v7:2f5af33c4be1d3e4e3fa999323265718ac1a4c81df4c0373d6ce8901613b1671',
'com.android.support:palette-v7:956276da2ed8b6c087c431da807a496f4908061c9c64d4c9f7b42c626d633662', 'com.android.support:palette-v7:6d24037fb375c7884f878edeb88c812b87a05c69221513507ecea21c257d6314',
'com.android.support:recyclerview-v7:375974a8724e359d97d77fa8522c614f813a3ac4583c1807f154a3f9a054b0a1', 'com.android.support:preference-v7:a1798a826b4097d00e49280f412b21af08f9bf1179c2e3838dc339d9f843416d',
'com.android.support:support-annotations:aedf76962584adfaed2bd3fcaa22406461c4310237fc27e301128edaa2dba2fa', 'com.android.support:recyclerview-v7:d735e4727878e99ef3980c10d15dc3468462fd509d4fb60cb8bd20b0f735085c',
'com.android.support:support-compat:e02d781268dc60aab6638d8dc20156ea11ca20b962d294b85e6f1e8418cabfa7', 'com.android.support:support-annotations:3365960206c3d2b09e845f555e7f88f8effc8d2f00b369e66c4be384029299cf',
'com.android.support:support-core-ui:6182278a6653e6c111c888963626cbb16f2d0022571cb239760475119e0b92a8', 'com.android.support:support-compat:880ce01ff5be42b233ff8ec0c61cefb7dc3dc9500fea9e24423214813ac27ea2',
'com.android.support:support-core-utils:32fac02eb2c20a77fa3e3bc3ede62392a19613f72b8f8e10f5dfa84bb4c89ea1', 'com.android.support:support-core-ui:a3ae20e6d5dffba69ac97b99846d2738003af8563843d5f3c9dc4c35b4804241',
'com.android.support:support-fragment:541d6393c1e024453aca2a14f31bea0c7270ff4e2a02783f3528aa426367444d', 'com.android.support:support-core-utils:61036832c54e8701aae954fc3bf96d1d80bf8d9dd531bff77d72def456ba087a',
'com.android.support:support-media-compat:cbed07d07e0e84fdb4b75712f5fd946229a8af155933c9a92e41db64d00791e0', 'com.android.support:support-fragment:ec72d6ac36a1a0e6523bbddba33d73ffad070b9b3dd246cc44d8727a41ddb5e6',
'com.android.support:support-v4:07d389154bcf73b47e514964df1578136b26cba78257b8a577a3ccb54beff0ae', 'com.android.support:support-media-compat:55e9837dda88b74a8c812c63a78c63fd83c6c039a8c22d318492663a493585eb',
'com.android.support:support-vector-drawable:13728f337f36d1c02d52198a6c20724edb447a0875454d829f95cb9eb4aa293b', 'com.android.support:support-v4:4f41dfc3e89f2738e45c86264a85c0934d055ee8ebe2020e23c97f303b80a48b',
'com.android.support:transition:36c688825a8c0e6e879e18886de83dc90673322822d5b606ee302f70fb558e16', 'com.android.support:support-vector-drawable:1c0f421114cf4627cf208776d6eb4f76340c78b7e96fe6e12b3e6eb950caf1b9',
'com.android.support:transition:c0765b2f3c78696567ec5b3f519d22da1e3df11ac994625adf4bb4dc571caacc',
'com.ashokvarma.android:bottom-navigation-bar:f18d740e1777927ad761349298b5d4981cd9f6d2abe70f505abf415ae069baaa',
'com.fasterxml.jackson.core:jackson-annotations:6b7802f6c22c09c4a92a2ebeb76e755c3c0a58dfbf419835fae470d89e469b86', 'com.fasterxml.jackson.core:jackson-annotations:6b7802f6c22c09c4a92a2ebeb76e755c3c0a58dfbf419835fae470d89e469b86',
'com.fasterxml.jackson.core:jackson-core:256ff34118ab292d1b4f3ee4d2c3e5e5f0f609d8e07c57e8ad1f51c46d4fbb46', 'com.fasterxml.jackson.core:jackson-core:256ff34118ab292d1b4f3ee4d2c3e5e5f0f609d8e07c57e8ad1f51c46d4fbb46',
'com.fasterxml.jackson.core:jackson-databind:4f74337b6d18664be0f5b15c6664b17aa3972c9c175092328b139b894ff66f19', 'com.fasterxml.jackson.core:jackson-databind:4f74337b6d18664be0f5b15c6664b17aa3972c9c175092328b139b894ff66f19',
'com.github.pserwylo:BottomNavigation:83d7941a7a8d21ba1a8a708cd683b1bb07c6cf898044dc92eadf18a7a7d54f90',
'com.google.zxing:core:52dd6211bbaf4e600de693834d597e49707f3e6606e1f5d3740fbb8274466abe', 'com.google.zxing:core:52dd6211bbaf4e600de693834d597e49707f3e6606e1f5d3740fbb8274466abe',
'com.hannesdorfmann:adapterdelegates3:1b20d099d6e7afe57aceca13b713b386959d94a247c3c06a7aeb65b866ece02f', 'com.hannesdorfmann:adapterdelegates3:1b20d099d6e7afe57aceca13b713b386959d94a247c3c06a7aeb65b866ece02f',
'com.madgag.spongycastle:core:1e7fa4b19ccccd1011364ab838d0b4702470c178bbbdd94c5c90b2d4d749ea1e',
'com.madgag.spongycastle:pkix:721a302f5ce18bf6fff89d514ef224c37b5dd9ca67a16b56fafaea4b24a51482',
'com.madgag.spongycastle:prov:cf89c550fda86c0f26858c3d851ac1d2ce49cd78dd144cd86f307b7ea3e6afd7',
'com.nostra13.universalimageloader:universal-image-loader:dbd5197ffec3a8317533190870a7c00ff3750dd6a31241448c6a5522d51b65b4', 'com.nostra13.universalimageloader:universal-image-loader:dbd5197ffec3a8317533190870a7c00ff3750dd6a31241448c6a5522d51b65b4',
'eu.chainfire:libsuperuser:018344ff19ee94d252c14b4a503ee8b519184db473a5af83513f5837c413b128', 'eu.chainfire:libsuperuser:018344ff19ee94d252c14b4a503ee8b519184db473a5af83513f5837c413b128',
'info.guardianproject.netcipher:netcipher:eeeb5d0d95ccfe176b4296cbd71a9a24c6efb0bab5c4025a8c6bc36abdddfc75', 'info.guardianproject.netcipher:netcipher:eeeb5d0d95ccfe176b4296cbd71a9a24c6efb0bab5c4025a8c6bc36abdddfc75',
'info.guardianproject.panic:panic:a7ed9439826db2e9901649892cf9afbe76f00991b768d8f4c26332d7c9406cb2', 'info.guardianproject.panic:panic:a7ed9439826db2e9901649892cf9afbe76f00991b768d8f4c26332d7c9406cb2',
'io.reactivex:rxandroid:35c1a90f8c1f499db3c1f3d608e1f191ac8afddb10c02dd91ef04c03a0a4bcda', 'io.reactivex:rxandroid:35c1a90f8c1f499db3c1f3d608e1f191ac8afddb10c02dd91ef04c03a0a4bcda',
'io.reactivex:rxjava:2c162afd78eba217cdfee78b60e85d3bfb667db61e12bc95e3cf2ddc5beeadf6', 'io.reactivex:rxjava:2c162afd78eba217cdfee78b60e85d3bfb667db61e12bc95e3cf2ddc5beeadf6',
'org.bouncycastle:bcpkix-jdk15on:601d85cfbcef76a1cb77cbf755a6234a4ba1d4c02a98d9a81028d471f388694f',
'org.bouncycastle:bcprov-jdk15on:1c31e44e331d25e46d293b3e8ee2d07028a67db011e74cb2443285aed1d59c85',
'org.jmdns:jmdns:24e7e3a50a579136400e8c9b0750399eb3c7558918bdf52c0ffa5e0fa5aad503', 'org.jmdns:jmdns:24e7e3a50a579136400e8c9b0750399eb3c7558918bdf52c0ffa5e0fa5aad503',
'org.nanohttpd:nanohttpd:de864c47818157141a24c9acb36df0c47d7bf15b7ff48c90610f3eb4e5df0e58',
'org.slf4j:slf4j-api:e56288031f5e60652c06e7bb6e9fa410a61231ab54890f7b708fc6adc4107c5b', 'org.slf4j:slf4j-api:e56288031f5e60652c06e7bb6e9fa410a61231ab54890f7b708fc6adc4107c5b',
] ]
} }
} else {
logger.info "Setting up *source* dependencies for F-Droid (because you passed in the -PsourceDeps argument to gradle while building)."
dependencies {
compile(project(':extern:support-v4-preferencefragment')) {
exclude module: 'support-v4'
}
compile project(':extern:nanohttpd:core')
compile project(':extern:zipsigner')
}
task binaryDeps(type: Copy, dependsOn: ':app:prepareReleaseDependencies') {
enabled = project.hasProperty('sourceDeps')
description = "Copies .jar and .aar files from subproject dependencies in extern/ to app/libs. Requires the sourceDeps property to be set (\"gradle -PsourceDeps binaryDeps\")"
from('../extern/') {
include 'support-v4-preferencefragment/build/outputs/aar/support-v4-preferencefragment-release.aar'
include 'nanohttpd/core/build/libs/nanohttpd-2.1.0.jar'
include 'zipsigner/build/libs/zipsigner.jar'
}
into 'libs/binaryDeps'
includeEmptyDirs false
eachFile { FileCopyDetails details ->
// Don't copy to a sub folder such as libs/binaryDeps/Project/build/outputs/aar/project.aar, but
// rather libs/binaryDeps/project.aar.
details.path = details.name
}
}
}
def isCi = "true".equals(System.getenv("CI")) def isCi = "true".equals(System.getenv("CI"))
def preDexEnabled = "true".equals(System.getProperty("pre-dex", "true")) def preDexEnabled = "true".equals(System.getProperty("pre-dex", "true"))
android { android {
compileSdkVersion 24 compileSdkVersion 27
buildToolsVersion '25.0.3' buildToolsVersion '27.0.3'
buildTypes { buildTypes {
// use proguard on debug too since we have unknowingly broken // use proguard on debug too since we have unknowingly broken
@ -258,6 +192,7 @@ android {
testOptions { testOptions {
unitTests { unitTests {
includeAndroidResources = true
// prevent tests from dying on android.util.Log calls // prevent tests from dying on android.util.Log calls
returnDefaultValues = true returnDefaultValues = true
all { all {
@ -324,6 +259,7 @@ task pmdMain(type: Pmd) {
ruleSets = [] // otherwise defaults clash with the list in rules.xml ruleSets = [] // otherwise defaults clash with the list in rules.xml
source 'src/main/java' source 'src/main/java'
include '**/*.java' include '**/*.java'
exclude '**/kellinwood/**/*.java'
} }
task pmdTest(type: Pmd) { task pmdTest(type: Pmd) {

Binary file not shown.

View File

@ -11,6 +11,8 @@
<!-- These are important to us, so promote from warning to error --> <!-- These are important to us, so promote from warning to error -->
<issue id="UnusedResources" severity="error"> <issue id="UnusedResources" severity="error">
<ignore path="src/main/res/drawable/category_**.png" /> <ignore path="src/main/res/drawable/category_**.png" />
<ignore path="src/main/res/values/dimens.xml"/>
<ignore path="src/main/res/values/styles.xml"/>
</issue> </issue>
<issue id="AppCompatMethod" severity="error"/> <issue id="AppCompatMethod" severity="error"/>
<issue id="NestedScrolling" severity="error"/> <issue id="NestedScrolling" severity="error"/>
@ -31,6 +33,10 @@
<ignore path="src/main/java/org/fdroid/fdroid/installer/ApkFileProvider.java"/> <ignore path="src/main/java/org/fdroid/fdroid/installer/ApkFileProvider.java"/>
</issue> </issue>
<issue id="ProtectedPermissions" severity="error">
<ignore path="src/main/AndroidManifest.xml"/>
</issue>
<!-- these should be fixed, but it'll be a chunk of work --> <!-- these should be fixed, but it'll be a chunk of work -->
<issue id="SetTextI18n" severity="error"> <issue id="SetTextI18n" severity="error">
<ignore path="src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java"/> <ignore path="src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java"/>

View File

@ -22,7 +22,7 @@
# removed, proguard will strip classes which are required, which may result in # removed, proguard will strip classes which are required, which may result in
# crashes. # crashes.
-keep class kellinwood.security.zipsigner.** {*;} -keep class kellinwood.security.zipsigner.** {*;}
-keep class org.spongycastle.** {*;} -keep class org.bouncycastle.** {*;}
# This keeps class members used for SystemInstaller IPC. # This keeps class members used for SystemInstaller IPC.
# Reference: https://gitlab.com/fdroid/fdroidclient/issues/79 # Reference: https://gitlab.com/fdroid/fdroidclient/issues/79

View File

@ -1,10 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="org.fdroid.fdroid" package="org.fdroid.fdroid"
android:installLocation="auto"> android:installLocation="auto">
<uses-sdk <uses-sdk
android:minSdkVersion="10" android:minSdkVersion="14"
android:targetSdkVersion="24" android:targetSdkVersion="24"
/> />
@ -150,13 +151,6 @@
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".views.main.MainActivity"/> android:value=".views.main.MainActivity"/>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="application/vnd.org.fdroid.fdroid.repo"/>
</intent-filter>
</activity> </activity>
<activity <activity
android:name=".NfcNotEnabledActivity" android:name=".NfcNotEnabledActivity"
@ -456,9 +450,9 @@
character set, making for more compact QR Codes. character set, making for more compact QR Codes.
--> -->
<data android:scheme="http"/> <data android:scheme="http"/>
<data android:scheme="HTTP"/> <data android:scheme="HTTP" tools:ignore="AppLinkUrlError"/>
<data android:scheme="https"/> <data android:scheme="https"/>
<data android:scheme="HTTPS"/> <data android:scheme="HTTPS" tools:ignore="AppLinkUrlError"/>
<data android:host="*"/> <data android:host="*"/>
@ -515,9 +509,9 @@
character set, making for more compact QR Codes. character set, making for more compact QR Codes.
--> -->
<data android:scheme="fdroidrepo"/> <data android:scheme="fdroidrepo"/>
<data android:scheme="FDROIDREPO"/> <data android:scheme="FDROIDREPO" tools:ignore="AppLinkUrlError"/>
<data android:scheme="fdroidrepos"/> <data android:scheme="fdroidrepos"/>
<data android:scheme="FDROIDREPOS"/> <data android:scheme="FDROIDREPOS" tools:ignore="AppLinkUrlError"/>
</intent-filter> </intent-filter>

View File

@ -1,100 +0,0 @@
/*
* *
* * This file is part of QuickLyric
* * Created by geecko
* *
* * QuickLyric is free software: you can redistribute it and/or modify
* * it under the terms of the GNU General Public License as published by
* * the Free Software Foundation, either version 3 of the License, or
* * (at your option) any later version.
* *
* * QuickLyric is distributed in the hope that it will be useful,
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* * GNU General Public License for more details.
* * You should have received a copy of the GNU General Public License
* * along with QuickLyric. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.geecko.QuickLyric.view;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.PreferenceManager;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatDialog;
import android.util.AttributeSet;
import java.lang.reflect.Method;
public class AppCompatListPreference extends ListPreference {
private AppCompatDialog appCompatDialog;
public AppCompatListPreference(Context context) {
super(context);
}
public AppCompatListPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public AppCompatDialog getDialog() {
return appCompatDialog;
}
@Override
protected void showDialog(Bundle state) {
if (getEntries() == null || getEntryValues() == null) {
throw new IllegalStateException(
"ListPreference requires an entries array and an entryValues array.");
}
int preselect = findIndexOfValue(getValue());
AlertDialog.Builder builder = new AlertDialog.Builder(getContext())
.setTitle(getDialogTitle())
.setIcon(getDialogIcon())
.setSingleChoiceItems(getEntries(), preselect, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (which >= 0 && getEntryValues() != null) {
String value = getEntryValues()[which].toString();
if (callChangeListener(value) && isPersistent()) {
setValue(value);
}
}
dialog.dismiss();
}
});
PreferenceManager pm = getPreferenceManager();
try {
Method method = pm.getClass().getDeclaredMethod(
"registerOnActivityDestroyListener",
PreferenceManager.OnActivityDestroyListener.class);
method.setAccessible(true);
method.invoke(pm, this);
} catch (Exception e) {
e.printStackTrace();
}
appCompatDialog = builder.create();
if (state != null) {
appCompatDialog.onRestoreInstanceState(state);
}
appCompatDialog.show();
}
@Override
public void onActivityDestroy() {
super.onActivityDestroy();
if (appCompatDialog != null && appCompatDialog.isShowing() &&
appCompatDialog.getWindow() != null && appCompatDialog.getWindow().getWindowManager() != null) {
appCompatDialog.dismiss();
}
}
}

View File

@ -0,0 +1,93 @@
/*
* Copyright (C) 2010 Ken Ellinwood.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package kellinwood.logging;
import java.text.SimpleDateFormat;
import java.util.Date;
public abstract class AbstractLogger implements LoggerInterface {
protected String category;
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
public AbstractLogger(String category) {
this.category = category;
}
protected String format(String level, String message) {
return String.format("%s %s %s: %s\n", dateFormat.format(new Date()), level, category, message);
}
protected abstract void write(String level, String message, Throwable t);
protected void writeFixNullMessage(String level, String message, Throwable t) {
if (message == null) {
if (t != null) message = t.getClass().getName();
else message = "null";
}
write(level, message, t);
}
public void debug(String message, Throwable t) {
writeFixNullMessage(DEBUG, message, t);
}
public void debug(String message) {
writeFixNullMessage(DEBUG, message, null);
}
public void error(String message, Throwable t) {
writeFixNullMessage(ERROR, message, t);
}
public void error(String message) {
writeFixNullMessage(ERROR, message, null);
}
public void info(String message, Throwable t) {
writeFixNullMessage(INFO, message, t);
}
public void info(String message) {
writeFixNullMessage(INFO, message, null);
}
public void warning(String message, Throwable t) {
writeFixNullMessage(WARNING, message, t);
}
public void warning(String message) {
writeFixNullMessage(WARNING, message, null);
}
public boolean isDebugEnabled() {
return true;
}
public boolean isErrorEnabled() {
return true;
}
public boolean isInfoEnabled() {
return true;
}
public boolean isWarningEnabled() {
return true;
}
}

View File

@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package kellinwood.logging; package kellinwood.logging;
public class ConsoleLoggerFactory implements LoggerFactory { public class ConsoleLoggerFactory implements LoggerFactory {

View File

@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package kellinwood.logging; package kellinwood.logging;
public interface LoggerFactory { public interface LoggerFactory {

View File

@ -0,0 +1,52 @@
/*
* Copyright (C) 2010 Ken Ellinwood.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package kellinwood.logging;
public interface LoggerInterface {
public static final String ERROR = "ERROR";
public static final String WARNING = "WARNING";
public static final String INFO = "INFO";
public static final String DEBUG = "DEBUG";
public boolean isErrorEnabled();
public void error(String message);
public void error(String message, Throwable t);
public boolean isWarningEnabled();
public void warning(String message);
public void warning(String message, Throwable t);
public boolean isInfoEnabled();
public void info(String message);
public void info(String message, Throwable t);
public boolean isDebugEnabled();
public void debug(String message);
public void debug(String message, Throwable t);
}

View File

@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package kellinwood.logging; package kellinwood.logging;
import java.util.Map; import java.util.Map;

View File

@ -0,0 +1,70 @@
/*
* Copyright (C) 2010 Ken Ellinwood.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package kellinwood.logging;
public class NullLoggerFactory implements LoggerFactory {
static LoggerInterface logger = new LoggerInterface() {
public void debug(String message) {
}
public void debug(String message, Throwable t) {
}
public void error(String message) {
}
public void error(String message, Throwable t) {
}
public void info(String message) {
}
public void info(String message, Throwable t) {
}
public boolean isDebugEnabled() {
return false;
}
public boolean isErrorEnabled() {
return false;
}
public boolean isInfoEnabled() {
return false;
}
public boolean isWarningEnabled() {
return false;
}
public void warning(String message) {
}
public void warning(String message, Throwable t) {
}
};
public LoggerInterface getLogger(String category) {
return logger;
}
}

View File

@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package kellinwood.logging; package kellinwood.logging;
import java.io.PrintStream; import java.io.PrintStream;
@ -21,8 +22,7 @@ public class StreamLogger extends AbstractLogger {
PrintStream out; PrintStream out;
public StreamLogger( String category, PrintStream out) public StreamLogger(String category, PrintStream out) {
{
super(category); super(category);
this.out = out; this.out = out;
} }

View File

@ -1,3 +1,4 @@
package kellinwood.security.zipsigner; package kellinwood.security.zipsigner;
public class AutoKeyException extends RuntimeException { public class AutoKeyException extends RuntimeException {

View File

@ -15,15 +15,16 @@
*/ */
package kellinwood.security.zipsigner; package kellinwood.security.zipsigner;
import kellinwood.logging.LoggerInterface;
import kellinwood.logging.LoggerManager;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import kellinwood.logging.LoggerInterface;
import kellinwood.logging.LoggerManager;
/* /*
* This class provides Base64 encoding services using one of several possible * This class provides Base64 encoding services using one of several possible
* implementations available elsewhere in the classpath. Supported implementations * implementations available elsewhere in the classpath. Supported implementations
@ -63,8 +64,8 @@ public class Base64 {
aEncodeMethod = clazz.getMethod("encode", byte[].class, Integer.TYPE); aEncodeMethod = clazz.getMethod("encode", byte[].class, Integer.TYPE);
aDecodeMethod = clazz.getMethod("decode", byte[].class, Integer.TYPE); aDecodeMethod = clazz.getMethod("decode", byte[].class, Integer.TYPE);
logger.info(clazz.getName() + " is available."); logger.info(clazz.getName() + " is available.");
} } catch (ClassNotFoundException x) {
catch (ClassNotFoundException x) {} // Ignore } // Ignore
catch (Exception x) { catch (Exception x) {
logger.error("Failed to initialize use of android.util.Base64", x); logger.error("Failed to initialize use of android.util.Base64", x);
} }
@ -78,8 +79,8 @@ public class Base64 {
// Looking for decode( byte[] input, int offset, int length, OutputStream output) // Looking for decode( byte[] input, int offset, int length, OutputStream output)
bDecodeMethod = clazz.getMethod("decode", byte[].class, Integer.TYPE, Integer.TYPE, OutputStream.class); bDecodeMethod = clazz.getMethod("decode", byte[].class, Integer.TYPE, Integer.TYPE, OutputStream.class);
} } catch (ClassNotFoundException x) {
catch (ClassNotFoundException x) {} // Ignore } // Ignore
catch (Exception x) { catch (Exception x) {
logger.error("Failed to initialize use of org.bouncycastle.util.encoders.Base64Encoder", x); logger.error("Failed to initialize use of org.bouncycastle.util.encoders.Base64Encoder", x);
} }
@ -95,14 +96,12 @@ public class Base64 {
// Invoking a static method call, using null for the instance value // Invoking a static method call, using null for the instance value
byte[] encodedBytes = (byte[]) aEncodeMethod.invoke(null, data, 2); byte[] encodedBytes = (byte[]) aEncodeMethod.invoke(null, data, 2);
return new String(encodedBytes); return new String(encodedBytes);
} } else if (bEncodeMethod != null) {
else if (bEncodeMethod != null) {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
bEncodeMethod.invoke(bEncoder, data, 0, data.length, baos); bEncodeMethod.invoke(bEncoder, data, 0, data.length, baos);
return new String(baos.toByteArray()); return new String(baos.toByteArray());
} }
} } catch (Exception x) {
catch (Exception x) {
throw new IllegalStateException(x.getClass().getName() + ": " + x.getMessage()); throw new IllegalStateException(x.getClass().getName() + ": " + x.getMessage());
} }
@ -116,14 +115,12 @@ public class Base64 {
// Invoking a static method call, using null for the instance value // Invoking a static method call, using null for the instance value
byte[] decodedBytes = (byte[]) aDecodeMethod.invoke(null, data, 2); byte[] decodedBytes = (byte[]) aDecodeMethod.invoke(null, data, 2);
return decodedBytes; return decodedBytes;
} } else if (bDecodeMethod != null) {
else if (bDecodeMethod != null) {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
bDecodeMethod.invoke(bEncoder, data, 0, data.length, baos); bDecodeMethod.invoke(bEncoder, data, 0, data.length, baos);
return baos.toByteArray(); return baos.toByteArray();
} }
} } catch (Exception x) {
catch (Exception x) {
throw new IllegalStateException(x.getClass().getName() + ": " + x.getMessage()); throw new IllegalStateException(x.getClass().getName() + ": " + x.getMessage());
} }

View File

@ -1,3 +1,4 @@
package kellinwood.security.zipsigner; package kellinwood.security.zipsigner;
/** /**

View File

@ -15,16 +15,17 @@
*/ */
package kellinwood.security.zipsigner; package kellinwood.security.zipsigner;
import java.io.IOException;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException;
/** Produces the classic hex dump with an address column, hex data /**
* Produces the classic hex dump with an address column, hex data
* section (16 bytes per row) and right-column printable character dislpay. * section (16 bytes per row) and right-column printable character dislpay.
*/ */
public class HexDumpEncoder public class HexDumpEncoder {
{
static HexEncoder encoder = new HexEncoder(); static HexEncoder encoder = new HexEncoder();
@ -64,8 +65,7 @@ public class HexDumpEncoder
} }
return hexDumpOut.toString(); return hexDumpOut.toString();
} } catch (IOException x) {
catch (IOException x) {
throw new IllegalStateException(x.getClass().getName() + ": " + x.getMessage()); throw new IllegalStateException(x.getClass().getName() + ": " + x.getMessage());
} }
} }

View File

@ -1,3 +1,4 @@
package kellinwood.security.zipsigner; package kellinwood.security.zipsigner;
/* /*
@ -32,8 +33,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
public class HexEncoder public class HexEncoder {
{
protected final byte[] encodingTable = protected final byte[] encodingTable =
{ {
(byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7',
@ -45,10 +45,8 @@ public class HexEncoder
*/ */
protected final byte[] decodingTable = new byte[128]; protected final byte[] decodingTable = new byte[128];
protected void initialiseDecodingTable() protected void initialiseDecodingTable() {
{ for (int i = 0; i < encodingTable.length; i++) {
for (int i = 0; i < encodingTable.length; i++)
{
decodingTable[encodingTable[i]] = (byte) i; decodingTable[encodingTable[i]] = (byte) i;
} }
@ -60,8 +58,7 @@ public class HexEncoder
decodingTable['F'] = decodingTable['f']; decodingTable['F'] = decodingTable['f'];
} }
public HexEncoder() public HexEncoder() {
{
initialiseDecodingTable(); initialiseDecodingTable();
} }
@ -75,10 +72,8 @@ public class HexEncoder
int off, int off,
int length, int length,
OutputStream out) OutputStream out)
throws IOException throws IOException {
{ for (int i = off; i < (off + length); i++) {
for (int i = off; i < (off + length); i++)
{
int v = data[i] & 0xff; int v = data[i] & 0xff;
out.write(encodingTable[(v >>> 4)]); out.write(encodingTable[(v >>> 4)]);
@ -89,8 +84,7 @@ public class HexEncoder
} }
private boolean ignore( private boolean ignore(
char c) char c) {
{
return (c == '\n' || c == '\r' || c == '\t' || c == ' '); return (c == '\n' || c == '\r' || c == '\t' || c == ' ');
} }
@ -105,17 +99,14 @@ public class HexEncoder
int off, int off,
int length, int length,
OutputStream out) OutputStream out)
throws IOException throws IOException {
{
byte b1, b2; byte b1, b2;
int outLen = 0; int outLen = 0;
int end = off + length; int end = off + length;
while (end > off) while (end > off) {
{ if (!ignore((char) data[end - 1])) {
if (!ignore((char)data[end - 1]))
{
break; break;
} }
@ -123,17 +114,14 @@ public class HexEncoder
} }
int i = off; int i = off;
while (i < end) while (i < end) {
{ while (i < end && ignore((char) data[i])) {
while (i < end && ignore((char)data[i]))
{
i++; i++;
} }
b1 = decodingTable[data[i++]]; b1 = decodingTable[data[i++]];
while (i < end && ignore((char)data[i])) while (i < end && ignore((char) data[i])) {
{
i++; i++;
} }
@ -156,17 +144,14 @@ public class HexEncoder
public int decode( public int decode(
String data, String data,
OutputStream out) OutputStream out)
throws IOException throws IOException {
{
byte b1, b2; byte b1, b2;
int length = 0; int length = 0;
int end = data.length(); int end = data.length();
while (end > 0) while (end > 0) {
{ if (!ignore(data.charAt(end - 1))) {
if (!ignore(data.charAt(end - 1)))
{
break; break;
} }
@ -174,17 +159,14 @@ public class HexEncoder
} }
int i = 0; int i = 0;
while (i < end) while (i < end) {
{ while (i < end && ignore(data.charAt(i))) {
while (i < end && ignore(data.charAt(i)))
{
i++; i++;
} }
b1 = decodingTable[data.charAt(i++)]; b1 = decodingTable[data.charAt(i++)];
while (i < end && ignore(data.charAt(i))) while (i < end && ignore(data.charAt(i))) {
{
i++; i++;
} }

View File

@ -1,5 +1,3 @@
/* /*
* Copyright (C) 2010 Ken Ellinwood * Copyright (C) 2010 Ken Ellinwood
* *
@ -15,6 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package kellinwood.security.zipsigner; package kellinwood.security.zipsigner;
import java.security.PrivateKey; import java.security.PrivateKey;
@ -38,16 +37,14 @@ public class KeySet {
public KeySet() { public KeySet() {
} }
public KeySet( String name, X509Certificate publicKey, PrivateKey privateKey, byte[] sigBlockTemplate) public KeySet(String name, X509Certificate publicKey, PrivateKey privateKey, byte[] sigBlockTemplate) {
{
this.name = name; this.name = name;
this.publicKey = publicKey; this.publicKey = publicKey;
this.privateKey = privateKey; this.privateKey = privateKey;
this.sigBlockTemplate = sigBlockTemplate; this.sigBlockTemplate = sigBlockTemplate;
} }
public KeySet( String name, X509Certificate publicKey, PrivateKey privateKey, String signatureAlgorithm, byte[] sigBlockTemplate) public KeySet(String name, X509Certificate publicKey, PrivateKey privateKey, String signatureAlgorithm, byte[] sigBlockTemplate) {
{
this.name = name; this.name = name;
this.publicKey = publicKey; this.publicKey = publicKey;
this.privateKey = privateKey; this.privateKey = privateKey;

View File

@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package kellinwood.security.zipsigner; package kellinwood.security.zipsigner;
public class ProgressEvent { public class ProgressEvent {
@ -27,18 +28,23 @@ public class ProgressEvent {
public String getMessage() { public String getMessage() {
return message; return message;
} }
public void setMessage(String message) { public void setMessage(String message) {
this.message = message; this.message = message;
} }
public int getPercentDone() { public int getPercentDone() {
return percentDone; return percentDone;
} }
public void setPercentDone(int percentDone) { public void setPercentDone(int percentDone) {
this.percentDone = percentDone; this.percentDone = percentDone;
} }
public int getPriority() { public int getPriority() {
return priority; return priority;
} }
public void setPriority(int priority) { public void setPriority(int priority) {
this.priority = priority; this.priority = priority;
} }

View File

@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package kellinwood.security.zipsigner; package kellinwood.security.zipsigner;
import java.util.ArrayList; import java.util.ArrayList;
@ -23,8 +24,7 @@ public class ProgressHelper {
private int progressCurrentItem = 0; private int progressCurrentItem = 0;
private ProgressEvent progressEvent = new ProgressEvent(); private ProgressEvent progressEvent = new ProgressEvent();
public void initProgress() public void initProgress() {
{
progressTotalItems = 10000; progressTotalItems = 10000;
progressCurrentItem = 0; progressCurrentItem = 0;
} }
@ -65,16 +65,14 @@ public class ProgressHelper {
private ArrayList<ProgressListener> listeners = new ArrayList<ProgressListener>(); private ArrayList<ProgressListener> listeners = new ArrayList<ProgressListener>();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public synchronized void addProgressListener( ProgressListener l) public synchronized void addProgressListener(ProgressListener l) {
{
ArrayList<ProgressListener> list = (ArrayList<ProgressListener>) listeners.clone(); ArrayList<ProgressListener> list = (ArrayList<ProgressListener>) listeners.clone();
list.add(l); list.add(l);
listeners = list; listeners = list;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public synchronized void removeProgressListener( ProgressListener l) public synchronized void removeProgressListener(ProgressListener l) {
{
ArrayList<ProgressListener> list = (ArrayList<ProgressListener>) listeners.clone(); ArrayList<ProgressListener> list = (ArrayList<ProgressListener>) listeners.clone();
list.remove(l); list.remove(l);
listeners = list; listeners = list;

View File

@ -13,12 +13,14 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package kellinwood.security.zipsigner; package kellinwood.security.zipsigner;
public interface ProgressListener { public interface ProgressListener {
/** Called to notify the listener that progress has been made during /**
the zip signing operation. * Called to notify the listener that progress has been made during
* the zip signing operation.
*/ */
public void onProgress(ProgressEvent event); public void onProgress(ProgressEvent event);
} }

View File

@ -1,3 +1,4 @@
package kellinwood.security.zipsigner; package kellinwood.security.zipsigner;
/** /**
@ -14,7 +15,9 @@ public interface ResourceAdapter {
GENERATING_SIGNATURE_FILE, GENERATING_SIGNATURE_FILE,
GENERATING_SIGNATURE_BLOCK, GENERATING_SIGNATURE_BLOCK,
COPYING_ZIP_ENTRY COPYING_ZIP_ENTRY
}; }
;
public String getString(Item item, Object... args); public String getString(Item item, Object... args);
} }

View File

@ -13,18 +13,18 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package kellinwood.security.zipsigner; package kellinwood.security.zipsigner;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import java.io.IOException; import java.io.IOException;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.PrivateKey; import java.security.PrivateKey;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
@SuppressWarnings("restriction") @SuppressWarnings("restriction")
public class ZipSignature { public class ZipSignature {
@ -41,14 +41,12 @@ public class ZipSignature {
MessageDigest md; MessageDigest md;
public ZipSignature() throws IOException, GeneralSecurityException public ZipSignature() throws IOException, GeneralSecurityException {
{
md = MessageDigest.getInstance("SHA1"); md = MessageDigest.getInstance("SHA1");
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
} }
public void initSign( PrivateKey privateKey) throws InvalidKeyException public void initSign(PrivateKey privateKey) throws InvalidKeyException {
{
cipher.init(Cipher.ENCRYPT_MODE, privateKey); cipher.init(Cipher.ENCRYPT_MODE, privateKey);
} }
@ -60,8 +58,7 @@ public class ZipSignature {
md.update(data, offset, count); md.update(data, offset, count);
} }
public byte[] sign() throws BadPaddingException, IllegalBlockSizeException public byte[] sign() throws BadPaddingException, IllegalBlockSizeException {
{
cipher.update(beforeAlgorithmIdBytes); cipher.update(beforeAlgorithmIdBytes);
cipher.update(algorithmIdBytes); cipher.update(algorithmIdBytes);
cipher.update(afterAlgorithmIdBytes); cipher.update(afterAlgorithmIdBytes);

View File

@ -26,6 +26,7 @@
* using signature block template files. * using signature block template files.
*/ */
package kellinwood.security.zipsigner; package kellinwood.security.zipsigner;
import kellinwood.logging.LoggerInterface; import kellinwood.logging.LoggerInterface;
@ -38,17 +39,40 @@ import javax.crypto.Cipher;
import javax.crypto.EncryptedPrivateKeyInfo; import javax.crypto.EncryptedPrivateKeyInfo;
import javax.crypto.SecretKeyFactory; import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.PBEKeySpec;
import java.io.*; import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.net.URL; import java.net.URL;
import java.security.*; import java.security.DigestOutputStream;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.Security;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec; import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.PKCS8EncodedKeySpec;
import java.util.*; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;
import java.util.TreeMap;
import java.util.jar.Attributes; import java.util.jar.Attributes;
import java.util.jar.JarFile; import java.util.jar.JarFile;
import java.util.jar.Manifest; import java.util.jar.Manifest;
@ -58,11 +82,10 @@ import java.util.regex.Pattern;
* This is a modified copy of com.android.signapk.SignApk.java. It provides an * This is a modified copy of com.android.signapk.SignApk.java. It provides an
* API to sign JAR files (including APKs and Zip/OTA updates) in * API to sign JAR files (including APKs and Zip/OTA updates) in
* a way compatible with the mincrypt verifier, using SHA1 and RSA keys. * a way compatible with the mincrypt verifier, using SHA1 and RSA keys.
* * <p>
* Please see the README.txt file in the root of this project for usage instructions. * Please see the README.txt file in the root of this project for usage instructions.
*/ */
public class ZipSigner public class ZipSigner {
{
private boolean canceled = false; private boolean canceled = false;
@ -102,8 +125,7 @@ public class ZipSigner
AutoKeyObservable autoKeyObservable = new AutoKeyObservable(); AutoKeyObservable autoKeyObservable = new AutoKeyObservable();
public ZipSigner() throws ClassNotFoundException, IllegalAccessException, InstantiationException public ZipSigner() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
{
// MD5 of the first 1458 bytes of the signature block generated by the key, mapped to the key name // MD5 of the first 1458 bytes of the signature block generated by the key, mapped to the key name
autoKeyDetect.put("aa9852bc5a53272ac8031d49b65e4b0e", "media"); autoKeyDetect.put("aa9852bc5a53272ac8031d49b65e4b0e", "media");
autoKeyDetect.put("e60418c4b638f20d0721e115674ca11f", "platform"); autoKeyDetect.put("e60418c4b638f20d0721e115674ca11f", "platform");
@ -129,14 +151,12 @@ public class ZipSigner
return keymode; return keymode;
} }
public void setKeymode(String km) throws IOException, GeneralSecurityException public void setKeymode(String km) throws IOException, GeneralSecurityException {
{
if (getLogger().isDebugEnabled()) getLogger().debug("setKeymode: " + km); if (getLogger().isDebugEnabled()) getLogger().debug("setKeymode: " + km);
keymode = km; keymode = km;
if (keymode.startsWith(MODE_AUTO)) { if (keymode.startsWith(MODE_AUTO)) {
keySet = null; keySet = null;
} } else {
else {
progressHelper.initProgress(); progressHelper.initProgress();
loadKeys(keymode); loadKeys(keymode);
} }
@ -148,8 +168,7 @@ public class ZipSigner
protected String autoDetectKey(String mode, Map<String, ZioEntry> zioEntries) protected String autoDetectKey(String mode, Map<String, ZioEntry> zioEntries)
throws NoSuchAlgorithmException, IOException throws NoSuchAlgorithmException, IOException {
{
boolean debug = getLogger().isDebugEnabled(); boolean debug = getLogger().isDebugEnabled();
if (!mode.startsWith(MODE_AUTO)) return mode; if (!mode.startsWith(MODE_AUTO)) return mode;
@ -197,8 +216,7 @@ public class ZipSigner
if (debug) getLogger().debug("Falling back to key=" + keyName); if (debug) getLogger().debug("Falling back to key=" + keyName);
return KEY_TESTKEY; return KEY_TESTKEY;
} } else if (mode.equals(MODE_AUTO_NONE)) {
else if (mode.equals(MODE_AUTO_NONE)) {
// in auto-node mode, simply copy the input to the output when the key can't be determined. // in auto-node mode, simply copy the input to the output when the key can't be determined.
if (debug) getLogger().debug("Unable to determine key, returning: " + KEY_NONE); if (debug) getLogger().debug("Unable to determine key, returning: " + KEY_NONE);
return KEY_NONE; return KEY_NONE;
@ -213,8 +231,7 @@ public class ZipSigner
// Loads one of the built-in keys (media, platform, shared, testkey) // Loads one of the built-in keys (media, platform, shared, testkey)
public void loadKeys(String name) public void loadKeys(String name)
throws IOException, GeneralSecurityException throws IOException, GeneralSecurityException {
{
keySet = loadedKeys.get(name); keySet = loadedKeys.get(name);
if (keySet != null) return; if (keySet != null) return;
@ -242,13 +259,11 @@ public class ZipSigner
} }
} }
public void setKeys( String name, X509Certificate publicKey, PrivateKey privateKey, byte[] signatureBlockTemplate) public void setKeys(String name, X509Certificate publicKey, PrivateKey privateKey, byte[] signatureBlockTemplate) {
{
keySet = new KeySet(name, publicKey, privateKey, signatureBlockTemplate); keySet = new KeySet(name, publicKey, privateKey, signatureBlockTemplate);
} }
public void setKeys( String name, X509Certificate publicKey, PrivateKey privateKey, String signatureAlgorithm, byte[] signatureBlockTemplate) public void setKeys(String name, X509Certificate publicKey, PrivateKey privateKey, String signatureAlgorithm, byte[] signatureBlockTemplate) {
{
keySet = new KeySet(name, publicKey, privateKey, signatureAlgorithm, signatureBlockTemplate); keySet = new KeySet(name, publicKey, privateKey, signatureAlgorithm, signatureBlockTemplate);
} }
@ -272,8 +287,7 @@ public class ZipSigner
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void loadProvider(String providerClassName) public void loadProvider(String providerClassName)
throws ClassNotFoundException, IllegalAccessException, InstantiationException throws ClassNotFoundException, IllegalAccessException, InstantiationException {
{
Class providerClass = Class.forName(providerClassName); Class providerClass = Class.forName(providerClassName);
Provider provider = (Provider) providerClass.newInstance(); Provider provider = (Provider) providerClass.newInstance();
Security.insertProviderAt(provider, 1); Security.insertProviderAt(provider, 1);
@ -293,7 +307,7 @@ public class ZipSigner
/** /**
* Decrypt an encrypted PKCS 8 format private key. * Decrypt an encrypted PKCS 8 format private key.
* * <p>
* Based on ghstark's post on Aug 6, 2006 at * Based on ghstark's post on Aug 6, 2006 at
* http://forums.sun.com/thread.jspa?threadID=758133&messageID=4330949 * http://forums.sun.com/thread.jspa?threadID=758133&messageID=4330949
* *
@ -326,15 +340,17 @@ public class ZipSigner
} }
} }
/** Fetch the content at the specified URL and return it as a byte array. */ /**
public byte[] readContentAsBytes( URL contentUrl) throws IOException * Fetch the content at the specified URL and return it as a byte array.
{ */
public byte[] readContentAsBytes(URL contentUrl) throws IOException {
return readContentAsBytes(contentUrl.openStream()); return readContentAsBytes(contentUrl.openStream());
} }
/** Fetch the content from the given stream and return it as a byte array. */ /**
public byte[] readContentAsBytes( InputStream input) throws IOException * Fetch the content from the given stream and return it as a byte array.
{ */
public byte[] readContentAsBytes(InputStream input) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[2048]; byte[] buffer = new byte[2048];
@ -349,7 +365,9 @@ public class ZipSigner
return bytes; return bytes;
} }
/** Read a PKCS 8 format private key. */ /**
* Read a PKCS 8 format private key.
*/
public PrivateKey readPrivateKey(URL privateKeyUrl, String keyPassword) public PrivateKey readPrivateKey(URL privateKeyUrl, String keyPassword)
throws IOException, GeneralSecurityException { throws IOException, GeneralSecurityException {
DataInputStream input = new DataInputStream(privateKeyUrl.openStream()); DataInputStream input = new DataInputStream(privateKeyUrl.openStream());
@ -371,17 +389,16 @@ public class ZipSigner
} }
} }
/** Add the SHA1 of every file to the manifest, creating it if necessary. */ /**
* Add the SHA1 of every file to the manifest, creating it if necessary.
*/
private Manifest addDigestsToManifest(Map<String, ZioEntry> entries) private Manifest addDigestsToManifest(Map<String, ZioEntry> entries)
throws IOException, GeneralSecurityException throws IOException, GeneralSecurityException {
{
Manifest input = null; Manifest input = null;
ZioEntry manifestEntry = entries.get(JarFile.MANIFEST_NAME); ZioEntry manifestEntry = entries.get(JarFile.MANIFEST_NAME);
if (manifestEntry != null) { if (manifestEntry != null) {
InputStream is = manifestEntry.getInputStream();
input = new Manifest(); input = new Manifest();
input.read(is); input.read(manifestEntry.getInputStream());
is.close();
} }
Manifest output = new Manifest(); Manifest output = new Manifest();
Attributes main = output.getMainAttributes(); Attributes main = output.getMainAttributes();
@ -413,8 +430,7 @@ public class ZipSigner
if (!entry.isDirectory() && !name.equals(JarFile.MANIFEST_NAME) && if (!entry.isDirectory() && !name.equals(JarFile.MANIFEST_NAME) &&
!name.equals(CERT_SF_NAME) && !name.equals(CERT_RSA_NAME) && !name.equals(CERT_SF_NAME) && !name.equals(CERT_RSA_NAME) &&
(stripPattern == null || (stripPattern == null ||
!stripPattern.matcher(name).matches())) !stripPattern.matcher(name).matches())) {
{
progressHelper.progress(ProgressEvent.PRORITY_NORMAL, resourceAdapter.getString(ResourceAdapter.Item.GENERATING_MANIFEST)); progressHelper.progress(ProgressEvent.PRORITY_NORMAL, resourceAdapter.getString(ResourceAdapter.Item.GENERATING_MANIFEST));
InputStream data = entry.getInputStream(); InputStream data = entry.getInputStream();
@ -437,7 +453,9 @@ public class ZipSigner
} }
/** Write the signature file to the given output stream. */ /**
* Write the signature file to the given output stream.
*/
private void generateSignatureFile(Manifest manifest, OutputStream out) private void generateSignatureFile(Manifest manifest, OutputStream out)
throws IOException, GeneralSecurityException { throws IOException, GeneralSecurityException {
out.write(("Signature-Version: 1.0\r\n").getBytes()); out.write(("Signature-Version: 1.0\r\n").getBytes());
@ -475,11 +493,12 @@ public class ZipSigner
} }
/** Write a .RSA file with a digital signature. */ /**
* Write a .RSA file with a digital signature.
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private void writeSignatureBlock(KeySet keySet, byte[] signatureFileBytes, OutputStream out) private void writeSignatureBlock(KeySet keySet, byte[] signatureFileBytes, OutputStream out)
throws IOException, GeneralSecurityException throws IOException, GeneralSecurityException {
{
if (keySet.getSigBlockTemplate() != null) { if (keySet.getSigBlockTemplate() != null) {
// Can't use default Signature on Android. Although it generates a signature that can be verified by jarsigner, // Can't use default Signature on Android. Although it generates a signature that can be verified by jarsigner,
@ -508,8 +527,7 @@ public class ZipSigner
byte[] tmpData = cipher.doFinal(signatureBytes); byte[] tmpData = cipher.doFinal(signatureBytes);
getLogger().debug("Signature Decrypted: \n" + HexDumpEncoder.encode(tmpData)); getLogger().debug("Signature Decrypted: \n" + HexDumpEncoder.encode(tmpData));
} }
} } else {
else {
try { try {
byte[] sigBlock = null; byte[] sigBlock = null;
// Use reflection to call the optional generator. // Use reflection to call the optional generator.
@ -530,8 +548,7 @@ public class ZipSigner
* more efficient. * more efficient.
*/ */
private void copyFiles(Manifest manifest, Map<String, ZioEntry> input, ZipOutput output, long timestamp) private void copyFiles(Manifest manifest, Map<String, ZioEntry> input, ZipOutput output, long timestamp)
throws IOException throws IOException {
{
Map<String, Attributes> entries = manifest.getEntries(); Map<String, Attributes> entries = manifest.getEntries();
List<String> names = new ArrayList<String>(entries.keySet()); List<String> names = new ArrayList<String>(entries.keySet());
Collections.sort(names); Collections.sort(names);
@ -551,8 +568,7 @@ public class ZipSigner
* Copy all the files from input to output. * Copy all the files from input to output.
*/ */
private void copyFiles(Map<String, ZioEntry> input, ZipOutput output) private void copyFiles(Map<String, ZioEntry> input, ZipOutput output)
throws IOException throws IOException {
{
int i = 1; int i = 1;
for (ZioEntry inEntry : input.values()) { for (ZioEntry inEntry : input.values()) {
if (canceled) break; if (canceled) break;
@ -573,8 +589,7 @@ public class ZipSigner
String inputZipFilename, String inputZipFilename,
String outputZipFilename) String outputZipFilename)
throws ClassNotFoundException, IllegalAccessException, InstantiationException, throws ClassNotFoundException, IllegalAccessException, InstantiationException,
IOException, GeneralSecurityException IOException, GeneralSecurityException {
{
signZip(keystoreURL, keystoreType, keystorePw.toCharArray(), certAlias, certPw.toCharArray(), "SHA1withRSA", inputZipFilename, outputZipFilename); signZip(keystoreURL, keystoreType, keystorePw.toCharArray(), certAlias, certPw.toCharArray(), "SHA1withRSA", inputZipFilename, outputZipFilename);
} }
@ -587,8 +602,7 @@ public class ZipSigner
String inputZipFilename, String inputZipFilename,
String outputZipFilename) String outputZipFilename)
throws ClassNotFoundException, IllegalAccessException, InstantiationException, throws ClassNotFoundException, IllegalAccessException, InstantiationException,
IOException, GeneralSecurityException IOException, GeneralSecurityException {
{
InputStream keystoreStream = null; InputStream keystoreStream = null;
@ -607,34 +621,31 @@ public class ZipSigner
setKeys("custom", publicKey, privateKey, signatureAlgorithm, null); setKeys("custom", publicKey, privateKey, signatureAlgorithm, null);
signZip(inputZipFilename, outputZipFilename); signZip(inputZipFilename, outputZipFilename);
} } finally {
finally {
if (keystoreStream != null) keystoreStream.close(); if (keystoreStream != null) keystoreStream.close();
} }
} }
/**
* Sign the input with the default test key and certificate.
/** Sign the input with the default test key and certificate.
* Save result to output file. * Save result to output file.
*/ */
public void signZip(Map<String, ZioEntry> zioEntries, String outputZipFilename) public void signZip(Map<String, ZioEntry> zioEntries, String outputZipFilename)
throws IOException, GeneralSecurityException throws IOException, GeneralSecurityException {
{
progressHelper.initProgress(); progressHelper.initProgress();
signZip(zioEntries, new FileOutputStream(outputZipFilename), outputZipFilename); signZip(zioEntries, new FileOutputStream(outputZipFilename), outputZipFilename);
} }
/** Sign the file using the given public key cert, private key, /**
* Sign the file using the given public key cert, private key,
* and signature block template. The signature block template * and signature block template. The signature block template
* parameter may be null, but if so * parameter may be null, but if so
* android-sun-jarsign-support.jar must be in the classpath. * android-sun-jarsign-support.jar must be in the classpath.
*/ */
public void signZip(String inputZipFilename, String outputZipFilename) public void signZip(String inputZipFilename, String outputZipFilename)
throws IOException, GeneralSecurityException throws IOException, GeneralSecurityException {
{
File inFile = new File(inputZipFilename).getCanonicalFile(); File inFile = new File(inputZipFilename).getCanonicalFile();
File outFile = new File(outputZipFilename).getCanonicalFile(); File outFile = new File(outputZipFilename).getCanonicalFile();
@ -645,19 +656,26 @@ public class ZipSigner
progressHelper.initProgress(); progressHelper.initProgress();
progressHelper.progress(ProgressEvent.PRORITY_IMPORTANT, resourceAdapter.getString(ResourceAdapter.Item.PARSING_CENTRAL_DIRECTORY)); progressHelper.progress(ProgressEvent.PRORITY_IMPORTANT, resourceAdapter.getString(ResourceAdapter.Item.PARSING_CENTRAL_DIRECTORY));
ZipInput input = ZipInput.read( inputZipFilename); ZipInput input = null;
signZip( input.getEntries(), new FileOutputStream( outputZipFilename), outputZipFilename); OutputStream outStream = null;
input.close(); try {
input = ZipInput.read(inputZipFilename);
outStream = new FileOutputStream(outputZipFilename);
signZip(input.getEntries(), outStream, outputZipFilename);
} finally {
if (input != null) input.close();
if (outStream != null) outStream.close();
}
} }
/** Sign the /**
* Sign the
* and signature block template. The signature block template * and signature block template. The signature block template
* parameter may be null, but if so * parameter may be null, but if so
* android-sun-jarsign-support.jar must be in the classpath. * android-sun-jarsign-support.jar must be in the classpath.
*/ */
public void signZip(Map<String, ZioEntry> zioEntries, OutputStream outputStream, String outputZipFilename) public void signZip(Map<String, ZioEntry> zioEntries, OutputStream outputStream, String outputZipFilename)
throws IOException, GeneralSecurityException throws IOException, GeneralSecurityException {
{
boolean debug = getLogger().isDebugEnabled(); boolean debug = getLogger().isDebugEnabled();
progressHelper.initProgress(); progressHelper.initProgress();
@ -677,7 +695,6 @@ public class ZipSigner
} }
ZipOutput zipOutput = null; ZipOutput zipOutput = null;
try { try {
@ -699,8 +716,7 @@ public class ZipSigner
if (!entry.isDirectory() && !name.equals(JarFile.MANIFEST_NAME) && if (!entry.isDirectory() && !name.equals(JarFile.MANIFEST_NAME) &&
!name.equals(CERT_SF_NAME) && !name.equals(CERT_RSA_NAME) && !name.equals(CERT_SF_NAME) && !name.equals(CERT_RSA_NAME) &&
(stripPattern == null || (stripPattern == null ||
!stripPattern.matcher(name).matches())) !stripPattern.matcher(name).matches())) {
{
progressTotalItems += 3; // digest for manifest, digest in sig file, copy data progressTotalItems += 3; // digest for manifest, digest in sig file, copy data
} }
} }
@ -748,33 +764,28 @@ public class ZipSigner
copyFiles(manifest, zioEntries, zipOutput, timestamp); copyFiles(manifest, zioEntries, zipOutput, timestamp);
if (canceled) return; if (canceled) return;
} } finally {
finally { if (zipOutput != null) zipOutput.close();
zipOutput.close();
if (canceled) { if (canceled) {
try { try {
if (outputZipFilename != null) new File(outputZipFilename).delete(); if (outputZipFilename != null) new File(outputZipFilename).delete();
} } catch (Throwable t) {
catch (Throwable t) {
getLogger().warning(t.getClass().getName() + ":" + t.getMessage()); getLogger().warning(t.getClass().getName() + ":" + t.getMessage());
} }
} }
} }
} }
public void addProgressListener( ProgressListener l) public void addProgressListener(ProgressListener l) {
{
progressHelper.addProgressListener(l); progressHelper.addProgressListener(l);
} }
public synchronized void removeProgressListener( ProgressListener l) public synchronized void removeProgressListener(ProgressListener l) {
{
progressHelper.removeProgressListener(l); progressHelper.removeProgressListener(l);
} }
public static class AutoKeyObservable extends Observable public static class AutoKeyObservable extends Observable {
{
@Override @Override
public void notifyObservers(Object arg) { public void notifyObservers(Object arg) {
super.setChanged(); super.setChanged();

View File

@ -1,13 +1,17 @@
package kellinwood.security.zipsigner.optional; package kellinwood.security.zipsigner.optional;
import kellinwood.security.zipsigner.KeySet; import kellinwood.security.zipsigner.KeySet;
import org.spongycastle.jce.X509Principal; import org.bouncycastle.jce.X509Principal;
import org.spongycastle.x509.X509V3CertificateGenerator; import org.bouncycastle.x509.X509V3CertificateGenerator;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.*; import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Date; import java.util.Date;
@ -17,7 +21,8 @@ import java.util.Date;
*/ */
public class CertCreator { public class CertCreator {
/** Creates a new keystore and self-signed key. The key will have the same password as the key, and will be /**
* Creates a new keystore and self-signed key. The key will have the same password as the key, and will be
* RSA 2048, with the cert signed using SHA1withRSA. The certificate will have a validity of * RSA 2048, with the cert signed using SHA1withRSA. The certificate will have a validity of
* 30 years). * 30 years).
* *
@ -27,8 +32,7 @@ public class CertCreator {
* @param distinguishedNameValues - contains Country, State, Locality,...,Common Name, etc. * @param distinguishedNameValues - contains Country, State, Locality,...,Common Name, etc.
*/ */
public static void createKeystoreAndKey(String storePath, char[] password, public static void createKeystoreAndKey(String storePath, char[] password,
String keyName, DistinguishedNameValues distinguishedNameValues) String keyName, DistinguishedNameValues distinguishedNameValues) {
{
createKeystoreAndKey(storePath, password, "RSA", 2048, keyName, password, "SHA1withRSA", 30, createKeystoreAndKey(storePath, password, "RSA", 2048, keyName, password, "SHA1withRSA", 30,
distinguishedNameValues); distinguishedNameValues);
} }
@ -63,8 +67,8 @@ public class CertCreator {
} }
} }
/** Create a new key and store it in an existing keystore. /**
* * Create a new key and store it in an existing keystore.
*/ */
public static KeySet createKey(String storePath, char[] storePass, public static KeySet createKey(String storePath, char[] storePass,
String keyAlgorithm, int keySize, String keyName, char[] keyPass, String keyAlgorithm, int keySize, String keyName, char[] keyPass,
@ -93,8 +97,7 @@ public class CertCreator {
} }
public static KeySet createKey(String keyAlgorithm, int keySize, String keyName, public static KeySet createKey(String keyAlgorithm, int keySize, String keyName,
String certSignatureAlgorithm, int certValidityYears, DistinguishedNameValues distinguishedNameValues) String certSignatureAlgorithm, int certValidityYears, DistinguishedNameValues distinguishedNameValues) {
{
try { try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(keyAlgorithm); KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(keyAlgorithm);
keyPairGenerator.initialize(keySize); keyPairGenerator.initialize(keySize);

View File

@ -0,0 +1,39 @@
package kellinwood.security.zipsigner.optional;
import kellinwood.security.zipsigner.ZipSigner;
import java.security.Key;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
/**
*/
public class CustomKeySigner {
/**
* KeyStore-type agnostic. This method will sign the zip file, automatically handling JKS or BKS keystores.
*/
public static void signZip(ZipSigner zipSigner,
String keystorePath,
char[] keystorePw,
String certAlias,
char[] certPw,
String signatureAlgorithm,
String inputZipFilename,
String outputZipFilename)
throws Exception {
zipSigner.issueLoadingCertAndKeysProgressEvent();
KeyStore keystore = KeyStoreFileManager.loadKeyStore(keystorePath, keystorePw);
Certificate cert = keystore.getCertificate(certAlias);
X509Certificate publicKey = (X509Certificate) cert;
Key key = keystore.getKey(certAlias, certPw);
PrivateKey privateKey = (PrivateKey) key;
zipSigner.setKeys("custom", publicKey, privateKey, signatureAlgorithm, null);
zipSigner.signZip(inputZipFilename, outputZipFilename);
}
}

View File

@ -0,0 +1,89 @@
package kellinwood.security.zipsigner.optional;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.jce.X509Principal;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Vector;
/**
* Helper class for dealing with the distinguished name RDNs.
*/
public class DistinguishedNameValues extends LinkedHashMap<ASN1ObjectIdentifier, String> {
public DistinguishedNameValues() {
put(BCStyle.C, null);
put(BCStyle.ST, null);
put(BCStyle.L, null);
put(BCStyle.STREET, null);
put(BCStyle.O, null);
put(BCStyle.OU, null);
put(BCStyle.CN, null);
}
public String put(ASN1ObjectIdentifier oid, String value) {
if (value != null && value.equals("")) value = null;
if (containsKey(oid)) super.put(oid, value); // preserve original ordering
else {
super.put(oid, value);
// String cn = remove(BCStyle.CN); // CN will always be last.
// put(BCStyle.CN,cn);
}
return value;
}
public void setCountry(String country) {
put(BCStyle.C, country);
}
public void setState(String state) {
put(BCStyle.ST, state);
}
public void setLocality(String locality) {
put(BCStyle.L, locality);
}
public void setStreet(String street) {
put(BCStyle.STREET, street);
}
public void setOrganization(String organization) {
put(BCStyle.O, organization);
}
public void setOrganizationalUnit(String organizationalUnit) {
put(BCStyle.OU, organizationalUnit);
}
public void setCommonName(String commonName) {
put(BCStyle.CN, commonName);
}
@Override
public int size() {
int result = 0;
for (String value : values()) {
if (value != null) result += 1;
}
return result;
}
public X509Principal getPrincipal() {
Vector<ASN1ObjectIdentifier> oids = new Vector<ASN1ObjectIdentifier>();
Vector<String> values = new Vector<String>();
for (Map.Entry<ASN1ObjectIdentifier, String> entry : entrySet()) {
if (entry.getValue() != null && !entry.getValue().equals("")) {
oids.add(entry.getKey());
values.add(entry.getValue());
}
}
return new X509Principal(oids, values);
}
}

View File

@ -1,9 +1,10 @@
package kellinwood.security.zipsigner.optional; package kellinwood.security.zipsigner.optional;
import kellinwood.logging.LoggerInterface; import kellinwood.logging.LoggerInterface;
import kellinwood.logging.LoggerManager; import kellinwood.logging.LoggerManager;
import kellinwood.security.zipsigner.Base64; import kellinwood.security.zipsigner.Base64;
import org.spongycastle.util.encoders.HexTranslator; import org.bouncycastle.util.encoders.HexTranslator;
import java.security.MessageDigest; import java.security.MessageDigest;

View File

@ -23,15 +23,17 @@ power to enforce restrictions on reverse-engineering of their software,
and it is irresponsible for them to claim they can. */ and it is irresponsible for them to claim they can. */
package kellinwood.security.zipsigner.optional; package kellinwood.security.zipsigner.optional;
import javax.crypto.EncryptedPrivateKeyInfo;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.security.DigestInputStream; import java.security.DigestInputStream;
import java.security.DigestOutputStream; import java.security.DigestOutputStream;
import java.security.Key; import java.security.Key;
@ -42,29 +44,24 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.security.UnrecoverableKeyException; import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Date; import java.util.Date;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.Vector; import java.util.Vector;
import javax.crypto.EncryptedPrivateKeyInfo;
import javax.crypto.spec.SecretKeySpec;
/** /**
* This is an implementation of Sun's proprietary key store * This is an implementation of Sun's proprietary key store
* algorithm, called "JKS" for "Java Key Store". This implementation was * algorithm, called "JKS" for "Java Key Store". This implementation was
* created entirely through reverse-engineering. * created entirely through reverse-engineering.
* * <p>
* <p>The format of JKS files is, from the start of the file: * <p>The format of JKS files is, from the start of the file:
* * <p>
* <ol> * <ol>
* <li>Magic bytes. This is a four-byte integer, in big-endian byte * <li>Magic bytes. This is a four-byte integer, in big-endian byte
* order, equal to <code>0xFEEDFEED</code>.</li> * order, equal to <code>0xFEEDFEED</code>.</li>
@ -82,7 +79,7 @@ import javax.crypto.spec.SecretKeySpec;
* href="http://java.sun.com/j2se/1.4.1/docs/api/java/io/DataOutput.html#writeUTF(java.lang.String)">DataOutput.writeUTF(String)</a>.</li> * href="http://java.sun.com/j2se/1.4.1/docs/api/java/io/DataOutput.html#writeUTF(java.lang.String)">DataOutput.writeUTF(String)</a>.</li>
* <li>An eight-byte integer, representing the entry's creation date, * <li>An eight-byte integer, representing the entry's creation date,
* in milliseconds since the epoch. * in milliseconds since the epoch.
* * <p>
* <p>Then, if the entry is a private key entry: * <p>Then, if the entry is a private key entry:
* <ol> * <ol>
* <li>The size of the encoded key as a four-byte int, then that * <li>The size of the encoded key as a four-byte int, then that
@ -94,7 +91,7 @@ import javax.crypto.spec.SecretKeySpec;
* certificates, encoded as described in the trusted certificates * certificates, encoded as described in the trusted certificates
* section.</li> * section.</li>
* </ol> * </ol>
* * <p>
* <p>Otherwise, the entry is a trusted certificate, which is encoded * <p>Otherwise, the entry is a trusted certificate, which is encoded
* as the name of the encoding algorithm (e.g. X.509), encoded the same * as the name of the encoding algorithm (e.g. X.509), encoded the same
* way as alias names. Then, a four-byte integer representing the size * way as alias names. Then, a four-byte integer representing the size
@ -108,12 +105,12 @@ import javax.crypto.spec.SecretKeySpec;
* </ol> * </ol>
* </li> * </li>
* </ol> * </ol>
* * <p>
* <p>(See <a href="genkey.java">this file</a> for some idea of how I * <p>(See <a href="genkey.java">this file</a> for some idea of how I
* was able to figure out these algorithms)</p> * was able to figure out these algorithms)</p>
* * <p>
* <p>Decrypting the key works as follows: * <p>Decrypting the key works as follows:
* * <p>
* <ol> * <ol>
* <li>The key length is the length of the ciphertext minus 40. The * <li>The key length is the length of the ciphertext minus 40. The
* encrypted key, <code>ekey</code>, is the middle bytes of the * encrypted key, <code>ekey</code>, is the middle bytes of the
@ -129,33 +126,34 @@ import javax.crypto.spec.SecretKeySpec;
* the last 20 bytes of the ciphertext, output <code>FAIL</code>. Otherwise, * the last 20 bytes of the ciphertext, output <code>FAIL</code>. Otherwise,
* output <code>key</code>.</li> * output <code>key</code>.</li>
* </ol> * </ol>
* * <p>
* <p>The signature is defined as <code>SHA-1(UTF-16BE(password) + * <p>The signature is defined as <code>SHA-1(UTF-16BE(password) +
* US_ASCII("Mighty Aphrodite") + encoded_keystore)</code> (yup, Sun * US_ASCII("Mighty Aphrodite") + encoded_keystore)</code> (yup, Sun
* engineers are just that clever). * engineers are just that clever).
* * <p>
* <p>(Above, SHA-1 denotes the secure hash algorithm, UTF-16BE the * <p>(Above, SHA-1 denotes the secure hash algorithm, UTF-16BE the
* big-endian byte representation of a UTF-16 string, and US_ASCII the * big-endian byte representation of a UTF-16 string, and US_ASCII the
* ASCII byte representation of the string.) * ASCII byte representation of the string.)
* * <p>
* <p>The source code of this class should be available in the file <a * <p>The source code of this class should be available in the file <a
* href="http://metastatic.org/source/JKS.java">JKS.java</a>. * href="http://metastatic.org/source/JKS.java">JKS.java</a>.
* *
* @author Casey Marshall (rsdio@metastatic.org) * @author Casey Marshall (rsdio@metastatic.org)
* * <p>
* Changes by Ken Ellinwood: * Changes by Ken Ellinwood:
* ** Fixed a NullPointerException in engineLoad(). This method must return gracefully if the keystore input stream is null. * ** Fixed a NullPointerException in engineLoad(). This method must return gracefully if the keystore input stream is null.
* ** engineGetCertificateEntry() was updated to return the first cert in the chain for private key entries. * ** engineGetCertificateEntry() was updated to return the first cert in the chain for private key entries.
* ** Lowercase the alias names, otherwise keytool chokes on the file created by this code. * ** Lowercase the alias names, otherwise keytool chokes on the file created by this code.
* ** Fixed the integrity check in engineLoad(), previously the exception was never thrown regardless of password value. * ** Fixed the integrity check in engineLoad(), previously the exception was never thrown regardless of password value.
*/ */
public class JKS extends KeyStoreSpi public class JKS extends KeyStoreSpi {
{
// Constants and fields. // Constants and fields.
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Ah, Sun. So goddamned clever with those magic bytes. */ /**
* Ah, Sun. So goddamned clever with those magic bytes.
*/
private static final int MAGIC = 0xFEEDFEED; private static final int MAGIC = 0xFEEDFEED;
private static final int PRIVATE_KEY = 1; private static final int PRIVATE_KEY = 1;
@ -170,8 +168,7 @@ public class JKS extends KeyStoreSpi
// Constructor. // Constructor.
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
public JKS() public JKS() {
{
super(); super();
aliases = new Vector(); aliases = new Vector();
trustedCerts = new HashMap(); trustedCerts = new HashMap();
@ -185,8 +182,7 @@ public class JKS extends KeyStoreSpi
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
public Key engineGetKey(String alias, char[] password) public Key engineGetKey(String alias, char[] password)
throws NoSuchAlgorithmException, UnrecoverableKeyException throws NoSuchAlgorithmException, UnrecoverableKeyException {
{
alias = alias.toLowerCase(); alias = alias.toLowerCase();
if (!privateKeys.containsKey(alias)) if (!privateKeys.containsKey(alias))
@ -194,32 +190,25 @@ public class JKS extends KeyStoreSpi
byte[] key = decryptKey((byte[]) privateKeys.get(alias), byte[] key = decryptKey((byte[]) privateKeys.get(alias),
charsToBytes(password)); charsToBytes(password));
Certificate[] chain = engineGetCertificateChain(alias); Certificate[] chain = engineGetCertificateChain(alias);
if (chain.length > 0) if (chain.length > 0) {
{ try {
try
{
// Private and public keys MUST have the same algorithm. // Private and public keys MUST have the same algorithm.
KeyFactory fact = KeyFactory.getInstance( KeyFactory fact = KeyFactory.getInstance(
chain[0].getPublicKey().getAlgorithm()); chain[0].getPublicKey().getAlgorithm());
return fact.generatePrivate(new PKCS8EncodedKeySpec(key)); return fact.generatePrivate(new PKCS8EncodedKeySpec(key));
} } catch (InvalidKeySpecException x) {
catch (InvalidKeySpecException x)
{
throw new UnrecoverableKeyException(x.getMessage()); throw new UnrecoverableKeyException(x.getMessage());
} }
} } else
else
return new SecretKeySpec(key, alias); return new SecretKeySpec(key, alias);
} }
public Certificate[] engineGetCertificateChain(String alias) public Certificate[] engineGetCertificateChain(String alias) {
{
alias = alias.toLowerCase(); alias = alias.toLowerCase();
return (Certificate[]) certChains.get(alias); return (Certificate[]) certChains.get(alias);
} }
public Certificate engineGetCertificate(String alias) public Certificate engineGetCertificate(String alias) {
{
alias = alias.toLowerCase(); alias = alias.toLowerCase();
if (engineIsKeyEntry(alias)) { if (engineIsKeyEntry(alias)) {
Certificate[] certChain = (Certificate[]) certChains.get(alias); Certificate[] certChain = (Certificate[]) certChains.get(alias);
@ -228,8 +217,7 @@ public class JKS extends KeyStoreSpi
return (Certificate) trustedCerts.get(alias); return (Certificate) trustedCerts.get(alias);
} }
public Date engineGetCreationDate(String alias) public Date engineGetCreationDate(String alias) {
{
alias = alias.toLowerCase(); alias = alias.toLowerCase();
return (Date) dates.get(alias); return (Date) dates.get(alias);
} }
@ -237,8 +225,7 @@ public class JKS extends KeyStoreSpi
// XXX implement writing methods. // XXX implement writing methods.
public void engineSetKeyEntry(String alias, Key key, char[] passwd, Certificate[] certChain) public void engineSetKeyEntry(String alias, Key key, char[] passwd, Certificate[] certChain)
throws KeyStoreException throws KeyStoreException {
{
alias = alias.toLowerCase(); alias = alias.toLowerCase();
if (trustedCerts.containsKey(alias)) if (trustedCerts.containsKey(alias))
throw new KeyStoreException("\"" + alias + " is a trusted certificate entry"); throw new KeyStoreException("\"" + alias + " is a trusted certificate entry");
@ -247,25 +234,20 @@ public class JKS extends KeyStoreSpi
certChains.put(alias, certChain); certChains.put(alias, certChain);
else else
certChains.put(alias, new Certificate[0]); certChains.put(alias, new Certificate[0]);
if (!aliases.contains(alias)) if (!aliases.contains(alias)) {
{
dates.put(alias, new Date()); dates.put(alias, new Date());
aliases.add(alias); aliases.add(alias);
} }
} }
public void engineSetKeyEntry(String alias, byte[] encodedKey, Certificate[] certChain) public void engineSetKeyEntry(String alias, byte[] encodedKey, Certificate[] certChain)
throws KeyStoreException throws KeyStoreException {
{
alias = alias.toLowerCase(); alias = alias.toLowerCase();
if (trustedCerts.containsKey(alias)) if (trustedCerts.containsKey(alias))
throw new KeyStoreException("\"" + alias + "\" is a trusted certificate entry"); throw new KeyStoreException("\"" + alias + "\" is a trusted certificate entry");
try try {
{
new EncryptedPrivateKeyInfo(encodedKey); new EncryptedPrivateKeyInfo(encodedKey);
} } catch (IOException ioe) {
catch (IOException ioe)
{
throw new KeyStoreException("encoded key is not an EncryptedPrivateKeyInfo"); throw new KeyStoreException("encoded key is not an EncryptedPrivateKeyInfo");
} }
privateKeys.put(alias, encodedKey); privateKeys.put(alias, encodedKey);
@ -273,67 +255,56 @@ public class JKS extends KeyStoreSpi
certChains.put(alias, certChain); certChains.put(alias, certChain);
else else
certChains.put(alias, new Certificate[0]); certChains.put(alias, new Certificate[0]);
if (!aliases.contains(alias)) if (!aliases.contains(alias)) {
{
dates.put(alias, new Date()); dates.put(alias, new Date());
aliases.add(alias); aliases.add(alias);
} }
} }
public void engineSetCertificateEntry(String alias, Certificate cert) public void engineSetCertificateEntry(String alias, Certificate cert)
throws KeyStoreException throws KeyStoreException {
{
alias = alias.toLowerCase(); alias = alias.toLowerCase();
if (privateKeys.containsKey(alias)) if (privateKeys.containsKey(alias))
throw new KeyStoreException("\"" + alias + "\" is a private key entry"); throw new KeyStoreException("\"" + alias + "\" is a private key entry");
if (cert == null) if (cert == null)
throw new NullPointerException(); throw new NullPointerException();
trustedCerts.put(alias, cert); trustedCerts.put(alias, cert);
if (!aliases.contains(alias)) if (!aliases.contains(alias)) {
{
dates.put(alias, new Date()); dates.put(alias, new Date());
aliases.add(alias); aliases.add(alias);
} }
} }
public void engineDeleteEntry(String alias) throws KeyStoreException public void engineDeleteEntry(String alias) throws KeyStoreException {
{
alias = alias.toLowerCase(); alias = alias.toLowerCase();
aliases.remove(alias); aliases.remove(alias);
} }
public Enumeration engineAliases() public Enumeration engineAliases() {
{
return aliases.elements(); return aliases.elements();
} }
public boolean engineContainsAlias(String alias) public boolean engineContainsAlias(String alias) {
{
alias = alias.toLowerCase(); alias = alias.toLowerCase();
return aliases.contains(alias); return aliases.contains(alias);
} }
public int engineSize() public int engineSize() {
{
return aliases.size(); return aliases.size();
} }
public boolean engineIsKeyEntry(String alias) public boolean engineIsKeyEntry(String alias) {
{
alias = alias.toLowerCase(); alias = alias.toLowerCase();
return privateKeys.containsKey(alias); return privateKeys.containsKey(alias);
} }
public boolean engineIsCertificateEntry(String alias) public boolean engineIsCertificateEntry(String alias) {
{
alias = alias.toLowerCase(); alias = alias.toLowerCase();
return trustedCerts.containsKey(alias); return trustedCerts.containsKey(alias);
} }
public String engineGetCertificateAlias(Certificate cert) public String engineGetCertificateAlias(Certificate cert) {
{ for (Iterator keys = trustedCerts.keySet().iterator(); keys.hasNext(); ) {
for (Iterator keys = trustedCerts.keySet().iterator(); keys.hasNext(); )
{
String alias = (String) keys.next(); String alias = (String) keys.next();
if (cert.equals(trustedCerts.get(alias))) if (cert.equals(trustedCerts.get(alias)))
return alias; return alias;
@ -342,8 +313,7 @@ public class JKS extends KeyStoreSpi
} }
public void engineStore(OutputStream out, char[] passwd) public void engineStore(OutputStream out, char[] passwd)
throws IOException, NoSuchAlgorithmException, CertificateException throws IOException, NoSuchAlgorithmException, CertificateException {
{
MessageDigest md = MessageDigest.getInstance("SHA1"); MessageDigest md = MessageDigest.getInstance("SHA1");
md.update(charsToBytes(passwd)); md.update(charsToBytes(passwd));
md.update("Mighty Aphrodite".getBytes("UTF-8")); md.update("Mighty Aphrodite".getBytes("UTF-8"));
@ -351,18 +321,14 @@ public class JKS extends KeyStoreSpi
dout.writeInt(MAGIC); dout.writeInt(MAGIC);
dout.writeInt(2); dout.writeInt(2);
dout.writeInt(aliases.size()); dout.writeInt(aliases.size());
for (Enumeration e = aliases.elements(); e.hasMoreElements(); ) for (Enumeration e = aliases.elements(); e.hasMoreElements(); ) {
{
String alias = (String) e.nextElement(); String alias = (String) e.nextElement();
if (trustedCerts.containsKey(alias)) if (trustedCerts.containsKey(alias)) {
{
dout.writeInt(TRUSTED_CERT); dout.writeInt(TRUSTED_CERT);
dout.writeUTF(alias); dout.writeUTF(alias);
dout.writeLong(((Date) dates.get(alias)).getTime()); dout.writeLong(((Date) dates.get(alias)).getTime());
writeCert(dout, (Certificate) trustedCerts.get(alias)); writeCert(dout, (Certificate) trustedCerts.get(alias));
} } else {
else
{
dout.writeInt(PRIVATE_KEY); dout.writeInt(PRIVATE_KEY);
dout.writeUTF(alias); dout.writeUTF(alias);
dout.writeLong(((Date) dates.get(alias)).getTime()); dout.writeLong(((Date) dates.get(alias)).getTime());
@ -380,8 +346,7 @@ public class JKS extends KeyStoreSpi
} }
public void engineLoad(InputStream in, char[] passwd) public void engineLoad(InputStream in, char[] passwd)
throws IOException, NoSuchAlgorithmException, CertificateException throws IOException, NoSuchAlgorithmException, CertificateException {
{
MessageDigest md = MessageDigest.getInstance("SHA"); MessageDigest md = MessageDigest.getInstance("SHA");
if (passwd != null) md.update(charsToBytes(passwd)); if (passwd != null) md.update(charsToBytes(passwd));
md.update("Mighty Aphrodite".getBytes("UTF-8")); // HAR HAR md.update("Mighty Aphrodite".getBytes("UTF-8")); // HAR HAR
@ -399,14 +364,12 @@ public class JKS extends KeyStoreSpi
aliases.ensureCapacity(n); aliases.ensureCapacity(n);
if (n < 0) if (n < 0)
throw new LoadKeystoreException("Malformed key store"); throw new LoadKeystoreException("Malformed key store");
for (int i = 0; i < n; i++) for (int i = 0; i < n; i++) {
{
int type = din.readInt(); int type = din.readInt();
String alias = din.readUTF(); String alias = din.readUTF();
aliases.add(alias); aliases.add(alias);
dates.put(alias, new Date(din.readLong())); dates.put(alias, new Date(din.readLong()));
switch (type) switch (type) {
{
case PRIVATE_KEY: case PRIVATE_KEY:
int len = din.readInt(); int len = din.readInt();
byte[] encoded = new byte[len]; byte[] encoded = new byte[len];
@ -442,8 +405,7 @@ public class JKS extends KeyStoreSpi
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
private static Certificate readCert(DataInputStream in) private static Certificate readCert(DataInputStream in)
throws IOException, CertificateException, NoSuchAlgorithmException throws IOException, CertificateException, NoSuchAlgorithmException {
{
String type = in.readUTF(); String type = in.readUTF();
int len = in.readInt(); int len = in.readInt();
byte[] encoded = new byte[len]; byte[] encoded = new byte[len];
@ -453,8 +415,7 @@ public class JKS extends KeyStoreSpi
} }
private static void writeCert(DataOutputStream dout, Certificate cert) private static void writeCert(DataOutputStream dout, Certificate cert)
throws IOException, CertificateException throws IOException, CertificateException {
{
dout.writeUTF(cert.getType()); dout.writeUTF(cert.getType());
byte[] b = cert.getEncoded(); byte[] b = cert.getEncoded();
dout.writeInt(b.length); dout.writeInt(b.length);
@ -462,10 +423,8 @@ public class JKS extends KeyStoreSpi
} }
private static byte[] decryptKey(byte[] encryptedPKI, byte[] passwd) private static byte[] decryptKey(byte[] encryptedPKI, byte[] passwd)
throws UnrecoverableKeyException throws UnrecoverableKeyException {
{ try {
try
{
EncryptedPrivateKeyInfo epki = EncryptedPrivateKeyInfo epki =
new EncryptedPrivateKeyInfo(encryptedPKI); new EncryptedPrivateKeyInfo(encryptedPKI);
byte[] encr = epki.getEncryptedData(); byte[] encr = epki.getEncryptedData();
@ -476,14 +435,12 @@ public class JKS extends KeyStoreSpi
byte[] key = new byte[encr.length - 40]; byte[] key = new byte[encr.length - 40];
MessageDigest sha = MessageDigest.getInstance("SHA1"); MessageDigest sha = MessageDigest.getInstance("SHA1");
int count = 0; int count = 0;
while (count < key.length) while (count < key.length) {
{
sha.reset(); sha.reset();
sha.update(passwd); sha.update(passwd);
sha.update(keystream); sha.update(keystream);
sha.digest(keystream, 0, keystream.length); sha.digest(keystream, 0, keystream.length);
for (int i = 0; i < keystream.length && count < key.length; i++) for (int i = 0; i < keystream.length && count < key.length; i++) {
{
key[count] = (byte) (keystream[i] ^ encr[count + 20]); key[count] = (byte) (keystream[i] ^ encr[count + 20]);
count++; count++;
} }
@ -494,18 +451,14 @@ public class JKS extends KeyStoreSpi
if (!MessageDigest.isEqual(check, sha.digest())) if (!MessageDigest.isEqual(check, sha.digest()))
throw new UnrecoverableKeyException("checksum mismatch"); throw new UnrecoverableKeyException("checksum mismatch");
return key; return key;
} } catch (Exception x) {
catch (Exception x)
{
throw new UnrecoverableKeyException(x.getMessage()); throw new UnrecoverableKeyException(x.getMessage());
} }
} }
private static byte[] encryptKey(Key key, byte[] passwd) private static byte[] encryptKey(Key key, byte[] passwd)
throws KeyStoreException throws KeyStoreException {
{ try {
try
{
MessageDigest sha = MessageDigest.getInstance("SHA1"); MessageDigest sha = MessageDigest.getInstance("SHA1");
SecureRandom rand = SecureRandom.getInstance("SHA1PRNG"); SecureRandom rand = SecureRandom.getInstance("SHA1PRNG");
byte[] k = key.getEncoded(); byte[] k = key.getEncoded();
@ -513,14 +466,12 @@ public class JKS extends KeyStoreSpi
byte[] keystream = rand.getSeed(20); byte[] keystream = rand.getSeed(20);
System.arraycopy(keystream, 0, encrypted, 0, 20); System.arraycopy(keystream, 0, encrypted, 0, 20);
int count = 0; int count = 0;
while (count < k.length) while (count < k.length) {
{
sha.reset(); sha.reset();
sha.update(passwd); sha.update(passwd);
sha.update(keystream); sha.update(keystream);
sha.digest(keystream, 0, keystream.length); sha.digest(keystream, 0, keystream.length);
for (int i = 0; i < keystream.length && count < k.length; i++) for (int i = 0; i < keystream.length && count < k.length; i++) {
{
encrypted[count + 20] = (byte) (keystream[i] ^ k[count]); encrypted[count + 20] = (byte) (keystream[i] ^ k[count]);
count++; count++;
} }
@ -533,18 +484,14 @@ public class JKS extends KeyStoreSpi
// encryption algorithm. // encryption algorithm.
return new EncryptedPrivateKeyInfo("1.3.6.1.4.1.42.2.17.1.1", return new EncryptedPrivateKeyInfo("1.3.6.1.4.1.42.2.17.1.1",
encrypted).getEncoded(); encrypted).getEncoded();
} } catch (Exception x) {
catch (Exception x)
{
throw new KeyStoreException(x.getMessage()); throw new KeyStoreException(x.getMessage());
} }
} }
private static byte[] charsToBytes(char[] passwd) private static byte[] charsToBytes(char[] passwd) {
{
byte[] buf = new byte[passwd.length * 2]; byte[] buf = new byte[passwd.length * 2];
for (int i = 0, j = 0; i < passwd.length; i++) for (int i = 0, j = 0; i < passwd.length; i++) {
{
buf[j++] = (byte) (passwd[i] >>> 8); buf[j++] = (byte) (passwd[i] >>> 8);
buf[j++] = (byte) passwd[i]; buf[j++] = (byte) passwd[i];
} }

View File

@ -1,3 +1,4 @@
package kellinwood.security.zipsigner.optional; package kellinwood.security.zipsigner.optional;

View File

@ -1,3 +1,4 @@
package kellinwood.security.zipsigner.optional; package kellinwood.security.zipsigner.optional;
public class KeyNameConflictException extends Exception { public class KeyNameConflictException extends Exception {

View File

@ -1,23 +1,33 @@
package kellinwood.security.zipsigner.optional; package kellinwood.security.zipsigner.optional;
import kellinwood.logging.LoggerInterface; import kellinwood.logging.LoggerInterface;
import kellinwood.logging.LoggerManager; import kellinwood.logging.LoggerManager;
import org.spongycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.io.*; import java.io.File;
import java.security.*; import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.security.Key;
import java.security.KeyStore;
import java.security.Provider;
import java.security.Security;
import java.security.cert.Certificate; import java.security.cert.Certificate;
/** /**
*/ */
public class KeyStoreFileManager { public class KeyStoreFileManager {
static Provider provider = new BouncyCastleProvider(); static Provider provider = new BouncyCastleProvider();
public static Provider getProvider() { return provider; } public static Provider getProvider() {
return provider;
}
public static void setProvider(Provider provider) { public static void setProvider(Provider provider) {
if (KeyStoreFileManager.provider != null) Security.removeProvider(KeyStoreFileManager.provider.getName()); if (KeyStoreFileManager.provider != null) Security.removeProvider(KeyStoreFileManager.provider.getName());
@ -28,8 +38,8 @@ public class KeyStoreFileManager {
static LoggerInterface logger = LoggerManager.getLogger(KeyStoreFileManager.class.getName()); static LoggerInterface logger = LoggerManager.getLogger(KeyStoreFileManager.class.getName());
static { static {
// Add the spongycastle version of the BC provider so that the implementation classes returned // Add the bouncycastle version of the BC provider so that the implementation classes returned
// from the keystore are all from the spongycastle libs. // from the keystore are all from the bouncycastle libs.
Security.addProvider(getProvider()); Security.addProvider(getProvider());
} }
@ -48,21 +58,18 @@ public class KeyStoreFileManager {
} }
public static KeyStore createKeyStore(String keystorePath, char[] password) public static KeyStore createKeyStore(String keystorePath, char[] password)
throws Exception throws Exception {
{
KeyStore ks = null; KeyStore ks = null;
if (keystorePath.toLowerCase().endsWith(".bks")) { if (keystorePath.toLowerCase().endsWith(".bks")) {
ks = KeyStore.getInstance("bks", new BouncyCastleProvider()); ks = KeyStore.getInstance("bks", new BouncyCastleProvider());
} } else ks = new JksKeyStore();
else ks = new JksKeyStore();
ks.load(null, password); ks.load(null, password);
return ks; return ks;
} }
public static KeyStore loadKeyStore(String keystorePath, char[] password) public static KeyStore loadKeyStore(String keystorePath, char[] password)
throws Exception throws Exception {
{
KeyStore ks = null; KeyStore ks = null;
try { try {
ks = new JksKeyStore(); ks = new JksKeyStore();
@ -89,8 +96,7 @@ public class KeyStoreFileManager {
} }
public static void writeKeyStore(KeyStore ks, String keystorePath, String encodedPassword) public static void writeKeyStore(KeyStore ks, String keystorePath, String encodedPassword)
throws Exception throws Exception {
{
char password[] = null; char password[] = null;
try { try {
password = PasswordObfuscator.getInstance().decodeKeystorePassword(keystorePath, encodedPassword); password = PasswordObfuscator.getInstance().decodeKeystorePassword(keystorePath, encodedPassword);
@ -101,8 +107,7 @@ public class KeyStoreFileManager {
} }
public static void writeKeyStore(KeyStore ks, String keystorePath, char[] password) public static void writeKeyStore(KeyStore ks, String keystorePath, char[] password)
throws Exception throws Exception {
{
File keystoreFile = new File(keystorePath); File keystoreFile = new File(keystorePath);
try { try {
@ -136,7 +141,8 @@ public class KeyStoreFileManager {
x.printStackTrace(pw); x.printStackTrace(pw);
pw.flush(); pw.flush();
pw.close(); pw.close();
} catch (Exception y) {} } catch (Exception y) {
}
throw x; throw x;
} }
} }
@ -159,10 +165,16 @@ public class KeyStoreFileManager {
count += n; count += n;
} }
} finally { } finally {
try { output.close(); } catch (IOException x) {} // Ignore try {
output.close();
} catch (IOException x) {
} // Ignore
} }
} finally { } finally {
try { input.close(); } catch (IOException x) {} try {
input.close();
} catch (IOException x) {
}
} }
if (srcFile.length() != destFile.length()) { if (srcFile.length() != destFile.length()) {
@ -176,23 +188,20 @@ public class KeyStoreFileManager {
public static void renameTo(File fromFile, File toFile) public static void renameTo(File fromFile, File toFile)
throws IOException throws IOException {
{
copyFile(fromFile, toFile, true); copyFile(fromFile, toFile, true);
if (!fromFile.delete()) throw new IOException("Failed to delete " + fromFile); if (!fromFile.delete()) throw new IOException("Failed to delete " + fromFile);
} }
public static void deleteKey(String storePath, String storePass, String keyName) public static void deleteKey(String storePath, String storePass, String keyName)
throws Exception throws Exception {
{
KeyStore ks = loadKeyStore(storePath, storePass); KeyStore ks = loadKeyStore(storePath, storePass);
ks.deleteEntry(keyName); ks.deleteEntry(keyName);
writeKeyStore(ks, storePath, storePass); writeKeyStore(ks, storePath, storePass);
} }
public static String renameKey(String keystorePath, String storePass, String oldKeyName, String newKeyName, String keyPass) public static String renameKey(String keystorePath, String storePass, String oldKeyName, String newKeyName, String keyPass)
throws Exception throws Exception {
{
char[] keyPw = null; char[] keyPw = null;
try { try {
@ -210,15 +219,13 @@ public class KeyStoreFileManager {
writeKeyStore(ks, keystorePath, storePass); writeKeyStore(ks, keystorePath, storePass);
return newKeyName; return newKeyName;
} } finally {
finally {
PasswordObfuscator.flush(keyPw); PasswordObfuscator.flush(keyPw);
} }
} }
public static KeyStore.Entry getKeyEntry(String keystorePath, String storePass, String keyName, String keyPass) public static KeyStore.Entry getKeyEntry(String keystorePath, String storePass, String keyName, String keyPass)
throws Exception throws Exception {
{
char[] keyPw = null; char[] keyPw = null;
KeyStore.PasswordProtection passwordProtection = null; KeyStore.PasswordProtection passwordProtection = null;
@ -227,30 +234,26 @@ public class KeyStoreFileManager {
keyPw = PasswordObfuscator.getInstance().decodeAliasPassword(keystorePath, keyName, keyPass); keyPw = PasswordObfuscator.getInstance().decodeAliasPassword(keystorePath, keyName, keyPass);
passwordProtection = new KeyStore.PasswordProtection(keyPw); passwordProtection = new KeyStore.PasswordProtection(keyPw);
return ks.getEntry(keyName, passwordProtection); return ks.getEntry(keyName, passwordProtection);
} } finally {
finally {
if (keyPw != null) PasswordObfuscator.flush(keyPw); if (keyPw != null) PasswordObfuscator.flush(keyPw);
if (passwordProtection != null) passwordProtection.destroy(); if (passwordProtection != null) passwordProtection.destroy();
} }
} }
public static boolean containsKey(String keystorePath, String storePass, String keyName) public static boolean containsKey(String keystorePath, String storePass, String keyName)
throws Exception throws Exception {
{
KeyStore ks = loadKeyStore(keystorePath, storePass); KeyStore ks = loadKeyStore(keystorePath, storePass);
return ks.containsAlias(keyName); return ks.containsAlias(keyName);
} }
/** /**
*
* @param keystorePath * @param keystorePath
* @param encodedPassword * @param encodedPassword
* @throws Exception if the password is invalid * @throws Exception if the password is invalid
*/ */
public static void validateKeystorePassword(String keystorePath, String encodedPassword) public static void validateKeystorePassword(String keystorePath, String encodedPassword)
throws Exception throws Exception {
{
char[] password = null; char[] password = null;
try { try {
KeyStore ks = KeyStoreFileManager.loadKeyStore(keystorePath, encodedPassword); KeyStore ks = KeyStoreFileManager.loadKeyStore(keystorePath, encodedPassword);
@ -261,15 +264,13 @@ public class KeyStoreFileManager {
} }
/** /**
*
* @param keystorePath * @param keystorePath
* @param keyName * @param keyName
* @param encodedPassword * @param encodedPassword
* @throws java.security.UnrecoverableKeyException if the password is invalid * @throws java.security.UnrecoverableKeyException if the password is invalid
*/ */
public static void validateKeyPassword(String keystorePath, String keyName, String encodedPassword) public static void validateKeyPassword(String keystorePath, String keyName, String encodedPassword)
throws Exception throws Exception {
{
char[] password = null; char[] password = null;
try { try {
KeyStore ks = KeyStoreFileManager.loadKeyStore(keystorePath, (char[]) null); KeyStore ks = KeyStoreFileManager.loadKeyStore(keystorePath, (char[]) null);

View File

@ -1,3 +1,4 @@
package kellinwood.security.zipsigner.optional; package kellinwood.security.zipsigner.optional;
import java.io.IOException; import java.io.IOException;

View File

@ -1,3 +1,4 @@
package kellinwood.security.zipsigner.optional; package kellinwood.security.zipsigner.optional;
import kellinwood.logging.LoggerInterface; import kellinwood.logging.LoggerInterface;
@ -6,7 +7,12 @@ import kellinwood.security.zipsigner.Base64;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import java.io.*; import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
public class PasswordObfuscator { public class PasswordObfuscator {

View File

@ -1,14 +1,19 @@
package kellinwood.security.zipsigner.optional; package kellinwood.security.zipsigner.optional;
import kellinwood.security.zipsigner.KeySet; import kellinwood.security.zipsigner.KeySet;
import org.spongycastle.cert.jcajce.JcaCertStore; import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.spongycastle.cms.*; import org.bouncycastle.cms.CMSProcessableByteArray;
import org.spongycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder; import org.bouncycastle.cms.CMSSignedData;
import org.spongycastle.operator.ContentSigner; import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.spongycastle.operator.DigestCalculatorProvider; import org.bouncycastle.cms.CMSTypedData;
import org.spongycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.cms.SignerInfoGenerator;
import org.spongycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.spongycastle.util.Store; import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DigestCalculatorProvider;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;

View File

@ -13,15 +13,15 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package kellinwood.zipio;
import java.io.IOException; package kellinwood.zipio;
import kellinwood.logging.LoggerInterface; import kellinwood.logging.LoggerInterface;
import kellinwood.logging.LoggerManager; import kellinwood.logging.LoggerManager;
public class CentralEnd import java.io.IOException;
{
public class CentralEnd {
public int signature = 0x06054b50; // end of central dir signature 4 bytes public int signature = 0x06054b50; // end of central dir signature 4 bytes
public short numberThisDisk = 0; // number of this disk 2 bytes public short numberThisDisk = 0; // number of this disk 2 bytes
public short centralStartDisk = 0; // number of the disk with the start of the central directory 2 bytes public short centralStartDisk = 0; // number of the disk with the start of the central directory 2 bytes
@ -34,8 +34,7 @@ public class CentralEnd
private static LoggerInterface log; private static LoggerInterface log;
public static CentralEnd read(ZipInput input) throws IOException public static CentralEnd read(ZipInput input) throws IOException {
{
int signature = input.readInt(); int signature = input.readInt();
if (signature != 0x06054b50) { if (signature != 0x06054b50) {
@ -56,8 +55,7 @@ public class CentralEnd
} }
private void doRead( ZipInput input) throws IOException private void doRead(ZipInput input) throws IOException {
{
boolean debug = getLogger().isDebugEnabled(); boolean debug = getLogger().isDebugEnabled();
@ -87,8 +85,7 @@ public class CentralEnd
} }
public void write( ZipOutput output) throws IOException public void write(ZipOutput output) throws IOException {
{
boolean debug = getLogger().isDebugEnabled(); boolean debug = getLogger().isDebugEnabled();

View File

@ -13,13 +13,17 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package kellinwood.zipio; package kellinwood.zipio;
import kellinwood.logging.LoggerInterface;
import kellinwood.logging.LoggerManager;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.File;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.SequenceInputStream; import java.io.SequenceInputStream;
import java.util.Date; import java.util.Date;
@ -27,9 +31,6 @@ import java.util.zip.CRC32;
import java.util.zip.Inflater; import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream; import java.util.zip.InflaterInputStream;
import kellinwood.logging.LoggerInterface;
import kellinwood.logging.LoggerManager;
public class ZioEntry implements Cloneable { public class ZioEntry implements Cloneable {
private ZipInput zipInput; private ZipInput zipInput;
@ -81,8 +82,7 @@ public class ZioEntry implements Cloneable {
public ZioEntry(String name, String sourceDataFile) public ZioEntry(String name, String sourceDataFile)
throws IOException throws IOException {
{
zipInput = new ZipInput(sourceDataFile); zipInput = new ZipInput(sourceDataFile);
filename = name; filename = name;
fileComment = ""; fileComment = "";
@ -116,10 +116,8 @@ public class ZioEntry implements Cloneable {
} }
public ZioEntry(String name, String sourceDataFile, short compression, int crc32, int compressedSize, int size) public ZioEntry(String name, String sourceDataFile, short compression, int crc32, int compressedSize, int size)
throws IOException throws IOException {
{
zipInput = new ZipInput(sourceDataFile); zipInput = new ZipInput(sourceDataFile);
filename = name; filename = name;
fileComment = ""; fileComment = "";
@ -133,23 +131,19 @@ public class ZioEntry implements Cloneable {
} }
// Return a copy with a new name // Return a copy with a new name
public ZioEntry getClonedEntry( String newName) public ZioEntry getClonedEntry(String newName) {
{
ZioEntry clone; ZioEntry clone;
try { try {
clone = (ZioEntry) this.clone(); clone = (ZioEntry) this.clone();
} } catch (CloneNotSupportedException e) {
catch (CloneNotSupportedException e)
{
throw new IllegalStateException("clone() failed!"); throw new IllegalStateException("clone() failed!");
} }
clone.setName(newName); clone.setName(newName);
return clone; return clone;
} }
public void readLocalHeader() throws IOException public void readLocalHeader() throws IOException {
{
ZipInput input = zipInput; ZipInput input = zipInput;
int tmp; int tmp;
boolean debug = getLogger().isDebugEnabled(); boolean debug = getLogger().isDebugEnabled();
@ -174,35 +168,43 @@ public class ZioEntry implements Cloneable {
short tmpShort; short tmpShort;
// 4 2 Version needed to extract (minimum) // 4 2 Version needed to extract (minimum)
/* versionRequired */ tmpShort = input.readShort(); /* versionRequired */
tmpShort = input.readShort();
if (debug) log.debug(String.format("Version required: 0x%04x", tmpShort /*versionRequired*/)); if (debug) log.debug(String.format("Version required: 0x%04x", tmpShort /*versionRequired*/));
// 6 2 General purpose bit flag // 6 2 General purpose bit flag
/* generalPurposeBits */ tmpShort = input.readShort(); /* generalPurposeBits */
tmpShort = input.readShort();
if (debug) log.debug(String.format("General purpose bits: 0x%04x", tmpShort /* generalPurposeBits */)); if (debug) log.debug(String.format("General purpose bits: 0x%04x", tmpShort /* generalPurposeBits */));
// 8 2 Compression method // 8 2 Compression method
/* compression */ tmpShort = input.readShort(); /* compression */
tmpShort = input.readShort();
if (debug) log.debug(String.format("Compression: 0x%04x", tmpShort /* compression */)); if (debug) log.debug(String.format("Compression: 0x%04x", tmpShort /* compression */));
// 10 2 File last modification time // 10 2 File last modification time
/* modificationTime */ tmpShort = input.readShort(); /* modificationTime */
tmpShort = input.readShort();
if (debug) log.debug(String.format("Modification time: 0x%04x", tmpShort /* modificationTime */)); if (debug) log.debug(String.format("Modification time: 0x%04x", tmpShort /* modificationTime */));
// 12 2 File last modification date // 12 2 File last modification date
/* modificationDate */ tmpShort = input.readShort(); /* modificationDate */
tmpShort = input.readShort();
if (debug) log.debug(String.format("Modification date: 0x%04x", tmpShort /* modificationDate */)); if (debug) log.debug(String.format("Modification date: 0x%04x", tmpShort /* modificationDate */));
// 14 4 CRC-32 // 14 4 CRC-32
/* crc32 */ tmpInt = input.readInt(); /* crc32 */
tmpInt = input.readInt();
if (debug) log.debug(String.format("CRC-32: 0x%04x", tmpInt /*crc32*/)); if (debug) log.debug(String.format("CRC-32: 0x%04x", tmpInt /*crc32*/));
// 18 4 Compressed size // 18 4 Compressed size
/* compressedSize*/ tmpInt = input.readInt(); /* compressedSize*/
tmpInt = input.readInt();
if (debug) log.debug(String.format("Compressed size: 0x%04x", tmpInt /*compressedSize*/)); if (debug) log.debug(String.format("Compressed size: 0x%04x", tmpInt /*compressedSize*/));
// 22 4 Uncompressed size // 22 4 Uncompressed size
/* size */ tmpInt = input.readInt(); /* size */
tmpInt = input.readInt();
if (debug) log.debug(String.format("Size: 0x%04x", tmpInt /*size*/)); if (debug) log.debug(String.format("Size: 0x%04x", tmpInt /*size*/));
// 26 2 File name length (n) // 26 2 File name length (n)
@ -226,8 +228,7 @@ public class ZioEntry implements Cloneable {
} }
public void writeLocalEntry( ZipOutput output) throws IOException public void writeLocalEntry(ZipOutput output) throws IOException {
{
if (data == null && dataPosition < 0 && zipInput != null) { if (data == null && dataPosition < 0 && zipInput != null) {
readLocalHeader(); readLocalHeader();
} }
@ -295,8 +296,7 @@ public class ZioEntry implements Cloneable {
if (data != null) { if (data != null) {
output.writeBytes(data); output.writeBytes(data);
if (debug) getLogger().debug(String.format("Wrote %d bytes", data.length)); if (debug) getLogger().debug(String.format("Wrote %d bytes", data.length));
} } else {
else {
if (debug) getLogger().debug(String.format("Seeking to position 0x%08x", dataPosition)); if (debug) getLogger().debug(String.format("Seeking to position 0x%08x", dataPosition));
zipInput.seek(dataPosition); zipInput.seek(dataPosition);
@ -311,14 +311,13 @@ public class ZioEntry implements Cloneable {
output.writeBytes(buffer, 0, numRead); output.writeBytes(buffer, 0, numRead);
if (debug) getLogger().debug(String.format("Wrote %d bytes", numRead)); if (debug) getLogger().debug(String.format("Wrote %d bytes", numRead));
totalCount += numRead; totalCount += numRead;
} } else
else throw new IllegalStateException(String.format("EOF reached while copying %s with %d bytes left to go", filename, compressedSize - totalCount)); throw new IllegalStateException(String.format("EOF reached while copying %s with %d bytes left to go", filename, compressedSize - totalCount));
} }
} }
} }
public static ZioEntry read(ZipInput input) throws IOException public static ZioEntry read(ZipInput input) throws IOException {
{
// 0 4 Central directory header signature = 0x02014b50 // 0 4 Central directory header signature = 0x02014b50
int signature = input.readInt(); int signature = input.readInt();
@ -334,8 +333,7 @@ public class ZioEntry implements Cloneable {
return entry; return entry;
} }
private void doRead( ZipInput input) throws IOException private void doRead(ZipInput input) throws IOException {
{
boolean debug = getLogger().isDebugEnabled(); boolean debug = getLogger().isDebugEnabled();
@ -422,9 +420,10 @@ public class ZioEntry implements Cloneable {
} }
/** Returns the entry's data. */ /**
public byte[] getData() throws IOException * Returns the entry's data.
{ */
public byte[] getData() throws IOException {
if (data != null) return data; if (data != null) return data;
byte[] tmpdata = new byte[size]; byte[] tmpdata = new byte[size];
@ -434,7 +433,8 @@ public class ZioEntry implements Cloneable {
while (count != size) { while (count != size) {
int numRead = din.read(tmpdata, count, size - count); int numRead = din.read(tmpdata, count, size - count);
if (numRead < 0) throw new IllegalStateException(String.format("Read failed, expecting %d bytes, got %d instead", size, count)); if (numRead < 0)
throw new IllegalStateException(String.format("Read failed, expecting %d bytes, got %d instead", size, count));
count += numRead; count += numRead;
} }
return tmpdata; return tmpdata;
@ -473,13 +473,11 @@ public class ZioEntry implements Cloneable {
// in order to support certain optimizations. // in order to support certain optimizations.
dataStream.setReturnDummyByte(true); dataStream.setReturnDummyByte(true);
return new InflaterInputStream(dataStream, new Inflater(true)); return new InflaterInputStream(dataStream, new Inflater(true));
} } else return dataStream;
else return dataStream;
} }
// Returns an output stream for writing an entry's data. // Returns an output stream for writing an entry's data.
public OutputStream getOutputStream() public OutputStream getOutputStream() {
{
entryOut = new ZioEntryOutputStream(compression, new ByteArrayOutputStream()); entryOut = new ZioEntryOutputStream(compression, new ByteArrayOutputStream());
return entryOut; return entryOut;
} }
@ -537,8 +535,7 @@ public class ZioEntry implements Cloneable {
int year = d.getYear() + 1900; int year = d.getYear() + 1900;
if (year < 1980) { if (year < 1980) {
dtime = (1 << 21) | (1 << 16); dtime = (1 << 21) | (1 << 16);
} } else {
else {
dtime = (year - 1980) << 25 | (d.getMonth() + 1) << 21 | dtime = (year - 1980) << 25 | (d.getMonth() + 1) << 21 |
d.getDate() << 16 | d.getHours() << 11 | d.getMinutes() << 5 | d.getDate() << 16 | d.getHours() << 11 | d.getMinutes() << 5 |
d.getSeconds() >> 1; d.getSeconds() >> 1;
@ -560,7 +557,9 @@ public class ZioEntry implements Cloneable {
this.filename = filename; this.filename = filename;
} }
/** Use 0 (STORED), or 8 (DEFLATE). */ /**
* Use 0 (STORED), or 8 (DEFLATE).
*/
public void setCompression(int compression) { public void setCompression(int compression) {
this.compression = (short) compression; this.compression = (short) compression;
} }

View File

@ -13,18 +13,21 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package kellinwood.zipio; package kellinwood.zipio;
import kellinwood.logging.LoggerInterface;
import kellinwood.logging.LoggerManager;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import kellinwood.logging.LoggerInterface;
import kellinwood.logging.LoggerManager;
/**
/** Input stream used to read just the data from a zip file entry. */ * Input stream used to read just the data from a zip file entry.
*/
public class ZioEntryInputStream extends InputStream { public class ZioEntryInputStream extends InputStream {
RandomAccessFile raf; RandomAccessFile raf;
@ -46,8 +49,7 @@ public class ZioEntryInputStream extends InputStream {
if (dpos >= 0) { if (dpos >= 0) {
if (debug) log.debug(String.format("Seeking to %d", entry.getDataPosition())); if (debug) log.debug(String.format("Seeking to %d", entry.getDataPosition()));
raf.seek(entry.getDataPosition()); raf.seek(entry.getDataPosition());
} } else {
else {
// seeks to, then reads, the local header, causing the // seeks to, then reads, the local header, causing the
// file pointer to be positioned at the start of the data. // file pointer to be positioned at the start of the data.
entry.readLocalHeader(); entry.readLocalHeader();
@ -87,16 +89,14 @@ public class ZioEntryInputStream extends InputStream {
if (returnDummyByte) { if (returnDummyByte) {
returnDummyByte = false; returnDummyByte = false;
return 0; return 0;
} } else return -1;
else return -1;
} }
int b = raf.read(); int b = raf.read();
if (b >= 0) { if (b >= 0) {
if (monitor != null) monitor.write(b); if (monitor != null) monitor.write(b);
if (debug) log.debug("Read 1 byte"); if (debug) log.debug("Read 1 byte");
offset += 1; offset += 1;
} } else if (debug) log.debug("Read 0 bytes");
else if (debug) log.debug("Read 0 bytes");
return b; return b;
} }
@ -111,8 +111,7 @@ public class ZioEntryInputStream extends InputStream {
returnDummyByte = false; returnDummyByte = false;
b[off] = 0; b[off] = 0;
return 1; return 1;
} } else return -1;
else return -1;
} }
int numToRead = Math.min(len, available()); int numToRead = Math.min(len, available());
int numRead = raf.read(b, off, numToRead); int numRead = raf.read(b, off, numToRead);

View File

@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package kellinwood.zipio; package kellinwood.zipio;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.zip.CRC32; import java.util.zip.CRC32;
@ -29,8 +29,7 @@ public class ZioEntryOutputStream extends OutputStream {
OutputStream wrapped; OutputStream wrapped;
OutputStream downstream; OutputStream downstream;
public ZioEntryOutputStream( int compression, OutputStream wrapped) public ZioEntryOutputStream(int compression, OutputStream wrapped) {
{
this.wrapped = wrapped; this.wrapped = wrapped;
if (compression != 0) if (compression != 0)
downstream = new DeflaterOutputStream(wrapped, new Deflater(Deflater.BEST_COMPRESSION, true)); downstream = new DeflaterOutputStream(wrapped, new Deflater(Deflater.BEST_COMPRESSION, true));
@ -73,8 +72,7 @@ public class ZioEntryOutputStream extends OutputStream {
return size; return size;
} }
public OutputStream getWrappedStream() public OutputStream getWrappedStream() {
{
return wrapped; return wrapped;
} }

View File

@ -13,15 +13,17 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package kellinwood.zipio; package kellinwood.zipio;
import kellinwood.logging.LoggerInterface;
import kellinwood.logging.LoggerManager;
import java.io.Closeable;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Collection; import java.util.Collection;
import java.util.Date;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -30,16 +32,10 @@ import java.util.jar.Manifest;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import kellinwood.logging.LoggerInterface;
import kellinwood.logging.LoggerManager;
/** /**
* *
*/ */
public class ZipInput public class ZipInput implements Closeable {
{
static LoggerInterface log; static LoggerInterface log;
public String inputFilename; public String inputFilename;
@ -51,8 +47,7 @@ public class ZipInput
CentralEnd centralEnd; CentralEnd centralEnd;
Manifest manifest; Manifest manifest;
public ZipInput( String filename) throws IOException public ZipInput(String filename) throws IOException {
{
this.inputFilename = filename; this.inputFilename = filename;
in = new RandomAccessFile(new File(inputFilename), "r"); in = new RandomAccessFile(new File(inputFilename), "r");
fileLength = in.length(); fileLength = in.length();
@ -86,12 +81,12 @@ public class ZipInput
return zioEntries; return zioEntries;
} }
/** Returns the names of immediate children in the directory with the given name. /**
* Returns the names of immediate children in the directory with the given name.
* The path value must end with a "/" character. Use a value of "/" * The path value must end with a "/" character. Use a value of "/"
* to get the root entries. * to get the root entries.
*/ */
public Collection<String> list(String path) public Collection<String> list(String path) {
{
if (!path.endsWith("/")) throw new IllegalArgumentException("Invalid path -- does not end with '/'"); if (!path.endsWith("/")) throw new IllegalArgumentException("Invalid path -- does not end with '/'");
if (path.startsWith("/")) path = path.substring(1); if (path.startsWith("/")) path = path.substring(1);
@ -117,12 +112,14 @@ public class ZipInput
return manifest; return manifest;
} }
/** Scan the end of the file for the end of central directory record (EOCDR). /**
Returns the file offset of the EOCD signature. The size parameter is an * Scan the end of the file for the end of central directory record (EOCDR).
initial buffer size (e.g., 256). * Returns the file offset of the EOCD signature. The size parameter is an
* initial buffer size (e.g., 256).
*/ */
public long scanForEOCDR(int size) throws IOException { public long scanForEOCDR(int size) throws IOException {
if (size > fileLength || size > 65536) throw new IllegalStateException( "End of central directory not found in " + inputFilename); if (size > fileLength || size > 65536)
throw new IllegalStateException("End of central directory not found in " + inputFilename);
int scanSize = (int) Math.min(fileLength, size); int scanSize = (int) Math.min(fileLength, size);
@ -143,8 +140,7 @@ public class ZipInput
} }
private void doRead() private void doRead() {
{
try { try {
long posEOCDR = scanForEOCDR(256); long posEOCDR = scanForEOCDR(256);
@ -168,14 +164,17 @@ public class ZipInput
if (debug) ZipListingHelper.listEntry(getLogger(), entry); if (debug) ZipListingHelper.listEntry(getLogger(), entry);
} }
} } catch (Throwable t) {
catch (Throwable t) {
t.printStackTrace(); t.printStackTrace();
} }
} }
@Override
public void close() { public void close() {
if (in != null) try { in.close(); } catch( Throwable t) {} if (in != null) try {
in.close();
} catch (Throwable t) {
}
} }
public long getFilePointer() throws IOException { public long getFilePointer() throws IOException {
@ -227,7 +226,6 @@ public class ZipInput
public int read(byte[] b, int offset, int length) throws IOException { public int read(byte[] b, int offset, int length) throws IOException {
return in.read(b, offset, length); return in.read(b, offset, length);
} }
} }

View File

@ -13,31 +13,29 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package kellinwood.zipio; package kellinwood.zipio;
import java.util.Date; import kellinwood.logging.LoggerInterface;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import kellinwood.logging.LoggerInterface; import java.util.Date;
import kellinwood.logging.LoggerManager;
/** /**
* *
*/ */
public class ZipListingHelper public class ZipListingHelper {
{
static DateFormat dateFormat = new SimpleDateFormat("MM-dd-yy HH:mm"); static DateFormat dateFormat = new SimpleDateFormat("MM-dd-yy HH:mm");
public static void listHeader(LoggerInterface log) public static void listHeader(LoggerInterface log) {
{
log.debug(" Length Method Size Ratio Date Time CRC-32 Name"); log.debug(" Length Method Size Ratio Date Time CRC-32 Name");
log.debug("-------- ------ ------- ----- ---- ---- ------ ----"); log.debug("-------- ------ ------- ----- ---- ---- ------ ----");
} }
public static void listEntry(LoggerInterface log, ZioEntry entry) public static void listEntry(LoggerInterface log, ZioEntry entry) {
{
int ratio = 0; int ratio = 0;
if (entry.getSize() > 0) ratio = (100 * (entry.getSize() - entry.getCompressedSize())) / entry.getSize(); if (entry.getSize() > 0) ratio = (100 * (entry.getSize() - entry.getCompressedSize())) / entry.getSize();
log.debug(String.format("%8d %6s %8d %4d%% %s %08x %s", log.debug(String.format("%8d %6s %8d %4d%% %s %08x %s",

View File

@ -13,24 +13,25 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package kellinwood.zipio; package kellinwood.zipio;
import kellinwood.logging.LoggerInterface;
import kellinwood.logging.LoggerManager;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.List;
import java.util.LinkedList;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set; import java.util.Set;
import kellinwood.logging.LoggerInterface;
import kellinwood.logging.LoggerManager;
/** /**
* *
*/ */
public class ZipOutput public class ZipOutput {
{
static LoggerInterface log; static LoggerInterface log;
@ -41,30 +42,26 @@ public class ZipOutput
List<ZioEntry> entriesWritten = new LinkedList<ZioEntry>(); List<ZioEntry> entriesWritten = new LinkedList<ZioEntry>();
Set<String> namesWritten = new HashSet<String>(); Set<String> namesWritten = new HashSet<String>();
public ZipOutput( String filename) throws IOException public ZipOutput(String filename) throws IOException {
{
this.outputFilename = filename; this.outputFilename = filename;
File ofile = new File(outputFilename); File ofile = new File(outputFilename);
init(ofile); init(ofile);
} }
public ZipOutput( File outputFile) throws IOException public ZipOutput(File outputFile) throws IOException {
{
this.outputFilename = outputFile.getAbsolutePath(); this.outputFilename = outputFile.getAbsolutePath();
File ofile = outputFile; File ofile = outputFile;
init(ofile); init(ofile);
} }
private void init( File ofile) throws IOException private void init(File ofile) throws IOException {
{
if (ofile.exists()) ofile.delete(); if (ofile.exists()) ofile.delete();
out = new FileOutputStream(ofile); out = new FileOutputStream(ofile);
if (getLogger().isDebugEnabled()) ZipListingHelper.listHeader(getLogger()); if (getLogger().isDebugEnabled()) ZipListingHelper.listHeader(getLogger());
} }
public ZipOutput( OutputStream os) throws IOException public ZipOutput(OutputStream os) throws IOException {
{
out = os; out = os;
} }
@ -87,9 +84,7 @@ public class ZipOutput
} }
public void close() throws IOException {
public void close() throws IOException
{
CentralEnd centralEnd = new CentralEnd(); CentralEnd centralEnd = new CentralEnd();
centralEnd.centralStartOffset = (int) getFilePointer(); centralEnd.centralStartOffset = (int) getFilePointer();
@ -104,7 +99,10 @@ public class ZipOutput
centralEnd.write(this); centralEnd.write(this);
if (out != null) try { out.close(); } catch( Throwable t) {} if (out != null) try {
out.close();
} catch (Throwable t) {
}
} }
public int getFilePointer() throws IOException { public int getFilePointer() throws IOException {

View File

@ -7,7 +7,6 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
@ -511,11 +510,6 @@ public final class AppUpdateStatusManager {
} }
break; break;
} }
if (Build.VERSION.SDK_INT < 11 && entry.intent == null) {
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
entry.intent = PendingIntent.getActivity(context, 0, intent, 0);
}
} }
/** /**

View File

@ -121,7 +121,7 @@ public class FDroidApp extends Application {
private static volatile int timeout = 10000; private static volatile int timeout = 10000;
// Leaving the fully qualified class name here to help clarify the difference between spongy/bouncy castle. // Leaving the fully qualified class name here to help clarify the difference between spongy/bouncy castle.
private static final org.spongycastle.jce.provider.BouncyCastleProvider SPONGYCASTLE_PROVIDER; private static final org.bouncycastle.jce.provider.BouncyCastleProvider BOUNCYCASTLE_PROVIDER;
@SuppressWarnings("unused") @SuppressWarnings("unused")
BluetoothAdapter bluetoothAdapter; BluetoothAdapter bluetoothAdapter;
@ -135,8 +135,8 @@ public class FDroidApp extends Application {
NotificationHelper notificationHelper; NotificationHelper notificationHelper;
static { static {
SPONGYCASTLE_PROVIDER = new org.spongycastle.jce.provider.BouncyCastleProvider(); BOUNCYCASTLE_PROVIDER = new org.bouncycastle.jce.provider.BouncyCastleProvider();
enableSpongyCastle(); enableBouncyCastle();
} }
private static Theme curTheme = Theme.light; private static Theme curTheme = Theme.light;
@ -209,19 +209,19 @@ public class FDroidApp extends Application {
activity.overridePendingTransition(0, 0); activity.overridePendingTransition(0, 0);
} }
public static void enableSpongyCastle() { public static void enableBouncyCastle() {
Security.addProvider(SPONGYCASTLE_PROVIDER); Security.addProvider(BOUNCYCASTLE_PROVIDER);
} }
public static void enableSpongyCastleOnLollipop() { public static void enableBouncyCastleOnLollipop() {
if (Build.VERSION.SDK_INT == 21) { if (Build.VERSION.SDK_INT == 21) {
Security.addProvider(SPONGYCASTLE_PROVIDER); Security.addProvider(BOUNCYCASTLE_PROVIDER);
} }
} }
public static void disableSpongyCastleOnLollipop() { public static void disableBouncyCastleOnLollipop() {
if (Build.VERSION.SDK_INT == 21) { if (Build.VERSION.SDK_INT == 21) {
Security.removeProvider(SPONGYCASTLE_PROVIDER.getName()); Security.removeProvider(BOUNCYCASTLE_PROVIDER.getName());
} }
} }

View File

@ -16,16 +16,10 @@ public class NfcHelper {
private static final String TAG = "NfcHelper"; private static final String TAG = "NfcHelper";
@TargetApi(14)
private static NfcAdapter getAdapter(Context context) { private static NfcAdapter getAdapter(Context context) {
if (Build.VERSION.SDK_INT < 14) {
return null;
}
return NfcAdapter.getDefaultAdapter(context.getApplicationContext()); return NfcAdapter.getDefaultAdapter(context.getApplicationContext());
} }
@TargetApi(14)
public static boolean setPushMessage(Activity activity, Uri toShare) { public static boolean setPushMessage(Activity activity, Uri toShare) {
NfcAdapter adapter = getAdapter(activity); NfcAdapter adapter = getAdapter(activity);
if (adapter != null) { if (adapter != null) {

View File

@ -6,10 +6,10 @@ import android.nfc.NfcAdapter;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.provider.Settings; import android.provider.Settings;
import android.support.v7.app.ActionBarActivity; import android.support.v7.app.AppCompatActivity;
// aka Android 4.0 aka Ice Cream Sandwich // aka Android 4.0 aka Ice Cream Sandwich
public class NfcNotEnabledActivity extends ActionBarActivity { public class NfcNotEnabledActivity extends AppCompatActivity {
/* /*
* ACTION_NFC_SETTINGS was added in 4.1 aka Jelly Bean MR1 as a * ACTION_NFC_SETTINGS was added in 4.1 aka Jelly Bean MR1 as a
@ -46,12 +46,8 @@ public class NfcNotEnabledActivity extends ActionBarActivity {
final Intent intent = new Intent(); final Intent intent = new Intent();
if (Build.VERSION.SDK_INT >= 16) { if (Build.VERSION.SDK_INT >= 16) {
doOnJellybean(intent); doOnJellybean(intent);
} else if (Build.VERSION.SDK_INT >= 14) {
doOnIceCreamSandwich(intent);
} else { } else {
// no NFC support, so nothing to do here doOnIceCreamSandwich(intent);
finish();
return;
} }
startActivity(intent); startActivity(intent);
finish(); finish();

View File

@ -117,7 +117,7 @@ class NotificationHelper {
} }
private boolean useStackedNotifications() { private boolean useStackedNotifications() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N; return Build.VERSION.SDK_INT >= 24;
} }
/** /**
@ -481,15 +481,8 @@ class NotificationHelper {
} }
private Point getLargeIconSize() { private Point getLargeIconSize() {
int w; int w = context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_width);
int h; int h = context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_height);
if (Build.VERSION.SDK_INT >= 11) {
w = context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_width);
h = context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_height);
} else {
w = context.getResources().getDimensionPixelSize(android.R.dimen.app_icon_size);
h = w;
}
return new Point(w, h); return new Point(w, h);
} }

View File

@ -5,11 +5,9 @@ import android.app.Activity;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Point; import android.graphics.Point;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Build;
import android.util.Log; import android.util.Log;
import android.view.Display; import android.view.Display;
import android.widget.ImageView; import android.widget.ImageView;
import com.google.zxing.BarcodeFormat; import com.google.zxing.BarcodeFormat;
import com.google.zxing.WriterException; import com.google.zxing.WriterException;
import com.google.zxing.encode.Contents; import com.google.zxing.encode.Contents;
@ -39,15 +37,9 @@ public class QrGenAsyncTask extends AsyncTask<String, Void, Void> {
Display display = activity.getWindowManager().getDefaultDisplay(); Display display = activity.getWindowManager().getDefaultDisplay();
Point outSize = new Point(); Point outSize = new Point();
int x, y, qrCodeDimension; int x, y, qrCodeDimension;
/* lame, got to use both the new and old APIs here */
if (Build.VERSION.SDK_INT >= 13) {
display.getSize(outSize); display.getSize(outSize);
x = outSize.x; x = outSize.x;
y = outSize.y; y = outSize.y;
} else {
x = display.getWidth();
y = display.getHeight();
}
if (x < y) { if (x < y) {
qrCodeDimension = x; qrCodeDimension = x;
} else { } else {

View File

@ -203,10 +203,10 @@ public class RepoUpdater {
throw new UpdateException(downloadedFile + " does not exist!"); throw new UpdateException(downloadedFile + " does not exist!");
} }
// Due to a bug in Android 5.0 Lollipop, the inclusion of spongycastle causes // Due to a bug in Android 5.0 Lollipop, the inclusion of bouncycastle causes
// breakage when verifying the signature of the downloaded .jar. For more // breakage when verifying the signature of the downloaded .jar. For more
// details, check out https://gitlab.com/fdroid/fdroidclient/issues/111. // details, check out https://gitlab.com/fdroid/fdroidclient/issues/111.
FDroidApp.disableSpongyCastleOnLollipop(); FDroidApp.disableBouncyCastleOnLollipop();
JarFile jarFile = new JarFile(downloadedFile, true); JarFile jarFile = new JarFile(downloadedFile, true);
JarEntry indexEntry = (JarEntry) jarFile.getEntry("index.xml"); JarEntry indexEntry = (JarEntry) jarFile.getEntry("index.xml");
@ -237,7 +237,7 @@ public class RepoUpdater {
} catch (SAXException | ParserConfigurationException | IOException e) { } catch (SAXException | ParserConfigurationException | IOException e) {
throw new UpdateException("Error parsing index", e); throw new UpdateException("Error parsing index", e);
} finally { } finally {
FDroidApp.enableSpongyCastleOnLollipop(); FDroidApp.enableBouncyCastleOnLollipop();
Utils.closeQuietly(indexInputStream); Utils.closeQuietly(indexInputStream);
if (downloadedFile != null) { if (downloadedFile != null) {
if (!downloadedFile.delete()) { if (!downloadedFile.delete()) {

View File

@ -1,49 +0,0 @@
package org.fdroid.fdroid.compat;
import android.annotation.TargetApi;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.Build;
public abstract class ClipboardCompat {
public abstract String getText();
public static ClipboardCompat create(Context context) {
if (Build.VERSION.SDK_INT >= 11) {
return new HoneycombClipboard(context);
}
return new OldClipboard();
}
@TargetApi(11)
private static class HoneycombClipboard extends ClipboardCompat {
private final ClipboardManager manager;
HoneycombClipboard(Context context) {
this.manager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
}
@Override
public String getText() {
CharSequence text = null;
if (manager.hasPrimaryClip()) {
ClipData data = manager.getPrimaryClip();
if (data.getItemCount() > 0) {
text = data.getItemAt(0).getText();
}
}
return text != null ? text.toString() : null;
}
}
private static class OldClipboard extends ClipboardCompat {
@Override
public String getText() {
return null;
}
}
}

View File

@ -1,21 +0,0 @@
package org.fdroid.fdroid.compat;
import android.net.Uri;
import android.os.Build;
public class UriCompat {
/**
* Uri#getQueryParameter(String) has the following warning:
*
* > Prior to Ice Cream Sandwich, this decoded the '+' character as '+' rather than ' '.
*/
public static String getQueryParameter(Uri uri, String key) {
String value = uri.getQueryParameter(key);
if (value != null && Build.VERSION.SDK_INT < 14) {
value = value.replaceAll("\\+", " ");
}
return value;
}
}

View File

@ -797,7 +797,7 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
// breakage when verifying the signature of most .jars. For more // breakage when verifying the signature of most .jars. For more
// details, check out https://gitlab.com/fdroid/fdroidclient/issues/111. // details, check out https://gitlab.com/fdroid/fdroidclient/issues/111.
try { try {
FDroidApp.disableSpongyCastleOnLollipop(); FDroidApp.disableBouncyCastleOnLollipop();
final InputStream tmpIn = apkJar.getInputStream(aSignedEntry); final InputStream tmpIn = apkJar.getInputStream(aSignedEntry);
byte[] buff = new byte[2048]; byte[] buff = new byte[2048];
//noinspection StatementWithEmptyBody //noinspection StatementWithEmptyBody
@ -818,7 +818,7 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
final Certificate signer = aSignedEntry.getCertificates()[0]; final Certificate signer = aSignedEntry.getCertificates()[0];
rawCertBytes = signer.getEncoded(); rawCertBytes = signer.getEncoded();
} finally { } finally {
FDroidApp.enableSpongyCastleOnLollipop(); FDroidApp.enableBouncyCastleOnLollipop();
} }
apkJar.close(); apkJar.close();

View File

@ -26,7 +26,7 @@ import android.content.pm.PackageManager;
import android.content.pm.Signature; import android.content.pm.Signature;
import org.acra.ACRA; import org.acra.ACRA;
import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.Utils;
import org.spongycastle.util.encoders.Hex; import org.bouncycastle.util.encoders.Hex;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;

View File

@ -5,19 +5,19 @@ import android.util.Log;
import kellinwood.security.zipsigner.ZipSigner; import kellinwood.security.zipsigner.ZipSigner;
import org.fdroid.fdroid.FDroidApp; import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.Utils;
import org.spongycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1Sequence;
import org.spongycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x500.X500Name;
import org.spongycastle.asn1.x509.GeneralName; import org.bouncycastle.asn1.x509.GeneralName;
import org.spongycastle.asn1.x509.GeneralNames; import org.bouncycastle.asn1.x509.GeneralNames;
import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.spongycastle.asn1.x509.Time; import org.bouncycastle.asn1.x509.Time;
import org.spongycastle.asn1.x509.X509Extension; import org.bouncycastle.asn1.x509.X509Extension;
import org.spongycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.X509CertificateHolder;
import org.spongycastle.cert.X509v3CertificateBuilder; import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.spongycastle.cert.jcajce.JcaX509CertificateConverter; import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.spongycastle.operator.ContentSigner; import org.bouncycastle.operator.ContentSigner;
import org.spongycastle.operator.OperatorCreationException; import org.bouncycastle.operator.OperatorCreationException;
import org.spongycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.KeyManagerFactory;

View File

@ -9,7 +9,7 @@ import org.apache.commons.io.FileUtils;
import org.fdroid.fdroid.BuildConfig; import org.fdroid.fdroid.BuildConfig;
import org.fdroid.fdroid.FDroidApp; import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.Utils;
import org.spongycastle.util.encoders.Base64; import org.bouncycastle.util.encoders.Base64;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.File; import java.io.File;

View File

@ -5,12 +5,13 @@ import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.util.Log; import android.util.Log;
import android.webkit.MimeTypeMap; import android.webkit.MimeTypeMap;
import fi.iki.elonen.NanoHTTPD;
import org.fdroid.fdroid.BuildConfig; import org.fdroid.fdroid.BuildConfig;
import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.localrepo.LocalRepoKeyStore; import org.fdroid.fdroid.localrepo.LocalRepoKeyStore;
import org.fdroid.fdroid.views.swap.SwapWorkflowActivity; import org.fdroid.fdroid.views.swap.SwapWorkflowActivity;
import javax.net.ssl.SSLServerSocketFactory;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FilenameFilter; import java.io.FilenameFilter;
@ -26,10 +27,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import javax.net.ssl.SSLServerSocketFactory;
import fi.iki.elonen.NanoHTTPD;
public class LocalHTTPD extends NanoHTTPD { public class LocalHTTPD extends NanoHTTPD {
private static final String TAG = "LocalHTTPD"; private static final String TAG = "LocalHTTPD";
@ -93,10 +90,10 @@ public class LocalHTTPD extends NanoHTTPD {
session.parseBody(new HashMap<String, String>()); session.parseBody(new HashMap<String, String>());
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, "An error occured while parsing the POST body", e); Log.e(TAG, "An error occured while parsing the POST body", e);
return new Response(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT,
"Internal server error, check logcat on server for details."); "Internal server error, check logcat on server for details.");
} catch (ResponseException re) { } catch (ResponseException re) {
return new Response(re.getStatus(), MIME_PLAINTEXT, re.getMessage()); return newFixedLengthResponse(re.getStatus(), MIME_PLAINTEXT, re.getMessage());
} }
return handlePost(session); return handlePost(session);
@ -111,13 +108,13 @@ public class LocalHTTPD extends NanoHTTPD {
if (!session.getParms().containsKey("repo")) { if (!session.getParms().containsKey("repo")) {
Log.e(TAG, "Malformed /request-swap request to local repo HTTP server." Log.e(TAG, "Malformed /request-swap request to local repo HTTP server."
+ " Should have posted a 'repo' parameter."); + " Should have posted a 'repo' parameter.");
return new Response(Response.Status.BAD_REQUEST, MIME_PLAINTEXT, return newFixedLengthResponse(Response.Status.BAD_REQUEST, MIME_PLAINTEXT,
"Requires 'repo' parameter to be posted."); "Requires 'repo' parameter to be posted.");
} }
requestSwap(session.getParms().get("repo")); requestSwap(session.getParms().get("repo"));
return new Response(Response.Status.OK, MIME_PLAINTEXT, "Swap request received."); return newFixedLengthResponse(Response.Status.OK, MIME_PLAINTEXT, "Swap request received.");
} }
return new Response(""); return newFixedLengthResponse("");
} }
private Response handleGet(IHTTPSession session) { private Response handleGet(IHTTPSession session) {
@ -155,7 +152,7 @@ public class LocalHTTPD extends NanoHTTPD {
SSLServerSocketFactory factory = NanoHTTPD.makeSSLSocketFactory( SSLServerSocketFactory factory = NanoHTTPD.makeSSLSocketFactory(
localRepoKeyStore.getKeyStore(), localRepoKeyStore.getKeyStore(),
localRepoKeyStore.getKeyManagers()); localRepoKeyStore.getKeyManagers());
makeSecure(factory); makeSecure(factory, null);
} catch (LocalRepoKeyStore.InitException | IOException e) { } catch (LocalRepoKeyStore.InitException | IOException e) {
Log.e(TAG, "Could not enable HTTPS", e); Log.e(TAG, "Could not enable HTTPS", e);
} }
@ -209,7 +206,7 @@ public class LocalHTTPD extends NanoHTTPD {
} }
} }
Response response = serveFile(headers, f, getMimeTypeForFile(uri)); Response response = serveFile(headers, f, getAndroidMimeTypeForFile(uri));
return response != null ? response : return response != null ? response :
createResponse(Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, createResponse(Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT,
"Error 404, file not found."); "Error 404, file not found.");
@ -298,19 +295,19 @@ public class LocalHTTPD extends NanoHTTPD {
// Announce that the file server accepts partial content requests // Announce that the file server accepts partial content requests
private Response createResponse(Response.Status status, String mimeType, InputStream message) { private Response createResponse(Response.Status status, String mimeType, InputStream message) {
Response res = new Response(status, mimeType, message); Response res = newChunkedResponse(status, mimeType, message);
res.addHeader("Accept-Ranges", "bytes"); res.addHeader("Accept-Ranges", "bytes");
return res; return res;
} }
// Announce that the file server accepts partial content requests // Announce that the file server accepts partial content requests
private Response createResponse(Response.Status status, String mimeType, String message) { private Response createResponse(Response.Status status, String mimeType, String message) {
Response res = new Response(status, mimeType, message); Response res = newFixedLengthResponse(status, mimeType, message);
res.addHeader("Accept-Ranges", "bytes"); res.addHeader("Accept-Ranges", "bytes");
return res; return res;
} }
private static String getMimeTypeForFile(String uri) { private static String getAndroidMimeTypeForFile(String uri) {
String type = null; String type = null;
String extension = MimeTypeMap.getFileExtensionFromUrl(uri); String extension = MimeTypeMap.getFileExtensionFromUrl(uri);
if (extension != null) { if (extension != null) {

View File

@ -1,9 +1,6 @@
package org.fdroid.fdroid.net.bluetooth; package org.fdroid.fdroid.net.bluetooth;
import android.annotation.TargetApi;
import android.bluetooth.BluetoothSocket; import android.bluetooth.BluetoothSocket;
import android.os.Build;
import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.Utils;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
@ -32,9 +29,8 @@ public class BluetoothConnection {
return output; return output;
} }
@TargetApi(14)
public void open() throws IOException { public void open() throws IOException {
if (Build.VERSION.SDK_INT >= 14 && !socket.isConnected()) { if (!socket.isConnected()) {
// Server sockets will already be connected when they are passed to us, // Server sockets will already be connected when they are passed to us,
// client sockets require us to call connect(). // client sockets require us to call connect().
socket.connect(); socket.connect();

View File

@ -20,6 +20,8 @@
package org.fdroid.fdroid.views; package org.fdroid.fdroid.views;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
@ -55,7 +57,6 @@ import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.R; import org.fdroid.fdroid.R;
import org.fdroid.fdroid.UpdateService; import org.fdroid.fdroid.UpdateService;
import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.compat.ClipboardCompat;
import org.fdroid.fdroid.compat.CursorAdapterCompat; import org.fdroid.fdroid.compat.CursorAdapterCompat;
import org.fdroid.fdroid.data.NewRepoConfig; import org.fdroid.fdroid.data.NewRepoConfig;
import org.fdroid.fdroid.data.Repo; import org.fdroid.fdroid.data.Repo;
@ -158,13 +159,24 @@ public class ManageReposActivity extends AppCompatActivity
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
public String getPrimaryClipAsText() {
CharSequence text = null;
ClipboardManager clipboardManager = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
if (clipboardManager.hasPrimaryClip()) {
ClipData data = clipboardManager.getPrimaryClip();
if (data.getItemCount() > 0) {
text = data.getItemAt(0).getText();
}
}
return text != null ? text.toString() : null;
}
private void showAddRepo() { private void showAddRepo() {
/* /*
* If there is text in the clipboard, and it looks like a URL, use that. * If there is text in the clipboard, and it looks like a URL, use that.
* Otherwise use "https://" as default repo string. * Otherwise use "https://" as default repo string.
*/ */
ClipboardCompat clipboard = ClipboardCompat.create(this); String text = getPrimaryClipAsText();
String text = clipboard.getText();
String fingerprint = null; String fingerprint = null;
String username = null; String username = null;
String password = null; String password = null;

View File

@ -2,14 +2,12 @@ package org.fdroid.fdroid.views;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.os.Build;
import android.support.v4.widget.CursorAdapter; import android.support.v4.widget.CursorAdapter;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.CompoundButton; import android.widget.CompoundButton;
import android.widget.TextView; import android.widget.TextView;
import org.fdroid.fdroid.R; import org.fdroid.fdroid.R;
import org.fdroid.fdroid.data.Repo; import org.fdroid.fdroid.data.Repo;
@ -24,11 +22,8 @@ public class RepoAdapter extends CursorAdapter {
private EnabledListener enabledListener; private EnabledListener enabledListener;
public static RepoAdapter create(Context context, Cursor cursor, int flags) { public static RepoAdapter create(Context context, Cursor cursor, int flags) {
if (Build.VERSION.SDK_INT >= 11) {
return new RepoAdapter(context, cursor, flags); return new RepoAdapter(context, cursor, flags);
} }
return new RepoAdapter(context, cursor);
}
private RepoAdapter(Context context, Cursor c, int flags) { private RepoAdapter(Context context, Cursor c, int flags) {
super(context, c, flags); super(context, c, flags);

View File

@ -15,8 +15,8 @@ import android.os.Bundle;
import android.os.Parcelable; import android.os.Parcelable;
import android.support.v4.app.NavUtils; import android.support.v4.app.NavUtils;
import android.support.v4.content.LocalBroadcastManager; import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.format.DateUtils; import android.text.format.DateUtils;
@ -40,7 +40,7 @@ import org.fdroid.fdroid.data.Schema.RepoTable;
import java.util.Locale; import java.util.Locale;
public class RepoDetailsActivity extends ActionBarActivity { public class RepoDetailsActivity extends AppCompatActivity {
private static final String TAG = "RepoDetailsActivity"; private static final String TAG = "RepoDetailsActivity";
public static final String ARG_REPO_ID = "repo_id"; public static final String ARG_REPO_ID = "repo_id";
@ -223,9 +223,7 @@ public class RepoDetailsActivity extends ActionBarActivity {
@Override @Override
public boolean onPrepareOptionsMenu(Menu menu) { public boolean onPrepareOptionsMenu(Menu menu) {
if (Build.VERSION.SDK_INT >= 14) {
prepareNfcMenuItems(menu); prepareNfcMenuItems(menu);
}
return true; return true;
} }

View File

@ -3,7 +3,6 @@ package org.fdroid.fdroid.views;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
@ -65,11 +64,9 @@ public class ScreenShotsActivity extends AppCompatActivity {
viewPager.setAdapter(adapter); viewPager.setAdapter(adapter);
viewPager.setCurrentItem(startPosition); viewPager.setCurrentItem(startPosition);
if (Build.VERSION.SDK_INT >= 11) {
// display some nice animation while swiping // display some nice animation while swiping
viewPager.setPageTransformer(true, new DepthPageTransformer()); viewPager.setPageTransformer(true, new DepthPageTransformer());
} }
}
@Override @Override
protected void onResume() { protected void onResume() {

View File

@ -1,7 +1,6 @@
package org.fdroid.fdroid.views.apps; package org.fdroid.fdroid.views.apps;
import android.animation.ValueAnimator; import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Canvas; import android.graphics.Canvas;
@ -10,7 +9,6 @@ import android.graphics.Paint;
import android.graphics.Path; import android.graphics.Path;
import android.graphics.Point; import android.graphics.Point;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
import android.os.Build;
import android.support.annotation.ColorInt; import android.support.annotation.ColorInt;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
@ -20,12 +18,10 @@ import android.support.v7.widget.AppCompatImageView;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View; import android.view.View;
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.FailReason; import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import org.fdroid.fdroid.R; import org.fdroid.fdroid.R;
import java.util.Random; import java.util.Random;
@ -34,7 +30,7 @@ import java.util.Random;
* A feature image can have a {@link android.graphics.drawable.Drawable} or a {@link Palette}. If * A feature image can have a {@link android.graphics.drawable.Drawable} or a {@link Palette}. If
* a Drawable is available, then it will draw that, otherwise it will attempt to fall back to the * a Drawable is available, then it will draw that, otherwise it will attempt to fall back to the
* Palette you gave it. If a Palette is given, it will draw a series of triangles like so: * Palette you gave it. If a Palette is given, it will draw a series of triangles like so:
* * <p>
* +_----+----_+_----+----_+ * +_----+----_+_----+----_+
* | \_ | _/ | \_ | _/ | * | \_ | _/ | \_ | _/ |
* | \_|_/ | \_|_/ | * | \_|_/ | \_|_/ |
@ -42,12 +38,12 @@ import java.util.Random;
* | \_ | _/ | \_ | _/ | * | \_ | _/ | \_ | _/ |
* | \_|_/ | \_|_/ | * | \_|_/ | \_|_/ |
* +-----+-----+-----+-----+ * +-----+-----+-----+-----+
* * <p>
* where each triangle is filled with one of two variations of the {@link Palette#getDominantColor(int)} * where each triangle is filled with one of two variations of the {@link Palette#getDominantColor(int)}
* that is chosen randomly. The randomness is first seeded with the colour that has been selected. * that is chosen randomly. The randomness is first seeded with the colour that has been selected.
* This is so that if this repaints itself in the future, it will have the same unique pattern rather * This is so that if this repaints itself in the future, it will have the same unique pattern rather
* than picking a new random pattern each time. * than picking a new random pattern each time.
* * <p>
* It is suggested that you obtain the Palette from the icon of an app. * It is suggested that you obtain the Palette from the icon of an app.
*/ */
@SuppressWarnings("LineLength") @SuppressWarnings("LineLength")
@ -143,12 +139,7 @@ public class FeatureImage extends AppCompatImageView {
private int currentAlpha = 255; private int currentAlpha = 255;
private ValueAnimator alphaAnimator = null; private ValueAnimator alphaAnimator = null;
@TargetApi(11)
private void animateColourChange() { private void animateColourChange() {
if (Build.VERSION.SDK_INT < 11) {
return;
}
if (alphaAnimator == null) { if (alphaAnimator == null) {
alphaAnimator = ValueAnimator.ofInt(0, 255); alphaAnimator = ValueAnimator.ofInt(0, 255);
} else { } else {
@ -294,12 +285,15 @@ public class FeatureImage extends AppCompatImageView {
private abstract static class ImageLoadingAdapter implements ImageLoadingListener { private abstract static class ImageLoadingAdapter implements ImageLoadingListener {
@Override @Override
public void onLoadingStarted(String imageUri, View view) { } public void onLoadingStarted(String imageUri, View view) {
}
@Override @Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) { } public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
}
@Override @Override
public void onLoadingCancelled(String imageUri, View view) { } public void onLoadingCancelled(String imageUri, View view) {
}
} }
} }

View File

@ -5,16 +5,15 @@ import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.preference.CheckBoxPreference; import android.support.v14.preference.PreferenceFragment;
import android.preference.EditTextPreference; import android.support.v7.preference.CheckBoxPreference;
import android.preference.ListPreference; import android.support.v7.preference.EditTextPreference;
import android.preference.Preference; import android.support.v7.preference.ListPreference;
import android.preference.PreferenceCategory; import android.support.v7.preference.Preference;
import android.support.v4.preference.PreferenceFragment; import android.support.v7.preference.PreferenceCategory;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.WindowManager; import android.view.WindowManager;
import com.geecko.QuickLyric.view.AppCompatListPreference;
import info.guardianproject.netcipher.NetCipher; import info.guardianproject.netcipher.NetCipher;
import info.guardianproject.netcipher.proxy.OrbotHelper; import info.guardianproject.netcipher.proxy.OrbotHelper;
import org.fdroid.fdroid.AppDetails2; import org.fdroid.fdroid.AppDetails2;
@ -59,15 +58,14 @@ public class PreferencesFragment extends PreferenceFragment
private FDroidApp fdroidApp; private FDroidApp fdroidApp;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreatePreferences(Bundle bundle, String s) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences); addPreferencesFromResource(R.xml.preferences);
useTorCheckPref = (CheckBoxPreference) findPreference(Preferences.PREF_USE_TOR); useTorCheckPref = (CheckBoxPreference) findPreference(Preferences.PREF_USE_TOR);
enableProxyCheckPref = (CheckBoxPreference) findPreference(Preferences.PREF_ENABLE_PROXY); enableProxyCheckPref = (CheckBoxPreference) findPreference(Preferences.PREF_ENABLE_PROXY);
updateAutoDownloadPref = findPreference(Preferences.PREF_AUTO_DOWNLOAD_INSTALL_UPDATES); updateAutoDownloadPref = findPreference(Preferences.PREF_AUTO_DOWNLOAD_INSTALL_UPDATES);
updatePrivilegedExtensionPref = findPreference(Preferences.PREF_UNINSTALL_PRIVILEGED_APP); updatePrivilegedExtensionPref = findPreference(Preferences.PREF_UNINSTALL_PRIVILEGED_APP);
AppCompatListPreference languagePref = (AppCompatListPreference) findPreference(Preferences.PREF_LANGUAGE); ListPreference languagePref = (ListPreference) findPreference(Preferences.PREF_LANGUAGE);
if (Build.VERSION.SDK_INT >= 24) { if (Build.VERSION.SDK_INT >= 24) {
PreferenceCategory category = (PreferenceCategory) findPreference("pref_category_display"); PreferenceCategory category = (PreferenceCategory) findPreference("pref_category_display");
category.removePreference(languagePref); category.removePreference(languagePref);
@ -162,8 +160,8 @@ public class PreferencesFragment extends PreferenceFragment
Activity activity = getActivity(); Activity activity = getActivity();
Languages.setLanguage(activity); Languages.setLanguage(activity);
RepoProvider.Helper.clearEtags(getContext()); RepoProvider.Helper.clearEtags(getActivity());
UpdateService.updateNow(getContext()); UpdateService.updateNow(getActivity());
Languages.forceChangeLanguage(activity); Languages.forceChangeLanguage(activity);
} }
@ -173,7 +171,7 @@ public class PreferencesFragment extends PreferenceFragment
entrySummary(key); entrySummary(key);
if (changing if (changing
&& currentKeepCacheTime != Preferences.get().getKeepCacheTime()) { && currentKeepCacheTime != Preferences.get().getKeepCacheTime()) {
CleanCacheService.schedule(getContext()); CleanCacheService.schedule(getActivity());
} }
break; break;
@ -219,9 +217,9 @@ public class PreferencesFragment extends PreferenceFragment
case Preferences.PREF_KEEP_INSTALL_HISTORY: case Preferences.PREF_KEEP_INSTALL_HISTORY:
CheckBoxPreference p = (CheckBoxPreference) findPreference(key); CheckBoxPreference p = (CheckBoxPreference) findPreference(key);
if (p.isChecked()) { if (p.isChecked()) {
InstallHistoryService.register(getContext()); InstallHistoryService.register(getActivity());
} else { } else {
InstallHistoryService.unregister(getContext()); InstallHistoryService.unregister(getActivity());
} }
break; break;
} }
@ -310,13 +308,13 @@ public class PreferencesFragment extends PreferenceFragment
@Override @Override
public boolean onPreferenceChange(Preference preference, Object newValue) { public boolean onPreferenceChange(Preference preference, Object newValue) {
if (newValue instanceof Boolean && (boolean) newValue) { if (newValue instanceof Boolean && (boolean) newValue) {
UpdateService.autoDownloadUpdates(getContext()); UpdateService.autoDownloadUpdates(getActivity());
} }
return true; return true;
} }
}); });
if (PrivilegedInstaller.isDefault(getContext())) { if (PrivilegedInstaller.isDefault(getActivity())) {
updateAutoDownloadPref.setTitle(R.string.update_auto_install); updateAutoDownloadPref.setTitle(R.string.update_auto_install);
updateAutoDownloadPref.setSummary(R.string.update_auto_install_summary); updateAutoDownloadPref.setSummary(R.string.update_auto_install_summary);
} }

View File

@ -16,9 +16,9 @@ import android.support.v7.widget.RecyclerView;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Toast; import android.widget.Toast;
import com.ashokvarma.bottomnavigation.BadgeItem;
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 org.fdroid.fdroid.AppDetails2; import org.fdroid.fdroid.AppDetails2;
import org.fdroid.fdroid.AppUpdateStatusManager; import org.fdroid.fdroid.AppUpdateStatusManager;
import org.fdroid.fdroid.AppUpdateStatusManager.AppUpdateStatus; import org.fdroid.fdroid.AppUpdateStatusManager.AppUpdateStatus;
@ -28,7 +28,6 @@ import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.R; import org.fdroid.fdroid.R;
import org.fdroid.fdroid.UpdateService; import org.fdroid.fdroid.UpdateService;
import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.compat.UriCompat;
import org.fdroid.fdroid.data.NewRepoConfig; import org.fdroid.fdroid.data.NewRepoConfig;
import org.fdroid.fdroid.views.ManageReposActivity; import org.fdroid.fdroid.views.ManageReposActivity;
import org.fdroid.fdroid.views.apps.AppListActivity; import org.fdroid.fdroid.views.apps.AppListActivity;
@ -67,7 +66,7 @@ public class MainActivity extends AppCompatActivity implements BottomNavigationB
private MainViewAdapter adapter; private MainViewAdapter adapter;
private BottomNavigationBar bottomNavigation; private BottomNavigationBar bottomNavigation;
private int selectedMenuId = R.id.whats_new; private int selectedMenuId = R.id.whats_new;
private BadgeItem updatesBadge; private TextBadgeItem updatesBadge;
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
@ -90,7 +89,7 @@ public class MainActivity extends AppCompatActivity implements BottomNavigationB
pager.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS); pager.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
} }
updatesBadge = new BadgeItem().hide(false); updatesBadge = new TextBadgeItem().hide(false);
bottomNavigation = (BottomNavigationBar) findViewById(R.id.bottom_navigation); bottomNavigation = (BottomNavigationBar) findViewById(R.id.bottom_navigation);
bottomNavigation.setTabSelectedListener(this) bottomNavigation.setTabSelectedListener(this)
@ -232,29 +231,29 @@ public class MainActivity extends AppCompatActivity implements BottomNavigationB
packageName = data.getLastPathSegment(); packageName = data.getLastPathSegment();
} else if (path.startsWith("/repository/browse")) { } else if (path.startsWith("/repository/browse")) {
// http://f-droid.org/repository/browse?fdfilter=search+query // http://f-droid.org/repository/browse?fdfilter=search+query
query = UriCompat.getQueryParameter(data, "fdfilter"); query = data.getQueryParameter("fdfilter");
// http://f-droid.org/repository/browse?fdid=packageName // http://f-droid.org/repository/browse?fdid=packageName
packageName = UriCompat.getQueryParameter(data, "fdid"); packageName = data.getQueryParameter("fdid");
} else if ("/app".equals(data.getPath()) || "/packages".equals(data.getPath())) { } else if ("/app".equals(data.getPath()) || "/packages".equals(data.getPath())) {
packageName = null; packageName = null;
} }
break; break;
case "details": case "details":
// market://details?id=app.id // market://details?id=app.id
packageName = UriCompat.getQueryParameter(data, "id"); packageName = data.getQueryParameter("id");
break; break;
case "search": case "search":
// market://search?q=query // market://search?q=query
query = UriCompat.getQueryParameter(data, "q"); query = data.getQueryParameter("q");
break; break;
case "play.google.com": case "play.google.com":
if (path.startsWith("/store/apps/details")) { if (path.startsWith("/store/apps/details")) {
// http://play.google.com/store/apps/details?id=app.id // http://play.google.com/store/apps/details?id=app.id
packageName = UriCompat.getQueryParameter(data, "id"); packageName = data.getQueryParameter("id");
} else if (path.startsWith("/store/search")) { } else if (path.startsWith("/store/search")) {
// http://play.google.com/store/search?q=foo // http://play.google.com/store/search?q=foo
query = UriCompat.getQueryParameter(data, "q"); query = data.getQueryParameter("q");
} }
break; break;
case "apps": case "apps":
@ -262,8 +261,8 @@ public class MainActivity extends AppCompatActivity implements BottomNavigationB
case "www.amazon.com": case "www.amazon.com":
// amzn://apps/android?p=app.id // amzn://apps/android?p=app.id
// http://amazon.com/gp/mas/dl/android?s=app.id // http://amazon.com/gp/mas/dl/android?s=app.id
packageName = UriCompat.getQueryParameter(data, "p"); packageName = data.getQueryParameter("p");
query = UriCompat.getQueryParameter(data, "s"); query = data.getQueryParameter("s");
break; break;
} }
} else if ("fdroid.app".equals(scheme)) { } else if ("fdroid.app".equals(scheme)) {

View File

@ -1,9 +1,9 @@
package org.fdroid.fdroid.views.main; package org.fdroid.fdroid.views.main;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.content.Context; import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.widget.FrameLayout; import android.widget.FrameLayout;
@ -55,7 +55,7 @@ public class SettingsView extends FrameLayout {
} }
if (currentTransaction == null) { if (currentTransaction == null) {
currentTransaction = activity.getSupportFragmentManager().beginTransaction(); currentTransaction = activity.getFragmentManager().beginTransaction();
} }
currentTransaction.replace(getId(), new PreferencesFragment(), "preferences-fragment"); currentTransaction.replace(getId(), new PreferencesFragment(), "preferences-fragment");
@ -73,18 +73,18 @@ public class SettingsView extends FrameLayout {
throw new IllegalArgumentException("Cannot add a SettingsView to activities which are not an AppCompatActivity"); throw new IllegalArgumentException("Cannot add a SettingsView to activities which are not an AppCompatActivity");
} }
Fragment existingFragment = activity.getSupportFragmentManager().findFragmentByTag("preferences-fragment"); Fragment existingFragment = activity.getFragmentManager().findFragmentByTag("preferences-fragment");
if (existingFragment == null) { if (existingFragment == null) {
return; return;
} }
if (currentTransaction == null) { if (currentTransaction == null) {
currentTransaction = activity.getSupportFragmentManager().beginTransaction(); currentTransaction = activity.getFragmentManager().beginTransaction();
} }
currentTransaction.remove(existingFragment); currentTransaction.remove(existingFragment);
currentTransaction.commitAllowingStateLoss(); currentTransaction.commitAllowingStateLoss();
currentTransaction = null; currentTransaction = null;
activity.getSupportFragmentManager().executePendingTransactions(); activity.getFragmentManager().executePendingTransactions();
} }
} }

View File

@ -11,30 +11,27 @@ import android.content.res.Resources;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.support.annotation.ColorInt; import android.support.annotation.ColorInt;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v14.preference.PreferenceFragment;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.support.v4.preference.PreferenceFragment;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.support.v7.preference.CheckBoxPreference;
import android.support.v7.preference.ListPreference;
import android.support.v7.preference.Preference;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.TypedValue; import android.util.TypedValue;
import info.guardianproject.panic.Panic;
import info.guardianproject.panic.PanicResponder;
import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.R; import org.fdroid.fdroid.R;
import org.fdroid.fdroid.views.hiding.HidingManager; import org.fdroid.fdroid.views.hiding.HidingManager;
import java.util.ArrayList; import java.util.ArrayList;
import info.guardianproject.panic.Panic; public class PanicPreferencesFragment extends PreferenceFragment
import info.guardianproject.panic.PanicResponder; implements SharedPreferences.OnSharedPreferenceChangeListener {
public class PanicPreferencesFragment extends PreferenceFragment implements SharedPreferences
.OnSharedPreferenceChangeListener {
private static final String PREF_EXIT = Preferences.PREF_PANIC_EXIT; private static final String PREF_EXIT = Preferences.PREF_PANIC_EXIT;
private static final String PREF_APP = "pref_panic_app"; private static final String PREF_APP = "pref_panic_app";
@ -46,8 +43,7 @@ public class PanicPreferencesFragment extends PreferenceFragment implements Shar
private CheckBoxPreference prefHide; private CheckBoxPreference prefHide;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreatePreferences(Bundle bundle, String s) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences_panic); addPreferencesFromResource(R.xml.preferences_panic);
pm = getActivity().getPackageManager(); pm = getActivity().getPackageManager();
@ -148,16 +144,16 @@ public class PanicPreferencesFragment extends PreferenceFragment implements Shar
// no panic app set // no panic app set
prefApp.setValue(Panic.PACKAGE_NAME_NONE); prefApp.setValue(Panic.PACKAGE_NAME_NONE);
prefApp.setSummary(getString(R.string.panic_app_setting_summary)); prefApp.setSummary(getString(R.string.panic_app_setting_summary));
if (Build.VERSION.SDK_INT >= 11) {
prefApp.setIcon(null); // otherwise re-setting view resource doesn't work prefApp.setIcon(null); // otherwise re-setting view resource doesn't work
Drawable icon = ContextCompat.getDrawable(getContext(), R.drawable.ic_cancel); Drawable icon = ContextCompat.getDrawable(getActivity(), R.drawable.ic_cancel);
TypedValue typedValue = new TypedValue(); TypedValue typedValue = new TypedValue();
Resources.Theme theme = getContext().getTheme(); Resources.Theme theme = getActivity().getTheme();
theme.resolveAttribute(R.attr.appListItem, typedValue, true); theme.resolveAttribute(R.attr.appListItem, typedValue, true);
@ColorInt int color = typedValue.data; @ColorInt int color = typedValue.data;
icon.setColorFilter(color, PorterDuff.Mode.SRC_IN); icon.setColorFilter(color, PorterDuff.Mode.SRC_IN);
prefApp.setIcon(icon); prefApp.setIcon(icon);
}
// disable destructive panic actions // disable destructive panic actions
prefHide.setEnabled(false); prefHide.setEnabled(false);
} else { } else {
@ -165,9 +161,7 @@ public class PanicPreferencesFragment extends PreferenceFragment implements Shar
try { try {
prefApp.setValue(packageName); prefApp.setValue(packageName);
prefApp.setSummary(pm.getApplicationLabel(pm.getApplicationInfo(packageName, 0))); prefApp.setSummary(pm.getApplicationLabel(pm.getApplicationInfo(packageName, 0)));
if (Build.VERSION.SDK_INT >= 11) {
prefApp.setIcon(pm.getApplicationIcon(packageName)); prefApp.setIcon(pm.getApplicationIcon(packageName));
}
prefHide.setEnabled(true); prefHide.setEnabled(true);
} catch (PackageManager.NameNotFoundException e) { } catch (PackageManager.NameNotFoundException e) {
// revert back to no app, just to be safe // revert back to no app, just to be safe
@ -194,7 +188,7 @@ public class PanicPreferencesFragment extends PreferenceFragment implements Shar
} }
}; };
AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(getString(R.string.panic_app_dialog_title)); builder.setTitle(getString(R.string.panic_app_dialog_title));
CharSequence app = getString(R.string.panic_app_unknown_app); CharSequence app = getString(R.string.panic_app_unknown_app);
@ -226,10 +220,10 @@ public class PanicPreferencesFragment extends PreferenceFragment implements Shar
private void showHideConfirmationDialog() { private void showHideConfirmationDialog() {
String appName = getString(R.string.app_name); String appName = getString(R.string.app_name);
AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(R.string.panic_hide_warning_title); builder.setTitle(R.string.panic_hide_warning_title);
builder.setMessage(getString(R.string.panic_hide_warning_message, appName, builder.setMessage(getString(R.string.panic_hide_warning_message, appName,
HidingManager.getUnhidePin(getContext()), getString(R.string.hiding_calculator))); HidingManager.getUnhidePin(getActivity()), getString(R.string.hiding_calculator)));
builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialogInterface, int i) { public void onClick(DialogInterface dialogInterface, int i) {

View File

@ -7,7 +7,6 @@ import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.graphics.LightingColorFilter; import android.graphics.LightingColorFilter;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.support.annotation.ColorRes; import android.support.annotation.ColorRes;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.v4.content.LocalBroadcastManager; import android.support.v4.content.LocalBroadcastManager;
@ -148,7 +147,6 @@ public class WifiQrView extends ScrollView implements SwapWorkflowActivity.Inner
qrUrlBuilder.append(sharingUri.getPath()); qrUrlBuilder.append(sharingUri.getPath());
boolean first = true; boolean first = true;
if (Build.VERSION.SDK_INT > 10) {
Set<String> names = sharingUri.getQueryParameterNames(); Set<String> names = sharingUri.getQueryParameterNames();
for (String name : names) { for (String name : names) {
if (!"ssid".equals(name)) { if (!"ssid".equals(name)) {
@ -163,7 +161,6 @@ public class WifiQrView extends ScrollView implements SwapWorkflowActivity.Inner
qrUrlBuilder.append(sharingUri.getQueryParameter(name).toUpperCase(Locale.ENGLISH)); qrUrlBuilder.append(sharingUri.getQueryParameter(name).toUpperCase(Locale.ENGLISH));
} }
} }
}
String qrUriString = qrUrlBuilder.toString(); String qrUriString = qrUrlBuilder.toString();
Utils.debugLog(TAG, "Encoded swap URI in QR Code: " + qrUriString); Utils.debugLog(TAG, "Encoded swap URI in QR Code: " + qrUriString);

View File

@ -4,7 +4,7 @@ import android.content.Context;
public abstract class CameraCharacteristicsChecker { public abstract class CameraCharacteristicsChecker {
public static CameraCharacteristicsChecker getInstance(final Context context) { public static CameraCharacteristicsChecker getInstance(final Context context) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { if (android.os.Build.VERSION.SDK_INT >= 21) {
return new CameraCharacteristicsMinApiLevel21(context); return new CameraCharacteristicsMinApiLevel21(context);
} else { } else {
return new CameraCharacteristicsMaxApiLevel20(); return new CameraCharacteristicsMaxApiLevel20();

View File

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="SwapTheme.Wizard" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorButtonNormal">@color/swap_bright_blue</item>
<item name="android:actionBarStyle">@style/Widget.AppCompat.ActionBar.Solid</item>
<item name="android:actionButtonStyle">@style/SwapTheme.Wizard.ActionButton</item>
</style>
<style name="AlertDialogBaseThemeDark" parent="Theme.AppCompat.Dialog.Alert">
<item name="android:windowMinWidthMajor">@android:dimen/dialog_min_width_major</item>
<item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
<item name="android:actionOverflowButtonStyle">@style/ActionButtonOverflow</item>
</style>
<style name="AlertDialogBaseThemeLight" parent="Theme.AppCompat.Light.Dialog.Alert">
<item name="android:windowMinWidthMajor">@android:dimen/dialog_min_width_major</item>
<item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
<item name="android:actionOverflowButtonStyle">@style/ActionButtonOverflow</item>
</style>
<style name="AppDetailsLink" parent="AppDetailsLinkBase">
<item name="android:background">?android:attr/selectableItemBackground</item>
</style>
</resources>

View File

@ -23,6 +23,7 @@
<item name="appListItem">#ffffff</item> <item name="appListItem">#ffffff</item>
<item name="lightGrayTextColor">#a6a6a6</item> <item name="lightGrayTextColor">#a6a6a6</item>
<item name="antiFeaturesWarning">@drawable/ic_warning_white_24dp</item> <item name="antiFeaturesWarning">@drawable/ic_warning_white_24dp</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
</style> </style>
<style name="AppBaseThemeLight" parent="Theme.AppCompat.Light.NoActionBar"> <style name="AppBaseThemeLight" parent="Theme.AppCompat.Light.NoActionBar">
@ -47,6 +48,7 @@
<item name="appListItem">#424242</item> <item name="appListItem">#424242</item>
<item name="lightGrayTextColor">#4a4a4a</item> <item name="lightGrayTextColor">#4a4a4a</item>
<item name="antiFeaturesWarning">@drawable/ic_warning_black_24dp</item> <item name="antiFeaturesWarning">@drawable/ic_warning_black_24dp</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
</style> </style>
<style name="AppBaseThemeNight" parent="AppThemeDark"> <style name="AppBaseThemeNight" parent="AppThemeDark">

View File

@ -23,7 +23,7 @@
</PreferenceScreen> </PreferenceScreen>
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="@string/updates"> <PreferenceCategory android:title="@string/updates">
<com.geecko.QuickLyric.view.AppCompatListPreference android:title="@string/update_interval" <ListPreference android:title="@string/update_interval"
android:key="updateInterval" android:key="updateInterval"
android:defaultValue="24" android:defaultValue="24"
android:entries="@array/updateIntervalNames" android:entries="@array/updateIntervalNames"
@ -41,9 +41,9 @@
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="@string/display" <PreferenceCategory android:title="@string/display"
android:key="pref_category_display"> android:key="pref_category_display">
<com.geecko.QuickLyric.view.AppCompatListPreference android:title="@string/pref_language" <ListPreference android:title="@string/pref_language"
android:key="language"/> android:key="language"/>
<com.geecko.QuickLyric.view.AppCompatListPreference android:title="@string/theme" <ListPreference android:title="@string/theme"
android:key="theme" android:key="theme"
android:defaultValue="light" android:defaultValue="light"
android:entries="@array/themeNames" android:entries="@array/themeNames"
@ -119,7 +119,7 @@
<PreferenceCategory android:title="@string/other" <PreferenceCategory android:title="@string/other"
android:key="pref_category_other"> android:key="pref_category_other">
<com.geecko.QuickLyric.view.AppCompatListPreference android:title="@string/cache_downloaded" <ListPreference android:title="@string/cache_downloaded"
android:key="keepCacheFor" android:key="keepCacheFor"
android:defaultValue="86400000" android:defaultValue="86400000"
android:entries="@array/keepCacheNames" android:entries="@array/keepCacheNames"

View File

@ -23,7 +23,7 @@ import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
@Config(constants = BuildConfig.class, application = Application.class, sdk = 24) @Config(constants = BuildConfig.class, application = Application.class)
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class AntiFeaturesTest extends FDroidProviderTest { public class AntiFeaturesTest extends FDroidProviderTest {

View File

@ -16,7 +16,7 @@ import java.util.List;
/** /**
* @author Michael Poehn (michael.poehn@fsfe.org) * @author Michael Poehn (michael.poehn@fsfe.org)
*/ */
@Config(constants = BuildConfig.class, sdk = 24) @Config(constants = BuildConfig.class)
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@SuppressWarnings("LineLength") @SuppressWarnings("LineLength")
public class ProvisionerTest { public class ProvisionerTest {

View File

@ -17,7 +17,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@Config(constants = BuildConfig.class, sdk = 24) @Config(constants = BuildConfig.class)
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@SuppressWarnings("LineLength") @SuppressWarnings("LineLength")
public class UtilsTest { public class UtilsTest {

View File

@ -28,7 +28,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
@Config(constants = BuildConfig.class, application = Application.class, sdk = 24) @Config(constants = BuildConfig.class, application = Application.class)
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class ApkProviderTest extends FDroidProviderTest { public class ApkProviderTest extends FDroidProviderTest {

View File

@ -15,7 +15,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@Config(constants = BuildConfig.class, application = Application.class, sdk = 24) @Config(constants = BuildConfig.class, application = Application.class)
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class AppPrefsProviderTest extends FDroidProviderTest { public class AppPrefsProviderTest extends FDroidProviderTest {

View File

@ -29,7 +29,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@Config(constants = BuildConfig.class, application = Application.class, sdk = 24) @Config(constants = BuildConfig.class, application = Application.class)
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@SuppressWarnings("LineLength") @SuppressWarnings("LineLength")
public class AppProviderTest extends FDroidProviderTest { public class AppProviderTest extends FDroidProviderTest {

View File

@ -19,7 +19,7 @@ import org.robolectric.Shadows;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowContentResolver; import org.robolectric.shadows.ShadowContentResolver;
@Config(constants = BuildConfig.class, application = Application.class, sdk = 24) @Config(constants = BuildConfig.class, application = Application.class)
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class DatabaseMigration { public class DatabaseMigration {

View File

@ -23,7 +23,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@Config(constants = BuildConfig.class, application = Application.class, sdk = 24) @Config(constants = BuildConfig.class, application = Application.class)
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class InstalledAppProviderTest extends FDroidProviderTest { public class InstalledAppProviderTest extends FDroidProviderTest {

View File

@ -13,7 +13,7 @@ import org.robolectric.annotation.Config;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@Config(constants = BuildConfig.class, application = Application.class, sdk = 24) @Config(constants = BuildConfig.class, application = Application.class)
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class PreferredSignatureTest extends FDroidProviderTest { public class PreferredSignatureTest extends FDroidProviderTest {

View File

@ -20,7 +20,7 @@ import java.util.List;
import static org.fdroid.fdroid.Assert.assertInvalidUri; import static org.fdroid.fdroid.Assert.assertInvalidUri;
import static org.fdroid.fdroid.Assert.assertValidUri; import static org.fdroid.fdroid.Assert.assertValidUri;
@Config(constants = BuildConfig.class, sdk = 24) @Config(constants = BuildConfig.class)
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@SuppressWarnings("LineLength") @SuppressWarnings("LineLength")
public class ProviderUriTests { public class ProviderUriTests {

View File

@ -42,7 +42,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
@Config(constants = BuildConfig.class, application = Application.class, sdk = 24) @Config(constants = BuildConfig.class, application = Application.class)
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class RepoProviderTest extends FDroidProviderTest { public class RepoProviderTest extends FDroidProviderTest {

View File

@ -16,7 +16,7 @@ import java.util.List;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@Config(constants = BuildConfig.class, application = Application.class, sdk = 24) @Config(constants = BuildConfig.class, application = Application.class)
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class SuggestedVersionTest extends FDroidProviderTest { public class SuggestedVersionTest extends FDroidProviderTest {

View File

@ -15,7 +15,7 @@ import java.net.URL;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
@Config(constants = BuildConfig.class, sdk = 24) @Config(constants = BuildConfig.class)
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@SuppressWarnings("LineLength") @SuppressWarnings("LineLength")
public class HttpDownloaderTest { public class HttpDownloaderTest {

View File

@ -19,7 +19,7 @@ import java.util.List;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
@Config(constants = BuildConfig.class, sdk = 24) @Config(constants = BuildConfig.class)
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class AcceptableMultiRepoUpdaterTest extends MultiRepoUpdaterTest { public class AcceptableMultiRepoUpdaterTest extends MultiRepoUpdaterTest {
private static final String TAG = "AcceptableMultiRepoTest"; private static final String TAG = "AcceptableMultiRepoTest";

View File

@ -14,7 +14,7 @@ import org.robolectric.annotation.Config;
* because there is so much metadata to parse in the main repo, covering many different aspects * because there is so much metadata to parse in the main repo, covering many different aspects
* of the available metadata. Some apps will be added, others updated, and it should all just work. * of the available metadata. Some apps will be added, others updated, and it should all just work.
*/ */
@Config(constants = BuildConfig.class, sdk = 24) @Config(constants = BuildConfig.class)
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class FDroidRepoUpdateTest extends MultiRepoUpdaterTest { public class FDroidRepoUpdateTest extends MultiRepoUpdaterTest {

View File

@ -55,7 +55,7 @@ import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
@Config(constants = BuildConfig.class, sdk = 24) @Config(constants = BuildConfig.class)
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class IndexV1UpdaterTest extends FDroidProviderTest { public class IndexV1UpdaterTest extends FDroidProviderTest {
public static final String TAG = "IndexV1UpdaterTest"; public static final String TAG = "IndexV1UpdaterTest";

View File

@ -18,7 +18,7 @@ import java.util.List;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@Config(constants = BuildConfig.class, sdk = 24) @Config(constants = BuildConfig.class)
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@SuppressWarnings("LineLength") @SuppressWarnings("LineLength")
public class Issue763MultiRepo extends MultiRepoUpdaterTest { public class Issue763MultiRepo extends MultiRepoUpdaterTest {

View File

@ -34,9 +34,8 @@ import java.util.Map;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
@Config(constants = BuildConfig.class, sdk = 24, shadows = ProperMultiRepoUpdaterTest.ArmSystemProperties.class) @Config(constants = BuildConfig.class, shadows = ProperMultiRepoUpdaterTest.ArmSystemProperties.class)
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@SuppressWarnings("LineLength")
public class ProperMultiRepoUpdaterTest extends MultiRepoUpdaterTest { public class ProperMultiRepoUpdaterTest extends MultiRepoUpdaterTest {
private static final String TAG = "ProperMultiRepoSupport"; private static final String TAG = "ProperMultiRepoSupport";
@ -136,7 +135,8 @@ public class ProperMultiRepoUpdaterTest extends MultiRepoUpdaterTest {
// Provided by both the "Main" and "Conflicting" repo, so need to fetch metdata from the // Provided by both the "Main" and "Conflicting" repo, so need to fetch metdata from the
// repo with the higher "Conflicting" repo has a higher priority. // repo with the higher "Conflicting" repo has a higher priority.
App adAway = AppProvider.Helper.findHighestPriorityMetadata(context.getContentResolver(), "org.adaway"); App adAway = AppProvider.Helper.findHighestPriorityMetadata(context.getContentResolver(),
"org.adaway");
assertAdAwayMetadata(adAway, higherPriority); assertAdAwayMetadata(adAway, higherPriority);
assertAdAwayMetadata(allApps.get("org.adaway"), higherPriority); assertAdAwayMetadata(allApps.get("org.adaway"), higherPriority);
@ -144,17 +144,20 @@ public class ProperMultiRepoUpdaterTest extends MultiRepoUpdaterTest {
// This is only provided by the "Main" or "Archive" repo. Both the main and archive repo both // This is only provided by the "Main" or "Archive" repo. Both the main and archive repo both
// pull their metadata from the same build recipe in fdroidserver. The only difference is that // pull their metadata from the same build recipe in fdroidserver. The only difference is that
// the archive repository contains .apks from further back, but their metadata is the same. // the archive repository contains .apks from further back, but their metadata is the same.
App a2048 = AppProvider.Helper.findHighestPriorityMetadata(context.getContentResolver(), "com.uberspot.a2048"); App a2048 = AppProvider.Helper.findHighestPriorityMetadata(context.getContentResolver(),
"com.uberspot.a2048");
assert2048Metadata(a2048, "Normal"); assert2048Metadata(a2048, "Normal");
assert2048Metadata(allApps.get("com.uberspot.a2048"), "Normal"); assert2048Metadata(allApps.get("com.uberspot.a2048"), "Normal");
// This is only provided by the "Conflicting" repo. // This is only provided by the "Conflicting" repo.
App calendar = AppProvider.Helper.findHighestPriorityMetadata(context.getContentResolver(), "org.dgtale.icsimport"); App calendar = AppProvider.Helper.findHighestPriorityMetadata(context.getContentResolver(),
"org.dgtale.icsimport");
assertCalendarMetadata(calendar, "Conflicting"); assertCalendarMetadata(calendar, "Conflicting");
assertCalendarMetadata(allApps.get("org.dgtale.icsimport"), "Conflicting"); assertCalendarMetadata(allApps.get("org.dgtale.icsimport"), "Conflicting");
// This is only provided by the "Main" repo. // This is only provided by the "Main" repo.
App adb = AppProvider.Helper.findHighestPriorityMetadata(context.getContentResolver(), "siir.es.adbWireless"); App adb = AppProvider.Helper.findHighestPriorityMetadata(context.getContentResolver(),
"siir.es.adbWireless");
assertAdbMetadata(adb, "Normal"); assertAdbMetadata(adb, "Normal");
assertAdbMetadata(allApps.get("siir.es.adbWireless"), "Normal"); assertAdbMetadata(allApps.get("siir.es.adbWireless"), "Normal");
} }
@ -244,7 +247,8 @@ public class ProperMultiRepoUpdaterTest extends MultiRepoUpdaterTest {
} }
private void assertCanUpdate(String packageName, int installedVersion, int expectedUpdateVersion) { private void assertCanUpdate(String packageName, int installedVersion, int expectedUpdateVersion) {
InstalledAppTestUtils.install(context, packageName, installedVersion, "v" + installedVersion, TestUtils.FDROID_CERT); InstalledAppTestUtils.install(context, packageName, installedVersion,
"v" + installedVersion, TestUtils.FDROID_CERT);
List<App> appsToUpdate = AppProvider.Helper.findCanUpdate(context, AppMetadataTable.Cols.ALL); List<App> appsToUpdate = AppProvider.Helper.findCanUpdate(context, AppMetadataTable.Cols.ALL);
assertEquals(1, appsToUpdate.size()); assertEquals(1, appsToUpdate.size());
assertEquals(installedVersion, appsToUpdate.get(0).installedVersionCode); assertEquals(installedVersion, appsToUpdate.get(0).installedVersionCode);
@ -281,7 +285,8 @@ public class ProperMultiRepoUpdaterTest extends MultiRepoUpdaterTest {
} }
private void assert2048Metadata(Repo repo, @RepoIdentifier String id) { private void assert2048Metadata(Repo repo, @RepoIdentifier String id) {
App a2048 = AppProvider.Helper.findSpecificApp(context.getContentResolver(), "com.uberspot.a2048", repo.getId(), AppMetadataTable.Cols.ALL); App a2048 = AppProvider.Helper.findSpecificApp(context.getContentResolver(), "com.uberspot.a2048",
repo.getId(), AppMetadataTable.Cols.ALL);
assert2048Metadata(a2048, id); assert2048Metadata(a2048, id);
} }
@ -300,26 +305,36 @@ public class ProperMultiRepoUpdaterTest extends MultiRepoUpdaterTest {
} }
private void assertAdAwayMetadata(Repo repo, @RepoIdentifier String id) { private void assertAdAwayMetadata(Repo repo, @RepoIdentifier String id) {
App adaway = AppProvider.Helper.findSpecificApp(context.getContentResolver(), "org.adaway", repo.getId(), AppMetadataTable.Cols.ALL); App adaway = AppProvider.Helper.findSpecificApp(context.getContentResolver(), "org.adaway",
repo.getId(), AppMetadataTable.Cols.ALL);
assertAdAwayMetadata(adaway, id); assertAdAwayMetadata(adaway, id);
} }
/** @see ProperMultiRepoUpdaterTest#assert2048Metadata(Repo, String) */ /** @see ProperMultiRepoUpdaterTest#assert2048Metadata(Repo, String) */
private void assertAdAwayMetadata(App adaway, @RepoIdentifier String id) { private void assertAdAwayMetadata(App adaway, @RepoIdentifier String id) {
assertNotNull(adaway); assertNotNull(adaway);
assertEquals(String.format("AdAway", id), adaway.name); assertEquals(String.format("AdAway", id),
assertEquals(String.format("<p>AdAway from %s repo.</p>", id), adaway.description); adaway.name);
assertEquals(String.format("Block advertisements (%s)", id), adaway.summary); assertEquals(String.format("<p>AdAway from %s repo.</p>", id),
assertEquals(String.format("http://sufficientlysecure.org/index.php/adaway?%s", id), adaway.webSite); adaway.description);
assertEquals(String.format("https://github.com/dschuermann/ad-away?%s", id), adaway.sourceCode); assertEquals(String.format("Block advertisements (%s)", id),
assertEquals(String.format("https://github.com/dschuermann/ad-away/issues?%s", id), adaway.issueTracker); adaway.summary);
assertEquals(String.format("https://github.com/dschuermann/ad-away/raw/HEAD/CHANGELOG?%s", id), adaway.changelog); assertEquals(String.format("http://sufficientlysecure.org/index.php/adaway?%s", id),
assertEquals(String.format("http://sufficientlysecure.org/index.php/adaway?%s", id), adaway.donate); adaway.webSite);
assertEquals(String.format("https://github.com/dschuermann/ad-away?%s", id),
adaway.sourceCode);
assertEquals(String.format("https://github.com/dschuermann/ad-away/issues?%s", id),
adaway.issueTracker);
assertEquals(String.format("https://github.com/dschuermann/ad-away/raw/HEAD/CHANGELOG?%s", id),
adaway.changelog);
assertEquals(String.format("http://sufficientlysecure.org/index.php/adaway?%s", id),
adaway.donate);
assertEquals(String.format("369138", id), adaway.flattrID); assertEquals(String.format("369138", id), adaway.flattrID);
} }
private void assertAdbMetadata(Repo repo, @RepoIdentifier String id) { private void assertAdbMetadata(Repo repo, @RepoIdentifier String id) {
App adb = AppProvider.Helper.findSpecificApp(context.getContentResolver(), "siir.es.adbWireless", repo.getId(), AppMetadataTable.Cols.ALL); App adb = AppProvider.Helper.findSpecificApp(context.getContentResolver(), "siir.es.adbWireless",
repo.getId(), AppMetadataTable.Cols.ALL);
assertAdbMetadata(adb, id); assertAdbMetadata(adb, id);
} }
@ -335,20 +350,28 @@ public class ProperMultiRepoUpdaterTest extends MultiRepoUpdaterTest {
} }
private void assertCalendarMetadata(Repo repo, @RepoIdentifier String id) { private void assertCalendarMetadata(Repo repo, @RepoIdentifier String id) {
App calendar = AppProvider.Helper.findSpecificApp(context.getContentResolver(), "org.dgtale.icsimport", repo.getId(), AppMetadataTable.Cols.ALL); App calendar = AppProvider.Helper.findSpecificApp(context.getContentResolver(), "org.dgtale.icsimport",
repo.getId(), AppMetadataTable.Cols.ALL);
assertCalendarMetadata(calendar, id); assertCalendarMetadata(calendar, id);
} }
/** @see ProperMultiRepoUpdaterTest#assert2048Metadata(Repo, String) */ /** @see ProperMultiRepoUpdaterTest#assert2048Metadata(Repo, String) */
private void assertCalendarMetadata(App calendar, @RepoIdentifier String id) { private void assertCalendarMetadata(App calendar, @RepoIdentifier String id) {
assertNotNull(calendar); assertNotNull(calendar);
assertEquals("Add to calendar", calendar.name); assertEquals("Add to calendar",
assertEquals(String.format("<p>Add to calendar from %s repo.</p>", id), calendar.description); calendar.name);
assertEquals(String.format("Import .ics files into calendar (%s)", id), calendar.summary); assertEquals(String.format("<p>Add to calendar from %s repo.</p>", id),
assertEquals(String.format("https://github.com/danielegobbetti/ICSImport/blob/HEAD/README.md?%s", id), calendar.webSite); calendar.description);
assertEquals(String.format("https://github.com/danielegobbetti/ICSImport?%s", id), calendar.sourceCode); assertEquals(String.format("Import .ics files into calendar (%s)", id),
assertEquals(String.format("https://github.com/danielegobbetti/ICSImport/issues?%s", id), calendar.issueTracker); calendar.summary);
assertEquals("2225390", calendar.flattrID); assertEquals(String.format("https://github.com/danielegobbetti/ICSImport/blob/HEAD/README.md?%s", id),
calendar.webSite);
assertEquals(String.format("https://github.com/danielegobbetti/ICSImport?%s", id),
calendar.sourceCode);
assertEquals(String.format("https://github.com/danielegobbetti/ICSImport/issues?%s", id),
calendar.issueTracker);
assertEquals("2225390",
calendar.flattrID);
} }
private void assertMainArchiveRepoMetadata() { private void assertMainArchiveRepoMetadata() {
@ -422,7 +445,7 @@ public class ProperMultiRepoUpdaterTest extends MultiRepoUpdaterTest {
if ("ro.product.cpu.abilist".equals(key)) { if ("ro.product.cpu.abilist".equals(key)) {
return "armeabi"; return "armeabi";
} }
return ShadowSystemProperties.get(key); return ShadowSystemProperties.native_get(key);
} }
} }

View File

@ -53,7 +53,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@Config(constants = BuildConfig.class, sdk = 24) @Config(constants = BuildConfig.class)
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class RepoXMLHandlerTest { public class RepoXMLHandlerTest {
private static final String TAG = "RepoXMLHandlerTest"; private static final String TAG = "RepoXMLHandlerTest";

View File

@ -27,7 +27,7 @@ import org.robolectric.annotation.Config;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@Config(constants = BuildConfig.class, application = Application.class, sdk = 24) @Config(constants = BuildConfig.class, application = Application.class)
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class AppDetailsAdapterTest extends FDroidProviderTest { public class AppDetailsAdapterTest extends FDroidProviderTest {

View File

@ -1,10 +1,22 @@
buildscript { buildscript {
repositories { repositories {
maven {
url "https://repo1.maven.org/maven2"
jcenter() jcenter()
} }
maven {
url 'https://maven.google.com/'
name 'Google'
}
}
dependencies { dependencies {
// 2.2.2 is the version that is included in Debian/stretch classpath 'com.android.tools.build:gradle:3.1.1'
classpath 'com.android.tools.build:gradle:2.2.2'
classpath files('libs/gradle-witness.jar') classpath files('libs/gradle-witness.jar')
} }
} }
allprojects {
repositories {
jcenter()
maven { url 'https://maven.google.com' }
}
}

View File

@ -142,4 +142,7 @@
<property name="influenceFormat" value="1"/> <property name="influenceFormat" value="1"/>
</module> </module>
<module name="SuppressionFilter">
<property name="file" value="config/checkstyle/suppressions.xml"/>
</module>
</module> </module>

View File

@ -0,0 +1,7 @@
<?xml version="1.0"?>
<!DOCTYPE suppressions PUBLIC
"-//Puppy Crawl//DTD Suppressions 1.1//EN"
"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
<suppressions>
<suppress checks="." files="[\\/]kellinwood[\\/].*\.java$"/>
</suppressions>

View File

@ -1,12 +0,0 @@
Copyright (c) 2012-2013 by Paul S. Hawke, 2001,2005-2013 by Jarno Elonen, 2010 by Konstantinos Togias
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of the NanoHttpd organization nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,14 +0,0 @@
apply plugin: 'java'
version = '2.1.0'
sourceCompatibility = 1.7
targetCompatibility = 1.7
jar {
baseName = 'nanohttpd'
}
repositories {
mavenCentral()
}

Some files were not shown because too many files have changed in this diff Show More