Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fba6dd7915 | ||
|
|
c28a51a266 | ||
|
|
751f221fd3 | ||
|
|
33fe753b91 | ||
|
|
aa4d305d2e | ||
|
|
f2b6e258ac | ||
|
|
bbff2a5a34 | ||
|
|
ac1dfce553 | ||
|
|
9c2fa9e35e | ||
|
|
3f00796888 | ||
|
|
10510f8466 | ||
|
|
a556144c2b | ||
|
|
e3055aa853 | ||
|
|
10b5d65a47 | ||
|
|
612f4f3a87 | ||
|
|
b25ab2e878 | ||
|
|
32c06bae83 | ||
|
|
ee39f92658 | ||
|
|
a6cb2050c7 | ||
|
|
25037e1e8e | ||
|
|
e0fc90d37d | ||
|
|
fc27816190 | ||
|
|
83ed52ed6e | ||
|
|
96047afe6a | ||
|
|
38a2d2f6cd | ||
|
|
444258d9ef | ||
|
|
7b47df7f87 | ||
|
|
8d96adcbba | ||
|
|
988a45006c | ||
|
|
3f904b8f7a | ||
|
|
52a282ea27 | ||
|
|
51e629d243 | ||
|
|
726ed177ed | ||
|
|
28ae8b0e48 | ||
|
|
958a76e4f6 | ||
|
|
83012b0163 | ||
|
|
1c02fe4bd2 | ||
|
|
1af27bddcd | ||
|
|
9abf57d1ff | ||
|
|
30d840e027 | ||
|
|
c229bd95dd | ||
|
|
e8ff70ee6a | ||
|
|
d46783405e | ||
|
|
f220ed894e |
@ -35,8 +35,8 @@ Setting up a development environment
|
||||
2. Make sure the "Android Support Repository" is installed in the Android Studio SDK.
|
||||
3. Make sure the latest "Android SDK build-tools" is installed in the Android Studio SDK.
|
||||
4. Create a new Android Studio project. from the Quickstart pannel (use File > Close Project to see it), choose "Checkout from Version Control" then "git".
|
||||
5. Paste the URL for the Flock project when prompted (https://github.com/rhodey/securesync.git)
|
||||
6. Android studio should detect the presence of a project file and ask you wethere to open it. Click "yes".
|
||||
5. Paste the URL for the Flock project when prompted (https://github.com/WhisperSystems/Flock.git)
|
||||
6. Android studio should detect the presence of a project file and ask you whether to open it. Click "yes".
|
||||
7. Default config options should be good enough.
|
||||
8. Project initialisation and build should proceed.
|
||||
|
||||
@ -48,6 +48,4 @@ Code contributions should be sent via github as pull requests, from feature bran
|
||||
Mailing list
|
||||
------------
|
||||
|
||||
Development discussion happens on the whispersystems mailing list.
|
||||
[To join](https://lists.riseup.net/www/info/whispersystems)
|
||||
Send emails to whispersystems@lists.riseup.net
|
||||
Development discussion happens on the [whispersystems mailing list](https://lists.riseup.net/www/info/whispersystems). Send emails to whispersystems@lists.riseup.net
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# Flock
|
||||
# Flock (Discontinued)
|
||||
|
||||
A secure contact and calendar syncing application for Android.
|
||||
|
||||
@ -11,7 +11,9 @@ multiple devices a "sync service" is required, this can be any standards complia
|
||||
3. RFC 6352 - CardDAV: vCard Extensions to Web Distributed Authoring and Versioning (WebDAV)
|
||||
4. RFC 4791 - Calendaring Extensions to WebDAV (CalDAV)
|
||||
|
||||
Currently available on the Play store.
|
||||
The Android app can be downloaded through [Google Play](https://play.google.com/store/apps/details?id=org.anhonesteffort.flock) or via our
|
||||
[alternative distribution channel](https://flock-supplychain.anhonesteffort.org/packages/org.anhonesteffort.flock/current). Note that the
|
||||
certificate presented by our alternative distribution channel is signed by the same private certificate authority pinned by [Flock's trust store](https://github.com/WhisperSystems/Flock/blob/master/flock/src/main/assets/flock.store).
|
||||
|
||||
*[](https://play.google.com/store/apps/details?id=org.anhonesteffort.flock)*
|
||||
|
||||
@ -25,6 +27,8 @@ Interested in helping to translate Flock? Contribute here:
|
||||
|
||||
https://www.transifex.com/projects/p/flock/
|
||||
|
||||
[](https://www.transifex.com/projects/p/flock/)
|
||||
|
||||
## Contributing code
|
||||
Instructions on how to build Flock, as well as on how to setup an IDE to modify it can be found in the [BUILDING.md](BUILDING.md) file.
|
||||
|
||||
|
||||
19
build.gradle
19
build.gradle
@ -1 +1,18 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:1.0.0'
|
||||
classpath files('libs/gradle-witness.jar')
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
||||
maven { url 'https://raw.github.com/whispersystems/maven/master/stripe-btc/releases' }
|
||||
maven { url 'https://raw.github.com/whispersystems/maven/master/gson/releases/' }
|
||||
}
|
||||
}
|
||||
|
||||
20
eol.txt
Normal file
20
eol.txt
Normal file
@ -0,0 +1,20 @@
|
||||
// flock end of life
|
||||
|
||||
1) client update
|
||||
** disable new account registration
|
||||
** disable subscription management
|
||||
** remind daily that eol is approaching
|
||||
** allow export of contacts & calendars to external storage
|
||||
|
||||
2) publish client update and warn in play store details
|
||||
3) ** remove statsd code, deploy new servers, terminate statsd
|
||||
4) update translations while waiting for eol
|
||||
|
||||
5) eol
|
||||
remove apk from play store
|
||||
terminate all flock amazon resources
|
||||
refund active subscriptions
|
||||
cancel active subscriptions
|
||||
close stripe account
|
||||
delete flock api project
|
||||
delete flock payments
|
||||
@ -1,30 +1,22 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:0.12.+'
|
||||
classpath files('libs/gradle-witness.jar')
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'witness'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
||||
maven { url 'https://raw.github.com/whispersystems/maven/master/stripe-btc/releases' }
|
||||
maven { url 'https://raw.github.com/whispersystems/maven/master/gson/releases/' }
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 19
|
||||
buildToolsVersion '19.1.0'
|
||||
|
||||
compileSdkVersion 21
|
||||
buildToolsVersion '21.1.2'
|
||||
|
||||
defaultConfig {
|
||||
applicationId "org.anhonesteffort.flock"
|
||||
versionCode 29
|
||||
versionName "0.9.7"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 19
|
||||
targetSdkVersion 21
|
||||
}
|
||||
|
||||
productFlavors {
|
||||
play { }
|
||||
nonplay { }
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
@ -42,38 +34,47 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile group: 'com.google.guava', name: 'guava', version: '16.0'
|
||||
compile group: 'com.android.support', name: 'support-v4', version: '19.0.1'
|
||||
compile group: 'com.android.support', name: 'support-v4', version: '21.0.3'
|
||||
compile group: 'org.apache.jackrabbit', name: 'jackrabbit-webdav', version: '2.3.7'
|
||||
compile group: 'commons-httpclient', name: 'commons-httpclient', version: '3.1'
|
||||
compile group: 'javax.servlet', name: 'servlet-api', version: '2.5'
|
||||
compile group: 'org.slf4j', name: 'slf4j-simple', version: '1.7.5'
|
||||
compile group: 'com.googlecode.ez-vcard', name: 'ez-vcard', version: '0.9.0'
|
||||
compile group: 'org.mnode.ical4j', name: 'ical4j', version: '1.0.5.2'
|
||||
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.3.0'
|
||||
compile group: 'com.google.code.gson', name: 'gson', version: '2.2.4'
|
||||
compile group: 'com.stripe', name: 'stripe-java-btc', version: '1.12.0-btc-beta'
|
||||
compile group: 'com.google.zxing', name: 'core', version: '3.1.0'
|
||||
compile(group: 'org.whispersystems', name: 'libpastelog', version: '1.0.1') {
|
||||
compile(group: 'org.whispersystems', name: 'libpastelog', version: '1.0.4') {
|
||||
exclude group: 'com.android.support', module: 'support-v4'
|
||||
}
|
||||
|
||||
androidTestCompile group: 'com.squareup', name: 'fest-android', version: '1.0.8'
|
||||
androidTestCompile group: 'com.google.dexmaker', name: 'dexmaker', version: '1.1'
|
||||
androidTestCompile group: 'com.google.dexmaker', name: 'dexmaker-mockito', version: '1.1'
|
||||
|
||||
nonplayCompile (group: 'org.whispersystems.supplychain', name:'libsupplychain', version:'1.0') {
|
||||
exclude group: 'com.android.support', module: 'support-v4'
|
||||
}
|
||||
}
|
||||
|
||||
dependencyVerification {
|
||||
verify = [
|
||||
'com.google.guava:guava:fa917f4f3f6a76375134ba89a40d3a1ce807945a91bbdbe39c31f76e03030868',
|
||||
'com.android.support:support-v4:a4268abd6370c3fd3f94d2a7f9e6e755f5ddd62450cf8bbc62ba789e1274d585',
|
||||
'com.android.support:support-v4:703572d3015a088cc5604b7e38885af3d307c829d0c5ceaf8654ff41c71cd160',
|
||||
'org.apache.jackrabbit:jackrabbit-webdav:9a11e030921bc21de7d6dcf168571a3d2671f72c351498a0daefaf79e0edc888',
|
||||
'commons-httpclient:commons-httpclient:dbd4953d013e10e7c1cc3701a3e6ccd8c950c892f08d804fabfac21705930443',
|
||||
'javax.servlet:servlet-api:c658ea360a70faeeadb66fb3c90a702e4142a0ab7768f9ae9828678e0d9ad4dc',
|
||||
'org.slf4j:slf4j-simple:6d06eedca4768119b2c6ac5adf97e7b5ad57a03d6578d69e734fa083c957dc06',
|
||||
'com.googlecode.ez-vcard:ez-vcard:2e09cc227f8f269be5fbdc17b999b9514e239281754a0b4d4c221e29ba2d0876',
|
||||
'org.mnode.ical4j:ical4j:dedb09e8975f0703b3ae5ad60205fedda7512f91d837070be99732001a08b138',
|
||||
'com.fasterxml.jackson.core:jackson-databind:9b789c2de23ff5a1ae1fc8193ea79e34f16d74c64c51491fbe76ca277349e694',
|
||||
'com.google.code.gson:gson:c0328cd07ca9e363a5acd00c1cf4afe8cf554bd6d373834981ba05cebec687fb',
|
||||
'com.stripe:stripe-java-btc:daaabd181eb6bc4868d739e023226d344a56bf6020a7ad057dc0c26fc7d223da',
|
||||
'com.google.zxing:core:f00b32f7a1b0edc914a8f74301e8dc34f189afc4698e9c8cc54e5d46772734a5',
|
||||
'org.whispersystems:libpastelog:3ccf00fe1597eb8ca1e5de99b17fc225387a1b80b5bbc00ec1bc4d4f3ea9cdde',
|
||||
'com.android.support:support-annotations:fdee2354787ef66b268e75958de3f7f6c4f8f325510a6dac9f49c929f83a63de',
|
||||
'org.slf4j:slf4j-api:367b909030f714ee1176ab096b681e06348f03385e98d1bce0ed801b5452357e',
|
||||
'org.slf4j:jcl-over-slf4j:261e66d2b5d95ce1bc9923ab5e614f58b2568787b2b55f74dbb861b60ff798e0',
|
||||
'org.jsoup:jsoup:37e3b44fb9476a677a956fb684090b9b4a1e4d38bcfc30b499dbc83f109422c0',
|
||||
'org.freemarker:freemarker:c26923394f3f1cf0427f515ee3bb6be66d1a7f4261e6d6f0504fdec63ab85da8',
|
||||
@ -82,9 +83,7 @@ dependencyVerification {
|
||||
'commons-lang:commons-lang:50f11b09f877c294d56f24463f47d28f929cf5044f648661c0f0cfbae9a2f49c',
|
||||
'backport-util-concurrent:backport-util-concurrent:f5759b7fcdfc83a525a036deedcbd32e5b536b625ebc282426f16ca137eb5902',
|
||||
'com.fasterxml.jackson.core:jackson-annotations:0c8c3811322cc84c09a93f34436fe784a1259dd5376a90aec5a73493456f757d',
|
||||
'org.slf4j:slf4j-api:fe30825245d2336c859dc38d60c0fc5f3668dbf29cd586828d2b5667ec355b91',
|
||||
'com.fasterxml.jackson.core:jackson-core:61f84f93e3f901134d7498b50119ee01074f10d59560e45ccd3e1d48cfec493b',
|
||||
'org.whispersystems:libpastelog:fdc5bea9c48d9b7bb6fbe775717f84537d3334cead157eefc57c8e7ec7e90c81'
|
||||
'com.fasterxml.jackson.core:jackson-core:61f84f93e3f901134d7498b50119ee01074f10d59560e45ccd3e1d48cfec493b'
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
@ -0,0 +1,24 @@
|
||||
{
|
||||
"id" : "ACCOUNT00",
|
||||
"version" : 1,
|
||||
"salt" : "salt",
|
||||
"password_sha512" : "OJb6HMgLjR16KN5vh5Jk1IgczieFnUtMQ2Hcx997eIfXzoeaMhseBOYH7B0B0NLS8VuPZpsK+0VLI1h7OMo7TA==",
|
||||
"stripe_customer_id" : "stripe00",
|
||||
"create_date" : 1391853600000,
|
||||
"last_stripe_charge_failed" : false,
|
||||
"auto_renew_enabled" : false,
|
||||
"subscription_plan" : {
|
||||
"account_id" : "nope",
|
||||
"plan_type" : 0,
|
||||
"plan_id" : "nope"
|
||||
},
|
||||
"subscriptions": [
|
||||
{
|
||||
"account_id" : "ACCOUNT00",
|
||||
"payment_id" : "payment00",
|
||||
"create_date" : 1391853600000,
|
||||
"days_credit" : 365,
|
||||
"cost_usd" : 1.337
|
||||
}
|
||||
]
|
||||
}
|
||||
15
flock/src/androidTest/assets/fixtures/FlockAccount.json
Normal file
15
flock/src/androidTest/assets/fixtures/FlockAccount.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"id" : "ACCOUNT00",
|
||||
"version" : 1,
|
||||
"salt" : "salt",
|
||||
"password_sha512" : "OJb6HMgLjR16KN5vh5Jk1IgczieFnUtMQ2Hcx997eIfXzoeaMhseBOYH7B0B0NLS8VuPZpsK+0VLI1h7OMo7TA==",
|
||||
"stripe_customer_id" : "stripe00",
|
||||
"create_date" : 1391853600000,
|
||||
"last_stripe_charge_failed" : false,
|
||||
"auto_renew_enabled" : false,
|
||||
"subscription_plan" : {
|
||||
"account_id" : "nope",
|
||||
"plan_type" : 0,
|
||||
"plan_id" : "nope"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
{
|
||||
"account_id" : "ACCOUNT00",
|
||||
"card_last_four" : "6666",
|
||||
"card_expiration" : "10/2020"
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
{
|
||||
"account_id" : "ACCOUNT00",
|
||||
"payment_id" : "payment00",
|
||||
"create_date" : 1391853600000,
|
||||
"days_credit" : 365,
|
||||
"cost_usd" : 1.337
|
||||
}
|
||||
8
flock/src/androidTest/assets/fixtures/GooglePlan.json
Normal file
8
flock/src/androidTest/assets/fixtures/GooglePlan.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"account_id" : "ACCOUNT00",
|
||||
"plan_type" : 2,
|
||||
"plan_id" : "google00",
|
||||
"package_name" : "org.anhonesteffort.flock",
|
||||
"purchase_token" : "purchase00",
|
||||
"expiration_date" : 1391853600000
|
||||
}
|
||||
5
flock/src/androidTest/assets/fixtures/StripePlan.json
Normal file
5
flock/src/androidTest/assets/fixtures/StripePlan.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"account_id" : "ACCOUNT00",
|
||||
"plan_type" : 1,
|
||||
"plan_id" : "stripe00"
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
{
|
||||
"account_id" : "nope",
|
||||
"plan_type" : 0,
|
||||
"plan_id" : "nope"
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
{
|
||||
"known" : "known",
|
||||
"unknown" : "unknown"
|
||||
}
|
||||
BIN
flock/src/androidTest/assets/flock.store
Normal file
BIN
flock/src/androidTest/assets/flock.store
Normal file
Binary file not shown.
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock.test;
|
||||
|
||||
import android.test.InstrumentationTestCase;
|
||||
|
||||
/**
|
||||
* rhodey
|
||||
*/
|
||||
public class InstrumentationTestCaseWithMocks extends InstrumentationTestCase {
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
System.setProperty(
|
||||
"dexmaker.dexcache",
|
||||
getInstrumentation().getTargetContext().getCacheDir().getPath()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock.test.auth;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
|
||||
/**
|
||||
* rhodey
|
||||
*/
|
||||
public class DavAccountTest extends AndroidTestCase {
|
||||
|
||||
private static final String MOCK_USER_ID = "rhodey";
|
||||
private static final String MOCK_AUTH_TOKEN = "token-token-token";
|
||||
private static final String MOCK_DAV_HOST = "https://flock.sync/";
|
||||
|
||||
public void testToFromBundle() {
|
||||
final DavAccount account = new DavAccount(MOCK_USER_ID, MOCK_AUTH_TOKEN, MOCK_DAV_HOST);
|
||||
final Optional<DavAccount> accountFromBundle = DavAccount.build(account.toBundle());
|
||||
|
||||
assertTrue(accountFromBundle.isPresent());
|
||||
assertTrue(accountFromBundle.get().getUserId().equals(MOCK_USER_ID));
|
||||
assertTrue(accountFromBundle.get().getAuthToken().equals(MOCK_AUTH_TOKEN));
|
||||
assertTrue(accountFromBundle.get().getDavHostHREF().equals(MOCK_DAV_HOST));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock.test.crypto;
|
||||
|
||||
import org.anhonesteffort.flock.crypto.HidingUtil;
|
||||
import org.anhonesteffort.flock.crypto.MasterCipher;
|
||||
import org.anhonesteffort.flock.test.InstrumentationTestCaseWithMocks;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* rhodey
|
||||
*/
|
||||
public class HidingUtilTest extends InstrumentationTestCaseWithMocks {
|
||||
|
||||
private final byte[] PLAINTEXT_STUFF = "plaintext stuff".getBytes();
|
||||
|
||||
private MasterCipher masterCipher;
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
masterCipher = mock(MasterCipher.class);
|
||||
|
||||
when(masterCipher.encryptAndEncode(any(byte[].class))).thenReturn(PLAINTEXT_STUFF);
|
||||
when(masterCipher.decodeAndDecrypt(any(byte[].class))).thenReturn(PLAINTEXT_STUFF);
|
||||
}
|
||||
|
||||
public void testEncryptAndDecrypt() throws Exception {
|
||||
final byte[] ciphertext = HidingUtil.encryptEncodeAndPrefix(masterCipher, PLAINTEXT_STUFF);
|
||||
final byte[] plaintext = HidingUtil.decodeAndDecryptIfNecessary(masterCipher, ciphertext);
|
||||
|
||||
assertTrue(Arrays.equals(PLAINTEXT_STUFF, plaintext));
|
||||
}
|
||||
|
||||
public void testDecryptNonEncrypted() throws Exception {
|
||||
final byte[] plaintext = HidingUtil.decodeAndDecryptIfNecessary(masterCipher, PLAINTEXT_STUFF);
|
||||
|
||||
assertTrue(Arrays.equals(PLAINTEXT_STUFF, plaintext));
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,10 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock.test.crypto;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
|
||||
import org.anhonesteffort.flock.crypto.KeyHelper;
|
||||
import org.anhonesteffort.flock.crypto.KeyStore;
|
||||
@ -27,8 +43,6 @@ import javax.crypto.spec.SecretKeySpec;
|
||||
*/
|
||||
public class KeyHelperTest extends AndroidTestCase {
|
||||
|
||||
private static final String HACK_PREFERENCES_NAME = "org.anhonesteffort.flock.crypto.KeyStore";
|
||||
|
||||
private Context context;
|
||||
|
||||
@Override
|
||||
@ -36,32 +50,6 @@ public class KeyHelperTest extends AndroidTestCase {
|
||||
context = this.getContext();
|
||||
}
|
||||
|
||||
private static void saveBytes(Context context, String key, byte[] value) {
|
||||
SharedPreferences settings = context.getSharedPreferences(HACK_PREFERENCES_NAME, Context.MODE_MULTI_PROCESS);
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
|
||||
editor.putString(key, Base64.encodeBytes(value));
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
private static void saveString(Context context, String key, String value) {
|
||||
SharedPreferences settings = context.getSharedPreferences(HACK_PREFERENCES_NAME, Context.MODE_MULTI_PROCESS);
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
|
||||
editor.putString(key, value);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
private static Optional<byte[]> retrieveBytes(Context context, String key) throws IOException {
|
||||
SharedPreferences settings = context.getSharedPreferences(HACK_PREFERENCES_NAME, Context.MODE_MULTI_PROCESS);
|
||||
String encodedValue = settings.getString(key, null);
|
||||
|
||||
if (encodedValue == null)
|
||||
return Optional.absent();
|
||||
|
||||
return Optional.of(Base64.decode(encodedValue));
|
||||
}
|
||||
|
||||
private byte[] encryptAndEncode(byte[] iv, SecretKey cipherKey, SecretKey macKey, byte[] data)
|
||||
throws IOException, GeneralSecurityException
|
||||
{
|
||||
@ -81,9 +69,9 @@ public class KeyHelperTest extends AndroidTestCase {
|
||||
public void testKeyHelperGenerateKeyMaterial() throws Exception {
|
||||
KeyHelper.generateAndSaveSaltAndKeyMaterial(context);
|
||||
|
||||
Optional<byte[]> resultCipherKeyBytes = retrieveBytes(context, "KEY_CIPHER_KEY");
|
||||
Optional<byte[]> resultMacKeyBytes = retrieveBytes(context, "KEY_MAC_KEY");
|
||||
Optional<byte[]> resultSaltBytes = retrieveBytes(context, "KEY_KEY_MATERIAL_SALT");
|
||||
Optional<byte[]> resultCipherKeyBytes = KeyStore.getCipherKey(context);
|
||||
Optional<byte[]> resultMacKeyBytes = KeyStore.getMacKey(context);
|
||||
Optional<byte[]> resultSaltBytes = KeyStore.getKeyMaterialSalt(context);
|
||||
|
||||
assertTrue("KeyHelper can generate key material.",
|
||||
resultCipherKeyBytes.get().length > 0 &&
|
||||
@ -102,8 +90,8 @@ public class KeyHelperTest extends AndroidTestCase {
|
||||
final SecretKey testCipherKey = new SecretKeySpec(cipherKeyBytes, "AES");
|
||||
final SecretKey testMacKey = new SecretKeySpec(macKeyBytes, "SHA256");
|
||||
|
||||
saveBytes(context, "KEY_CIPHER_KEY", cipherKeyBytes);
|
||||
saveBytes(context, "KEY_MAC_KEY", macKeyBytes);
|
||||
KeyStore.saveCipherKey(context, cipherKeyBytes);
|
||||
KeyStore.saveMacKey(context, macKeyBytes);
|
||||
|
||||
byte[] encodedKeyHelperResult = KeyHelper.getMasterCipher(context).get().encryptAndEncode(plaintext);
|
||||
byte[] keyHelperResult = Base64.decode(encodedKeyHelperResult);
|
||||
@ -122,10 +110,10 @@ public class KeyHelperTest extends AndroidTestCase {
|
||||
final byte[] macKeyBytes = new byte[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
|
||||
final byte[] saltBytes = new byte[] {2, 2, 2, 2, 2, 2, 2, 2};
|
||||
|
||||
saveBytes(context, "KEY_CIPHER_KEY", cipherKeyBytes);
|
||||
saveBytes(context, "KEY_MAC_KEY", macKeyBytes);
|
||||
saveBytes(context, "KEY_KEY_MATERIAL_SALT", saltBytes);
|
||||
saveString(context, "KEY_OLD_MASTER_PASSPHRASE", masterPassphrase);
|
||||
KeyStore.saveCipherKey(context, cipherKeyBytes);
|
||||
KeyStore.saveMacKey(context, macKeyBytes);
|
||||
KeyStore.saveKeyMaterialSalt(context, saltBytes);
|
||||
KeyStore.saveMasterPassphrase(context, masterPassphrase);
|
||||
|
||||
Optional<String> encodedSalt = KeyHelper.buildEncodedSalt(context);
|
||||
Optional<String> encryptedKeyMaterial = KeyHelper.buildEncryptedKeyMaterial(context);
|
||||
@ -135,17 +123,37 @@ public class KeyHelperTest extends AndroidTestCase {
|
||||
};
|
||||
|
||||
KeyStore.invalidateKeyMaterial(context);
|
||||
saveString(context, "KEY_OLD_MASTER_PASSPHRASE", masterPassphrase);
|
||||
KeyStore.saveMasterPassphrase(context, masterPassphrase);
|
||||
|
||||
KeyHelper.importSaltAndEncryptedKeyMaterial(context, saltAndEncryptedKeyMaterial);
|
||||
|
||||
Optional<byte[]> resultCipherKeyBytes = retrieveBytes(context, "KEY_CIPHER_KEY");
|
||||
Optional<byte[]> resultMacKeyBytes = retrieveBytes(context, "KEY_MAC_KEY");
|
||||
Optional<byte[]> resultSaltBytes = retrieveBytes(context, "KEY_KEY_MATERIAL_SALT");
|
||||
Optional<byte[]> resultCipherKeyBytes = KeyStore.getCipherKey(context);
|
||||
Optional<byte[]> resultMacKeyBytes = KeyStore.getMacKey(context);
|
||||
Optional<byte[]> resultSaltBytes = KeyStore.getKeyMaterialSalt(context);
|
||||
|
||||
assertTrue("KeyHelper can export and import encrypted key material.",
|
||||
Arrays.equals(resultCipherKeyBytes.get(), cipherKeyBytes) &&
|
||||
Arrays.equals(resultMacKeyBytes.get(), macKeyBytes) &&
|
||||
Arrays.equals(resultSaltBytes.get(), saltBytes));
|
||||
}
|
||||
|
||||
public void testMasterPassphraseIsValid() throws Exception {
|
||||
final String masterPassphrase = "oioioi";
|
||||
|
||||
KeyStore.saveMasterPassphrase(context, masterPassphrase);
|
||||
KeyHelper.generateAndSaveSaltAndKeyMaterial(context);
|
||||
|
||||
assertTrue(KeyHelper.masterPassphraseIsValid(context));
|
||||
}
|
||||
|
||||
public void testMasterPassphraseIsInvalid() throws Exception {
|
||||
final String masterPassphrase = "oioioi";
|
||||
|
||||
KeyStore.saveMasterPassphrase(context, masterPassphrase);
|
||||
KeyHelper.generateAndSaveSaltAndKeyMaterial(context);
|
||||
KeyStore.saveMasterPassphrase(context, masterPassphrase.concat("nope"));
|
||||
|
||||
assertTrue(!KeyHelper.masterPassphraseIsValid(context));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock.test.registration;
|
||||
|
||||
import android.test.InstrumentationTestCase;
|
||||
|
||||
import org.anhonesteffort.flock.registration.HttpClientFactory;
|
||||
import org.apache.http.conn.scheme.Scheme;
|
||||
import org.apache.http.conn.scheme.SchemeRegistry;
|
||||
import org.apache.http.conn.ssl.SSLSocketFactory;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
|
||||
/**
|
||||
* rhodey
|
||||
*/
|
||||
public class HttpClientFactoryTest extends InstrumentationTestCase {
|
||||
|
||||
public void testScheme() throws Exception {
|
||||
final HttpClientFactory httpFactory = new HttpClientFactory(getInstrumentation().getContext());
|
||||
final DefaultHttpClient httpClient = httpFactory.buildClient();
|
||||
final SchemeRegistry schemes = httpClient.getConnectionManager().getSchemeRegistry();
|
||||
|
||||
final Scheme httpScheme = schemes.getScheme("http");
|
||||
final Scheme httpsScheme = schemes.getScheme("https");
|
||||
|
||||
assertTrue(httpScheme != null && httpsScheme != null);
|
||||
assertTrue(schemes.getSchemeNames().size() == 2);
|
||||
|
||||
assertTrue(httpsScheme.getDefaultPort() == 443);
|
||||
assertTrue(httpsScheme.getSocketFactory() instanceof SSLSocketFactory);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock.test.registration;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
|
||||
import org.anhonesteffort.flock.registration.ModelFactory;
|
||||
import org.anhonesteffort.flock.test.InstrumentationTestCaseWithMocks;
|
||||
import org.anhonesteffort.flock.test.registration.model.AugmentedFlockAccountTest;
|
||||
import org.anhonesteffort.flock.test.registration.model.FlockCardInformationTest;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpResponse;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import static org.anhonesteffort.flock.test.util.JsonHelpers.jsonFixture;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* rhodey
|
||||
*/
|
||||
public class ModelFactoryTest extends InstrumentationTestCaseWithMocks {
|
||||
|
||||
private AssetManager assets;
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
assets = getInstrumentation().getContext().getAssets();
|
||||
}
|
||||
|
||||
private InputStream asStream(String filename) throws IOException {
|
||||
return new ByteArrayInputStream(jsonFixture(assets, filename).getBytes());
|
||||
}
|
||||
|
||||
public void testBuildAccount() throws Exception {
|
||||
final HttpResponse response = mock(HttpResponse.class);
|
||||
final HttpEntity entity = mock(HttpEntity.class);
|
||||
|
||||
when(response.getEntity()).thenReturn(entity);
|
||||
when(entity.getContent()).thenReturn(asStream("fixtures/AugmentedFlockAccount.json"));
|
||||
|
||||
assertTrue(
|
||||
ModelFactory.buildAccount(response).equals(AugmentedFlockAccountTest.accountNoPlan())
|
||||
);
|
||||
}
|
||||
|
||||
public void testBuildCard() throws Exception {
|
||||
final HttpResponse response = mock(HttpResponse.class);
|
||||
final HttpEntity entity = mock(HttpEntity.class);
|
||||
|
||||
when(response.getEntity()).thenReturn(entity);
|
||||
when(entity.getContent()).thenReturn(asStream("fixtures/FlockCardInformation.json"));
|
||||
|
||||
assertTrue(
|
||||
ModelFactory.buildCard(response).equals(FlockCardInformationTest.card())
|
||||
);
|
||||
}
|
||||
|
||||
public void testBuildBooleanTrue() throws Exception {
|
||||
final HttpResponse response = mock(HttpResponse.class);
|
||||
final HttpEntity entity = mock(HttpEntity.class);
|
||||
final InputStream trueStream = new ByteArrayInputStream("true".getBytes());
|
||||
|
||||
when(response.getEntity()).thenReturn(entity);
|
||||
when(entity.getContent()).thenReturn(trueStream);
|
||||
|
||||
assertTrue(
|
||||
ModelFactory.buildBoolean(response).equals(Boolean.TRUE)
|
||||
);
|
||||
}
|
||||
|
||||
public void testBuildBooleanFalse() throws Exception {
|
||||
final HttpResponse response = mock(HttpResponse.class);
|
||||
final HttpEntity entity = mock(HttpEntity.class);
|
||||
final InputStream falseStream = new ByteArrayInputStream("false".getBytes());
|
||||
|
||||
when(response.getEntity()).thenReturn(entity);
|
||||
when(entity.getContent()).thenReturn(falseStream);
|
||||
|
||||
assertTrue(
|
||||
ModelFactory.buildBoolean(response).equals(Boolean.FALSE)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock.test.registration.model;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
import android.test.InstrumentationTestCase;
|
||||
|
||||
import org.anhonesteffort.flock.registration.model.AugmentedFlockAccount;
|
||||
import org.anhonesteffort.flock.registration.model.FlockSubscription;
|
||||
import org.anhonesteffort.flock.util.TimeUtil;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.anhonesteffort.flock.test.util.JsonHelpers.fromJson;
|
||||
import static org.anhonesteffort.flock.test.util.JsonHelpers.jsonFixture;
|
||||
|
||||
/**
|
||||
* Programmer: rhodey
|
||||
*/
|
||||
public class AugmentedFlockAccountTest extends InstrumentationTestCase {
|
||||
|
||||
private AssetManager assets;
|
||||
|
||||
public static AugmentedFlockAccount accountNoPlan() {
|
||||
final List<FlockSubscription> subscriptions = new LinkedList<>();
|
||||
subscriptions.add(FlockSubscriptionTest.subscription());
|
||||
|
||||
return new AugmentedFlockAccount(
|
||||
FlockAccountTest.accountNoPlan(),
|
||||
subscriptions
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
assets = getInstrumentation().getContext().getAssets();
|
||||
}
|
||||
|
||||
public void testDeserialize() throws Exception {
|
||||
final AugmentedFlockAccount account = fromJson(
|
||||
jsonFixture(assets, "fixtures/AugmentedFlockAccount.json"),
|
||||
AugmentedFlockAccount.class
|
||||
);
|
||||
|
||||
assertTrue(accountNoPlan().equals(account));
|
||||
}
|
||||
|
||||
public void testDaysCredit() throws Exception {
|
||||
final AugmentedFlockAccount account = accountNoPlan();
|
||||
Long daysCredit = 0L;
|
||||
|
||||
for (FlockSubscription subscription : account.getSubscriptions())
|
||||
daysCredit += subscription.getDaysCredit();
|
||||
|
||||
final Long msSinceCreate = (new Date().getTime() - account.getCreateDate().getTime());
|
||||
final Long daysSinceCreate = TimeUtil.millisecondsToDays(msSinceCreate);
|
||||
final Long daysRemaining = (daysCredit - daysSinceCreate);
|
||||
|
||||
assertTrue("days remaining is calculated correctly",
|
||||
daysRemaining.equals(account.getDaysRemaining()));
|
||||
}
|
||||
|
||||
public void testDaysCreditMany() throws Exception {
|
||||
final AugmentedFlockAccount account = accountNoPlan();
|
||||
Long daysCredit = 0L;
|
||||
|
||||
account.getSubscriptions().add(new FlockSubscription(account.getId(), "nope", new Date(), 9001, 0D));
|
||||
for (FlockSubscription subscription : account.getSubscriptions())
|
||||
daysCredit += subscription.getDaysCredit();
|
||||
|
||||
final Long msSinceCreate = (new Date().getTime() - account.getCreateDate().getTime());
|
||||
final Long daysSinceCreate = TimeUtil.millisecondsToDays(msSinceCreate);
|
||||
final Long daysRemaining = (daysCredit - daysSinceCreate);
|
||||
|
||||
assertTrue("days remaining is calculated correctly",
|
||||
daysRemaining.equals(account.getDaysRemaining()));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock.test.registration.model;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
import android.test.InstrumentationTestCase;
|
||||
|
||||
import org.anhonesteffort.flock.registration.model.FlockAccount;
|
||||
import org.anhonesteffort.flock.test.util.DateHelper;
|
||||
|
||||
import static org.anhonesteffort.flock.test.util.JsonHelpers.fromJson;
|
||||
import static org.anhonesteffort.flock.test.util.JsonHelpers.jsonFixture;
|
||||
|
||||
/**
|
||||
* rhodey
|
||||
*/
|
||||
public class FlockAccountTest extends InstrumentationTestCase {
|
||||
|
||||
public static final String TEST_ACCOUNT_ID = "ACCOUNT00";
|
||||
public static final Integer TEST_VERSION = 1;
|
||||
public static final String TEST_SALT = "salt";
|
||||
public static final String TEST_PLAINTEXT_PASSWORD = "0000";
|
||||
public static final String TEST_STRIPE_ID = "stripe00";
|
||||
public static final Boolean TEST_LAST_CHARGE_FAILED = false;
|
||||
public static final Boolean TEST_AUTO_RENEW = false;
|
||||
|
||||
private AssetManager assets;
|
||||
|
||||
public static FlockAccount accountNoPlan() {
|
||||
final FlockAccount account = new FlockAccount(
|
||||
TEST_ACCOUNT_ID, TEST_VERSION, TEST_SALT,
|
||||
null, TEST_STRIPE_ID, DateHelper.getMockDate(),
|
||||
TEST_LAST_CHARGE_FAILED, TEST_AUTO_RENEW, SubscriptionPlanTest.nonePlan()
|
||||
);
|
||||
account.setPassword(TEST_PLAINTEXT_PASSWORD);
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
public static FlockAccount accountStripePlan() {
|
||||
final FlockAccount account = new FlockAccount(
|
||||
TEST_ACCOUNT_ID, TEST_VERSION, TEST_SALT,
|
||||
null, TEST_STRIPE_ID, DateHelper.getMockDate(),
|
||||
TEST_LAST_CHARGE_FAILED, TEST_AUTO_RENEW, SubscriptionPlanTest.stripePlan()
|
||||
);
|
||||
account.setPassword(TEST_PLAINTEXT_PASSWORD);
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
public static FlockAccount accountGooglePlan() {
|
||||
final FlockAccount account = new FlockAccount(
|
||||
TEST_ACCOUNT_ID, TEST_VERSION, TEST_SALT,
|
||||
null, TEST_STRIPE_ID, DateHelper.getMockDate(),
|
||||
TEST_LAST_CHARGE_FAILED, TEST_AUTO_RENEW, SubscriptionPlanTest.googlePlan()
|
||||
);
|
||||
account.setPassword(TEST_PLAINTEXT_PASSWORD);
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
assets = getInstrumentation().getContext().getAssets();
|
||||
}
|
||||
|
||||
public void testDeserialize() throws Exception {
|
||||
final FlockAccount account = fromJson(
|
||||
jsonFixture(assets, "fixtures/FlockAccount.json"),
|
||||
FlockAccount.class
|
||||
);
|
||||
|
||||
assertTrue(accountNoPlan().equals(account));
|
||||
}
|
||||
|
||||
public void testToFromBundle() throws Exception {
|
||||
assertTrue(FlockAccount.build(accountNoPlan().toBundle()).get().equals(accountNoPlan()));
|
||||
assertTrue(FlockAccount.build(accountStripePlan().toBundle()).get().equals(accountStripePlan()));
|
||||
assertTrue(FlockAccount.build(accountGooglePlan().toBundle()).get().equals(accountGooglePlan()));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock.test.registration.model;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
import android.test.InstrumentationTestCase;
|
||||
|
||||
import org.anhonesteffort.flock.registration.model.FlockCardInformation;
|
||||
|
||||
import static org.anhonesteffort.flock.test.util.JsonHelpers.fromJson;
|
||||
import static org.anhonesteffort.flock.test.util.JsonHelpers.jsonFixture;
|
||||
|
||||
/**
|
||||
* rhodey
|
||||
*/
|
||||
public class FlockCardInformationTest extends InstrumentationTestCase {
|
||||
|
||||
public static final String TEST_CARD_LAST_FOUR = "6666";
|
||||
public static final String TEST_CARD_EXPIRATION = "10/2020";
|
||||
|
||||
private AssetManager assets;
|
||||
|
||||
public static FlockCardInformation card() {
|
||||
return new FlockCardInformation(
|
||||
FlockAccountTest.TEST_ACCOUNT_ID,
|
||||
TEST_CARD_LAST_FOUR,
|
||||
TEST_CARD_EXPIRATION
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
assets = getInstrumentation().getContext().getAssets();
|
||||
}
|
||||
|
||||
public void testDeserialize() throws Exception {
|
||||
final FlockCardInformation card = fromJson(
|
||||
jsonFixture(assets, "fixtures/FlockCardInformation.json"),
|
||||
FlockCardInformation.class
|
||||
);
|
||||
|
||||
assertTrue(card().equals(card));
|
||||
}
|
||||
|
||||
public void testToFromBundle() throws Exception {
|
||||
assertTrue(
|
||||
FlockCardInformation.build(card().toBundle()).get().equals(card())
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock.test.registration.model;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
import android.test.InstrumentationTestCase;
|
||||
|
||||
import org.anhonesteffort.flock.registration.model.FlockSubscription;
|
||||
import org.anhonesteffort.flock.test.util.DateHelper;
|
||||
|
||||
import static org.anhonesteffort.flock.test.util.JsonHelpers.fromJson;
|
||||
import static org.anhonesteffort.flock.test.util.JsonHelpers.jsonFixture;
|
||||
|
||||
/**
|
||||
* Programmer: rhodey
|
||||
*/
|
||||
public class FlockSubscriptionTest extends InstrumentationTestCase {
|
||||
|
||||
public static final String TEST_PAYMENT_ID = "payment00";
|
||||
public static final Integer TEST_DAYS_CREDIT = 365;
|
||||
public static final Double TEST_COST_USD = 1.337;
|
||||
|
||||
private AssetManager assets;
|
||||
|
||||
public static FlockSubscription subscription() {
|
||||
return new FlockSubscription(
|
||||
FlockAccountTest.TEST_ACCOUNT_ID, TEST_PAYMENT_ID, DateHelper.getMockDate(),
|
||||
TEST_DAYS_CREDIT, TEST_COST_USD
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
assets = getInstrumentation().getContext().getAssets();
|
||||
}
|
||||
|
||||
public void testDeserialize() throws Exception {
|
||||
final FlockSubscription subscription = fromJson(
|
||||
jsonFixture(assets, "fixtures/FlockSubscription.json"),
|
||||
FlockSubscription.class
|
||||
);
|
||||
|
||||
assertTrue(subscription().equals(subscription));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock.test.registration.model;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
import android.test.InstrumentationTestCase;
|
||||
|
||||
import org.anhonesteffort.flock.registration.model.GooglePlan;
|
||||
import org.anhonesteffort.flock.registration.model.StripePlan;
|
||||
import org.anhonesteffort.flock.registration.model.SubscriptionPlan;
|
||||
import org.anhonesteffort.flock.test.util.DateHelper;
|
||||
|
||||
import static org.anhonesteffort.flock.test.util.JsonHelpers.fromJson;
|
||||
import static org.anhonesteffort.flock.test.util.JsonHelpers.jsonFixture;
|
||||
|
||||
/**
|
||||
* Programmer: rhodey
|
||||
*/
|
||||
public class SubscriptionPlanTest extends InstrumentationTestCase {
|
||||
|
||||
public static final String TEST_STRIPE_PLAN_ID = "stripe00";
|
||||
public static final String TEST_GOOGLE_PLAN_ID = "google00";
|
||||
public static final String TEST_GOOGLE_PURCHASE_TOKEN = "purchase00";
|
||||
|
||||
private AssetManager assets;
|
||||
|
||||
public static SubscriptionPlan nonePlan() {
|
||||
return SubscriptionPlan.PLAN_NONE;
|
||||
}
|
||||
|
||||
public static StripePlan stripePlan() {
|
||||
return new StripePlan(FlockAccountTest.TEST_ACCOUNT_ID, TEST_STRIPE_PLAN_ID);
|
||||
}
|
||||
|
||||
public static GooglePlan googlePlan() {
|
||||
return new GooglePlan(
|
||||
FlockAccountTest.TEST_ACCOUNT_ID, TEST_GOOGLE_PLAN_ID,
|
||||
TEST_GOOGLE_PURCHASE_TOKEN, DateHelper.getMockDate().getTime()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
assets = getInstrumentation().getContext().getAssets();
|
||||
}
|
||||
|
||||
public void testDeserializeFixtureNonePlan() throws Exception {
|
||||
final SubscriptionPlan plan = fromJson(
|
||||
jsonFixture(assets, "fixtures/SubscriptionPlanNone.json"),
|
||||
SubscriptionPlan.class
|
||||
);
|
||||
|
||||
assertTrue(nonePlan().equals(plan));
|
||||
}
|
||||
|
||||
public void testDeserializeFixtureStripePlan() throws Exception {
|
||||
final StripePlan plan = fromJson(
|
||||
jsonFixture(assets, "fixtures/StripePlan.json"),
|
||||
StripePlan.class
|
||||
);
|
||||
|
||||
assertTrue(stripePlan().equals(plan));
|
||||
}
|
||||
|
||||
public void testDeserializeFixtureGooglePlan() throws Exception {
|
||||
final GooglePlan plan = fromJson(
|
||||
jsonFixture(assets, "fixtures/GooglePlan.json"),
|
||||
GooglePlan.class
|
||||
);
|
||||
|
||||
assertTrue(googlePlan().equals(plan));
|
||||
}
|
||||
|
||||
public void testSerializeDeserializeNonePlan() throws Exception {
|
||||
final SubscriptionPlan plan = SubscriptionPlan.buildFromSerialized(
|
||||
SubscriptionPlan.PLAN_TYPE_NONE,
|
||||
nonePlan().serialize()
|
||||
);
|
||||
|
||||
assertTrue(nonePlan().equals(plan));
|
||||
}
|
||||
|
||||
public void testSerializeDeserializeStripePlan() throws Exception {
|
||||
final SubscriptionPlan plan = SubscriptionPlan.buildFromSerialized(
|
||||
SubscriptionPlan.PLAN_TYPE_STRIPE,
|
||||
stripePlan().serialize()
|
||||
);
|
||||
|
||||
assertTrue(stripePlan().equals(plan));
|
||||
}
|
||||
|
||||
public void testSerializeDeserializeGooglePlan() throws Exception {
|
||||
final SubscriptionPlan plan = SubscriptionPlan.buildFromSerialized(
|
||||
SubscriptionPlan.PLAN_TYPE_GOOGLE,
|
||||
googlePlan().serialize()
|
||||
);
|
||||
|
||||
assertTrue(googlePlan().equals(plan));
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
package org.anhonesteffort.flock.test.sync;
|
||||
|
||||
import org.anhonesteffort.flock.crypto.MasterCipher;
|
||||
import org.anhonesteffort.flock.util.Base64;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Programmer: rhodey
|
||||
* Date: 3/21/14
|
||||
*/
|
||||
|
||||
// This class is meant to assist in the testing of Hiding*Store and Hiding*Collection.
|
||||
public class MockMasterCipher extends MasterCipher {
|
||||
|
||||
public MockMasterCipher() {
|
||||
super(false, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encryptAndEncode(byte[] data) {
|
||||
return Base64.encodeBytes(data).getBytes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encryptAndEncode(String data) {
|
||||
return Base64.encodeBytes(data.getBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] decodeAndDecrypt(byte[] encodedVersionIvCiphertextAndMac) {
|
||||
return Base64.decode(encodedVersionIvCiphertextAndMac);
|
||||
}
|
||||
|
||||
public String decodeAndDecrypt(String data) throws IOException {
|
||||
return new String(Base64.decode(data));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock.test.sync.account;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import org.anhonesteffort.flock.registration.model.AugmentedFlockAccount;
|
||||
import org.anhonesteffort.flock.registration.model.FlockCardInformation;
|
||||
import org.anhonesteffort.flock.registration.model.SubscriptionPlan;
|
||||
import org.anhonesteffort.flock.sync.account.AccountStore;
|
||||
import org.anhonesteffort.flock.test.registration.model.AugmentedFlockAccountTest;
|
||||
import org.anhonesteffort.flock.test.registration.model.FlockCardInformationTest;
|
||||
import org.anhonesteffort.flock.test.registration.model.SubscriptionPlanTest;
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
|
||||
/**
|
||||
* rhodey
|
||||
*/
|
||||
public class AccountStoreTest extends AndroidTestCase {
|
||||
|
||||
public void testGetSetLastStripeChargeFailed() throws Exception {
|
||||
AccountStore.setLastChargeFailed(getContext(), true);
|
||||
assertTrue(AccountStore.getLastChargeFailed(getContext()));
|
||||
|
||||
AccountStore.setLastChargeFailed(getContext(), false);
|
||||
assertTrue(!AccountStore.getLastChargeFailed(getContext()));
|
||||
}
|
||||
|
||||
public void testGetSetAutoRenew() throws Exception {
|
||||
AccountStore.setAutoRenew(getContext(), true);
|
||||
assertTrue(AccountStore.getAutoRenew(getContext()));
|
||||
|
||||
AccountStore.setAutoRenew(getContext(), false);
|
||||
assertTrue(!AccountStore.getAutoRenew(getContext()));
|
||||
}
|
||||
|
||||
public void testSetGetDaysRemainingPositive() throws Exception {
|
||||
final Long daysRemaining = 1337L;
|
||||
|
||||
AccountStore.setDaysRemaining(getContext(), daysRemaining);
|
||||
assertTrue(AccountStore.getDaysRemaining(getContext()).get().equals(daysRemaining));
|
||||
}
|
||||
|
||||
public void testSetGetDaysRemainingNegative() throws Exception {
|
||||
final Long daysRemaining = -1337L;
|
||||
|
||||
AccountStore.setDaysRemaining(getContext(), daysRemaining);
|
||||
assertTrue(AccountStore.getDaysRemaining(getContext()).get().equals(daysRemaining));
|
||||
}
|
||||
|
||||
public void testSetGetSubscriptionPlanNone() throws Exception {
|
||||
final SubscriptionPlan planNone = SubscriptionPlanTest.nonePlan();
|
||||
|
||||
AccountStore.setSubscriptionPlan(getContext(), planNone);
|
||||
assertTrue(AccountStore.getSubscriptionPlanType(getContext()).equals(planNone.getPlanType()));
|
||||
assertTrue(AccountStore.getSubscriptionPlan(getContext()).equals(planNone));
|
||||
}
|
||||
|
||||
public void testSetGetSubscriptionPlanStripe() throws Exception {
|
||||
final SubscriptionPlan stripePlan = SubscriptionPlanTest.stripePlan();
|
||||
|
||||
AccountStore.setSubscriptionPlan(getContext(), stripePlan);
|
||||
assertTrue(AccountStore.getSubscriptionPlanType(getContext()).equals(stripePlan.getPlanType()));
|
||||
assertTrue(AccountStore.getSubscriptionPlan(getContext()).equals(stripePlan));
|
||||
}
|
||||
|
||||
public void testSetGetSubscriptionPlanGoogle() throws Exception {
|
||||
final SubscriptionPlan googlePlan = SubscriptionPlanTest.googlePlan();
|
||||
|
||||
AccountStore.setSubscriptionPlan(getContext(), googlePlan);
|
||||
assertTrue(AccountStore.getSubscriptionPlanType(getContext()).equals(googlePlan.getPlanType()));
|
||||
assertTrue(AccountStore.getSubscriptionPlan(getContext()).equals(googlePlan));
|
||||
}
|
||||
|
||||
public void testSetGetCardInformationPresent() throws Exception {
|
||||
final FlockCardInformation card = FlockCardInformationTest.card();
|
||||
|
||||
AccountStore.setCardInformation(getContext(), Optional.of(card));
|
||||
assertTrue(AccountStore.getCardInformation(getContext()).get().equals(card));
|
||||
}
|
||||
|
||||
public void testSetGetCardInformationAbsent() throws Exception {
|
||||
AccountStore.setCardInformation(getContext(), Optional.<FlockCardInformation>absent());
|
||||
assertTrue(!AccountStore.getCardInformation(getContext()).isPresent());
|
||||
}
|
||||
|
||||
public void testUpdateStore() throws Exception {
|
||||
final AugmentedFlockAccount account = AugmentedFlockAccountTest.accountNoPlan();
|
||||
|
||||
AccountStore.updateStore(getContext(), account);
|
||||
|
||||
assertTrue(AccountStore.getLastChargeFailed(getContext()) == account.getLastStripeChargeFailed());
|
||||
assertTrue(AccountStore.getAutoRenew(getContext()) == account.getAutoRenewEnabled());
|
||||
assertTrue(AccountStore.getDaysRemaining(getContext()).get().equals(account.getDaysRemaining()));
|
||||
assertTrue(AccountStore.getSubscriptionPlanType(getContext()).equals(account.getSubscriptionPlan().getPlanType()));
|
||||
assertTrue(AccountStore.getSubscriptionPlan(getContext()).equals(account.getSubscriptionPlan()));
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,84 +0,0 @@
|
||||
package org.anhonesteffort.flock.test.sync.addressbook;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import ezvcard.VCard;
|
||||
import ezvcard.VCardVersion;
|
||||
import ezvcard.property.StructuredName;
|
||||
import ezvcard.property.Uid;
|
||||
import org.anhonesteffort.flock.test.sync.MockMasterCipher;
|
||||
import org.anhonesteffort.flock.sync.addressbook.HidingCardDavCollection;
|
||||
import org.anhonesteffort.flock.sync.addressbook.HidingCardDavStore;
|
||||
import org.anhonesteffort.flock.webdav.ComponentETagPair;
|
||||
import org.anhonesteffort.flock.test.webdav.DavTestParams;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Programmer: rhodey
|
||||
* Date: 2/25/14
|
||||
*/
|
||||
public class HidingCardDavCollectionTest extends AndroidTestCase {
|
||||
|
||||
private HidingCardDavCollection hidingCardDavCollection;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
HidingCardDavStore hidingCardDavStore = new HidingCardDavStore(new MockMasterCipher(),
|
||||
DavTestParams.WEBDAV_HOST,
|
||||
DavTestParams.USERNAME,
|
||||
DavTestParams.PASSWORD,
|
||||
Optional.<String>absent(),
|
||||
Optional.<String>absent());
|
||||
|
||||
Optional<String> addressbookHomeSet = hidingCardDavStore.getAddressbookHomeSet();
|
||||
String COLLECTION_PATH = addressbookHomeSet.get().concat("addressbook/");
|
||||
|
||||
hidingCardDavCollection = hidingCardDavStore.getCollection(COLLECTION_PATH).get();
|
||||
}
|
||||
|
||||
public void testEditProperties() throws Exception {
|
||||
final Optional<String> ORIGINAL_DISPLAY_NAME = hidingCardDavCollection.getHiddenDisplayName();
|
||||
|
||||
final String NEW_DISPLAY_NAME = "Only 1337 people in here";
|
||||
|
||||
hidingCardDavCollection.setHiddenDisplayName(NEW_DISPLAY_NAME);
|
||||
|
||||
assertEquals("Addressbook display name must be maintained.",
|
||||
NEW_DISPLAY_NAME,
|
||||
hidingCardDavCollection.getHiddenDisplayName().get());
|
||||
|
||||
if (ORIGINAL_DISPLAY_NAME.isPresent())
|
||||
hidingCardDavCollection.setDisplayName(ORIGINAL_DISPLAY_NAME.get());
|
||||
}
|
||||
|
||||
public void testAddGetRemoveComponent() throws Exception {
|
||||
final StructuredName structuredName = new StructuredName();
|
||||
structuredName.setFamily("Strangelove");
|
||||
structuredName.setGiven("idk");
|
||||
structuredName.addPrefix("Dr");
|
||||
structuredName.addSuffix("");
|
||||
|
||||
VCard putVCard = new VCard();
|
||||
putVCard.setVersion(VCardVersion.V3_0);
|
||||
putVCard.setUid(new Uid(UUID.randomUUID().toString()));
|
||||
putVCard.setStructuredName(structuredName);
|
||||
putVCard.setFormattedName("you need this too");
|
||||
|
||||
hidingCardDavCollection.addComponent(putVCard);
|
||||
|
||||
Optional<ComponentETagPair<VCard>> gotVCard = hidingCardDavCollection.getComponent(putVCard.getUid().getValue());
|
||||
assertTrue("Added component must be found in collection.", gotVCard.isPresent());
|
||||
|
||||
assertEquals("vCard structured name must be maintained within the collection.",
|
||||
gotVCard.get().getComponent().getStructuredName().getFamily(),
|
||||
putVCard.getStructuredName().getFamily());
|
||||
|
||||
hidingCardDavCollection.removeComponent(putVCard.getUid().getValue());
|
||||
|
||||
assertTrue("Removed component must not be found in collection.",
|
||||
!hidingCardDavCollection.getComponent(putVCard.getUid().getValue()).isPresent());
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,41 +0,0 @@
|
||||
package org.anhonesteffort.flock.test.sync.addressbook;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.test.sync.MockMasterCipher;
|
||||
import org.anhonesteffort.flock.sync.addressbook.HidingCardDavStore;
|
||||
import org.anhonesteffort.flock.test.webdav.DavTestParams;
|
||||
import org.anhonesteffort.flock.webdav.carddav.CardDavCollection;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
|
||||
/**
|
||||
* Programmer: rhodey
|
||||
* Date: 2/25/14
|
||||
*/
|
||||
public class HidingCardDavStoreTest extends AndroidTestCase {
|
||||
|
||||
private HidingCardDavStore hidingCardDavStore;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
hidingCardDavStore = new HidingCardDavStore(new MockMasterCipher(),
|
||||
DavTestParams.WEBDAV_HOST,
|
||||
DavTestParams.USERNAME,
|
||||
DavTestParams.PASSWORD,
|
||||
Optional.<String>absent(),
|
||||
Optional.<String>absent());
|
||||
}
|
||||
|
||||
public void testGetCollections() throws Exception {
|
||||
final String DEFAULT_COLLECTION_OWNER = "/principals/__uids__/" + URLEncoder.encode(DavTestParams.USERNAME) + "/";
|
||||
|
||||
CardDavCollection collection = hidingCardDavStore.getCollections().get(0);
|
||||
|
||||
assertEquals("Default addressbook collection must be owned by " + DavTestParams.USERNAME,
|
||||
collection.getOwnerHref().get(),
|
||||
DEFAULT_COLLECTION_OWNER);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock.test.sync.calendar;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.net.Uri;
|
||||
import android.provider.CalendarContract;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import net.fortuna.ical4j.model.Calendar;
|
||||
import net.fortuna.ical4j.model.Dur;
|
||||
import net.fortuna.ical4j.model.component.VAlarm;
|
||||
import net.fortuna.ical4j.model.component.VEvent;
|
||||
import net.fortuna.ical4j.model.parameter.Cn;
|
||||
import net.fortuna.ical4j.model.parameter.PartStat;
|
||||
import net.fortuna.ical4j.model.parameter.Role;
|
||||
import net.fortuna.ical4j.model.property.Attendee;
|
||||
|
||||
import org.anhonesteffort.flock.sync.calendar.EventFactory;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* rhodey
|
||||
*/
|
||||
public class EventFactoryTest extends AndroidTestCase {
|
||||
|
||||
public void testGetValuesForAttendees() throws Exception {
|
||||
final String EMAIL = "doge@such.wow";
|
||||
final String NAME = "crypto doge";
|
||||
final Role ROLE = Role.OPT_PARTICIPANT;
|
||||
final PartStat STATUS = PartStat.DECLINED;
|
||||
|
||||
final Calendar inCalendar = new Calendar();
|
||||
final VEvent inVEvent = new VEvent();
|
||||
final Attendee inAttendee = new Attendee(new URI("mailto", EMAIL, null));
|
||||
|
||||
inAttendee.getParameters().add(new Cn(NAME));
|
||||
inAttendee.getParameters().add(ROLE);
|
||||
inAttendee.getParameters().add(STATUS);
|
||||
|
||||
inVEvent.getProperties().add(inAttendee);
|
||||
inCalendar.getComponents().add(inVEvent);
|
||||
|
||||
final List<ContentValues> outValuesList = EventFactory.getValuesForAttendees(inCalendar);
|
||||
|
||||
assertTrue(outValuesList.size() == 1);
|
||||
final ContentValues outValues = outValuesList.get(0);
|
||||
|
||||
assertTrue(outValues.getAsString(CalendarContract.Attendees.ATTENDEE_EMAIL).equals(EMAIL));
|
||||
assertTrue(outValues.getAsString(CalendarContract.Attendees.ATTENDEE_NAME).equals(NAME));
|
||||
assertTrue(outValues.getAsInteger(CalendarContract.Attendees.ATTENDEE_TYPE).equals(CalendarContract.Attendees.TYPE_OPTIONAL));
|
||||
assertTrue(outValues.getAsInteger(CalendarContract.Attendees.ATTENDEE_RELATIONSHIP).equals(CalendarContract.Attendees.RELATIONSHIP_ATTENDEE));
|
||||
assertTrue(outValues.getAsInteger(CalendarContract.Attendees.ATTENDEE_STATUS).equals(CalendarContract.Attendees.ATTENDEE_STATUS_DECLINED));
|
||||
}
|
||||
|
||||
public void testAddAttendee() throws Exception {
|
||||
final String EMAIL = "doge@such.wow";
|
||||
final String NAME = "crypto doge";
|
||||
final Integer ROLE = CalendarContract.Attendees.RELATIONSHIP_ORGANIZER;
|
||||
final Integer TYPE = CalendarContract.Attendees.TYPE_REQUIRED;
|
||||
final Integer STATUS = CalendarContract.Attendees.STATUS_CONFIRMED;
|
||||
|
||||
final ContentValues inValues = new ContentValues();
|
||||
inValues.put(CalendarContract.Attendees.ATTENDEE_EMAIL, EMAIL);
|
||||
inValues.put(CalendarContract.Attendees.ATTENDEE_NAME, NAME);
|
||||
inValues.put(CalendarContract.Attendees.ATTENDEE_RELATIONSHIP, ROLE);
|
||||
inValues.put(CalendarContract.Attendees.ATTENDEE_TYPE, TYPE);
|
||||
inValues.put(CalendarContract.Attendees.ATTENDEE_STATUS, STATUS);
|
||||
|
||||
final Calendar outCalendar = new Calendar();
|
||||
outCalendar.getComponents().add(new VEvent());
|
||||
|
||||
EventFactory.addAttendee("wow", outCalendar, inValues);
|
||||
|
||||
assertTrue(outCalendar.getComponent(VEvent.VEVENT) != null);
|
||||
final VEvent outVEvent = (VEvent) outCalendar.getComponent(VEvent.VEVENT);
|
||||
|
||||
assertTrue(outVEvent.getProperties(Attendee.ATTENDEE).size() == 1);
|
||||
final Attendee outAttendee = (Attendee) outVEvent.getProperties(Attendee.ATTENDEE).get(0);
|
||||
|
||||
final String outEmail = Uri.parse(outAttendee.getValue()).getSchemeSpecificPart();
|
||||
final String outName = outAttendee.getParameter(Cn.CN).getValue();
|
||||
final Role outRole = (Role) outAttendee.getParameter(Role.ROLE);
|
||||
final PartStat outStatus = (PartStat) outAttendee.getParameter(PartStat.PARTSTAT);
|
||||
|
||||
assertTrue(outEmail.equals(EMAIL));
|
||||
assertTrue(outName.equals(NAME));
|
||||
assertTrue(outRole == Role.CHAIR);
|
||||
assertTrue(outStatus == PartStat.ACCEPTED);
|
||||
}
|
||||
|
||||
public void testGetValuesForReminders() throws Exception {
|
||||
final Integer MINUTES_BEFORE_EVENT = 1337;
|
||||
|
||||
final Calendar inCalendar = new Calendar();
|
||||
final VEvent inVEvent = new VEvent();
|
||||
final VAlarm inAlarm = new VAlarm(new Dur(0, 0, -MINUTES_BEFORE_EVENT, 0));
|
||||
|
||||
inVEvent.getAlarms().add(inAlarm);
|
||||
inCalendar.getComponents().add(inVEvent);
|
||||
|
||||
final List<ContentValues> outValuesList = EventFactory.getValuesForReminders(inCalendar);
|
||||
assertTrue(outValuesList.size() == 1);
|
||||
|
||||
final ContentValues outValues = outValuesList.get(0);
|
||||
|
||||
assertTrue(outValues.getAsInteger(CalendarContract.Reminders.MINUTES).equals(MINUTES_BEFORE_EVENT));
|
||||
assertTrue(outValues.getAsInteger(CalendarContract.Reminders.METHOD).equals(CalendarContract.Reminders.METHOD_ALERT));
|
||||
}
|
||||
|
||||
public void testAddReminder() throws Exception {
|
||||
final Integer MINUTES_BEFORE_EVENT = 1337;
|
||||
|
||||
final ContentValues inValues = new ContentValues();
|
||||
inValues.put(CalendarContract.Reminders.MINUTES, MINUTES_BEFORE_EVENT);
|
||||
|
||||
final Calendar outCalendar = new Calendar();
|
||||
outCalendar.getComponents().add(new VEvent());
|
||||
EventFactory.addReminder(outCalendar, inValues);
|
||||
|
||||
assertTrue(outCalendar.getComponent(VEvent.VEVENT) != null);
|
||||
final VEvent outVEvent = (VEvent) outCalendar.getComponent(VEvent.VEVENT);
|
||||
|
||||
assertTrue(outVEvent.getAlarms().size() == 1);
|
||||
final VAlarm outAlarm = (VAlarm) outVEvent.getAlarms().get(0);
|
||||
|
||||
assertTrue(outAlarm.getTrigger().getDuration().getMinutes() == MINUTES_BEFORE_EVENT);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,131 +0,0 @@
|
||||
package org.anhonesteffort.flock.test.sync.calendar;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import net.fortuna.ical4j.model.Date;
|
||||
import net.fortuna.ical4j.model.TimeZone;
|
||||
import net.fortuna.ical4j.model.TimeZoneRegistry;
|
||||
import net.fortuna.ical4j.model.TimeZoneRegistryFactory;
|
||||
import net.fortuna.ical4j.model.component.VEvent;
|
||||
import net.fortuna.ical4j.model.component.VTimeZone;
|
||||
import net.fortuna.ical4j.model.property.CalScale;
|
||||
import net.fortuna.ical4j.model.property.Description;
|
||||
import net.fortuna.ical4j.model.property.Uid;
|
||||
import net.fortuna.ical4j.model.property.Version;
|
||||
import net.fortuna.ical4j.util.Calendars;
|
||||
import org.anhonesteffort.flock.test.sync.MockMasterCipher;
|
||||
import org.anhonesteffort.flock.sync.calendar.HidingCalDavCollection;
|
||||
import org.anhonesteffort.flock.sync.calendar.HidingCalDavStore;
|
||||
import org.anhonesteffort.flock.webdav.ComponentETagPair;
|
||||
import org.anhonesteffort.flock.test.webdav.DavTestParams;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Programmer: rhodey
|
||||
* Date: 2/25/14
|
||||
*/
|
||||
public class HidingCalDavCollectionTest extends AndroidTestCase {
|
||||
|
||||
private HidingCalDavCollection hidingCalDavCollection;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
HidingCalDavStore hidingCalDavStore = new HidingCalDavStore(new MockMasterCipher(),
|
||||
DavTestParams.WEBDAV_HOST,
|
||||
DavTestParams.USERNAME,
|
||||
DavTestParams.PASSWORD,
|
||||
Optional.<String>absent(),
|
||||
Optional.<String>absent());
|
||||
|
||||
Optional<String> calendarHomeSet = hidingCalDavStore.getCalendarHomeSet();
|
||||
String COLLECTION_PATH = calendarHomeSet.get().concat("calendar/");
|
||||
|
||||
hidingCalDavCollection = hidingCalDavStore.getCollection(COLLECTION_PATH).get();
|
||||
}
|
||||
|
||||
public void testEditTimeZone() throws Exception {
|
||||
net.fortuna.ical4j.model.Calendar putCalendar = new net.fortuna.ical4j.model.Calendar();
|
||||
putCalendar.getProperties().add(Version.VERSION_2_0);
|
||||
putCalendar.getProperties().add(CalScale.GREGORIAN);
|
||||
|
||||
TimeZoneRegistry registry = TimeZoneRegistryFactory.getInstance().createRegistry();
|
||||
TimeZone timezone = registry.getTimeZone("America/Mexico_City");
|
||||
VTimeZone putTimeZone = timezone.getVTimeZone();
|
||||
|
||||
putCalendar.getComponents().add(putTimeZone);
|
||||
hidingCalDavCollection.setTimeZone(putCalendar);
|
||||
|
||||
VTimeZone gotTimeZone = (VTimeZone) hidingCalDavCollection.getTimeZone().get().getComponent(VTimeZone.VTIMEZONE);
|
||||
|
||||
assertEquals("Time zone thing must be maintained.",
|
||||
putTimeZone.getTimeZoneId().getValue(),
|
||||
gotTimeZone.getTimeZoneId().getValue());
|
||||
}
|
||||
|
||||
public void testEditProperties() throws Exception {
|
||||
final Optional<String> ORIGINAL_DISPLAY_NAME = hidingCalDavCollection.getHiddenDisplayName();
|
||||
final Optional<Integer> ORIGINAL_COLOR = hidingCalDavCollection.getHiddenColor();
|
||||
|
||||
final String NEW_DISPLAY_NAME = "GOTO FAIL";
|
||||
final Integer NEW_COLOR = 0xFFFFFFFF;
|
||||
|
||||
hidingCalDavCollection.setHiddenDisplayName(NEW_DISPLAY_NAME);
|
||||
hidingCalDavCollection.setHiddenColor(NEW_COLOR);
|
||||
|
||||
assertEquals("Display name should be maintained.", NEW_DISPLAY_NAME, hidingCalDavCollection.getHiddenDisplayName().get());
|
||||
assertEquals("Color should be maintained.", NEW_COLOR, hidingCalDavCollection.getHiddenColor().get());
|
||||
|
||||
if (ORIGINAL_DISPLAY_NAME.isPresent())
|
||||
hidingCalDavCollection.setDisplayName(ORIGINAL_DISPLAY_NAME.get());
|
||||
if (ORIGINAL_COLOR.isPresent())
|
||||
hidingCalDavCollection.setColor(ORIGINAL_COLOR.get());
|
||||
}
|
||||
|
||||
public void testAddGetRemoveComponent() throws Exception {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.set(Calendar.MONTH, Calendar.JUNE);
|
||||
calendar.set(Calendar.DAY_OF_MONTH, 5);
|
||||
|
||||
net.fortuna.ical4j.model.Calendar putCalendar = new net.fortuna.ical4j.model.Calendar();
|
||||
putCalendar.getProperties().add(Version.VERSION_2_0);
|
||||
putCalendar.getProperties().add(CalScale.GREGORIAN);
|
||||
|
||||
Date putStartDate = new Date(calendar.getTime());
|
||||
Date putEndDate = new Date(putStartDate.getTime() + (1000 * 60 * 60 * 24));
|
||||
|
||||
VEvent vEventPut = new VEvent(putStartDate, putEndDate, "Tank Man");
|
||||
vEventPut.getProperties().add(new Uid(UUID.randomUUID().toString()));
|
||||
vEventPut.getProperties().add(new Description("THIS IS A LINE LONG ENOUGH TO BE SPLIT IN TWO BY THE ICAL FOLDING NONSENSE WHY DOES THIS EXIST?!?!??!?!?!?!?!??"));
|
||||
putCalendar.getComponents().add(vEventPut);
|
||||
|
||||
hidingCalDavCollection.addComponent(putCalendar);
|
||||
|
||||
Optional<ComponentETagPair<net.fortuna.ical4j.model.Calendar>> gotCalendar = hidingCalDavCollection.getComponent(Calendars.getUid(putCalendar).getValue());
|
||||
assertTrue("Added component must be found in the collection.", gotCalendar.isPresent());
|
||||
|
||||
VEvent vEventGot = (VEvent) putCalendar.getComponent(VEvent.VEVENT);
|
||||
assertEquals("vEvent summary must be maintained.",
|
||||
vEventGot.getSummary().getValue(),
|
||||
vEventPut.getSummary().getValue());
|
||||
|
||||
assertEquals("vEvent description must be maintained.",
|
||||
vEventGot.getDescription().getValue(),
|
||||
vEventPut.getDescription().getValue());
|
||||
|
||||
assertEquals("VEvent start date must be maintained.",
|
||||
vEventGot.getStartDate().getDate().getTime(),
|
||||
putStartDate.getTime());
|
||||
|
||||
assertEquals("VEvent end date must be maintained.",
|
||||
vEventGot.getEndDate().getDate().getTime(),
|
||||
putEndDate.getTime());
|
||||
|
||||
hidingCalDavCollection.removeComponent(Calendars.getUid(putCalendar).getValue());
|
||||
assertTrue("Removed component must not be found in the collection.",
|
||||
!hidingCalDavCollection.getComponent(Calendars.getUid(putCalendar).getValue()).isPresent());
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,78 +0,0 @@
|
||||
package org.anhonesteffort.flock.test.sync.calendar;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.test.sync.MockMasterCipher;
|
||||
import org.anhonesteffort.flock.sync.calendar.HidingCalDavCollection;
|
||||
import org.anhonesteffort.flock.sync.calendar.HidingCalDavStore;
|
||||
import org.anhonesteffort.flock.test.webdav.DavTestParams;
|
||||
import org.anhonesteffort.flock.webdav.caldav.CalDavCollection;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
|
||||
/**
|
||||
* Programmer: rhodey
|
||||
* Date: 2/25/14
|
||||
*/
|
||||
public class HidingCalDavStoreTest extends AndroidTestCase {
|
||||
|
||||
private HidingCalDavStore hidingCalDavStore;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
hidingCalDavStore = new HidingCalDavStore(new MockMasterCipher(),
|
||||
DavTestParams.WEBDAV_HOST,
|
||||
DavTestParams.USERNAME,
|
||||
DavTestParams.PASSWORD,
|
||||
Optional.<String>absent(),
|
||||
Optional.<String>absent());
|
||||
}
|
||||
|
||||
public void testGetCollections() throws Exception {
|
||||
final String DEFAULT_COLLECTION_OWNER = "/principals/__uids__/" + URLEncoder.encode(DavTestParams.USERNAME) + "/";
|
||||
|
||||
CalDavCollection collection = hidingCalDavStore.getCollections().get(0);
|
||||
|
||||
assertEquals("Default calendar collection must be owned by " + DavTestParams.USERNAME,
|
||||
collection.getOwnerHref().get(),
|
||||
DEFAULT_COLLECTION_OWNER);
|
||||
}
|
||||
|
||||
public void testAddGetRemoveSimpleCollection() throws Exception {
|
||||
Optional<String> calendarHomeSet = hidingCalDavStore.getCalendarHomeSet();
|
||||
assertTrue("Calendar home set property must be found.", calendarHomeSet.isPresent());
|
||||
|
||||
final String COLLECTION_PATH = calendarHomeSet.get().concat("delete-me/");
|
||||
|
||||
hidingCalDavStore.addCollection(COLLECTION_PATH);
|
||||
assertTrue("Added collection must be found in the store.",
|
||||
hidingCalDavStore.getCollection(COLLECTION_PATH).isPresent());
|
||||
|
||||
hidingCalDavStore.removeCollection(COLLECTION_PATH);
|
||||
assertTrue("Removed collection must not be found in the store.",
|
||||
!hidingCalDavStore.getCollection(COLLECTION_PATH).isPresent());
|
||||
}
|
||||
|
||||
public void testAddRemoveCollectionWithProperties() throws Exception {
|
||||
final String DISPLAY_NAME = "Test Collection";
|
||||
final Integer COLOR = 0xFFFFFFFF;
|
||||
|
||||
Optional<String> calendarHomeSet = hidingCalDavStore.getCalendarHomeSet();
|
||||
assertTrue("Calendar home set property must be found.", calendarHomeSet.isPresent());
|
||||
|
||||
final String COLLECTION_PATH = calendarHomeSet.get().concat("delete-me/");
|
||||
|
||||
hidingCalDavStore.addCollection(COLLECTION_PATH, DISPLAY_NAME, COLOR);
|
||||
Optional<HidingCalDavCollection> collection = hidingCalDavStore.getCollection(COLLECTION_PATH);
|
||||
|
||||
assertTrue( "Added collection must be found in the store.", collection.isPresent());
|
||||
assertEquals("Added collection display name must be maintained.", collection.get().getHiddenDisplayName().get(), DISPLAY_NAME);
|
||||
assertEquals("Added collection color must be maintained.", collection.get().getHiddenColor().get(), COLOR);
|
||||
|
||||
hidingCalDavStore.removeCollection(COLLECTION_PATH);
|
||||
assertTrue("Removed collection must not be found in the store.",
|
||||
!hidingCalDavStore.getCollection(COLLECTION_PATH).isPresent());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock.test.util;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* rhodey
|
||||
*/
|
||||
public class DateHelper {
|
||||
|
||||
public static Date getMockDate() {
|
||||
final Calendar calendar = new GregorianCalendar(2014, 1, 8);
|
||||
calendar.setTimeZone(TimeZone.getTimeZone("GMT-10"));
|
||||
|
||||
return calendar.getTime();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock.test.util;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
import org.anhonesteffort.flock.util.MapperUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class JsonHelpers {
|
||||
|
||||
public static String asJson(Object object) throws JsonProcessingException {
|
||||
return MapperUtil.getMapper().writeValueAsString(object);
|
||||
}
|
||||
|
||||
public static <T> T fromJson(String value, Class<T> clazz) throws IOException {
|
||||
return MapperUtil.getMapper().readValue(value, clazz);
|
||||
}
|
||||
|
||||
public static String jsonFixture(AssetManager assets, String filename) throws IOException {
|
||||
return MapperUtil.getMapper().writeValueAsString(
|
||||
MapperUtil.getMapper().readValue(assets.open(filename), JsonNode.class)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock.test.util;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
import android.test.InstrumentationTestCase;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import static org.anhonesteffort.flock.test.util.JsonHelpers.fromJson;
|
||||
import static org.anhonesteffort.flock.test.util.JsonHelpers.jsonFixture;
|
||||
|
||||
/**
|
||||
* rhodey
|
||||
*
|
||||
*/
|
||||
public class MapperUtilTest extends InstrumentationTestCase {
|
||||
|
||||
public static final String KNOWN = "known";
|
||||
|
||||
public static TestObj buildTestObj() {
|
||||
return new TestObj(KNOWN);
|
||||
}
|
||||
|
||||
public void testDeserializeWithUnknownProperty() throws Exception {
|
||||
final AssetManager assets = getInstrumentation().getContext().getAssets();
|
||||
final TestObj testObj = fromJson(
|
||||
jsonFixture(assets, "fixtures/TestObjWithUnknown.json"),
|
||||
TestObj.class
|
||||
);
|
||||
|
||||
assertTrue(testObj.equals(buildTestObj()));
|
||||
}
|
||||
|
||||
public static class TestObj {
|
||||
|
||||
@JsonProperty
|
||||
private String known;
|
||||
|
||||
public TestObj() { }
|
||||
|
||||
public TestObj(String known) {
|
||||
this.known = known;
|
||||
}
|
||||
|
||||
public String getKnown() {
|
||||
return known;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other == null) return false;
|
||||
if (!(other instanceof TestObj)) return false;
|
||||
|
||||
final TestObj that = (TestObj) other;
|
||||
|
||||
return this.known.equals(that.known);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,46 +0,0 @@
|
||||
package org.anhonesteffort.flock.test.webdav;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.webdav.caldav.CalDavCollection;
|
||||
import org.anhonesteffort.flock.webdav.caldav.CalDavStore;
|
||||
|
||||
/**
|
||||
* Programmer: rhodey
|
||||
* Date: 2/4/14
|
||||
*/
|
||||
public class DavCollectionTest extends AndroidTestCase {
|
||||
|
||||
private CalDavCollection davCollection;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
CalDavStore calDavStore = new CalDavStore(DavTestParams.WEBDAV_HOST,
|
||||
DavTestParams.USERNAME,
|
||||
DavTestParams.PASSWORD,
|
||||
Optional.<String>absent(),
|
||||
Optional.<String>absent());
|
||||
|
||||
Optional<String> calendarHomeSet = calDavStore.getCalendarHomeSet();
|
||||
String COLLECTION_PATH = calendarHomeSet.get().concat("calendar/");
|
||||
|
||||
davCollection = calDavStore.getCollection(COLLECTION_PATH).get();
|
||||
}
|
||||
|
||||
public void testEditProperties() throws Exception {
|
||||
final String DISPLAY_NAME = "calendar";
|
||||
|
||||
davCollection.setDisplayName(DISPLAY_NAME);
|
||||
|
||||
assertEquals("Collection must persist property changes.",
|
||||
DISPLAY_NAME,
|
||||
davCollection.getDisplayName().get());
|
||||
}
|
||||
|
||||
public void testCTag() throws Exception {
|
||||
Optional<String> cTag = davCollection.getCTag();
|
||||
assertTrue("All dav collections should have a CTag.", cTag.isPresent());
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
package org.anhonesteffort.flock.test.webdav;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.webdav.caldav.CalDavStore;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Programmer: rhodey
|
||||
* Date: 2/4/14
|
||||
*/
|
||||
public class DavStoreTest extends AndroidTestCase {
|
||||
|
||||
private static final String TAG = "DavStoreTest";
|
||||
|
||||
private CalDavStore davStore;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
davStore = new CalDavStore(DavTestParams.WEBDAV_HOST,
|
||||
DavTestParams.USERNAME,
|
||||
DavTestParams.PASSWORD,
|
||||
Optional.<String>absent(),
|
||||
Optional.<String>absent());
|
||||
}
|
||||
|
||||
public void testDavOptions() throws Exception {
|
||||
List<String> davOptions = davStore.getDavOptions();
|
||||
assertTrue("DAVOptions should be something.", davOptions.size() > 0);
|
||||
Log.d(TAG, "DAV Options: " + davOptions);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
package org.anhonesteffort.flock.test.webdav;
|
||||
|
||||
/**
|
||||
* Programmer: rhodey
|
||||
* Date: 2/25/14
|
||||
*/
|
||||
public class DavTestParams {
|
||||
|
||||
public static final String WEBDAV_HOST = "http://192.168.1.105:8008";
|
||||
public static final String USERNAME = "testn@flock.sync".toUpperCase();
|
||||
public static final String PASSWORD = "test";
|
||||
|
||||
}
|
||||
@ -1,145 +0,0 @@
|
||||
package org.anhonesteffort.flock.test.webdav.caldav;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import net.fortuna.ical4j.model.Date;
|
||||
import net.fortuna.ical4j.model.TimeZone;
|
||||
import net.fortuna.ical4j.model.TimeZoneRegistry;
|
||||
import net.fortuna.ical4j.model.TimeZoneRegistryFactory;
|
||||
import net.fortuna.ical4j.model.component.VEvent;
|
||||
import net.fortuna.ical4j.model.component.VTimeZone;
|
||||
import net.fortuna.ical4j.model.property.CalScale;
|
||||
import net.fortuna.ical4j.model.property.Description;
|
||||
import net.fortuna.ical4j.model.property.Uid;
|
||||
import net.fortuna.ical4j.model.property.Version;
|
||||
import net.fortuna.ical4j.util.Calendars;
|
||||
import org.anhonesteffort.flock.webdav.ComponentETagPair;
|
||||
import org.anhonesteffort.flock.test.webdav.DavTestParams;
|
||||
import org.anhonesteffort.flock.webdav.caldav.CalDavCollection;
|
||||
import org.anhonesteffort.flock.webdav.caldav.CalDavStore;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Programmer: rhodey
|
||||
* Date: 2/4/14
|
||||
*/
|
||||
public class CalDavCollectionTest extends AndroidTestCase {
|
||||
|
||||
private CalDavCollection calDavCollection;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
CalDavStore calDavStore = new CalDavStore(DavTestParams.WEBDAV_HOST,
|
||||
DavTestParams.USERNAME,
|
||||
DavTestParams.PASSWORD,
|
||||
Optional.<String>absent(),
|
||||
Optional.<String>absent());
|
||||
|
||||
Optional<String> calendarHomeSet = calDavStore.getCalendarHomeSet();
|
||||
String COLLECTION_PATH = calendarHomeSet.get().concat("calendar/");
|
||||
|
||||
calDavCollection = calDavStore.getCollection(COLLECTION_PATH).get();
|
||||
}
|
||||
|
||||
public void testEditProperties() throws Exception {
|
||||
final Optional<String> ORIGINAL_DISPLAY_NAME = calDavCollection.getDisplayName();
|
||||
final Optional<String> ORIGINAL_DESCRIPTION = calDavCollection.getDescription();
|
||||
final Optional<Integer> ORIGINAL_COLOR = calDavCollection.getColor();
|
||||
|
||||
final String NEW_DISPLAY_NAME = "GOTO FAIL";
|
||||
final String NEW_DESCRIPTION = "TYPO-- I SWEAR!";
|
||||
final Integer NEW_COLOR = 0xFF;
|
||||
|
||||
calDavCollection.setDisplayName(NEW_DISPLAY_NAME);
|
||||
calDavCollection.setDescription(NEW_DESCRIPTION);
|
||||
calDavCollection.setColor(NEW_COLOR);
|
||||
|
||||
assertEquals("Display name should be maintained.",
|
||||
NEW_DISPLAY_NAME,
|
||||
calDavCollection.getDisplayName().get());
|
||||
assertEquals("Description should be maintained.",
|
||||
NEW_DESCRIPTION,
|
||||
calDavCollection.getDescription().get());
|
||||
assertEquals("Color should be maintained.",
|
||||
NEW_COLOR,
|
||||
calDavCollection.getColor().get());
|
||||
|
||||
if (ORIGINAL_DISPLAY_NAME.isPresent())
|
||||
calDavCollection.setDisplayName(ORIGINAL_DISPLAY_NAME.get());
|
||||
if (ORIGINAL_DESCRIPTION.isPresent())
|
||||
calDavCollection.setDescription(ORIGINAL_DESCRIPTION.get());
|
||||
if (ORIGINAL_COLOR.isPresent())
|
||||
calDavCollection.setColor(ORIGINAL_COLOR.get());
|
||||
}
|
||||
|
||||
public void testEditTimeZone() throws Exception {
|
||||
net.fortuna.ical4j.model.Calendar putCalendar = new net.fortuna.ical4j.model.Calendar();
|
||||
putCalendar.getProperties().add(Version.VERSION_2_0);
|
||||
putCalendar.getProperties().add(CalScale.GREGORIAN);
|
||||
|
||||
TimeZoneRegistry registry = TimeZoneRegistryFactory.getInstance().createRegistry();
|
||||
TimeZone timezone = registry.getTimeZone("America/Mexico_City");
|
||||
VTimeZone putTimeZone = timezone.getVTimeZone();
|
||||
|
||||
putCalendar.getComponents().add(putTimeZone);
|
||||
calDavCollection.setTimeZone(putCalendar);
|
||||
|
||||
VTimeZone gotTimeZone = (VTimeZone) calDavCollection.getTimeZone().get().getComponent(VTimeZone.VTIMEZONE);
|
||||
|
||||
assertEquals("Time zone thing must be maintained.",
|
||||
putTimeZone.getTimeZoneId().getValue(),
|
||||
gotTimeZone.getTimeZoneId().getValue());
|
||||
}
|
||||
|
||||
public void testAddGetRemoveComponent() throws Exception {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.set(Calendar.MONTH, Calendar.JUNE);
|
||||
calendar.set(Calendar.DAY_OF_MONTH, 5);
|
||||
|
||||
net.fortuna.ical4j.model.Calendar putCalendar = new net.fortuna.ical4j.model.Calendar();
|
||||
putCalendar.getProperties().add(Version.VERSION_2_0);
|
||||
putCalendar.getProperties().add(CalScale.GREGORIAN);
|
||||
|
||||
Date putStartDate = new Date(calendar.getTime());
|
||||
Date putEndDate = new Date(putStartDate.getTime() + (1000 * 60 * 60 * 24));
|
||||
|
||||
VEvent vEventPut = new VEvent(putStartDate, putEndDate, "Tank Man");
|
||||
vEventPut.getProperties().add(new Uid(UUID.randomUUID().toString()));
|
||||
vEventPut.getProperties().add(new Description("THIS IS A LINE LONG ENOUGH TO BE SPLIT IN TWO BY THE ICAL FOLDING NONSENSE WHY DOES THIS EXIST?!?!??!?!?!?!?!??"));
|
||||
putCalendar.getComponents().add(vEventPut);
|
||||
|
||||
calDavCollection.addComponent(putCalendar);
|
||||
|
||||
Optional<ComponentETagPair<net.fortuna.ical4j.model.Calendar>> gotCalendar =
|
||||
calDavCollection.getComponent(Calendars.getUid(putCalendar).getValue());
|
||||
assertTrue("Added component must be found in the collection.",
|
||||
gotCalendar.isPresent());
|
||||
|
||||
VEvent vEventGot = (VEvent) gotCalendar.get().getComponent().getComponent(VEvent.VEVENT);
|
||||
assertEquals("VEvent summary must be maintained.",
|
||||
vEventGot.getSummary().getValue(),
|
||||
vEventPut.getSummary().getValue());
|
||||
|
||||
assertEquals("vEvent description must be maintained.",
|
||||
vEventGot.getDescription().getValue(),
|
||||
vEventPut.getDescription().getValue());
|
||||
|
||||
assertEquals("VEvent start date must be maintained.",
|
||||
vEventGot.getStartDate().getDate().getTime(),
|
||||
putStartDate.getTime());
|
||||
|
||||
assertEquals("VEvent end date must be maintained.",
|
||||
vEventGot.getEndDate().getDate().getTime(),
|
||||
putEndDate.getTime());
|
||||
|
||||
assertTrue("VEvent should have an ETag", calDavCollection.getComponentETags().size() > 0);
|
||||
|
||||
calDavCollection.removeComponent(Calendars.getUid(putCalendar).getValue());
|
||||
assertTrue("Removed component must not be found in the collection.",
|
||||
!calDavCollection.getComponent(Calendars.getUid(putCalendar).getValue()).isPresent());
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,86 +0,0 @@
|
||||
package org.anhonesteffort.flock.test.webdav.caldav;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.test.webdav.DavTestParams;
|
||||
import org.anhonesteffort.flock.webdav.caldav.CalDavCollection;
|
||||
import org.anhonesteffort.flock.webdav.caldav.CalDavStore;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
|
||||
/**
|
||||
* Programmer: rhodey
|
||||
* Date: 2/4/14
|
||||
*/
|
||||
public class CalDavStoreTest extends AndroidTestCase {
|
||||
|
||||
private CalDavStore calDavStore;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
calDavStore = new CalDavStore(DavTestParams.WEBDAV_HOST,
|
||||
DavTestParams.USERNAME,
|
||||
DavTestParams.PASSWORD,
|
||||
Optional.<String>absent(),
|
||||
Optional.<String>absent());
|
||||
}
|
||||
|
||||
public void testDavCurrentUserPrincipal() throws Exception {
|
||||
Optional<String> currentUserPrincipal = calDavStore.getCurrentUserPrincipal();
|
||||
assertTrue("DAV:current-user-principal should be something.", currentUserPrincipal.isPresent());
|
||||
}
|
||||
|
||||
public void testGetCollections() throws Exception {
|
||||
final String DEFAULT_COLLECTION_OWNER = "/principals/__uids__/" + URLEncoder.encode(DavTestParams.USERNAME) + "/";
|
||||
|
||||
CalDavCollection collection = calDavStore.getCollections().get(0);
|
||||
|
||||
assertEquals("Default calendar collection must be owned by " + DavTestParams.USERNAME,
|
||||
collection.getOwnerHref().get(),
|
||||
DEFAULT_COLLECTION_OWNER);
|
||||
}
|
||||
|
||||
public void testAddGetRemoveSimpleCollection() throws Exception {
|
||||
Optional<String> calendarHomeSet = calDavStore.getCalendarHomeSet();
|
||||
assertTrue("Calendar home set property must be found.", calendarHomeSet.isPresent());
|
||||
|
||||
final String COLLECTION_PATH = calendarHomeSet.get().concat("test-collection/");
|
||||
|
||||
calDavStore.addCollection(COLLECTION_PATH);
|
||||
assertTrue("Added collection must be found in the store.",
|
||||
calDavStore.getCollection(COLLECTION_PATH).isPresent());
|
||||
|
||||
calDavStore.removeCollection(COLLECTION_PATH);
|
||||
assertTrue("Removed collection must not be found in the store.",
|
||||
!calDavStore.getCollection(COLLECTION_PATH).isPresent());
|
||||
}
|
||||
|
||||
public void testAddRemoveCollectionWithProperties() throws Exception {
|
||||
final String DISPLAY_NAME = "Test Collection";
|
||||
final String DESCRIPTION = "This is a test collection.";
|
||||
final Integer COLOR = 0xFF;
|
||||
|
||||
Optional<String> calendarHomeSet = calDavStore.getCalendarHomeSet();
|
||||
assertTrue("Calendar home set property must be found.", calendarHomeSet.isPresent());
|
||||
|
||||
final String COLLECTION_PATH = calendarHomeSet.get().concat("test-collection/");
|
||||
|
||||
calDavStore.addCollection(COLLECTION_PATH, DISPLAY_NAME, DESCRIPTION, COLOR);
|
||||
Optional<CalDavCollection> collection = calDavStore.getCollection(COLLECTION_PATH);
|
||||
|
||||
assertTrue("Added collection must be found in the store.",
|
||||
collection.isPresent());
|
||||
assertEquals("Added collection display name must be maintained.",
|
||||
collection.get().getDisplayName().get(), DISPLAY_NAME);
|
||||
assertEquals("Added collection description must be maintained.",
|
||||
collection.get().getDescription().get(), DESCRIPTION);
|
||||
assertEquals("Added collection color must be maintained.",
|
||||
collection.get().getColor().get(), COLOR);
|
||||
|
||||
calDavStore.removeCollection(COLLECTION_PATH);
|
||||
assertTrue("Removed collection must not be found in the store.",
|
||||
!calDavStore.getCollection(COLLECTION_PATH).isPresent());
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,90 +0,0 @@
|
||||
package org.anhonesteffort.flock.test.webdav.carddav;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import ezvcard.VCard;
|
||||
import ezvcard.VCardVersion;
|
||||
import ezvcard.property.StructuredName;
|
||||
import ezvcard.property.Uid;
|
||||
import org.anhonesteffort.flock.test.webdav.DavTestParams;
|
||||
import org.anhonesteffort.flock.webdav.ComponentETagPair;
|
||||
import org.anhonesteffort.flock.webdav.carddav.CardDavCollection;
|
||||
import org.anhonesteffort.flock.webdav.carddav.CardDavStore;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Programmer: rhodey
|
||||
* Date: 2/4/14
|
||||
*/
|
||||
public class CardDavCollectionTest extends AndroidTestCase {
|
||||
|
||||
private CardDavCollection cardDavCollection;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
CardDavStore cardDavStore = new CardDavStore(DavTestParams.WEBDAV_HOST,
|
||||
DavTestParams.USERNAME,
|
||||
DavTestParams.PASSWORD,
|
||||
Optional.<String>absent(),
|
||||
Optional.<String>absent());
|
||||
|
||||
Optional<String> addressbookHomeSet = cardDavStore.getAddressbookHomeSet();
|
||||
String COLLECTION_PATH = addressbookHomeSet.get().concat("addressbook/");
|
||||
|
||||
cardDavCollection = cardDavStore.getCollection(COLLECTION_PATH).get();
|
||||
}
|
||||
|
||||
public void testEditProperties() throws Exception {
|
||||
final Optional<String> ORIGINAL_DISPLAY_NAME = cardDavCollection.getDisplayName();
|
||||
final Optional<String> ORIGINAL_DESCRIPTION = cardDavCollection.getDescription();
|
||||
|
||||
final String NEW_DISPLAY_NAME = "Only 1337 people in here";
|
||||
final String NEW_DESCRIPTION = "CardDAV things insist that addressbook is one word.";
|
||||
|
||||
cardDavCollection.setDisplayName(NEW_DISPLAY_NAME);
|
||||
cardDavCollection.setDescription(NEW_DESCRIPTION);
|
||||
|
||||
assertEquals("Addressbook display name must be maintained.",
|
||||
NEW_DISPLAY_NAME,
|
||||
cardDavCollection.getDisplayName().get());
|
||||
assertEquals("Addressbook description must be maintained.",
|
||||
NEW_DESCRIPTION,
|
||||
cardDavCollection.getDescription().get());
|
||||
|
||||
if (ORIGINAL_DISPLAY_NAME.isPresent())
|
||||
cardDavCollection.setDisplayName(ORIGINAL_DISPLAY_NAME.get());
|
||||
if (ORIGINAL_DESCRIPTION.isPresent())
|
||||
cardDavCollection.setDescription(ORIGINAL_DESCRIPTION.get());
|
||||
}
|
||||
|
||||
public void testAddGetRemoveComponent() throws Exception {
|
||||
final StructuredName structuredName = new StructuredName();
|
||||
structuredName.setFamily("Strangelove");
|
||||
structuredName.setGiven("?");
|
||||
structuredName.addPrefix("Dr");
|
||||
structuredName.addSuffix("");
|
||||
|
||||
VCard putVCard = new VCard();
|
||||
putVCard.setVersion(VCardVersion.V3_0);
|
||||
putVCard.setUid(new Uid(UUID.randomUUID().toString()));
|
||||
putVCard.setStructuredName(structuredName);
|
||||
putVCard.setFormattedName("you need this too");
|
||||
|
||||
cardDavCollection.addComponent(putVCard);
|
||||
|
||||
Optional<ComponentETagPair<VCard>> gotVCard = cardDavCollection.getComponent(putVCard.getUid().getValue());
|
||||
assertTrue("Added component must be found in collection.", gotVCard.isPresent());
|
||||
|
||||
assertEquals("vCard structured name must be maintained within the collection.",
|
||||
gotVCard.get().getComponent().getStructuredName().getFamily(),
|
||||
structuredName.getFamily());
|
||||
|
||||
cardDavCollection.removeComponent(putVCard.getUid().getValue());
|
||||
|
||||
assertTrue("Removed component must not be found in collection.",
|
||||
!cardDavCollection.getComponent(putVCard.getUid().getValue()).isPresent());
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,60 +0,0 @@
|
||||
package org.anhonesteffort.flock.test.webdav.carddav;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.test.webdav.DavTestParams;
|
||||
import org.anhonesteffort.flock.webdav.carddav.CardDavCollection;
|
||||
import org.anhonesteffort.flock.webdav.carddav.CardDavStore;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
|
||||
/**
|
||||
* Programmer: rhodey
|
||||
* Date: 2/4/14
|
||||
*/
|
||||
public class CardDavStoreTest extends AndroidTestCase {
|
||||
|
||||
private CardDavStore cardDavStore;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
cardDavStore = new CardDavStore(DavTestParams.WEBDAV_HOST,
|
||||
DavTestParams.USERNAME,
|
||||
DavTestParams.PASSWORD,
|
||||
Optional.<String>absent(),
|
||||
Optional.<String>absent());
|
||||
}
|
||||
|
||||
public void testDavCurrentUserPrincipal() throws Exception {
|
||||
Optional<String> currentUserPrincipal = cardDavStore.getCurrentUserPrincipal();
|
||||
assertTrue("DAV:current-user-principal should be something.", currentUserPrincipal.isPresent());
|
||||
}
|
||||
|
||||
public void testGetCollections() throws Exception {
|
||||
final String DEFAULT_COLLECTION_OWNER = "/principals/__uids__/" + URLEncoder.encode(DavTestParams.USERNAME) + "/";
|
||||
|
||||
CardDavCollection collection = cardDavStore.getCollections().get(0);
|
||||
|
||||
assertEquals("Default addressbook collection must be owned by " + DavTestParams.USERNAME,
|
||||
collection.getOwnerHref().get(),
|
||||
DEFAULT_COLLECTION_OWNER);
|
||||
}
|
||||
|
||||
// TODO: implement address book creation in Darwin Calendar Server so this can be tested.
|
||||
/*
|
||||
@Test
|
||||
public void addGetRemoveSimpleCollection() throws Exception {
|
||||
Optional<String> addressbookHomeSet = cardDavStore.getAddressbookHomeSet();
|
||||
assertTrue("Address book home set property must be found.", addressbookHomeSet.isPresent());
|
||||
|
||||
final String COLLECTION_PATH = addressbookHomeSet.get().concat("test-collection/");
|
||||
|
||||
cardDavStore.addCollection(COLLECTION_PATH);
|
||||
assertTrue("Added collection must be found in the store.", cardDavStore.getCollection(COLLECTION_PATH).isPresent());
|
||||
|
||||
cardDavStore.removeCollection(COLLECTION_PATH);
|
||||
assertTrue("Removed collection must not be found in the store.", !cardDavStore.getCollection(COLLECTION_PATH).isPresent());
|
||||
} */
|
||||
|
||||
}
|
||||
@ -1,12 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.anhonesteffort.flock"
|
||||
android:versionCode="15"
|
||||
android:versionName="0.8.7" >
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="16"
|
||||
android:targetSdkVersion="19" />
|
||||
package="org.anhonesteffort.flock" >
|
||||
|
||||
<uses-permission android:name="android.permission.USE_CREDENTIALS"/>
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
|
||||
@ -25,6 +19,9 @@
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
|
||||
<uses-permission android:name="com.android.vending.BILLING"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@drawable/flock_icon"
|
||||
@ -110,13 +107,6 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="org.anhonesteffort.flock.ManageSubscriptionActivity" >
|
||||
<intent-filter>
|
||||
<action android:name="org.anhonesteffort.flock.ManageSubscriptionActivity"/>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name=".DeleteAllContactsActivity"
|
||||
android:windowSoftInputMode="adjustPan"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
|
||||
@ -126,17 +116,19 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="org.anhonesteffort.flock.EditAutoRenewActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
|
||||
<activity android:name="org.anhonesteffort.flock.SendBitcoinActivity" />
|
||||
|
||||
<activity android:name="org.anhonesteffort.flock.MigrationReleaseNotesActivity" />
|
||||
|
||||
<activity android:name="org.anhonesteffort.flock.SendDebugLogActivity"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
|
||||
android:windowSoftInputMode="stateHidden"/>
|
||||
|
||||
<activity android:name="org.anhonesteffort.flock.EolActivity" >
|
||||
<intent-filter>
|
||||
<action android:name="org.anhonesteffort.flock.EolActivity"/>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service android:name="org.anhonesteffort.flock.auth.AccountAuthenticatorService">
|
||||
<intent-filter>
|
||||
<action android:name="android.accounts.AccountAuthenticator" />
|
||||
@ -151,8 +143,7 @@
|
||||
android:label="KeySyncService" />
|
||||
|
||||
<service android:name="org.anhonesteffort.flock.sync.key.KeySyncService"
|
||||
android:exported="true"
|
||||
android:process=":flock" >
|
||||
android:exported="true" >
|
||||
<intent-filter>
|
||||
<action android:name="android.content.SyncAdapter"/>
|
||||
</intent-filter>
|
||||
@ -160,9 +151,22 @@
|
||||
android:resource="@xml/keys_syncadapter" />
|
||||
</service>
|
||||
|
||||
<provider android:name="org.anhonesteffort.flock.sync.account.AccountProviderStub"
|
||||
android:authorities="org.anhonesteffort.flock.sync.account"
|
||||
android:syncable="true"
|
||||
android:label="AccountSyncService" />
|
||||
|
||||
<service android:name="org.anhonesteffort.flock.sync.account.AccountSyncService"
|
||||
android:exported="true" >
|
||||
<intent-filter>
|
||||
<action android:name="android.content.SyncAdapter"/>
|
||||
</intent-filter>
|
||||
<meta-data android:name="android.content.SyncAdapter"
|
||||
android:resource="@xml/account_syncadapter" />
|
||||
</service>
|
||||
|
||||
<service android:name="org.anhonesteffort.flock.sync.addressbook.AddressbookSyncService"
|
||||
android:exported="true"
|
||||
android:process=":flock" >
|
||||
android:exported="true" >
|
||||
<intent-filter>
|
||||
<action android:name="android.content.SyncAdapter"/>
|
||||
</intent-filter>
|
||||
@ -173,8 +177,7 @@
|
||||
</service>
|
||||
|
||||
<service android:name="org.anhonesteffort.flock.sync.calendar.CalendarsSyncService"
|
||||
android:exported="true"
|
||||
android:process=":flock" >
|
||||
android:exported="true" >
|
||||
<intent-filter>
|
||||
<action android:name="android.content.SyncAdapter"/>
|
||||
</intent-filter>
|
||||
@ -203,7 +206,7 @@
|
||||
<service android:name="org.anhonesteffort.flock.ChangeEncryptionPasswordService"
|
||||
android:exported="false"/>
|
||||
|
||||
<service android:name="org.anhonesteffort.flock.MigrationService"
|
||||
<service android:name="org.anhonesteffort.flock.ExportService"
|
||||
android:exported="false"/>
|
||||
|
||||
<receiver android:name="org.anhonesteffort.flock.sync.SyncBooter">
|
||||
@ -218,13 +221,11 @@
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name="org.anhonesteffort.flock.MigrationHelperBroadcastReceiver">
|
||||
<receiver android:name="org.anhonesteffort.flock.EolNotifier">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
||||
<action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
|
||||
<action android:name="org.anhonesteffort.flock.MigrationService.ACTION_MIGRATION_STARTED"/>
|
||||
<action android:name="org.anhonesteffort.flock.MigrationService.ACTION_MIGRATION_COMPLETE"/>
|
||||
<action android:name="org.anhonesteffort.flock.sync.key.ACTION_KEY_MATERIAL_IMPORTED"/>
|
||||
<action android:name="org.anhonesteffort.flock.sync.addressbook.AddressbookSyncWorker.ACTION_PUSH_CREATED_CONTACTS"/>
|
||||
<action android:name="org.anhonesteffort.flock.INTENT_ALARM_24_HOURS"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
|
||||
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* 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 com.android.vending.billing;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
/**
|
||||
* InAppBillingService is the service that provides in-app billing version 3 and beyond.
|
||||
* This service provides the following features:
|
||||
* 1. Provides a new API to get details of in-app items published for the app including
|
||||
* price, type, title and description.
|
||||
* 2. The purchase flow is synchronous and purchase information is available immediately
|
||||
* after it completes.
|
||||
* 3. Purchase information of in-app purchases is maintained within the Google Play system
|
||||
* till the purchase is consumed.
|
||||
* 4. An API to consume a purchase of an inapp item. All purchases of one-time
|
||||
* in-app items are consumable and thereafter can be purchased again.
|
||||
* 5. An API to get current purchases of the user immediately. This will not contain any
|
||||
* consumed purchases.
|
||||
*
|
||||
* All calls will give a response code with the following possible values
|
||||
* RESULT_OK = 0 - success
|
||||
* RESULT_USER_CANCELED = 1 - user pressed back or canceled a dialog
|
||||
* RESULT_BILLING_UNAVAILABLE = 3 - this billing API version is not supported for the type requested
|
||||
* RESULT_ITEM_UNAVAILABLE = 4 - requested SKU is not available for purchase
|
||||
* RESULT_DEVELOPER_ERROR = 5 - invalid arguments provided to the API
|
||||
* RESULT_ERROR = 6 - Fatal error during the API action
|
||||
* RESULT_ITEM_ALREADY_OWNED = 7 - Failure to purchase since item is already owned
|
||||
* RESULT_ITEM_NOT_OWNED = 8 - Failure to consume since item is not owned
|
||||
*/
|
||||
interface IInAppBillingService {
|
||||
/**
|
||||
* Checks support for the requested billing API version, package and in-app type.
|
||||
* Minimum API version supported by this interface is 3.
|
||||
* @param apiVersion the billing version which the app is using
|
||||
* @param packageName the package name of the calling app
|
||||
* @param type type of the in-app item being purchased "inapp" for one-time purchases
|
||||
* and "subs" for subscription.
|
||||
* @return RESULT_OK(0) on success, corresponding result code on failures
|
||||
*/
|
||||
int isBillingSupported(int apiVersion, String packageName, String type);
|
||||
|
||||
/**
|
||||
* Provides details of a list of SKUs
|
||||
* Given a list of SKUs of a valid type in the skusBundle, this returns a bundle
|
||||
* with a list JSON strings containing the productId, price, title and description.
|
||||
* This API can be called with a maximum of 20 SKUs.
|
||||
* @param apiVersion billing API version that the Third-party is using
|
||||
* @param packageName the package name of the calling app
|
||||
* @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST"
|
||||
* @return Bundle containing the following key-value pairs
|
||||
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
|
||||
* failure as listed above.
|
||||
* "DETAILS_LIST" with a StringArrayList containing purchase information
|
||||
* in JSON format similar to:
|
||||
* '{ "productId" : "exampleSku", "type" : "inapp", "price" : "$5.00",
|
||||
* "title : "Example Title", "description" : "This is an example description" }'
|
||||
*/
|
||||
Bundle getSkuDetails(int apiVersion, String packageName, String type, in Bundle skusBundle);
|
||||
|
||||
/**
|
||||
* Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU,
|
||||
* the type, a unique purchase token and an optional developer payload.
|
||||
* @param apiVersion billing API version that the app is using
|
||||
* @param packageName package name of the calling app
|
||||
* @param sku the SKU of the in-app item as published in the developer console
|
||||
* @param type the type of the in-app item ("inapp" for one-time purchases
|
||||
* and "subs" for subscription).
|
||||
* @param developerPayload optional argument to be sent back with the purchase information
|
||||
* @return Bundle containing the following key-value pairs
|
||||
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
|
||||
* failure as listed above.
|
||||
* "BUY_INTENT" - PendingIntent to start the purchase flow
|
||||
*
|
||||
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow
|
||||
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
|
||||
* If the purchase is successful, the result data will contain the following key-value pairs
|
||||
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
|
||||
* failure as listed above.
|
||||
* "INAPP_PURCHASE_DATA" - String in JSON format similar to
|
||||
* '{"orderId":"12999763169054705758.1371079406387615",
|
||||
* "packageName":"com.example.app",
|
||||
* "productId":"exampleSku",
|
||||
* "purchaseTime":1345678900000,
|
||||
* "purchaseToken" : "122333444455555",
|
||||
* "developerPayload":"example developer payload" }'
|
||||
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
|
||||
* was signed with the private key of the developer
|
||||
* TODO: change this to app-specific keys.
|
||||
*/
|
||||
Bundle getBuyIntent(int apiVersion, String packageName, String sku, String type,
|
||||
String developerPayload);
|
||||
|
||||
/**
|
||||
* Returns the current SKUs owned by the user of the type and package name specified along with
|
||||
* purchase information and a signature of the data to be validated.
|
||||
* This will return all SKUs that have been purchased in V3 and managed items purchased using
|
||||
* V1 and V2 that have not been consumed.
|
||||
* @param apiVersion billing API version that the app is using
|
||||
* @param packageName package name of the calling app
|
||||
* @param type the type of the in-app items being requested
|
||||
* ("inapp" for one-time purchases and "subs" for subscription).
|
||||
* @param continuationToken to be set as null for the first call, if the number of owned
|
||||
* skus are too many, a continuationToken is returned in the response bundle.
|
||||
* This method can be called again with the continuation token to get the next set of
|
||||
* owned skus.
|
||||
* @return Bundle containing the following key-value pairs
|
||||
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
|
||||
* failure as listed above.
|
||||
* "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs
|
||||
* "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information
|
||||
* "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures
|
||||
* of the purchase information
|
||||
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
|
||||
* next set of in-app purchases. Only set if the
|
||||
* user has more owned skus than the current list.
|
||||
*/
|
||||
Bundle getPurchases(int apiVersion, String packageName, String type, String continuationToken);
|
||||
|
||||
/**
|
||||
* Consume the last purchase of the given SKU. This will result in this item being removed
|
||||
* from all subsequent responses to getPurchases() and allow re-purchase of this item.
|
||||
* @param apiVersion billing API version that the app is using
|
||||
* @param packageName package name of the calling app
|
||||
* @param purchaseToken token in the purchase information JSON that identifies the purchase
|
||||
* to be consumed
|
||||
* @return 0 if consumption succeeded. Appropriate error values for failures.
|
||||
*/
|
||||
int consumePurchase(int apiVersion, String packageName, String purchaseToken);
|
||||
}
|
||||
@ -28,7 +28,7 @@ import android.widget.ArrayAdapter;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.crypto.InvalidMacException;
|
||||
import org.anhonesteffort.flock.sync.HidingDavCollection;
|
||||
import org.anhonesteffort.flock.sync.LocalComponentStore;
|
||||
|
||||
@ -35,7 +35,7 @@ import android.widget.CompoundButton;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@ -78,7 +78,7 @@ public abstract class AbstractMyCollectionsFragment extends AccountAndKeyRequire
|
||||
activity = getActivity();
|
||||
View fragmentView = inflater.inflate(R.layout.fragment_list_sync_collections, container, false);
|
||||
|
||||
if (accountAndKeyAvailableAndMigrationComplete())
|
||||
if (accountAndKeyAvailable())
|
||||
initButtons();
|
||||
|
||||
return fragmentView;
|
||||
@ -105,7 +105,7 @@ public abstract class AbstractMyCollectionsFragment extends AccountAndKeyRequire
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (!accountAndKeyAvailableAndMigrationComplete())
|
||||
if (!accountAndKeyAvailable())
|
||||
return;
|
||||
|
||||
activity = getActivity();
|
||||
|
||||
@ -25,7 +25,7 @@ import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.crypto.KeyHelper;
|
||||
import org.anhonesteffort.flock.crypto.MasterCipher;
|
||||
@ -97,16 +97,7 @@ public class AccountAndKeyRequiredActivity extends Activity {
|
||||
masterCipher = handleGetMasterCipherOrFail(this);
|
||||
}
|
||||
|
||||
protected boolean accountAndKeyAvailableAndMigrationComplete() {
|
||||
if (MigrationHelperBroadcastReceiver.getUiDisabledForMigration(getBaseContext())) {
|
||||
Toast.makeText(getBaseContext(),
|
||||
R.string.migration_in_progress_please_wait,
|
||||
Toast.LENGTH_LONG).show();
|
||||
|
||||
finish();
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean accountAndKeyAvailable() {
|
||||
return account != null && masterCipher != null;
|
||||
}
|
||||
|
||||
|
||||
@ -21,7 +21,6 @@ package org.anhonesteffort.flock;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.crypto.MasterCipher;
|
||||
@ -42,16 +41,7 @@ public class AccountAndKeyRequiredFragment extends Fragment {
|
||||
masterCipher = AccountAndKeyRequiredActivity.handleGetMasterCipherOrFail(getActivity());
|
||||
}
|
||||
|
||||
protected boolean accountAndKeyAvailableAndMigrationComplete() {
|
||||
if (MigrationHelperBroadcastReceiver.getUiDisabledForMigration(getActivity())) {
|
||||
Toast.makeText(getActivity(),
|
||||
R.string.migration_in_progress_please_wait,
|
||||
Toast.LENGTH_LONG).show();
|
||||
|
||||
getActivity().finish();
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean accountAndKeyAvailable() {
|
||||
return account != null && masterCipher != null;
|
||||
}
|
||||
|
||||
|
||||
@ -29,7 +29,7 @@ import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@ -93,7 +93,6 @@ public class CalendarCopyService extends Service implements CalendarCopiedListen
|
||||
private void handleCopyComplete() {
|
||||
Log.d(TAG, "handleCopyComplete()");
|
||||
|
||||
new CalendarsSyncScheduler(getBaseContext()).requestSync();
|
||||
stopForeground(false);
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@ import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.crypto.KeyStore;
|
||||
import org.anhonesteffort.flock.sync.key.KeySyncScheduler;
|
||||
import org.anhonesteffort.flock.util.PasswordUtil;
|
||||
@ -54,7 +54,7 @@ public class ChangeEncryptionPasswordActivity extends AccountAndKeyRequiredActiv
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (!accountAndKeyAvailableAndMigrationComplete())
|
||||
if (!accountAndKeyAvailable())
|
||||
return;
|
||||
|
||||
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
|
||||
@ -116,7 +116,6 @@ public class ChangeEncryptionPasswordActivity extends AccountAndKeyRequiredActiv
|
||||
return;
|
||||
|
||||
Bundle result = new Bundle();
|
||||
String cipherPassphrase = ((TextView)findViewById(R.id.cipher_passphrase)).getText().toString().trim();
|
||||
String newCipherPassphrase = ((TextView)findViewById(R.id.new_cipher_passphrase)).getText().toString().trim();
|
||||
String newCipherPassphraseRepeat = ((TextView)findViewById(R.id.new_cipher_passphrase_repeat)).getText().toString().trim();
|
||||
|
||||
@ -136,11 +135,10 @@ public class ChangeEncryptionPasswordActivity extends AccountAndKeyRequiredActiv
|
||||
}
|
||||
|
||||
Optional<String> savedPassphrase = KeyStore.getMasterPassphrase(getBaseContext());
|
||||
if (!savedPassphrase.isPresent() || !savedPassphrase.get().equals(cipherPassphrase)) {
|
||||
if (!savedPassphrase.isPresent()) {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_INVALID_CIPHER_PASSPHRASE);
|
||||
ErrorToaster.handleDisplayToastBundledError(getBaseContext(), result);
|
||||
|
||||
((TextView)findViewById(R.id.cipher_passphrase)).setText("");
|
||||
((TextView)findViewById(R.id.new_cipher_passphrase)).setText("");
|
||||
((TextView)findViewById(R.id.new_cipher_passphrase_repeat)).setText("");
|
||||
return;
|
||||
@ -149,7 +147,7 @@ public class ChangeEncryptionPasswordActivity extends AccountAndKeyRequiredActiv
|
||||
Intent changeService = new Intent(getBaseContext(), ChangeEncryptionPasswordService.class);
|
||||
|
||||
changeService.putExtra(ChangeEncryptionPasswordService.KEY_MESSENGER, new Messenger(new MessageHandler()));
|
||||
changeService.putExtra(ChangeEncryptionPasswordService.KEY_OLD_MASTER_PASSPHRASE, cipherPassphrase);
|
||||
changeService.putExtra(ChangeEncryptionPasswordService.KEY_OLD_MASTER_PASSPHRASE, savedPassphrase.get());
|
||||
changeService.putExtra(ChangeEncryptionPasswordService.KEY_NEW_MASTER_PASSPHRASE, newCipherPassphrase);
|
||||
changeService.putExtra(ChangeEncryptionPasswordService.KEY_ACCOUNT, account.toBundle());
|
||||
|
||||
@ -177,8 +175,7 @@ public class ChangeEncryptionPasswordActivity extends AccountAndKeyRequiredActiv
|
||||
errorBundler.putInt(ErrorToaster.KEY_STATUS_CODE, message.arg1);
|
||||
ErrorToaster.handleDisplayToastBundledError(getBaseContext(), errorBundler);
|
||||
|
||||
if (findViewById(R.id.cipher_passphrase) != null) {
|
||||
((TextView)findViewById(R.id.cipher_passphrase)).setText("");
|
||||
if (findViewById(R.id.new_cipher_passphrase) != null) {
|
||||
((TextView)findViewById(R.id.new_cipher_passphrase)).setText("");
|
||||
((TextView)findViewById(R.id.new_cipher_passphrase_repeat)).setText("");
|
||||
}
|
||||
|
||||
@ -15,8 +15,7 @@ import android.os.RemoteException;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.crypto.KeyHelper;
|
||||
import org.anhonesteffort.flock.crypto.KeyStore;
|
||||
@ -100,11 +99,11 @@ public class ChangeEncryptionPasswordService extends Service {
|
||||
|
||||
private void handleChangeOwsAuthToken(Bundle result, String passphrase) {
|
||||
Log.d(TAG, "handleChangeOwsAuthToken()");
|
||||
RegistrationApi registrationApi = new RegistrationApi(getBaseContext());
|
||||
|
||||
try {
|
||||
|
||||
String newAuthToken = KeyUtil.getAuthTokenForPassphrase(passphrase);
|
||||
RegistrationApi registrationApi = new RegistrationApi(getBaseContext());
|
||||
String newAuthToken = KeyUtil.getAuthTokenForPassphrase(passphrase);
|
||||
|
||||
registrationApi.setAccountPassword(account, newAuthToken);
|
||||
DavAccountHelper.setAccountPassword(getBaseContext(), newAuthToken);
|
||||
|
||||
@ -132,7 +132,6 @@ public class ContactCopyService extends Service implements ContactCopiedListener
|
||||
private void handleCopyComplete() {
|
||||
Log.d(TAG, "handleCopyComplete()");
|
||||
|
||||
new AddressbookSyncScheduler(getBaseContext()).requestSync();
|
||||
stopForeground(false);
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ import android.view.Window;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.sync.addressbook.AddressbookSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.calendar.CalendarsSyncScheduler;
|
||||
|
||||
@ -15,13 +15,11 @@ import android.os.RemoteException;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.crypto.KeyHelper;
|
||||
import org.anhonesteffort.flock.crypto.KeyStore;
|
||||
import org.anhonesteffort.flock.crypto.KeyUtil;
|
||||
import org.anhonesteffort.flock.sync.AbstractDavSyncAdapter;
|
||||
import org.anhonesteffort.flock.webdav.PropertyParseException;
|
||||
import org.apache.jackrabbit.webdav.DavException;
|
||||
|
||||
|
||||
@ -24,7 +24,7 @@ import android.accounts.AccountManager;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.crypto.MasterCipher;
|
||||
import org.anhonesteffort.flock.sync.AndroidDavClient;
|
||||
@ -33,10 +33,10 @@ import org.anhonesteffort.flock.sync.addressbook.HidingCardDavStore;
|
||||
import org.anhonesteffort.flock.sync.calendar.HidingCalDavStore;
|
||||
import org.anhonesteffort.flock.sync.key.DavKeyStore;
|
||||
import org.anhonesteffort.flock.webdav.PropertyParseException;
|
||||
import org.anhonesteffort.flock.webdav.WebDavConstants;
|
||||
import org.anhonesteffort.flock.webdav.caldav.CalDavStore;
|
||||
import org.anhonesteffort.flock.webdav.carddav.CardDavStore;
|
||||
import org.apache.jackrabbit.webdav.DavException;
|
||||
import org.apache.jackrabbit.webdav.DavServletResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
@ -55,7 +55,7 @@ public class DavAccountHelper {
|
||||
private static final String KEY_DAV_HOST = "org.anhonesteffort.flock.auth.AccountAuthenticator.KEY_DAV_HOST";
|
||||
|
||||
private static SharedPreferences getSharedPreferences(Context context) {
|
||||
return context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_MULTI_PROCESS);
|
||||
return context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
public static String correctUsername(Context context, String username) {
|
||||
@ -74,7 +74,7 @@ public class DavAccountHelper {
|
||||
}
|
||||
|
||||
public static void setAccountUsername(Context context, String username) {
|
||||
getSharedPreferences(context).edit().putString(KEY_DAV_USERNAME, username).commit();
|
||||
getSharedPreferences(context).edit().putString(KEY_DAV_USERNAME, username).apply();
|
||||
}
|
||||
|
||||
public static Optional<String> getAccountPassword(Context context) {
|
||||
@ -82,11 +82,11 @@ public class DavAccountHelper {
|
||||
}
|
||||
|
||||
public static void setAccountPassword(Context context, String password) {
|
||||
getSharedPreferences(context).edit().putString(KEY_DAV_PASSWORD, password).commit();
|
||||
getSharedPreferences(context).edit().putString(KEY_DAV_PASSWORD, password).apply();
|
||||
}
|
||||
|
||||
public static void invalidateAccountPassword(Context context) {
|
||||
getSharedPreferences(context).edit().remove(KEY_DAV_PASSWORD).commit();
|
||||
getSharedPreferences(context).edit().remove(KEY_DAV_PASSWORD).apply();
|
||||
}
|
||||
|
||||
public static Optional<String> getAccountDavHREF(Context context) {
|
||||
@ -94,13 +94,13 @@ public class DavAccountHelper {
|
||||
}
|
||||
|
||||
public static void setAccountDavHREF(Context context, String href) {
|
||||
getSharedPreferences(context).edit().putString(KEY_DAV_HOST, href).commit();
|
||||
getSharedPreferences(context).edit().putString(KEY_DAV_HOST, href).apply();
|
||||
}
|
||||
|
||||
public static void invalidateAccount(Context context) {
|
||||
getSharedPreferences(context).edit().remove(KEY_DAV_HOST).commit();
|
||||
getSharedPreferences(context).edit().remove(KEY_DAV_USERNAME).commit();
|
||||
getSharedPreferences(context).edit().remove(KEY_DAV_PASSWORD).commit();
|
||||
getSharedPreferences(context).edit().remove(KEY_DAV_HOST).apply();
|
||||
getSharedPreferences(context).edit().remove(KEY_DAV_USERNAME).apply();
|
||||
getSharedPreferences(context).edit().remove(KEY_DAV_PASSWORD).apply();
|
||||
}
|
||||
|
||||
public static boolean isUsingOurServers(DavAccount account) {
|
||||
@ -244,7 +244,7 @@ public class DavAccountHelper {
|
||||
|
||||
if (e.getErrorCode() == OwsWebDav.STATUS_PAYMENT_REQUIRED)
|
||||
return true;
|
||||
else if (e.getErrorCode() == DavServletResponse.SC_UNAUTHORIZED)
|
||||
else if (e.getErrorCode() == WebDavConstants.SC_UNAUTHORIZED)
|
||||
return false;
|
||||
else
|
||||
throw e;
|
||||
|
||||
@ -33,10 +33,8 @@ import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.crypto.KeyStore;
|
||||
import org.anhonesteffort.flock.sync.addressbook.AddressbookSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.addressbook.LocalAddressbookStore;
|
||||
import org.anhonesteffort.flock.sync.addressbook.LocalContactCollection;
|
||||
|
||||
@ -53,7 +51,7 @@ public class DeleteAllContactsActivity extends AccountAndKeyRequiredActivity {
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (!accountAndKeyAvailableAndMigrationComplete())
|
||||
if (!accountAndKeyAvailable())
|
||||
return;
|
||||
|
||||
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
|
||||
@ -111,8 +109,6 @@ public class DeleteAllContactsActivity extends AccountAndKeyRequiredActivity {
|
||||
private void handleAllContactsDeleted() {
|
||||
Log.d(TAG, "handleAllContactsDeleted()");
|
||||
|
||||
new AddressbookSyncScheduler(getBaseContext()).requestSync();
|
||||
|
||||
Toast.makeText(getBaseContext(),
|
||||
R.string.all_contacts_have_been_deleted,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
|
||||
@ -1,750 +0,0 @@
|
||||
/*
|
||||
* *
|
||||
* Copyright (C) 2014 Open Whisper Systems
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* /
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.stripe.exception.CardException;
|
||||
import com.stripe.exception.StripeException;
|
||||
import com.stripe.model.Token;
|
||||
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.registration.OwsRegistration;
|
||||
import org.anhonesteffort.flock.registration.RegistrationApi;
|
||||
import org.anhonesteffort.flock.registration.RegistrationApiException;
|
||||
import org.anhonesteffort.flock.registration.model.AugmentedFlockAccount;
|
||||
import org.anhonesteffort.flock.registration.model.FlockAccount;
|
||||
import org.anhonesteffort.flock.registration.model.FlockCardInformation;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Programmer: rhodey
|
||||
*/
|
||||
public class EditAutoRenewActivity extends Activity {
|
||||
|
||||
private static final String TAG = "org.anhonesteffort.flock.EditAutoRenewActivity";
|
||||
|
||||
public static final String KEY_DAV_ACCOUNT_BUNDLE = "KEY_DAV_ACCOUNT_BUNDLE";
|
||||
public static final String KEY_FLOCK_ACCOUNT_BUNDLE = "KEY_FLOCK_ACCOUNT_BUNDLE";
|
||||
public static final String KEY_CARD_INFORMATION_BUNDLE = "KEY_CARD_INFORMATION_BUNDLE";
|
||||
|
||||
private DavAccount davAccount;
|
||||
private AsyncTask asyncTask;
|
||||
private TextWatcher cardNumberTextWatcher;
|
||||
private TextWatcher cardExpirationTextWatcher;
|
||||
|
||||
private Optional<FlockAccount> flockAccount = Optional.absent();
|
||||
private Optional<FlockCardInformation> cardInformation = Optional.absent();
|
||||
|
||||
private int lastCardExpirationLength = 0;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
|
||||
requestWindowFeature(Window.FEATURE_PROGRESS);
|
||||
|
||||
setContentView(R.layout.activity_edit_auto_renew);
|
||||
getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getActionBar().setTitle(R.string.button_edit_payment_details);
|
||||
|
||||
if (savedInstanceState != null && !savedInstanceState.isEmpty()) {
|
||||
if (!DavAccount.build(savedInstanceState.getBundle(KEY_DAV_ACCOUNT_BUNDLE)).isPresent()) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
davAccount = DavAccount.build(savedInstanceState.getBundle(KEY_DAV_ACCOUNT_BUNDLE)).get();
|
||||
flockAccount = FlockAccount.build(savedInstanceState.getBundle(KEY_FLOCK_ACCOUNT_BUNDLE));
|
||||
cardInformation = FlockCardInformation.build(savedInstanceState.getBundle(KEY_CARD_INFORMATION_BUNDLE));
|
||||
}
|
||||
else if (getIntent().getExtras() != null) {
|
||||
if (!DavAccount.build(getIntent().getExtras().getBundle(KEY_DAV_ACCOUNT_BUNDLE)).isPresent()) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
davAccount = DavAccount.build(getIntent().getExtras().getBundle(KEY_DAV_ACCOUNT_BUNDLE)).get();
|
||||
flockAccount = FlockAccount.build(getIntent().getExtras().getBundle(KEY_FLOCK_ACCOUNT_BUNDLE));
|
||||
cardInformation = FlockCardInformation.build(getIntent().getExtras().getBundle(KEY_CARD_INFORMATION_BUNDLE));
|
||||
}
|
||||
|
||||
initCostPerYear();
|
||||
}
|
||||
|
||||
private void initCostPerYear() {
|
||||
TextView costPerYearView = (TextView) findViewById(R.id.cost_per_year);
|
||||
double costPerYearUsd = (double) getResources().getInteger(R.integer.cost_per_year_usd);
|
||||
|
||||
costPerYearView.setText(getString(R.string.usd_per_year, costPerYearUsd));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
((CheckBox) findViewById(R.id.checkbox_enable_auto_renew)).setChecked(false);
|
||||
handleDisableForm();
|
||||
|
||||
handleGetAccountAndCardAsync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle savedInstanceState) {
|
||||
savedInstanceState.putBundle(KEY_DAV_ACCOUNT_BUNDLE, davAccount.toBundle());
|
||||
|
||||
if (flockAccount.isPresent())
|
||||
savedInstanceState.putBundle(KEY_FLOCK_ACCOUNT_BUNDLE, flockAccount.get().toBundle());
|
||||
|
||||
if (cardInformation.isPresent())
|
||||
savedInstanceState.putBundle(KEY_CARD_INFORMATION_BUNDLE, cardInformation.get().toBundle());
|
||||
|
||||
super.onSaveInstanceState(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
|
||||
if (asyncTask != null && !asyncTask.isCancelled())
|
||||
asyncTask.cancel(true);
|
||||
|
||||
((EditText) findViewById(R.id.card_number)).setText("");
|
||||
((EditText) findViewById(R.id.card_expiration)).setText("");
|
||||
}
|
||||
|
||||
private void handleDisableForm() {
|
||||
Log.d(TAG, "handleDisableForm()");
|
||||
|
||||
Button verifyAndSaveButton = (Button) findViewById(R.id.button_verify_and_save);
|
||||
EditText cardExpirationView = (EditText) findViewById(R.id.card_expiration);
|
||||
EditText cardNumberView = (EditText) findViewById(R.id.card_number);
|
||||
TextView cardCVCView = (TextView) findViewById(R.id.card_cvc);
|
||||
|
||||
cardNumberView.setFocusable(false);
|
||||
cardNumberView.setFocusableInTouchMode(false);
|
||||
cardExpirationView.setFocusable(false);
|
||||
cardExpirationView.setFocusableInTouchMode(false);
|
||||
cardCVCView.setFocusable(false);
|
||||
cardCVCView.setFocusableInTouchMode(false);
|
||||
|
||||
cardNumberView.setText("");
|
||||
cardExpirationView.setText("");
|
||||
cardCVCView.setText("");
|
||||
|
||||
cardNumberView.setOnTouchListener(null);
|
||||
cardExpirationView.setOnTouchListener(null);
|
||||
cardCVCView.setOnTouchListener(null);
|
||||
|
||||
verifyAndSaveButton.setText(R.string.button_save);
|
||||
verifyAndSaveButton.setOnClickListener(null);
|
||||
}
|
||||
|
||||
private void handleInitFormAsAutoRenewDisabled() {
|
||||
Log.d(TAG, "handleInitFormFormAsAutoRenewDisabled()");
|
||||
|
||||
Button verifyAndSaveButton = (Button) findViewById(R.id.button_verify_and_save);
|
||||
EditText cardExpirationView = (EditText) findViewById(R.id.card_expiration);
|
||||
EditText cardNumberView = (EditText) findViewById(R.id.card_number);
|
||||
TextView cardCVCView = (TextView) findViewById(R.id.card_cvc);
|
||||
|
||||
cardNumberView.setFocusable(false);
|
||||
cardNumberView.setFocusableInTouchMode(false);
|
||||
cardExpirationView.setFocusable(false);
|
||||
cardExpirationView.setFocusableInTouchMode(false);
|
||||
cardCVCView.setFocusable(false);
|
||||
cardCVCView.setFocusableInTouchMode(false);
|
||||
|
||||
cardNumberView.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN)
|
||||
((CheckBox) findViewById(R.id.checkbox_enable_auto_renew)).setChecked(true);
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
cardExpirationView.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN)
|
||||
((CheckBox) findViewById(R.id.checkbox_enable_auto_renew)).setChecked(true);
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
cardCVCView.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN)
|
||||
((CheckBox) findViewById(R.id.checkbox_enable_auto_renew)).setChecked(true);
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
cardNumberView.setText("");
|
||||
cardExpirationView.setText("");
|
||||
cardCVCView.setText("");
|
||||
|
||||
verifyAndSaveButton.setText(R.string.button_save);
|
||||
verifyAndSaveButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
handleSaveAutoRenewAndFinish(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleEnableForm() {
|
||||
Log.d(TAG, "handleEnableForm()");
|
||||
|
||||
EditText cardExpirationView = (EditText) findViewById(R.id.card_expiration);
|
||||
EditText cardNumberView = (EditText) findViewById(R.id.card_number);
|
||||
TextView cardCVCView = (TextView) findViewById(R.id.card_cvc);
|
||||
|
||||
cardNumberView.setFocusable(true);
|
||||
cardNumberView.setFocusableInTouchMode(true);
|
||||
cardExpirationView.setFocusable(true);
|
||||
cardExpirationView.setFocusableInTouchMode(true);
|
||||
cardCVCView.setFocusable(true);
|
||||
cardCVCView.setFocusableInTouchMode(true);
|
||||
|
||||
cardNumberView.setOnTouchListener(null);
|
||||
cardExpirationView.setOnTouchListener(null);
|
||||
cardCVCView.setOnTouchListener(null);
|
||||
}
|
||||
|
||||
private void handleInitFormForEditing() {
|
||||
Log.d(TAG, "handleInitFormForEditing()");
|
||||
|
||||
Button verifyAndSaveButton = (Button) findViewById(R.id.button_verify_and_save);
|
||||
|
||||
handleEnableForm();
|
||||
initCardNumberHelper();
|
||||
initCardExpirationHelper();
|
||||
|
||||
verifyAndSaveButton.setText(R.string.button_verify_and_save);
|
||||
verifyAndSaveButton.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
handleVerifyCardAndFinish();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private void handleInitFormForFixingError() {
|
||||
Log.d(TAG, "handleInitFormForFixingError()");
|
||||
|
||||
EditText cardNumberView = (EditText) findViewById(R.id.card_number);
|
||||
EditText cardExpirationView = (EditText) findViewById(R.id.card_expiration);
|
||||
TextView cardCVCView = (TextView) findViewById(R.id.card_cvc);
|
||||
Button verifyAndSaveButton = (Button) findViewById(R.id.button_verify_and_save);
|
||||
|
||||
if (StringUtils.isEmpty(cardNumberView.getText().toString()))
|
||||
cardNumberView.setText("**** **** **** " + cardInformation.get().getCardLastFour());
|
||||
|
||||
cardNumberView.setError(getString(R.string.error_your_card_could_not_be_verified));
|
||||
|
||||
if (StringUtils.isEmpty(cardExpirationView.getText().toString()))
|
||||
cardExpirationView.setText(cardInformation.get().getCardExpiration());
|
||||
|
||||
cardCVCView.setText("");
|
||||
|
||||
handleEnableForm();
|
||||
initCardNumberHelper();
|
||||
initCardExpirationHelper();
|
||||
|
||||
verifyAndSaveButton.setText(R.string.button_verify_and_save);
|
||||
verifyAndSaveButton.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
handleVerifyCardAndFinish();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private void handleInitFormForViewingSuccess() {
|
||||
Log.d(TAG, "handleInitFormForViewingSuccess()");
|
||||
|
||||
Button verifyAndSaveButton = (Button) findViewById(R.id.button_verify_and_save);
|
||||
EditText cardNumberView = (EditText) findViewById(R.id.card_number);
|
||||
EditText cardExpirationView = (EditText) findViewById(R.id.card_expiration);
|
||||
TextView cardCVCView = (TextView) findViewById(R.id.card_cvc);
|
||||
|
||||
handleEnableForm();
|
||||
|
||||
cardNumberView.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN)
|
||||
handleInitFormForEditing();
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
cardExpirationView.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN)
|
||||
handleInitFormForEditing();
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
cardCVCView.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN)
|
||||
handleInitFormForEditing();
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (StringUtils.isEmpty(cardNumberView.getText().toString()))
|
||||
cardNumberView.setText("**** **** **** " + cardInformation.get().getCardLastFour());
|
||||
|
||||
if (StringUtils.isEmpty(cardExpirationView.getText().toString()))
|
||||
cardExpirationView.setText(cardInformation.get().getCardExpiration());
|
||||
|
||||
cardCVCView.setText("");
|
||||
|
||||
verifyAndSaveButton.setText(R.string.button_save);
|
||||
verifyAndSaveButton.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
handleSaveAutoRenewAndFinish(true);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private void initCardNumberHelper() {
|
||||
Log.d(TAG, "initCardNumberHelper()");
|
||||
|
||||
final EditText cardNumberView = (EditText) findViewById(R.id.card_number);
|
||||
final EditText cardExpirationView = (EditText) findViewById(R.id.card_expiration);
|
||||
final CheckBox autoRenewIsEnabled = (CheckBox) findViewById(R.id.checkbox_enable_auto_renew);
|
||||
|
||||
cardNumberView.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (cardNumberView.getText() != null &&
|
||||
cardNumberView.getText().toString().contains("*"))
|
||||
{
|
||||
cardNumberView.setText("");
|
||||
}
|
||||
|
||||
if (autoRenewIsEnabled.isChecked())
|
||||
handleInitFormForEditing();
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (cardNumberTextWatcher != null)
|
||||
cardNumberView.removeTextChangedListener(cardNumberTextWatcher);
|
||||
|
||||
cardNumberTextWatcher = new TextWatcher() {
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
String cardNumber = s.toString().replace(" ", "");
|
||||
String formattedCardNumber = "";
|
||||
|
||||
for (int i = 0; i < cardNumber.length(); i++) {
|
||||
if (i > 0 && i % 4 == 0)
|
||||
formattedCardNumber += " ";
|
||||
|
||||
formattedCardNumber += cardNumber.charAt(i);
|
||||
}
|
||||
|
||||
cardNumberView.removeTextChangedListener(this);
|
||||
cardNumberView.setText(formattedCardNumber);
|
||||
cardNumberView.setSelection(formattedCardNumber.length());
|
||||
cardNumberView.addTextChangedListener(this);
|
||||
|
||||
if (!cardNumber.contains("*") && cardNumber.length() == 16)
|
||||
cardExpirationView.requestFocus();
|
||||
}
|
||||
};
|
||||
|
||||
cardNumberView.addTextChangedListener(cardNumberTextWatcher);
|
||||
}
|
||||
|
||||
private void initCardExpirationHelper() {
|
||||
Log.d(TAG, "initCardExpirationHelper()");
|
||||
|
||||
final EditText cardExpirationView = (EditText) findViewById(R.id.card_expiration);
|
||||
final EditText cardCvcView = (EditText) findViewById(R.id.card_cvc);
|
||||
final CheckBox autoRenewIsEnabled = (CheckBox) findViewById(R.id.checkbox_enable_auto_renew);
|
||||
|
||||
cardExpirationView.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (autoRenewIsEnabled.isChecked())
|
||||
handleInitFormForEditing();
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (cardExpirationTextWatcher != null)
|
||||
cardExpirationView.removeTextChangedListener(cardExpirationTextWatcher);
|
||||
|
||||
cardExpirationTextWatcher = new TextWatcher() {
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
String formattedCardExpiration = s.toString();
|
||||
|
||||
if (lastCardExpirationLength <= formattedCardExpiration.length() &&
|
||||
formattedCardExpiration.length() == 2)
|
||||
{
|
||||
formattedCardExpiration = formattedCardExpiration + "/";
|
||||
}
|
||||
|
||||
lastCardExpirationLength = formattedCardExpiration.length();
|
||||
|
||||
cardExpirationView.removeTextChangedListener(this);
|
||||
cardExpirationView.setText(formattedCardExpiration);
|
||||
cardExpirationView.setSelection(formattedCardExpiration.length());
|
||||
cardExpirationView.addTextChangedListener(this);
|
||||
|
||||
if (formattedCardExpiration.length() == 5)
|
||||
cardCvcView.requestFocus();
|
||||
}
|
||||
};
|
||||
|
||||
cardExpirationView.addTextChangedListener(cardExpirationTextWatcher);
|
||||
}
|
||||
|
||||
private void handleRefreshForm(boolean isCallback) {
|
||||
Log.d(TAG, "handleRefreshForm() is callback >> " + isCallback);
|
||||
|
||||
CheckBox autoRenewIsEnabled = (CheckBox) findViewById(R.id.checkbox_enable_auto_renew);
|
||||
|
||||
if (!isCallback)
|
||||
autoRenewIsEnabled.setChecked(flockAccount.get().getAutoRenewEnabled());
|
||||
|
||||
autoRenewIsEnabled.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
handleRefreshForm(true);
|
||||
}
|
||||
});
|
||||
|
||||
if (!autoRenewIsEnabled.isChecked())
|
||||
handleInitFormAsAutoRenewDisabled();
|
||||
else {
|
||||
if (!flockAccount.get().getLastStripeChargeFailed() && cardInformation.isPresent())
|
||||
handleInitFormForViewingSuccess();
|
||||
else if (flockAccount.get().getLastStripeChargeFailed() && cardInformation.isPresent())
|
||||
handleInitFormForFixingError();
|
||||
else
|
||||
handleInitFormForEditing();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSaveAutoRenewAndFinish(final Boolean autoRenewIsEnabled) {
|
||||
if (asyncTask != null)
|
||||
return;
|
||||
|
||||
asyncTask = new AsyncTask<Void, Void, Bundle>() {
|
||||
boolean autoRenewChanged = false;
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
Log.d(TAG, "handleSaveAutoRenewAndFinish()");
|
||||
setProgressBarIndeterminateVisibility(true);
|
||||
setProgressBarVisibility(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bundle doInBackground(Void... params) {
|
||||
Bundle result = new Bundle();
|
||||
RegistrationApi registrationApi = new RegistrationApi(getBaseContext());
|
||||
|
||||
if (flockAccount.get().getAutoRenewEnabled() == autoRenewIsEnabled) {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SUCCESS);
|
||||
return result;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
registrationApi.setAccountAutoRenew(davAccount, autoRenewIsEnabled);
|
||||
flockAccount = Optional.of((FlockAccount) registrationApi.getAccount(davAccount));
|
||||
autoRenewChanged = true;
|
||||
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SUCCESS);
|
||||
|
||||
} catch (RegistrationApiException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
} catch (IOException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Bundle result) {
|
||||
asyncTask = null;
|
||||
setProgressBarIndeterminateVisibility(false);
|
||||
setProgressBarVisibility(false);
|
||||
|
||||
if (result.getInt(ErrorToaster.KEY_STATUS_CODE) == ErrorToaster.CODE_SUCCESS) {
|
||||
if (autoRenewChanged)
|
||||
Toast.makeText(getBaseContext(),
|
||||
R.string.autorenew_saved,
|
||||
Toast.LENGTH_LONG).show();
|
||||
|
||||
finish();
|
||||
}
|
||||
else
|
||||
ErrorToaster.handleDisplayToastBundledError(getBaseContext(), result);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private void handleVerifyCardAndFinish() {
|
||||
if (asyncTask != null)
|
||||
return;
|
||||
|
||||
asyncTask = new AsyncTask<Void, Void, Bundle>() {
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
Log.d(TAG, "handleVerifyCardAndFinish()");
|
||||
setProgressBarIndeterminateVisibility(true);
|
||||
setProgressBarVisibility(true);
|
||||
}
|
||||
|
||||
private String handleGetStripeCardTokenId(String cardNumber,
|
||||
String cardExpiration,
|
||||
String cardCVC)
|
||||
throws StripeException
|
||||
{
|
||||
String[] expiration = cardExpiration.split("/");
|
||||
Integer expirationMonth = Integer.valueOf(expiration[0]);
|
||||
Integer expirationYear;
|
||||
|
||||
if (expiration[1].length() == 4)
|
||||
expirationYear = Integer.valueOf(expiration[1]);
|
||||
else
|
||||
expirationYear = Integer.valueOf(expiration[1]) + 2000;
|
||||
|
||||
java.util.Map<String, Object> cardParams = new HashMap<String, Object>();
|
||||
java.util.Map<String, Object> tokenParams = new HashMap<String, Object>();
|
||||
|
||||
cardParams.put("number", cardNumber.replace(" ", ""));
|
||||
cardParams.put("exp_month", expirationMonth);
|
||||
cardParams.put("exp_year", expirationYear);
|
||||
cardParams.put("cvc", cardCVC);
|
||||
|
||||
tokenParams.put("card", cardParams);
|
||||
|
||||
return Token.create(tokenParams, OwsRegistration.STRIPE_PUBLIC_KEY).getId();
|
||||
}
|
||||
|
||||
private void handlePutStripeTokenToServer(String stripeTokenId)
|
||||
throws IOException, RegistrationApiException, CardException
|
||||
{
|
||||
RegistrationApi registrationApi = new RegistrationApi(getBaseContext());
|
||||
registrationApi.updateAccountStripeCard(davAccount, stripeTokenId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bundle doInBackground(Void... params) {
|
||||
Bundle result = new Bundle();
|
||||
String cardNumber = ((TextView)findViewById(R.id.card_number)).getText().toString();
|
||||
String cardExpiration = ((TextView)findViewById(R.id.card_expiration)).getText().toString();
|
||||
String cardCVC = ((TextView)findViewById(R.id.card_cvc)).getText().toString();
|
||||
|
||||
if (StringUtils.isEmpty(cardNumber)) {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_CARD_NUMBER_INVALID);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(cardExpiration) || cardExpiration.split("/").length != 2) {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_CARD_EXPIRATION_INVALID);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(cardCVC) || cardCVC.length() < 1) {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_CARD_CVC_INVALID);
|
||||
return result;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
String stripeTokenId = handleGetStripeCardTokenId(cardNumber, cardExpiration, cardCVC);
|
||||
handlePutStripeTokenToServer(stripeTokenId);
|
||||
|
||||
if (!flockAccount.get().getAutoRenewEnabled())
|
||||
new RegistrationApi(getBaseContext()).setAccountAutoRenew(davAccount, true);
|
||||
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SUCCESS);
|
||||
|
||||
} catch (CardException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
} catch (StripeException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
} catch (RegistrationApiException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
} catch (IOException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Bundle result) {
|
||||
asyncTask = null;
|
||||
setProgressBarIndeterminateVisibility(false);
|
||||
setProgressBarVisibility(false);
|
||||
|
||||
if (result.getInt(ErrorToaster.KEY_STATUS_CODE) == ErrorToaster.CODE_SUCCESS) {
|
||||
Toast.makeText(getBaseContext(), R.string.card_verified_and_saved, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
}
|
||||
|
||||
else {
|
||||
handleInitFormForEditing();
|
||||
ErrorToaster.handleDisplayToastBundledError(getBaseContext(), result);
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private void handleGetAccountAndCardAsync() {
|
||||
if (flockAccount.isPresent() && cardInformation.isPresent()) {
|
||||
handleRefreshForm(false);
|
||||
return;
|
||||
}
|
||||
|
||||
asyncTask = new AsyncTask<String, Void, Bundle>() {
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
Log.d(TAG, "handleGetAccountAndCardAsync()");
|
||||
setProgressBarIndeterminateVisibility(true);
|
||||
setProgressBarVisibility(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bundle doInBackground(String... params) {
|
||||
Bundle result = new Bundle();
|
||||
RegistrationApi registrationApi = new RegistrationApi(getBaseContext());
|
||||
|
||||
try {
|
||||
|
||||
if (!flockAccount.isPresent()) {
|
||||
|
||||
AugmentedFlockAccount augmentedAccount = registrationApi.getAccount(davAccount);
|
||||
flockAccount = Optional.of((FlockAccount) augmentedAccount);
|
||||
}
|
||||
|
||||
if (!cardInformation.isPresent())
|
||||
cardInformation = registrationApi.getCard(davAccount);
|
||||
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SUCCESS);
|
||||
|
||||
} catch (RegistrationApiException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
} catch (IOException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Bundle result) {
|
||||
asyncTask = null;
|
||||
setProgressBarIndeterminateVisibility(false);
|
||||
setProgressBarVisibility(false);
|
||||
|
||||
if (result.getInt(ErrorToaster.KEY_STATUS_CODE) == ErrorToaster.CODE_SUCCESS)
|
||||
handleRefreshForm(false);
|
||||
else
|
||||
ErrorToaster.handleDisplayToastBundledError(getBaseContext(), result);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *
|
||||
* Copyright (C) 2014 Open Whisper Systems
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -16,44 +16,47 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* /
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
/**
|
||||
* rhodey
|
||||
*/
|
||||
public class MigrationReleaseNotesActivity extends Activity {
|
||||
public class EolActivity extends Activity {
|
||||
|
||||
public static final String EXTRA_BACK_DISABLED = "EolActivity.EXTRA_BACK_DISABLED";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.migration_release_notes);
|
||||
setContentView(R.layout.eol_activity);
|
||||
getActionBar().setDisplayHomeAsUpEnabled(false);
|
||||
getActionBar().setTitle(R.string.release_notes);
|
||||
getActionBar().setTitle(R.string.shutting_down);
|
||||
|
||||
initButtons();
|
||||
}
|
||||
|
||||
private void initButtons() {
|
||||
findViewById(R.id.button_ok).setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
findViewById(R.id.button_export).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
startService(new Intent(getBaseContext(), ExportService.class));
|
||||
Toast.makeText(getBaseContext(), R.string.export_started, Toast.LENGTH_SHORT).show();
|
||||
finish();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
|
||||
if (!getIntent().getBooleanExtra(EXTRA_BACK_DISABLED, false))
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
||||
}
|
||||
101
flock/src/main/java/org/anhonesteffort/flock/EolNotifier.java
Normal file
101
flock/src/main/java/org/anhonesteffort/flock/EolNotifier.java
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* *
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* /
|
||||
*/
|
||||
package org.anhonesteffort.flock;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* rhodey
|
||||
*/
|
||||
public class EolNotifier extends BroadcastReceiver {
|
||||
|
||||
public static final String INTENT_ALARM_24_HOURS = "org.anhonesteffort.flock.INTENT_ALARM_24_HOURS";
|
||||
public static final String KEY_TIME_LAST_ALARM = "KEY_TIME_LAST_ALARM";
|
||||
|
||||
private static final String TAG = EolNotifier.class.getSimpleName();
|
||||
|
||||
private Long getMsSinceLastAlarm(Context context) {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
Long timeLastAlarm = preferences.getLong(KEY_TIME_LAST_ALARM, -1);
|
||||
|
||||
if (timeLastAlarm < 0 || timeLastAlarm > System.currentTimeMillis())
|
||||
return AlarmManager.INTERVAL_DAY;
|
||||
|
||||
return System.currentTimeMillis() - timeLastAlarm;
|
||||
}
|
||||
|
||||
private void handleDeviceBooted(Context context) {
|
||||
Long msSinceLastAlarm = getMsSinceLastAlarm(context);
|
||||
Long msTillNextAlarm = AlarmManager.INTERVAL_DAY - msSinceLastAlarm;
|
||||
|
||||
if (msTillNextAlarm < 0)
|
||||
msTillNextAlarm = 0L;
|
||||
|
||||
Intent alarmIntent = new Intent(INTENT_ALARM_24_HOURS);
|
||||
PendingIntent pendingAlarm = PendingIntent.getBroadcast(context, 0, alarmIntent, 0);
|
||||
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
||||
|
||||
alarmManager.setInexactRepeating(
|
||||
AlarmManager.RTC,
|
||||
System.currentTimeMillis() + msTillNextAlarm,
|
||||
AlarmManager.INTERVAL_DAY,
|
||||
pendingAlarm
|
||||
);
|
||||
|
||||
Log.d(TAG, "scheduled 24 hour alarm to begin firing repeatedly in " + msTillNextAlarm + "ms");
|
||||
}
|
||||
|
||||
private void handleAlarmFired(Context context) {
|
||||
Log.d(TAG, "EOL alarm fired");
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
preferences.edit().putLong(KEY_TIME_LAST_ALARM, System.currentTimeMillis()).apply();
|
||||
NotificationDrawer.handleNotifyEol(context);
|
||||
}
|
||||
|
||||
private void scheduleAlarmIfNotExists(Context context) {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
if (preferences.getLong(KEY_TIME_LAST_ALARM, -1) == -1L)
|
||||
handleDeviceBooted(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent.getAction().equals(Intent.ACTION_MY_PACKAGE_REPLACED)) {
|
||||
Intent nextIntent = new Intent(context, EolActivity.class);
|
||||
nextIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
nextIntent.putExtra(EolActivity.EXTRA_BACK_DISABLED, true);
|
||||
context.startActivity(nextIntent);
|
||||
scheduleAlarmIfNotExists(context);
|
||||
}
|
||||
else if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED))
|
||||
handleDeviceBooted(context);
|
||||
else if (intent.getAction().equals(INTENT_ALARM_24_HOURS))
|
||||
handleAlarmFired(context);
|
||||
else
|
||||
Log.e(TAG, "received broadcast intent with unknown action " + intent.getAction());
|
||||
}
|
||||
|
||||
}
|
||||
@ -31,14 +31,15 @@ import com.stripe.exception.CardException;
|
||||
import com.stripe.exception.StripeException;
|
||||
import org.anhonesteffort.flock.crypto.InvalidMacException;
|
||||
import org.anhonesteffort.flock.registration.AuthorizationException;
|
||||
import org.anhonesteffort.flock.registration.RegistrationApiClientException;
|
||||
import org.anhonesteffort.flock.registration.PaymentRequiredException;
|
||||
import org.anhonesteffort.flock.registration.RegistrationApiParseException;
|
||||
import org.anhonesteffort.flock.registration.RegistrationApiException;
|
||||
import org.anhonesteffort.flock.sync.InvalidRemoteComponentException;
|
||||
import org.anhonesteffort.flock.sync.OwsWebDav;
|
||||
import org.anhonesteffort.flock.webdav.InvalidComponentException;
|
||||
import org.anhonesteffort.flock.webdav.PropertyParseException;
|
||||
import org.anhonesteffort.flock.webdav.WebDavConstants;
|
||||
import org.apache.jackrabbit.webdav.DavException;
|
||||
import org.apache.jackrabbit.webdav.DavServletResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketException;
|
||||
@ -92,20 +93,24 @@ public class ErrorToaster {
|
||||
|
||||
protected static final int CODE_ACCOUNT_MANAGER_ERROR = 26;
|
||||
|
||||
protected static final int CODE_GOOGLE_PLAY_ERROR = 27;
|
||||
|
||||
protected static void handleBundleError(Exception e, Bundle bundle) {
|
||||
Log.e(TAG, "handleBundleError() - ", e);
|
||||
|
||||
if (e instanceof AuthorizationException)
|
||||
bundle.putInt(KEY_STATUS_CODE, CODE_UNAUTHORIZED);
|
||||
else if (e instanceof RegistrationApiClientException)
|
||||
else if (e instanceof RegistrationApiParseException)
|
||||
bundle.putInt(KEY_STATUS_CODE, CODE_REGISTRATION_API_CLIENT_ERROR);
|
||||
else if (e instanceof PaymentRequiredException)
|
||||
bundle.putInt(KEY_STATUS_CODE, CODE_STRIPE_REJECTED_CARD);
|
||||
else if (e instanceof RegistrationApiException)
|
||||
bundle.putInt(KEY_STATUS_CODE, CODE_REGISTRATION_API_SERVER_ERROR);
|
||||
|
||||
else if (e instanceof DavException) {
|
||||
DavException ex = (DavException) e;
|
||||
|
||||
if (ex.getErrorCode() == DavServletResponse.SC_UNAUTHORIZED)
|
||||
if (ex.getErrorCode() == WebDavConstants.SC_UNAUTHORIZED)
|
||||
bundle.putInt(KEY_STATUS_CODE, CODE_UNAUTHORIZED);
|
||||
else if (ex.getErrorCode() == OwsWebDav.STATUS_PAYMENT_REQUIRED)
|
||||
bundle.putInt(KEY_STATUS_CODE, CODE_SUBSCRIPTION_EXPIRED);
|
||||
@ -163,7 +168,7 @@ public class ErrorToaster {
|
||||
else if (((CardException) e).getCode().equals(CODE_INVALID_CVC))
|
||||
bundle.putInt(KEY_STATUS_CODE, CODE_CARD_CVC_INVALID);
|
||||
else
|
||||
bundle.putInt(KEY_STATUS_CODE, CODE_STRIPE_REJECTED_CARD);
|
||||
bundle.putInt(KEY_STATUS_CODE, CODE_STRIPE_REJECTED_CARD);
|
||||
}
|
||||
else if (e instanceof APIConnectionException)
|
||||
bundle.putInt(KEY_STATUS_CODE, CODE_STRIPE_CONNECTION_ERROR);
|
||||
@ -266,6 +271,10 @@ public class ErrorToaster {
|
||||
handleShowAccountManagerError(context);
|
||||
break;
|
||||
|
||||
case CODE_GOOGLE_PLAY_ERROR:
|
||||
handleShowGooglePlayError(context);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -378,4 +387,8 @@ public class ErrorToaster {
|
||||
handleShowError(context, R.string.error_android_account_manager_error);
|
||||
}
|
||||
|
||||
private static void handleShowGooglePlayError(Context context) {
|
||||
handleShowError(context, R.string.google_play_error_please_update_google_play_services);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
462
flock/src/main/java/org/anhonesteffort/flock/ExportService.java
Normal file
462
flock/src/main/java/org/anhonesteffort/flock/ExportService.java
Normal file
@ -0,0 +1,462 @@
|
||||
/*
|
||||
* *
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* /
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import net.fortuna.ical4j.data.CalendarOutputter;
|
||||
import net.fortuna.ical4j.model.Calendar;
|
||||
import net.fortuna.ical4j.model.Property;
|
||||
import net.fortuna.ical4j.model.ValidationException;
|
||||
import net.fortuna.ical4j.model.component.VEvent;
|
||||
import net.fortuna.ical4j.model.property.Name;
|
||||
import net.fortuna.ical4j.model.property.Version;
|
||||
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.sync.AbstractLocalComponentCollection;
|
||||
import org.anhonesteffort.flock.sync.InvalidLocalComponentException;
|
||||
import org.anhonesteffort.flock.sync.addressbook.AddressbookSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.addressbook.ContactFactory;
|
||||
import org.anhonesteffort.flock.sync.addressbook.LocalAddressbookStore;
|
||||
import org.anhonesteffort.flock.sync.addressbook.LocalContactCollection;
|
||||
import org.anhonesteffort.flock.sync.calendar.CalendarsSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.calendar.LocalCalendarStore;
|
||||
import org.anhonesteffort.flock.sync.calendar.LocalEventCollection;
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import ezvcard.VCard;
|
||||
import ezvcard.VCardVersion;
|
||||
import ezvcard.io.text.VCardWriter;
|
||||
import ezvcard.property.Photo;
|
||||
import ezvcard.property.Uid;
|
||||
|
||||
/**
|
||||
* Programmer: rhodey
|
||||
*/
|
||||
public class ExportService extends Service {
|
||||
|
||||
private static final String TAG = ExportService.class.getSimpleName();
|
||||
|
||||
private static final int NOTIFY_ID = 1025;
|
||||
|
||||
private ServiceHandler serviceHandler;
|
||||
private NotificationManager notifyManager;
|
||||
private NotificationCompat.Builder notificationBuilder;
|
||||
|
||||
private int countFailedContactExports = 0;
|
||||
private int countFailedEventExports = 0;
|
||||
|
||||
private enum EndState {
|
||||
SUCCESS, PROMPT_LOGIN,
|
||||
PROMPT_MAKE_SPACE, PROMPT_RESTART
|
||||
}
|
||||
private EndState endState = null;
|
||||
|
||||
private void handleContactExportFailed() {
|
||||
countFailedContactExports++;
|
||||
Log.d(TAG, "contact export failed, counter: " + countFailedContactExports);
|
||||
}
|
||||
|
||||
private void handleEventExportFailed() {
|
||||
countFailedEventExports++;
|
||||
Log.d(TAG, "event export failed, counter: " + countFailedEventExports);
|
||||
}
|
||||
|
||||
private void handleInitializeNotification() {
|
||||
notificationBuilder
|
||||
.setProgress(0, 0, true)
|
||||
.setContentTitle(getString(R.string.export))
|
||||
.setContentText(getString(R.string.exporting_contacts_and_calendars))
|
||||
.setSmallIcon(R.drawable.flock_actionbar_icon);
|
||||
|
||||
startForeground(NOTIFY_ID, notificationBuilder.build());
|
||||
}
|
||||
|
||||
private Optional<LocalContactCollection> getAddressbook(ContentProviderClient client, DavAccount account) {
|
||||
LocalAddressbookStore addressbookStore = new LocalAddressbookStore(getBaseContext(), client, account);
|
||||
List<LocalContactCollection> addressbooks = addressbookStore.getCollections();
|
||||
|
||||
if (addressbooks.isEmpty()) return Optional.absent();
|
||||
else return Optional.of(addressbooks.get(0));
|
||||
}
|
||||
|
||||
private List<LocalEventCollection> getCalendars(ContentProviderClient client, Account account)
|
||||
throws RemoteException
|
||||
{
|
||||
LocalCalendarStore calendarStore = new LocalCalendarStore(client, account);
|
||||
return calendarStore.getCollections();
|
||||
}
|
||||
|
||||
private Optional<File> createExternalFile(String filename) {
|
||||
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
Log.w(TAG, "external media not mounted?");
|
||||
return Optional.absent();
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
File file = new File(Environment.getExternalStorageDirectory(), filename);
|
||||
|
||||
if (file.exists()) return Optional.of(file);
|
||||
else if (file.createNewFile()) return Optional.of(file);
|
||||
|
||||
return Optional.absent();
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "unable to create file " + filename, e);
|
||||
return Optional.absent();
|
||||
}
|
||||
}
|
||||
|
||||
private List<File> createFilesForCollections(List<AbstractLocalComponentCollection<?>> collections) {
|
||||
List<File> files = new LinkedList<>();
|
||||
Optional<File> file = null;
|
||||
int calCount = 1;
|
||||
|
||||
for (AbstractLocalComponentCollection collection : collections) {
|
||||
if (collection instanceof LocalContactCollection)
|
||||
file = createExternalFile(getString(R.string.flock_contacts_vcf));
|
||||
else {
|
||||
file = createExternalFile(getString(R.string.flock_calendar_ical, calCount));
|
||||
calCount++;
|
||||
}
|
||||
if (file.isPresent())
|
||||
files.add(file.get());
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
private void simulateExport(AbstractLocalComponentCollection<?> collection, File output)
|
||||
throws IOException, RemoteException
|
||||
{
|
||||
FileOutputStream stream = new FileOutputStream(output, false);
|
||||
|
||||
try {
|
||||
|
||||
for (int i = 0; i < collection.getComponentIds().size(); i++)
|
||||
stream.write(new byte[512]);
|
||||
|
||||
} finally {
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isStorageSpaceAvailable(List<AbstractLocalComponentCollection<?>> collections, List<File> files)
|
||||
throws RemoteException
|
||||
{
|
||||
if (files.size() != collections.size()) {
|
||||
Log.w(TAG, "collection count and output file count differ");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
for (int i = 0; i < collections.size(); i++)
|
||||
simulateExport(collections.get(i), files.get(i));
|
||||
return true;
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "error during export simulation, not enough space?", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleExportContacts(LocalContactCollection addressbook, File output)
|
||||
throws RemoteException, IOException
|
||||
{
|
||||
VCardWriter vCardWriter = new VCardWriter(output, false, VCardVersion.V3_0);
|
||||
|
||||
try {
|
||||
for (Long contactId : addressbook.getComponentIds()) {
|
||||
try {
|
||||
|
||||
Optional<VCard> vCard = addressbook.getComponent(contactId);
|
||||
if (vCard.isPresent()) {
|
||||
vCard.get().removeProperties(Uid.class);
|
||||
vCard.get().removeProperties(Photo.class);
|
||||
vCard.get().removeExtendedProperty(ContactFactory.PROPERTY_STARRED);
|
||||
vCardWriter.write(vCard.get());
|
||||
} else {
|
||||
Log.w(TAG, "couldn't find " + contactId + " in addressbook");
|
||||
}
|
||||
|
||||
} catch (InvalidLocalComponentException e) {
|
||||
handleContactExportFailed();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
vCardWriter.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleExportCalendars(List<LocalEventCollection> eventCollections, List<File> outputs)
|
||||
throws ValidationException, RemoteException, IOException
|
||||
{
|
||||
CalendarOutputter calendarWriter = new CalendarOutputter(false);
|
||||
for (int i = 0; i < eventCollections.size(); i++) {
|
||||
LocalEventCollection eventCollection = eventCollections.get(i);
|
||||
List<Long> eventIds = eventCollection.getComponentIds();
|
||||
Calendar calendar = new Calendar();
|
||||
FileOutputStream output = new FileOutputStream(outputs.get(i), false);
|
||||
|
||||
Optional<String> displayName = eventCollection.getDisplayName();
|
||||
if (displayName.isPresent() && !displayName.get().isEmpty())
|
||||
calendar.getProperties().add(new Name(displayName.get()));
|
||||
|
||||
try {
|
||||
|
||||
for (Long eventId : eventIds) {
|
||||
try {
|
||||
|
||||
Optional<Calendar> event = eventCollection.getComponent(eventId);
|
||||
if (event.isPresent()) {
|
||||
VEvent vEvent = (VEvent) event.get().getComponent(VEvent.VEVENT);
|
||||
if (vEvent != null) {
|
||||
if (vEvent.getProperty(Property.ORGANIZER) != null)
|
||||
vEvent.getProperties().remove(vEvent.getProperty(Property.ORGANIZER));
|
||||
calendar.getComponents().add(vEvent);
|
||||
}
|
||||
else
|
||||
Log.w(TAG, "couldn't parse VEVENT from local calendar component");
|
||||
} else {
|
||||
Log.w(TAG, "couldn't find " + eventId + " in calendar " + eventCollection.getPath());
|
||||
}
|
||||
|
||||
} catch (InvalidLocalComponentException e) {
|
||||
handleEventExportFailed();
|
||||
}
|
||||
}
|
||||
|
||||
calendar.getProperties().add(Version.VERSION_2_0);
|
||||
calendarWriter.output(calendar, output);
|
||||
|
||||
} finally {
|
||||
output.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleIndexFilesWithMediaScanner(List<File> files) {
|
||||
for (File file : files) {
|
||||
sendBroadcast(new Intent(
|
||||
Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,
|
||||
Uri.fromFile(file)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
private void handleExportComplete(EndState endState) {
|
||||
Log.d(TAG, "HANDLE EXPORT COMPLETE: " + endState);
|
||||
this.endState = endState;
|
||||
stopForeground(false);
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
private void handlePromptLoginAndRetry() {
|
||||
Log.w(TAG, "HANDLE PROMPT LOGIN AND RETRY");
|
||||
|
||||
Intent clickIntent = new Intent(getBaseContext(), CorrectPasswordActivity.class);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(getBaseContext(), 0, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
notificationBuilder
|
||||
.setAutoCancel(true)
|
||||
.setProgress(0, 0, false)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setContentTitle(getString(R.string.export_failed))
|
||||
.setContentText(getString(R.string.tap_to_login_then_retry_export));
|
||||
notifyManager.notify(NOTIFY_ID, notificationBuilder.build());
|
||||
}
|
||||
|
||||
private void handlePromptClearSpaceAndRetry() {
|
||||
Log.w(TAG, "HANDLE PROMPT CLEAR SPACE AND RETRY");
|
||||
notificationBuilder
|
||||
.setProgress(0, 0, false)
|
||||
.setContentTitle(getString(R.string.export_failed))
|
||||
.setContentText(getString(R.string.try_making_more_storage_space_available));
|
||||
notifyManager.notify(NOTIFY_ID, notificationBuilder.build());
|
||||
}
|
||||
|
||||
private void handleUnrecoverableError() {
|
||||
Log.w(TAG, "HANDLE UNRECOVERABLE ERROR");
|
||||
notificationBuilder
|
||||
.setProgress(0, 0, false)
|
||||
.setContentTitle(getString(R.string.export_failed))
|
||||
.setContentText(getString(R.string.try_a_separate_export_app_if_error_continues));
|
||||
notifyManager.notify(NOTIFY_ID, notificationBuilder.build());
|
||||
}
|
||||
|
||||
private void handleStartExport() {
|
||||
Log.d(TAG, "HANDLE START EXPORT");
|
||||
handleInitializeNotification();
|
||||
|
||||
try {
|
||||
Optional<DavAccount> account = DavAccountHelper.getAccount(getBaseContext());
|
||||
ContentProviderClient contactClient = getBaseContext().getContentResolver()
|
||||
.acquireContentProviderClient(AddressbookSyncScheduler.CONTENT_AUTHORITY);
|
||||
ContentProviderClient calendarClient = getBaseContext().getContentResolver()
|
||||
.acquireContentProviderClient(CalendarsSyncScheduler.CONTENT_AUTHORITY);
|
||||
|
||||
if (account.isPresent()) {
|
||||
try {
|
||||
|
||||
Optional<LocalContactCollection> addressbook = getAddressbook(contactClient, account.get());
|
||||
List<LocalEventCollection> calendars = getCalendars(calendarClient, account.get().getOsAccount());
|
||||
List<AbstractLocalComponentCollection<?>> collections = new LinkedList<>();
|
||||
|
||||
if (!addressbook.isPresent()) {
|
||||
throw new RemoteException("addressbook missing, what is going on?");
|
||||
}
|
||||
|
||||
collections.add(addressbook.get());
|
||||
collections.addAll(calendars);
|
||||
List<File> outputFiles = createFilesForCollections(collections);
|
||||
|
||||
if (isStorageSpaceAvailable(collections, outputFiles)) {
|
||||
File contactsFile = outputFiles.remove(0);
|
||||
handleExportContacts(addressbook.get(), contactsFile);
|
||||
handleExportCalendars(calendars, outputFiles);
|
||||
outputFiles.add(contactsFile);
|
||||
handleIndexFilesWithMediaScanner(outputFiles);
|
||||
handleExportComplete(EndState.SUCCESS);
|
||||
return;
|
||||
} else {
|
||||
handleExportComplete(EndState.PROMPT_MAKE_SPACE);
|
||||
return;
|
||||
}
|
||||
|
||||
} catch (ValidationException e) {
|
||||
Log.e(TAG, "WTF ical4j", e);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "why android?", e);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "why android?", e);
|
||||
}
|
||||
} else {
|
||||
handleExportComplete(EndState.PROMPT_LOGIN);
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "caught unexpected runtime exception", e);
|
||||
}
|
||||
|
||||
handleExportComplete(EndState.PROMPT_RESTART);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
Log.d(TAG, "ON DESTROY");
|
||||
|
||||
switch (endState) {
|
||||
case PROMPT_LOGIN:
|
||||
handlePromptLoginAndRetry();
|
||||
break;
|
||||
|
||||
case PROMPT_MAKE_SPACE:
|
||||
handlePromptClearSpaceAndRetry();
|
||||
break;
|
||||
|
||||
case PROMPT_RESTART:
|
||||
handleUnrecoverableError();
|
||||
break;
|
||||
}
|
||||
|
||||
if (endState != EndState.SUCCESS) {
|
||||
Toast.makeText(getBaseContext(), R.string.export_failed, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (countFailedContactExports == 0 && countFailedEventExports == 0) {
|
||||
notificationBuilder
|
||||
.setProgress(0, 0, false)
|
||||
.setContentTitle(getString(R.string.export_complete))
|
||||
.setContentText(getString(R.string.export_completed_successfully));
|
||||
} else {
|
||||
notificationBuilder
|
||||
.setProgress(0, 0, false)
|
||||
.setContentTitle(getString(R.string.export_complete))
|
||||
.setContentText(getString(
|
||||
R.string.failed_to_copy_contacts_and_events,
|
||||
countFailedContactExports,
|
||||
countFailedEventExports
|
||||
));
|
||||
}
|
||||
|
||||
notifyManager.notify(NOTIFY_ID, notificationBuilder.build());
|
||||
Toast.makeText(getBaseContext(), R.string.export_complete, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
HandlerThread thread = new HandlerThread(getClass().getSimpleName(), HandlerThread.NORM_PRIORITY);
|
||||
thread.start();
|
||||
|
||||
serviceHandler = new ServiceHandler(thread.getLooper());
|
||||
notifyManager = (NotificationManager)getBaseContext().getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationBuilder = new NotificationCompat.Builder(getBaseContext());
|
||||
}
|
||||
|
||||
private final class ServiceHandler extends Handler {
|
||||
|
||||
public ServiceHandler(Looper looper) {
|
||||
super(looper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
handleStartExport();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
serviceHandler.sendMessage(serviceHandler.obtainMessage());
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@ -5,18 +5,15 @@ import android.app.Service;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.auth.AccountAuthenticator;
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.crypto.InvalidMacException;
|
||||
import org.anhonesteffort.flock.crypto.KeyHelper;
|
||||
import org.anhonesteffort.flock.crypto.KeyStore;
|
||||
import org.anhonesteffort.flock.sync.OwsWebDav;
|
||||
import org.anhonesteffort.flock.sync.key.DavKeyCollection;
|
||||
import org.anhonesteffort.flock.sync.key.DavKeyStore;
|
||||
import org.anhonesteffort.flock.sync.key.KeySyncScheduler;
|
||||
import org.anhonesteffort.flock.webdav.InvalidComponentException;
|
||||
import org.anhonesteffort.flock.webdav.PropertyParseException;
|
||||
import org.apache.jackrabbit.webdav.DavException;
|
||||
|
||||
@ -56,15 +53,6 @@ public abstract class ImportAccountService extends Service {
|
||||
}
|
||||
);
|
||||
}
|
||||
if (!keyCollection.get().isMigrationComplete() &&
|
||||
!keyCollection.get().isMigrationStarted())
|
||||
{
|
||||
KeyStore.setUseCipherVersionZero(getBaseContext(), true);
|
||||
}
|
||||
else if (keyCollection.get().isMigrationComplete()) {
|
||||
MigrationHelperBroadcastReceiver.setMigrationUpdateHandled(getBaseContext());
|
||||
MigrationHelperBroadcastReceiver.setUiDisabledForMigration(getBaseContext(), false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
DavKeyStore.createCollection(getBaseContext(), account);
|
||||
@ -74,14 +62,8 @@ public abstract class ImportAccountService extends Service {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_DAV_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
keyCollection.get().setMigrationComplete(getBaseContext());
|
||||
MigrationHelperBroadcastReceiver.setMigrationUpdateHandled(getBaseContext());
|
||||
MigrationHelperBroadcastReceiver.setUiDisabledForMigration(getBaseContext(), false);
|
||||
}
|
||||
|
||||
} catch (InvalidComponentException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
} catch (PropertyParseException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
} catch (DavException e) {
|
||||
|
||||
@ -41,8 +41,8 @@ import android.widget.ListView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.chiralcode.colorpicker.ColorPicker;
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.sync.calendar.LocalCalendarStore;
|
||||
import org.anhonesteffort.flock.sync.calendar.LocalEventCollection;
|
||||
|
||||
@ -82,7 +82,7 @@ public class ImportCalendarsFragment extends AccountAndKeyRequiredFragment
|
||||
{
|
||||
View fragmentView = inflater.inflate(R.layout.fragment_simple_list, container, false);
|
||||
|
||||
if (!accountAndKeyAvailableAndMigrationComplete())
|
||||
if (!accountAndKeyAvailable())
|
||||
return fragmentView;
|
||||
|
||||
initButtons();
|
||||
@ -94,7 +94,7 @@ public class ImportCalendarsFragment extends AccountAndKeyRequiredFragment
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (!accountAndKeyAvailableAndMigrationComplete())
|
||||
if (!accountAndKeyAvailable())
|
||||
return ;
|
||||
|
||||
initializeList();
|
||||
|
||||
@ -75,7 +75,7 @@ public class ImportContactsFragment extends AccountAndKeyRequiredFragment
|
||||
{
|
||||
View fragmentView = inflater.inflate(R.layout.fragment_simple_list, container, false);
|
||||
|
||||
if (!accountAndKeyAvailableAndMigrationComplete())
|
||||
if (!accountAndKeyAvailable())
|
||||
return fragmentView;
|
||||
|
||||
initButtons();
|
||||
@ -87,7 +87,7 @@ public class ImportContactsFragment extends AccountAndKeyRequiredFragment
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (!accountAndKeyAvailableAndMigrationComplete())
|
||||
if (!accountAndKeyAvailable())
|
||||
return ;
|
||||
|
||||
initializeList();
|
||||
|
||||
@ -36,7 +36,7 @@ import android.widget.EditText;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.util.PasswordUtil;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
@ -33,7 +33,7 @@ import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
@ -134,10 +134,6 @@ public class ImportOwsAccountFragment extends Fragment {
|
||||
Toast.makeText(getActivity(),
|
||||
R.string.notification_flock_subscription_expired,
|
||||
Toast.LENGTH_LONG).show();
|
||||
|
||||
Intent nextIntent = new Intent(getActivity(), ManageSubscriptionActivity.class);
|
||||
nextIntent.putExtra(ManageSubscriptionActivity.KEY_DAV_ACCOUNT_BUNDLE, account.toBundle());
|
||||
startActivity(nextIntent);
|
||||
}
|
||||
|
||||
private void handleImportAccountAsync() {
|
||||
|
||||
@ -14,8 +14,7 @@ import android.os.RemoteException;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.crypto.InvalidMacException;
|
||||
import org.anhonesteffort.flock.crypto.KeyHelper;
|
||||
|
||||
@ -22,9 +22,12 @@ package org.anhonesteffort.flock;
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.text.Html;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* Programmer: rhodey
|
||||
@ -49,7 +52,9 @@ public class IntroductionFragment extends Fragment {
|
||||
Bundle savedInstanceState)
|
||||
{
|
||||
View view = inflater.inflate(R.layout.fragment_intro, container, false);
|
||||
|
||||
initButtons();
|
||||
initDescription(view);
|
||||
|
||||
return view;
|
||||
}
|
||||
@ -64,4 +69,11 @@ public class IntroductionFragment extends Fragment {
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private void initDescription(View fragmentView) {
|
||||
final TextView appDescription = (TextView) fragmentView.findViewById(R.id.flock_description);
|
||||
|
||||
appDescription.setText(Html.fromHtml(getString(R.string.flock_syncs_your_contacts_and_calendars_between_multiple_devices)));
|
||||
appDescription.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.sync.calendar.LocalEventCollection;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -1,398 +0,0 @@
|
||||
/*
|
||||
* *
|
||||
* Copyright (C) 2014 Open Whisper Systems
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* /
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.registration.RegistrationApi;
|
||||
import org.anhonesteffort.flock.registration.RegistrationApiException;
|
||||
import org.anhonesteffort.flock.registration.model.AugmentedFlockAccount;
|
||||
import org.anhonesteffort.flock.registration.model.FlockAccount;
|
||||
import org.anhonesteffort.flock.registration.model.FlockCardInformation;
|
||||
import org.anhonesteffort.flock.registration.model.FlockSubscription;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
import de.passsy.holocircularprogressbar.HoloCircularProgressBar;
|
||||
|
||||
/**
|
||||
* Programmer: rhodey
|
||||
*/
|
||||
public class ManageSubscriptionActivity extends Activity {
|
||||
|
||||
private static final String TAG = "org.anhonesteffort.flock.ManageSubscriptionActivity";
|
||||
|
||||
public static final String KEY_DAV_ACCOUNT_BUNDLE = "KEY_DAV_ACCOUNT_BUNDLE";
|
||||
public static final String KEY_CARD_INFORMATION_BUNDLE = "KEY_CARD_INFORMATION_BUNDLE";
|
||||
|
||||
private Optional<AugmentedFlockAccount> flockAccount = Optional.absent();
|
||||
private Optional<FlockCardInformation> cardInformation = Optional.absent();
|
||||
|
||||
private final Handler uiHandler = new Handler();
|
||||
private Timer intervalTimer = new Timer();
|
||||
|
||||
private DavAccount davAccount;
|
||||
private AsyncTask asyncTask;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
|
||||
requestWindowFeature(Window.FEATURE_PROGRESS);
|
||||
|
||||
setContentView(R.layout.activity_manage_subscription);
|
||||
getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getActionBar().setTitle(R.string.title_manage_subscription);
|
||||
|
||||
if (savedInstanceState != null && !savedInstanceState.isEmpty()) {
|
||||
if (!DavAccount.build(savedInstanceState.getBundle(KEY_DAV_ACCOUNT_BUNDLE)).isPresent()) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
davAccount = DavAccount.build(savedInstanceState.getBundle(KEY_DAV_ACCOUNT_BUNDLE)).get();
|
||||
cardInformation = FlockCardInformation.build(savedInstanceState.getBundle(KEY_CARD_INFORMATION_BUNDLE));
|
||||
}
|
||||
else if (getIntent().getExtras() != null) {
|
||||
if (!DavAccount.build(getIntent().getExtras().getBundle(KEY_DAV_ACCOUNT_BUNDLE)).isPresent()) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
davAccount = DavAccount.build(getIntent().getExtras().getBundle(KEY_DAV_ACCOUNT_BUNDLE)).get();
|
||||
cardInformation = FlockCardInformation.build(getIntent().getExtras().getBundle(KEY_CARD_INFORMATION_BUNDLE));
|
||||
}
|
||||
|
||||
initButtons();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.manage_subscription, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
break;
|
||||
|
||||
|
||||
case R.id.button_send_bitcoin:
|
||||
Intent nextIntent = new Intent(getBaseContext(), SendBitcoinActivity.class);
|
||||
nextIntent.putExtra(SendBitcoinActivity.KEY_DAV_ACCOUNT_BUNDLE, davAccount.toBundle());
|
||||
startActivity(nextIntent);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
Optional<Pair<Long, Boolean[]>> cachedSubscriptionDetails =
|
||||
RegistrationApi.getCachedSubscriptionDetails(getBaseContext());
|
||||
|
||||
if (cachedSubscriptionDetails.isPresent()) {
|
||||
handleUpdateUi(cachedSubscriptionDetails.get().first,
|
||||
cachedSubscriptionDetails.get().second[0],
|
||||
cachedSubscriptionDetails.get().second[1],
|
||||
Optional.<List<FlockSubscription>>absent());
|
||||
handleStartPerpetualRefresh();
|
||||
}
|
||||
else
|
||||
handleInitSubscriptionDetailsCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle savedInstanceState) {
|
||||
savedInstanceState.putBundle(KEY_DAV_ACCOUNT_BUNDLE, davAccount.toBundle());
|
||||
|
||||
if (cardInformation.isPresent())
|
||||
savedInstanceState.putBundle(KEY_CARD_INFORMATION_BUNDLE, cardInformation.get().toBundle());
|
||||
|
||||
super.onSaveInstanceState(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
|
||||
if (asyncTask != null && !asyncTask.isCancelled())
|
||||
asyncTask.cancel(true);
|
||||
|
||||
if (intervalTimer != null)
|
||||
intervalTimer.cancel();
|
||||
}
|
||||
|
||||
private void initButtons() {
|
||||
findViewById(R.id.button_edit_auto_renew).setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent nextIntent = new Intent(getBaseContext(), EditAutoRenewActivity.class);
|
||||
nextIntent.putExtra(EditAutoRenewActivity.KEY_DAV_ACCOUNT_BUNDLE, davAccount.toBundle());
|
||||
|
||||
if (flockAccount.isPresent()) {
|
||||
nextIntent.putExtra(EditAutoRenewActivity.KEY_FLOCK_ACCOUNT_BUNDLE,
|
||||
flockAccount.get().toBundle());
|
||||
}
|
||||
|
||||
if (cardInformation.isPresent()) {
|
||||
nextIntent.putExtra(EditAutoRenewActivity.KEY_CARD_INFORMATION_BUNDLE,
|
||||
cardInformation.get().toBundle());
|
||||
}
|
||||
|
||||
startActivity(nextIntent);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private void handleUpdateSubscriptionHistory(List<FlockSubscription> subscriptions) {
|
||||
LinearLayout subscriptionHistory = (LinearLayout)findViewById(R.id.subscription_history);
|
||||
|
||||
subscriptionHistory.removeAllViews();
|
||||
for (FlockSubscription subscription : subscriptions) {
|
||||
TextView subscriptionDetails = new TextView(getBaseContext());
|
||||
View spacerView = new View(getBaseContext());
|
||||
String createDate = new SimpleDateFormat("dd/MM/yy").format(subscription.getCreateDate());
|
||||
|
||||
subscriptionDetails.setText(createDate + " - " + subscription.getDaysCredit() +
|
||||
" " + getString(R.string.days));
|
||||
|
||||
LinearLayout.LayoutParams subscriptionParams = new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
subscriptionParams.setMargins(0, 8, 0, 0);
|
||||
subscriptionDetails.setLayoutParams(subscriptionParams);
|
||||
subscriptionDetails.setTextAppearance(getBaseContext(), android.R.style.TextAppearance_Medium);
|
||||
subscriptionDetails.setTextColor(Color.BLACK);
|
||||
|
||||
LinearLayout.LayoutParams spacerParams = new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT, 1
|
||||
);
|
||||
subscriptionParams.setMargins(0, 0, 0, 0);
|
||||
spacerView.setLayoutParams(spacerParams);
|
||||
spacerView.setBackground(getResources().getDrawable(android.R.drawable.divider_horizontal_bright));
|
||||
|
||||
subscriptionHistory.addView(subscriptionDetails);
|
||||
subscriptionHistory.addView(spacerView);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleUpdateUi(Long daysRemaining,
|
||||
Boolean autoRenewEnabled,
|
||||
Boolean lastChargeFailed,
|
||||
Optional<List<FlockSubscription>> subscriptions)
|
||||
{
|
||||
TextView daysRemainingView = (TextView)findViewById(R.id.days_remaining);
|
||||
TextView autoRenewView = (TextView)findViewById(R.id.auto_renew_status);
|
||||
Button editAutoRenewButton = (Button)findViewById(R.id.button_edit_auto_renew);
|
||||
HoloCircularProgressBar progressBarView = (HoloCircularProgressBar)findViewById(R.id.days_remaining_progress);
|
||||
int daysRemainingTrigger = getResources().getInteger(R.integer.auto_renew_trigger_days_remaining);
|
||||
Long daysRemainingProgress = daysRemaining;
|
||||
|
||||
if (daysRemaining < 0) {
|
||||
daysRemaining = 0L;
|
||||
daysRemainingProgress = 0L;
|
||||
}
|
||||
else if (daysRemaining > 365)
|
||||
daysRemainingProgress = 365L;
|
||||
|
||||
daysRemainingView.setText(daysRemaining.toString());
|
||||
progressBarView.setProgress(1.0F - ((float) daysRemainingProgress / 365.0F));
|
||||
editAutoRenewButton.setText(R.string.button_edit_payment_details);
|
||||
|
||||
if (!autoRenewEnabled) {
|
||||
autoRenewView.setTextColor(getResources().getColor(R.color.disaled_grey));
|
||||
autoRenewView.setText(R.string.auto_renew_disabled);
|
||||
}
|
||||
else if (!lastChargeFailed && daysRemaining < daysRemainingTrigger) {
|
||||
autoRenewView.setTextColor(getResources().getColor(R.color.success_green));
|
||||
autoRenewView.setText(getString(R.string.processing_payment));
|
||||
}
|
||||
else if (!lastChargeFailed) {
|
||||
autoRenewView.setTextColor(getResources().getColor(R.color.success_green));
|
||||
autoRenewView.setText(getString(R.string.auto_renew_enabled));
|
||||
}
|
||||
else {
|
||||
autoRenewView.setTextColor(getResources().getColor(R.color.error_red));
|
||||
autoRenewView.setText(getString(R.string.auto_renew_error));
|
||||
}
|
||||
|
||||
if (subscriptions.isPresent())
|
||||
handleUpdateSubscriptionHistory(subscriptions.get());
|
||||
}
|
||||
|
||||
private void handleRefreshSubscriptionDetails() {
|
||||
asyncTask = new AsyncTask<String, Void, Bundle>() {
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
Log.d(TAG, "handleRefreshSubscriptionDetails()");
|
||||
setProgressBarIndeterminateVisibility(true);
|
||||
setProgressBarVisibility(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bundle doInBackground(String... params) {
|
||||
Bundle result = new Bundle();
|
||||
|
||||
try {
|
||||
|
||||
RegistrationApi registrationApi = new RegistrationApi(getBaseContext());
|
||||
flockAccount = Optional.of(registrationApi.getAccount(davAccount));
|
||||
cardInformation = registrationApi.getCard(davAccount);
|
||||
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SUCCESS);
|
||||
|
||||
} catch (RegistrationApiException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
} catch (SSLException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
} catch (IOException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Bundle result) {
|
||||
asyncTask = null;
|
||||
setProgressBarIndeterminateVisibility(false);
|
||||
setProgressBarVisibility(false);
|
||||
|
||||
if (result.getInt(ErrorToaster.KEY_STATUS_CODE) == ErrorToaster.CODE_SUCCESS) {
|
||||
handleUpdateUi(flockAccount.get().getDaysRemaining(),
|
||||
flockAccount.get().getAutoRenewEnabled(),
|
||||
flockAccount.get().getLastStripeChargeFailed(),
|
||||
Optional.of(flockAccount.get().getSubscriptions()));
|
||||
}
|
||||
else
|
||||
ErrorToaster.handleDisplayToastBundledError(getBaseContext(), result);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private void handleInitSubscriptionDetailsCache() {
|
||||
asyncTask = new AsyncTask<String, Void, Bundle>() {
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
Log.d(TAG, "handleInitSubscriptionDetailsCache()");
|
||||
setProgressBarIndeterminateVisibility(true);
|
||||
setProgressBarVisibility(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bundle doInBackground(String... params) {
|
||||
Bundle result = new Bundle();
|
||||
|
||||
try {
|
||||
|
||||
flockAccount = Optional.of(new RegistrationApi(getBaseContext()).getAccount(davAccount));
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SUCCESS);
|
||||
|
||||
} catch (RegistrationApiException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
} catch (IOException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Bundle result) {
|
||||
Log.d(TAG, "STATUS: " + result.getInt(ErrorToaster.KEY_STATUS_CODE));
|
||||
setProgressBarIndeterminateVisibility(false);
|
||||
setProgressBarVisibility(false);
|
||||
|
||||
if (result.getInt(ErrorToaster.KEY_STATUS_CODE) == ErrorToaster.CODE_SUCCESS) {
|
||||
handleUpdateUi(flockAccount.get().getDaysRemaining(),
|
||||
flockAccount.get().getAutoRenewEnabled(),
|
||||
flockAccount.get().getLastStripeChargeFailed(),
|
||||
Optional.of(flockAccount.get().getSubscriptions()));
|
||||
handleStartPerpetualRefresh();
|
||||
}
|
||||
else
|
||||
ErrorToaster.handleDisplayToastBundledError(getBaseContext(), result);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private final Runnable refreshUiRunnable = new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (asyncTask == null || asyncTask.isCancelled())
|
||||
handleRefreshSubscriptionDetails();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private void handleStartPerpetualRefresh() {
|
||||
intervalTimer = new Timer();
|
||||
TimerTask uiTask = new TimerTask() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
uiHandler.post(refreshUiRunnable);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
intervalTimer.schedule(uiTask, 0, 15000);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,151 +0,0 @@
|
||||
/*
|
||||
* *
|
||||
* Copyright (C) 2014 Open Whisper Systems
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* /
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.sync.addressbook.AddressbookSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.addressbook.AddressbookSyncWorker;
|
||||
import org.anhonesteffort.flock.sync.calendar.CalendarsSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.key.DavKeyCollection;
|
||||
import org.anhonesteffort.flock.sync.key.KeySyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.key.KeySyncWorker;
|
||||
|
||||
/**
|
||||
* rhodey.
|
||||
*/
|
||||
public class MigrationHelperBroadcastReceiver extends BroadcastReceiver {
|
||||
|
||||
private static final String TAG = MigrationHelperBroadcastReceiver.class.getSimpleName();
|
||||
|
||||
private static final String KEY_MIGRATION_UPDATED_HANDLED = "MigrationHelperBroadcastReceiver.KEY_MIGRATION_UPDATED_HANDLED";
|
||||
private static final String KEY_UI_DISABLED_FOR_MIGRATION = "MigrationHelperBroadcastReceiver.KEY_UI_DISABLED_FOR_MIGRATION";
|
||||
|
||||
public static void setMigrationUpdateHandled(Context context) {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
preferences.edit().putBoolean(KEY_MIGRATION_UPDATED_HANDLED, true).commit();
|
||||
}
|
||||
|
||||
private static boolean getMigrationUpdateHandled(Context context) {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
return preferences.getBoolean(KEY_MIGRATION_UPDATED_HANDLED, false);
|
||||
}
|
||||
|
||||
public static void setUiDisabledForMigration(Context context,
|
||||
boolean uiDisabled)
|
||||
{
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
preferences.edit().putBoolean(KEY_UI_DISABLED_FOR_MIGRATION, uiDisabled).commit();
|
||||
}
|
||||
|
||||
public static boolean getUiDisabledForMigration(Context context) {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
return preferences.getBoolean(KEY_UI_DISABLED_FOR_MIGRATION, false);
|
||||
}
|
||||
|
||||
private void handleActionMyPackageReplaced(Context context) {
|
||||
Log.d(TAG, "handleActionMyPackageReplaced");
|
||||
|
||||
if (MigrationHelperBroadcastReceiver.getMigrationUpdateHandled(context)) {
|
||||
Log.d(TAG, "migration already handled for this update, nothing to do.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!DavAccountHelper.isUsingOurServers(context)) {
|
||||
Intent nextIntent = new Intent(context, MigrationReleaseNotesActivity.class);
|
||||
nextIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(nextIntent);
|
||||
}
|
||||
|
||||
new KeySyncScheduler(context).requestSync();
|
||||
MigrationHelperBroadcastReceiver.setUiDisabledForMigration(context, true);
|
||||
MigrationHelperBroadcastReceiver.setMigrationUpdateHandled(context);
|
||||
|
||||
Optional<Account> account = DavAccountHelper.getOsAccount(context);
|
||||
if (account.isPresent()) {
|
||||
new CalendarsSyncScheduler(context).cancelPendingSyncs(account.get());
|
||||
new AddressbookSyncScheduler(context).cancelPendingSyncs(account.get());
|
||||
|
||||
new CalendarsSyncScheduler(context).setSyncEnabled(account.get(), false);
|
||||
new AddressbookSyncScheduler(context).setSyncEnabled(account.get(), false);
|
||||
}
|
||||
else
|
||||
Log.w(TAG, "account not present at handleActionMyPackageReplaced()");
|
||||
}
|
||||
|
||||
private void handleActionMigrationStarted(Context context) {
|
||||
Log.d(TAG, "handleActionMigrationStarted");
|
||||
MigrationHelperBroadcastReceiver.setUiDisabledForMigration(context, true);
|
||||
}
|
||||
|
||||
private void handleActionMigrationComplete(Context context) {
|
||||
Log.d(TAG, "handleActionMigrationComplete");
|
||||
MigrationHelperBroadcastReceiver.setUiDisabledForMigration(context, false);
|
||||
}
|
||||
|
||||
private void handleActionKeyMaterialImported(Context context) {
|
||||
Log.d(TAG, "handleActionKeyMaterialImported");
|
||||
|
||||
if (!DavKeyCollection.weStartedMigration(context))
|
||||
MigrationHelperBroadcastReceiver.setUiDisabledForMigration(context, false);
|
||||
}
|
||||
|
||||
private void handleActionPushCreatedContacts(Context context) {
|
||||
Log.d(TAG, "handleActionPushCreatedContacts");
|
||||
MigrationService.hackOnActionPushCreatedContacts(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Log.d(TAG, "onReceive()");
|
||||
|
||||
if (intent == null || intent.getAction() == null) {
|
||||
Log.e(TAG, "received null intent or intent with null action.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (intent.getAction().equals(Intent.ACTION_MY_PACKAGE_REPLACED))
|
||||
handleActionMyPackageReplaced(context);
|
||||
else if (intent.getAction().equals(AddressbookSyncWorker.ACTION_PUSH_CREATED_CONTACTS))
|
||||
handleActionPushCreatedContacts(context);
|
||||
else if (intent.getPackage() == null ||
|
||||
!intent.getPackage().equals(MigrationHelperBroadcastReceiver.class.getPackage().getName()))
|
||||
{
|
||||
Log.e(TAG, "received intent from untrusted package.");
|
||||
}
|
||||
else if (intent.getAction().equals(MigrationService.ACTION_MIGRATION_STARTED))
|
||||
handleActionMigrationStarted(context);
|
||||
else if (intent.getAction().equals(MigrationService.ACTION_MIGRATION_COMPLETE))
|
||||
handleActionMigrationComplete(context);
|
||||
else if (intent.getAction().equals(KeySyncWorker.ACTION_KEY_MATERIAL_IMPORTED))
|
||||
handleActionKeyMaterialImported(context);
|
||||
else
|
||||
Log.e(TAG, "received intent with unwanted action.");
|
||||
}
|
||||
}
|
||||
@ -1,898 +0,0 @@
|
||||
/*
|
||||
* *
|
||||
* Copyright (C) 2014 Open Whisper Systems
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* /
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.app.Service;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.OperationApplicationException;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.crypto.KeyHelper;
|
||||
import org.anhonesteffort.flock.crypto.KeyStore;
|
||||
import org.anhonesteffort.flock.crypto.MasterCipher;
|
||||
import org.anhonesteffort.flock.registration.RegistrationApi;
|
||||
import org.anhonesteffort.flock.registration.RegistrationApiException;
|
||||
import org.anhonesteffort.flock.sync.AbstractLocalComponentCollection;
|
||||
import org.anhonesteffort.flock.sync.addressbook.AddressbookSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.addressbook.HidingCardDavStore;
|
||||
import org.anhonesteffort.flock.sync.addressbook.LocalAddressbookStore;
|
||||
import org.anhonesteffort.flock.sync.addressbook.LocalContactCollection;
|
||||
import org.anhonesteffort.flock.sync.calendar.CalendarsSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.calendar.HidingCalDavStore;
|
||||
import org.anhonesteffort.flock.sync.calendar.LocalCalendarStore;
|
||||
import org.anhonesteffort.flock.sync.calendar.LocalEventCollection;
|
||||
import org.anhonesteffort.flock.sync.key.DavKeyCollection;
|
||||
import org.anhonesteffort.flock.sync.key.DavKeyStore;
|
||||
import org.anhonesteffort.flock.webdav.InvalidComponentException;
|
||||
import org.anhonesteffort.flock.webdav.PropertyParseException;
|
||||
import org.anhonesteffort.flock.webdav.caldav.CalDavStore;
|
||||
import org.anhonesteffort.flock.webdav.carddav.CardDavStore;
|
||||
import org.apache.jackrabbit.webdav.DavException;
|
||||
import org.apache.jackrabbit.webdav.DavServletResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Date;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
|
||||
/**
|
||||
* rhodey
|
||||
*/
|
||||
public class MigrationService extends Service {
|
||||
|
||||
public static final String ACTION_MIGRATION_STARTED = "org.anhonesteffort.flock.MigrationService.ACTION_MIGRATION_STARTED";
|
||||
public static final String ACTION_MIGRATION_COMPLETE = "org.anhonesteffort.flock.MigrationService.ACTION_MIGRATION_COMPLETE";
|
||||
|
||||
private static final String TAG = MigrationService.class.getSimpleName();
|
||||
private static final String PREFERENCES_NAME = "MigrationService.PREFERENCES_NAME";
|
||||
|
||||
private static final String KEY_STATE = "MigrationService.KEY_STATE";
|
||||
private static final String KEY_TIME_FIRST_CALENDAR_SYNC = "MigrationService.KEY_TIME_FIRST_CALENDAR_SYNC";
|
||||
private static final String KEY_TIME_FIRST_ADDRESSBOOK_SYNC = "MigrationService.KEY_TIME_FIRST_ADDRESSBOOK_SYNC";
|
||||
|
||||
private static final int STATE_STARTED_MIGRATION = 1;
|
||||
private static final int STATE_SYNCED_WITH_REMOTE = 2;
|
||||
private static final int STATE_DELETED_KEY_COLLECTION = 3;
|
||||
private static final int STATE_GENERATED_NEW_KEYS = 4;
|
||||
private static final int STATE_REPLACED_KEY_COLLECTION = 5;
|
||||
private static final int STATE_REPLACED_KEYS = 6;
|
||||
private static final int STATE_DELETED_REMOTE_CALENDARS_AND_ADDRESSBOOKS = 7;
|
||||
private static final int STATE_REPLACED_REMOTE_CALENDARS = 8;
|
||||
private static final int STATE_REPLACED_REMOTE_ADDRESSBOOKS = 9;
|
||||
private static final int STATE_READY_TO_REPLACE_EVENTS_AND_CONTACTS = 10;
|
||||
private static final int STATE_REPLACED_EVENTS_AND_CONTACTS = 11;
|
||||
private static final int STATE_MIGRATION_COMPLETE = 12;
|
||||
|
||||
private static final long KICK_INTERVAL_MILLISECONDS = 5000;
|
||||
|
||||
private final Handler messageHandler = new Handler();
|
||||
private ServiceHandler serviceHandler;
|
||||
private Timer intervalTimer;
|
||||
private NotificationManager notifyManager;
|
||||
private NotificationCompat.Builder notificationBuilder;
|
||||
|
||||
private DavAccount account;
|
||||
private MasterCipher masterCipher;
|
||||
|
||||
private void setState(int state) {
|
||||
Log.d(TAG, "setState() >> " + state);
|
||||
|
||||
SharedPreferences settings =
|
||||
getBaseContext().getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
|
||||
settings.edit().putInt(KEY_STATE, state).commit();
|
||||
handleUpdateNotificationUsingState();
|
||||
}
|
||||
|
||||
private static int getState(Context context) {
|
||||
SharedPreferences settings =
|
||||
context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
|
||||
Log.d(TAG, "getState() >> " + settings.getInt(KEY_STATE, 0));
|
||||
|
||||
return settings.getInt(KEY_STATE, 0);
|
||||
}
|
||||
|
||||
public static void hackOnActionPushCreatedContacts(Context context) {
|
||||
if (getState(context) == STATE_READY_TO_REPLACE_EVENTS_AND_CONTACTS) {
|
||||
Log.d(TAG, "just finished pushing new contacts during replace events and contacts state" +
|
||||
", marking early finish of contact sync.");
|
||||
new AddressbookSyncScheduler(context).setTimeLastSync(new Date().getTime());
|
||||
}
|
||||
}
|
||||
|
||||
private void recordTimeFirstSync(String key, long timeMilliseconds) {
|
||||
Log.d(TAG, "recordTimeFirstSync() >> " + key + " " + timeMilliseconds);
|
||||
|
||||
SharedPreferences settings =
|
||||
getBaseContext().getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
|
||||
settings.edit().putLong(key, timeMilliseconds).commit();
|
||||
}
|
||||
|
||||
private long getTimeFirstSync(String key) {
|
||||
SharedPreferences settings =
|
||||
getBaseContext().getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
|
||||
return settings.getLong(key, -1);
|
||||
}
|
||||
|
||||
private void handleInitializeNotification() {
|
||||
Log.d(TAG, "handleInitializeNotification()");
|
||||
|
||||
notificationBuilder.setContentTitle(getString(R.string.migrating_to_new_version))
|
||||
.setContentText(getString(R.string.preparing_to_upgrade_sync_protocol))
|
||||
.setProgress(0, 0, true)
|
||||
.setSmallIcon(R.drawable.flock_actionbar_icon);
|
||||
|
||||
startForeground(1030, notificationBuilder.build());
|
||||
}
|
||||
|
||||
private void handleAskUserToEnableSync() {
|
||||
Log.d(TAG, "handleAskUserToEnableSync()");
|
||||
|
||||
notificationBuilder.setContentTitle(getString(R.string.please_enable_sync))
|
||||
.setContentText(getString(R.string.please_enable_anrdoid_sync_to_complete_migration))
|
||||
.setProgress(0, 0, false)
|
||||
.setSmallIcon(R.drawable.alert_warning_light);
|
||||
|
||||
startForeground(1030, notificationBuilder.build());
|
||||
}
|
||||
|
||||
private void handleUpdateNotificationUsingState() {
|
||||
Log.d(TAG, "handleUpdateNotificationUsingState() >> " + getState(getBaseContext()));
|
||||
|
||||
notificationBuilder.setContentTitle(getString(R.string.migrating_to_new_version))
|
||||
.setSmallIcon(R.drawable.flock_actionbar_icon)
|
||||
.setProgress(0, 0, true);
|
||||
|
||||
switch (getState(getBaseContext())) {
|
||||
case STATE_STARTED_MIGRATION:
|
||||
notificationBuilder.setContentText(getString(R.string.checking_sync_for_new_contacts_and_calendar));
|
||||
break;
|
||||
|
||||
case STATE_SYNCED_WITH_REMOTE:
|
||||
case STATE_DELETED_KEY_COLLECTION:
|
||||
case STATE_GENERATED_NEW_KEYS:
|
||||
case STATE_REPLACED_KEY_COLLECTION:
|
||||
notificationBuilder.setContentText(getString(R.string.generating_new_encryption_secrets));
|
||||
break;
|
||||
|
||||
case STATE_REPLACED_KEYS:
|
||||
case STATE_DELETED_REMOTE_CALENDARS_AND_ADDRESSBOOKS:
|
||||
case STATE_REPLACED_REMOTE_CALENDARS:
|
||||
notificationBuilder.setContentText(getString(R.string.replacing_addressbooks_and_calendars));
|
||||
break;
|
||||
|
||||
case STATE_REPLACED_REMOTE_ADDRESSBOOKS:
|
||||
case STATE_READY_TO_REPLACE_EVENTS_AND_CONTACTS:
|
||||
notificationBuilder.setContentText(getString(R.string.replacing_old_contacts_and_events));
|
||||
break;
|
||||
|
||||
case STATE_REPLACED_EVENTS_AND_CONTACTS:
|
||||
notificationBuilder.setContentText(getString(R.string.finalizing_migration));
|
||||
break;
|
||||
|
||||
case STATE_MIGRATION_COMPLETE:
|
||||
notificationBuilder.setContentTitle(getString(R.string.migration_complete));
|
||||
notificationBuilder.setContentText(getString(R.string.please_update_flock_on_all_your_devices));
|
||||
break;
|
||||
}
|
||||
|
||||
notifyManager.notify(1030, notificationBuilder.build());
|
||||
}
|
||||
|
||||
private void handleMigrationComplete() {
|
||||
Log.d(TAG, "handleMigrationComplete()");
|
||||
|
||||
if (intervalTimer != null)
|
||||
intervalTimer.cancel();
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setPackage(MigrationHelperBroadcastReceiver.class.getPackage().getName());
|
||||
intent.setAction(ACTION_MIGRATION_COMPLETE);
|
||||
sendBroadcast(intent);
|
||||
|
||||
stopForeground(false);
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
Log.d(TAG, "onDestroy()");
|
||||
|
||||
if (getState(getBaseContext()) == STATE_MIGRATION_COMPLETE) {
|
||||
notificationBuilder
|
||||
.setSmallIcon(R.drawable.flock_actionbar_icon)
|
||||
.setContentTitle(getString(R.string.migration_complete))
|
||||
.setContentText(getString(R.string.please_update_flock_on_all_your_devices))
|
||||
.setProgress(0, 0, false);
|
||||
|
||||
notifyManager.notify(1030, notificationBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleEnableAllSyncAdapters() {
|
||||
new CalendarsSyncScheduler(getBaseContext()).setSyncEnabled(account.getOsAccount(), true);
|
||||
new AddressbookSyncScheduler(getBaseContext()).setSyncEnabled(account.getOsAccount(), true);
|
||||
}
|
||||
|
||||
private void handleDisableAllSyncAdapters() {
|
||||
new CalendarsSyncScheduler(getBaseContext()).setSyncEnabled(account.getOsAccount(), false);
|
||||
new AddressbookSyncScheduler(getBaseContext()).setSyncEnabled(account.getOsAccount(), false);
|
||||
|
||||
new CalendarsSyncScheduler(getBaseContext()).cancelPendingSyncs(account.getOsAccount());
|
||||
new AddressbookSyncScheduler(getBaseContext()).cancelPendingSyncs(account.getOsAccount());
|
||||
}
|
||||
|
||||
private void handleException(Exception e) {
|
||||
if (e instanceof IOException) {
|
||||
IOException ex = (IOException) e;
|
||||
|
||||
if (ex instanceof SocketException ||
|
||||
ex instanceof UnknownHostException ||
|
||||
ex instanceof SocketTimeoutException)
|
||||
{
|
||||
Log.d(TAG, "experienced connection error during migration, will continue trying.", e);
|
||||
}
|
||||
else
|
||||
Log.d(TAG, "caught unknown IOException during migration >> " + e.toString(), e);
|
||||
}
|
||||
else
|
||||
Log.d(TAG, "caught exception during migration >> " + e.toString(), e);
|
||||
}
|
||||
|
||||
private void handleStartMigration() {
|
||||
Log.d(TAG, "handleStartMigration()");
|
||||
|
||||
handleInitializeNotification();
|
||||
handleStartKickingMigration();
|
||||
|
||||
KeyStore.setUseCipherVersionZero(getBaseContext(), true);
|
||||
|
||||
if (DavAccountHelper.isUsingOurServers(getBaseContext())) {
|
||||
try {
|
||||
|
||||
new RegistrationApi(getBaseContext()).setAccountVersion(account, 2);
|
||||
|
||||
} catch (RegistrationApiException e) {
|
||||
handleException(e);
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setPackage(MigrationHelperBroadcastReceiver.class.getPackage().getName());
|
||||
intent.setAction(ACTION_MIGRATION_STARTED);
|
||||
sendBroadcast(intent);
|
||||
|
||||
setState(STATE_STARTED_MIGRATION);
|
||||
}
|
||||
|
||||
private void handleSyncWithRemote() {
|
||||
Log.d(TAG, "handleSyncWithRemote()");
|
||||
|
||||
handleEnableAllSyncAdapters();
|
||||
if (!ContentResolver.getMasterSyncAutomatically()) {
|
||||
handleAskUserToEnableSync();
|
||||
return;
|
||||
}
|
||||
handleUpdateNotificationUsingState();
|
||||
|
||||
Long firstRecordedCalendarSync = getTimeFirstSync(KEY_TIME_FIRST_CALENDAR_SYNC);
|
||||
Optional<Long> timeLastCalendarSync = new CalendarsSyncScheduler(getBaseContext()).getTimeLastSync();
|
||||
|
||||
if (firstRecordedCalendarSync <= 0) {
|
||||
if (!timeLastCalendarSync.isPresent())
|
||||
recordTimeFirstSync(KEY_TIME_FIRST_CALENDAR_SYNC, new Date().getTime());
|
||||
else
|
||||
recordTimeFirstSync(KEY_TIME_FIRST_CALENDAR_SYNC, timeLastCalendarSync.get());
|
||||
|
||||
new CalendarsSyncScheduler(getBaseContext()).requestSync();
|
||||
}
|
||||
|
||||
Long firstRecordedAddressbookSync = getTimeFirstSync(KEY_TIME_FIRST_ADDRESSBOOK_SYNC);
|
||||
Optional<Long> timeLastAddressbookSync = new AddressbookSyncScheduler(getBaseContext()).getTimeLastSync();
|
||||
|
||||
if (firstRecordedAddressbookSync <= 0) {
|
||||
if (!timeLastAddressbookSync.isPresent())
|
||||
recordTimeFirstSync(KEY_TIME_FIRST_ADDRESSBOOK_SYNC, new Date().getTime());
|
||||
else
|
||||
recordTimeFirstSync(KEY_TIME_FIRST_ADDRESSBOOK_SYNC, timeLastAddressbookSync.get());
|
||||
|
||||
new AddressbookSyncScheduler(getBaseContext()).requestSync();
|
||||
}
|
||||
|
||||
if (firstRecordedCalendarSync > 0 && firstRecordedAddressbookSync > 0) {
|
||||
if (timeLastCalendarSync.isPresent() && timeLastAddressbookSync.isPresent()) {
|
||||
if (timeLastCalendarSync.get() > firstRecordedCalendarSync &&
|
||||
timeLastAddressbookSync.get() > firstRecordedAddressbookSync)
|
||||
{
|
||||
if (new CalendarsSyncScheduler(getBaseContext()).syncInProgress(account.getOsAccount()) ||
|
||||
new AddressbookSyncScheduler(getBaseContext()).syncInProgress(account.getOsAccount()))
|
||||
{
|
||||
Log.w(TAG, "finished syncing with remote but waiting for active syncs to complete.");
|
||||
handleDisableAllSyncAdapters();
|
||||
return;
|
||||
}
|
||||
|
||||
KeyStore.setUseCipherVersionZero(getBaseContext(), false);
|
||||
handleDisableAllSyncAdapters();
|
||||
|
||||
recordTimeFirstSync(KEY_TIME_FIRST_CALENDAR_SYNC, -1);
|
||||
recordTimeFirstSync(KEY_TIME_FIRST_ADDRESSBOOK_SYNC, -1);
|
||||
|
||||
setState(STATE_SYNCED_WITH_REMOTE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDeleteKeyCollection() {
|
||||
Log.d(TAG, "handleDeleteKeyCollection()");
|
||||
|
||||
try {
|
||||
|
||||
DavKeyStore keyStore = DavAccountHelper.getDavKeyStore(getBaseContext(), account);
|
||||
|
||||
try {
|
||||
|
||||
Optional<String> calendarHomeSet = keyStore.getCalendarHomeSet();
|
||||
keyStore.removeCollection(calendarHomeSet.get().concat(DavKeyStore.PATH_KEY_COLLECTION));
|
||||
|
||||
setState(STATE_DELETED_KEY_COLLECTION);
|
||||
|
||||
} catch (PropertyParseException e) {
|
||||
handleException(e);
|
||||
} catch (DavException e) {
|
||||
handleException(e);
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
} finally {
|
||||
keyStore.closeHttpConnection();
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleGenerateNewKeys() {
|
||||
Log.d(TAG, "handleGenerateNewKeys()");
|
||||
|
||||
try {
|
||||
|
||||
KeyHelper.generateAndSaveSaltAndKeyMaterial(getBaseContext());
|
||||
masterCipher = KeyHelper.getMasterCipher(getBaseContext()).get();
|
||||
|
||||
setState(STATE_GENERATED_NEW_KEYS);
|
||||
|
||||
} catch (GeneralSecurityException e) {
|
||||
handleException(e);
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCreateKeyCollection() {
|
||||
Log.d(TAG, "handleCreateKeyCollection()");
|
||||
|
||||
try {
|
||||
|
||||
DavKeyStore.createCollection(getBaseContext(), account);
|
||||
|
||||
setState(STATE_REPLACED_KEY_COLLECTION);
|
||||
|
||||
} catch (PropertyParseException e) {
|
||||
handleException(e);
|
||||
} catch (DavException e) {
|
||||
|
||||
if (e.getErrorCode() == DavServletResponse.SC_FORBIDDEN) {
|
||||
Log.w(TAG, "caught 403 when trying to create key collection, assuming already exists.");
|
||||
setState(STATE_REPLACED_KEY_COLLECTION);
|
||||
}
|
||||
else
|
||||
handleException(e);
|
||||
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleReplaceKeys() {
|
||||
Log.d(TAG, "handleReplaceKeys()");
|
||||
|
||||
try {
|
||||
|
||||
DavKeyStore keyStore = DavAccountHelper.getDavKeyStore(getBaseContext(), account);
|
||||
|
||||
try {
|
||||
|
||||
Optional<DavKeyCollection> keyCollection = keyStore.getCollection();
|
||||
|
||||
if (!keyCollection.isPresent()) {
|
||||
Log.e(TAG, "missing key collection, reverting state to regenerate keys!");
|
||||
setState(STATE_SYNCED_WITH_REMOTE);
|
||||
return;
|
||||
}
|
||||
|
||||
keyCollection.get().setMigrationStarted(getBaseContext());
|
||||
|
||||
Optional<String> localKeyMaterialSalt = KeyHelper.buildEncodedSalt(getBaseContext());
|
||||
Optional<String> localEncryptedKeyMaterial = KeyStore.getEncryptedKeyMaterial(getBaseContext());
|
||||
|
||||
if (localKeyMaterialSalt.isPresent() && localEncryptedKeyMaterial.isPresent()) {
|
||||
keyCollection.get().setKeyMaterialSalt(localKeyMaterialSalt.get());
|
||||
keyCollection.get().setEncryptedKeyMaterial(localEncryptedKeyMaterial.get());
|
||||
|
||||
setState(STATE_REPLACED_KEYS);
|
||||
}
|
||||
else {
|
||||
Log.e(TAG, "missing key material, reverting state to regenerate keys!");
|
||||
setState(STATE_SYNCED_WITH_REMOTE);
|
||||
}
|
||||
|
||||
} catch (InvalidComponentException e) {
|
||||
handleException(e);
|
||||
} catch (PropertyParseException e) {
|
||||
handleException(e);
|
||||
} catch (DavException e) {
|
||||
handleException(e);
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
} finally {
|
||||
keyStore.closeHttpConnection();
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDeleteRemoteCalendarsAndAddressbooks() {
|
||||
Log.d(TAG, "handleDeleteRemoteCalendarsAndAddressbooks()");
|
||||
|
||||
try {
|
||||
|
||||
CalDavStore remoteCalendarStore = DavAccountHelper.getCalDavStore(getBaseContext(), account);
|
||||
CardDavStore remoteAddressbookStore = DavAccountHelper.getCardDavStore(getBaseContext(), account);
|
||||
|
||||
try {
|
||||
|
||||
LocalCalendarStore localCalendarStore = new LocalCalendarStore(getBaseContext(), account.getOsAccount());
|
||||
LocalAddressbookStore localAddressbookStore = new LocalAddressbookStore(getBaseContext(), account);
|
||||
|
||||
for (LocalEventCollection localCollection : localCalendarStore.getCollections()) {
|
||||
if (remoteCalendarStore.getCollection(localCollection.getPath()).isPresent()) {
|
||||
Log.d(TAG, "deleting remote caldav collection at >> " + localCollection.getPath());
|
||||
remoteCalendarStore.removeCollection(localCollection.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
for (LocalContactCollection localCollection : localAddressbookStore.getCollections()) {
|
||||
if (remoteAddressbookStore.getCollection(localCollection.getPath()).isPresent()) {
|
||||
Log.d(TAG, "deleting remote carddav collection at >> " + localCollection.getPath());
|
||||
remoteAddressbookStore.removeCollection(localCollection.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
setState(STATE_DELETED_REMOTE_CALENDARS_AND_ADDRESSBOOKS);
|
||||
|
||||
} catch (RemoteException e) {
|
||||
handleException(e);
|
||||
} catch (DavException e) {
|
||||
handleException(e);
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
} finally {
|
||||
remoteCalendarStore.closeHttpConnection();
|
||||
remoteAddressbookStore.closeHttpConnection();
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleReplaceRemoteCalendars() {
|
||||
Log.d(TAG, "handleReplaceRemoteCalendars()");
|
||||
|
||||
try {
|
||||
|
||||
HidingCalDavStore remoteCalendarStore = DavAccountHelper.getHidingCalDavStore(getBaseContext(), account, masterCipher);
|
||||
|
||||
try {
|
||||
|
||||
LocalCalendarStore localCalendarStore = new LocalCalendarStore(getBaseContext(), account.getOsAccount());
|
||||
|
||||
for (LocalEventCollection localCollection : localCalendarStore.getCollections()) {
|
||||
if (!remoteCalendarStore.getCollection(localCollection.getPath()).isPresent()) {
|
||||
Log.d(TAG, "creating remote caldav collection at >> " + localCollection.getPath());
|
||||
|
||||
Optional<String> displayName = localCollection.getDisplayName();
|
||||
Optional<Integer> color = localCollection.getColor();
|
||||
|
||||
if (displayName.isPresent() && color.isPresent())
|
||||
remoteCalendarStore.addCollection(localCollection.getPath(), displayName.get(), color.get());
|
||||
else if (displayName.isPresent()) {
|
||||
remoteCalendarStore.addCollection(localCollection.getPath(),
|
||||
displayName.get(),
|
||||
getBaseContext().getResources().getColor(R.color.flocktheme_color));
|
||||
}
|
||||
else
|
||||
remoteCalendarStore.addCollection(localCollection.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
setState(STATE_REPLACED_REMOTE_CALENDARS);
|
||||
|
||||
} catch (RemoteException e) {
|
||||
handleException(e);
|
||||
} catch (DavException e) {
|
||||
|
||||
if (e.getErrorCode() != DavServletResponse.SC_FORBIDDEN)
|
||||
handleException(e);
|
||||
|
||||
} catch (GeneralSecurityException e) {
|
||||
handleException(e);
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
} finally {
|
||||
remoteCalendarStore.releaseConnections();
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleReplaceRemoteAddressbooks() {
|
||||
Log.d(TAG, "handleReplaceRemoteAddressbooks()");
|
||||
|
||||
try {
|
||||
|
||||
HidingCardDavStore remoteAddressbookStore = DavAccountHelper.getHidingCardDavStore(getBaseContext(), account, masterCipher);
|
||||
|
||||
try {
|
||||
|
||||
LocalAddressbookStore localAddressbookStore = new LocalAddressbookStore(getBaseContext(), account);
|
||||
|
||||
for (LocalContactCollection localCollection : localAddressbookStore.getCollections()) {
|
||||
if (!remoteAddressbookStore.getCollection(localCollection.getPath()).isPresent()) {
|
||||
Log.d(TAG, "creating remote carddav collection at >> " + localCollection.getPath());
|
||||
|
||||
Optional<String> displayName = localCollection.getDisplayName();
|
||||
if (displayName.isPresent())
|
||||
remoteAddressbookStore.addCollection(localCollection.getPath(), displayName.get());
|
||||
else
|
||||
remoteAddressbookStore.addCollection(localCollection.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
setState(STATE_REPLACED_REMOTE_ADDRESSBOOKS);
|
||||
|
||||
} catch (DavException e) {
|
||||
|
||||
if (e.getErrorCode() != DavServletResponse.SC_FORBIDDEN)
|
||||
handleException(e);
|
||||
|
||||
} catch (GeneralSecurityException e) {
|
||||
handleException(e);
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
} finally {
|
||||
remoteAddressbookStore.releaseConnections();
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleMarkAllComponentsAsNew(AbstractLocalComponentCollection<?> localCollection)
|
||||
throws RemoteException, OperationApplicationException
|
||||
{
|
||||
Log.d(TAG, "handleMarkAllComponentsAsNew() >> " + localCollection.getPath());
|
||||
|
||||
for (Long componentId : localCollection.getComponentIds()) {
|
||||
localCollection.queueForMigration(componentId);
|
||||
localCollection.commitPendingOperations();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleMarkAllLocalEventsAndContactsAsNew() {
|
||||
Log.d(TAG, "handleMarkAllLocalEventsAndContactsAsNew()");
|
||||
|
||||
LocalCalendarStore localCalendarStore = new LocalCalendarStore(getBaseContext(), account.getOsAccount());
|
||||
LocalAddressbookStore localAddressbookStore = new LocalAddressbookStore(getBaseContext(), account);
|
||||
|
||||
try {
|
||||
|
||||
for (LocalEventCollection collection : localCalendarStore.getCollections()) {
|
||||
Log.d(TAG, "marking all components as new in collection >> " + collection.getPath());
|
||||
handleMarkAllComponentsAsNew(collection);
|
||||
}
|
||||
|
||||
for (LocalContactCollection collection : localAddressbookStore.getCollections()) {
|
||||
Log.d(TAG, "marking all components as new in collection >> " + collection.getPath());
|
||||
handleMarkAllComponentsAsNew(collection);
|
||||
}
|
||||
|
||||
setState(STATE_READY_TO_REPLACE_EVENTS_AND_CONTACTS);
|
||||
|
||||
} catch (OperationApplicationException e) {
|
||||
handleException(e);
|
||||
} catch (RemoteException e) {
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleReplaceEventsAndContacts() {
|
||||
Log.d(TAG, "handleReplaceEventsAndContacts()");
|
||||
|
||||
handleEnableAllSyncAdapters();
|
||||
if (!ContentResolver.getMasterSyncAutomatically()) {
|
||||
handleAskUserToEnableSync();
|
||||
return;
|
||||
}
|
||||
handleUpdateNotificationUsingState();
|
||||
|
||||
Long firstRecordedCalendarSync = getTimeFirstSync(KEY_TIME_FIRST_CALENDAR_SYNC);
|
||||
Optional<Long> timeLastCalendarSync = new CalendarsSyncScheduler(getBaseContext()).getTimeLastSync();
|
||||
|
||||
if (firstRecordedCalendarSync <= 0) {
|
||||
if (!timeLastCalendarSync.isPresent())
|
||||
recordTimeFirstSync(KEY_TIME_FIRST_CALENDAR_SYNC, new Date().getTime());
|
||||
else
|
||||
recordTimeFirstSync(KEY_TIME_FIRST_CALENDAR_SYNC, timeLastCalendarSync.get());
|
||||
|
||||
new CalendarsSyncScheduler(getBaseContext()).requestSync();
|
||||
}
|
||||
|
||||
Long firstRecordedAddressbookSync = getTimeFirstSync(KEY_TIME_FIRST_ADDRESSBOOK_SYNC);
|
||||
Optional<Long> timeLastAddressbookSync = new AddressbookSyncScheduler(getBaseContext()).getTimeLastSync();
|
||||
|
||||
if (firstRecordedAddressbookSync <= 0) {
|
||||
if (!timeLastAddressbookSync.isPresent())
|
||||
recordTimeFirstSync(KEY_TIME_FIRST_ADDRESSBOOK_SYNC, new Date().getTime());
|
||||
else
|
||||
recordTimeFirstSync(KEY_TIME_FIRST_ADDRESSBOOK_SYNC, timeLastAddressbookSync.get());
|
||||
|
||||
new AddressbookSyncScheduler(getBaseContext()).requestSync();
|
||||
}
|
||||
|
||||
if (firstRecordedCalendarSync > 0 && firstRecordedAddressbookSync > 0) {
|
||||
if (timeLastCalendarSync.isPresent() && timeLastAddressbookSync.isPresent()) {
|
||||
if (timeLastCalendarSync.get() > firstRecordedCalendarSync &&
|
||||
timeLastAddressbookSync.get() > firstRecordedAddressbookSync)
|
||||
{
|
||||
setState(STATE_REPLACED_EVENTS_AND_CONTACTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSetMigrationComplete() {
|
||||
Log.d(TAG, "handleSetMigrationComplete()");
|
||||
|
||||
try {
|
||||
|
||||
DavKeyStore davKeyStore = DavAccountHelper.getDavKeyStore(getBaseContext(), account);
|
||||
|
||||
try {
|
||||
|
||||
Optional<DavKeyCollection> keyCollection = davKeyStore.getCollection();
|
||||
|
||||
if (keyCollection.isPresent()) {
|
||||
keyCollection.get().setMigrationComplete(getBaseContext());
|
||||
setState(STATE_MIGRATION_COMPLETE);
|
||||
}
|
||||
else {
|
||||
Log.e(TAG, "missing key collection, reverting state to regenerate keys!");
|
||||
setState(STATE_SYNCED_WITH_REMOTE);
|
||||
}
|
||||
|
||||
} catch (InvalidComponentException e) {
|
||||
handleException(e);
|
||||
} catch (PropertyParseException e) {
|
||||
handleException(e);
|
||||
} catch (DavException e) {
|
||||
handleException(e);
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
} finally {
|
||||
davKeyStore.closeHttpConnection();
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleStartOrResumeMigration() {
|
||||
Log.d(TAG, "handleStartOrResumeMigration()");
|
||||
|
||||
switch (getState(getBaseContext())) {
|
||||
case STATE_STARTED_MIGRATION:
|
||||
handleSyncWithRemote();
|
||||
if (getState(getBaseContext()) == STATE_STARTED_MIGRATION)
|
||||
break;
|
||||
|
||||
case STATE_SYNCED_WITH_REMOTE:
|
||||
handleDeleteKeyCollection();
|
||||
if (getState(getBaseContext()) <= STATE_SYNCED_WITH_REMOTE)
|
||||
break;
|
||||
|
||||
case STATE_DELETED_KEY_COLLECTION:
|
||||
handleGenerateNewKeys();
|
||||
if (getState(getBaseContext()) <= STATE_DELETED_KEY_COLLECTION)
|
||||
break;
|
||||
|
||||
case STATE_GENERATED_NEW_KEYS:
|
||||
handleCreateKeyCollection();
|
||||
if (getState(getBaseContext()) <= STATE_GENERATED_NEW_KEYS)
|
||||
break;
|
||||
|
||||
case STATE_REPLACED_KEY_COLLECTION:
|
||||
handleReplaceKeys();
|
||||
if (getState(getBaseContext()) <= STATE_REPLACED_KEY_COLLECTION)
|
||||
break;
|
||||
|
||||
case STATE_REPLACED_KEYS:
|
||||
handleDeleteRemoteCalendarsAndAddressbooks();
|
||||
if (getState(getBaseContext()) <= STATE_REPLACED_KEYS)
|
||||
break;
|
||||
|
||||
case STATE_DELETED_REMOTE_CALENDARS_AND_ADDRESSBOOKS:
|
||||
handleReplaceRemoteCalendars();
|
||||
if (getState(getBaseContext()) <= STATE_DELETED_REMOTE_CALENDARS_AND_ADDRESSBOOKS)
|
||||
break;
|
||||
|
||||
case STATE_REPLACED_REMOTE_CALENDARS:
|
||||
handleReplaceRemoteAddressbooks();
|
||||
if (getState(getBaseContext()) <= STATE_REPLACED_REMOTE_CALENDARS)
|
||||
break;
|
||||
|
||||
case STATE_REPLACED_REMOTE_ADDRESSBOOKS:
|
||||
handleMarkAllLocalEventsAndContactsAsNew();
|
||||
if (getState(getBaseContext()) <= STATE_REPLACED_REMOTE_ADDRESSBOOKS)
|
||||
break;
|
||||
|
||||
case STATE_READY_TO_REPLACE_EVENTS_AND_CONTACTS:
|
||||
handleReplaceEventsAndContacts();
|
||||
if (getState(getBaseContext()) <= STATE_READY_TO_REPLACE_EVENTS_AND_CONTACTS)
|
||||
break;
|
||||
|
||||
case STATE_REPLACED_EVENTS_AND_CONTACTS:
|
||||
handleSetMigrationComplete();
|
||||
if (getState(getBaseContext()) <= STATE_REPLACED_EVENTS_AND_CONTACTS)
|
||||
break;
|
||||
|
||||
case STATE_MIGRATION_COMPLETE:
|
||||
handleMigrationComplete();
|
||||
break;
|
||||
|
||||
default:
|
||||
handleStartMigration();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
HandlerThread thread = new HandlerThread("MigrationService", HandlerThread.NORM_PRIORITY);
|
||||
thread.start();
|
||||
|
||||
Looper serviceLooper = thread.getLooper();
|
||||
|
||||
serviceHandler = new ServiceHandler(serviceLooper);
|
||||
notifyManager = (NotificationManager)getBaseContext().getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationBuilder = new NotificationCompat.Builder(getBaseContext());
|
||||
|
||||
try {
|
||||
|
||||
Optional<DavAccount> account = DavAccountHelper.getAccount(getBaseContext());
|
||||
Optional<MasterCipher> masterCipher = KeyHelper.getMasterCipher(getBaseContext());
|
||||
|
||||
if (account.isPresent() && masterCipher.isPresent()) {
|
||||
this.account = account.get();
|
||||
this.masterCipher = masterCipher.get();
|
||||
}
|
||||
else
|
||||
Log.e(TAG, "ACCOUNT NOT PRESENT xxx 0.O");
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "exception while getting MasterCipher >> " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private final class ServiceHandler extends Handler {
|
||||
|
||||
public ServiceHandler(Looper looper) {
|
||||
super(looper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
Log.d(TAG, "handleMessage()");
|
||||
|
||||
if (account != null && masterCipher != null)
|
||||
handleStartOrResumeMigration();
|
||||
else
|
||||
Log.e(TAG, "missing account or master cipher! xxx");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
Log.d(TAG, "onStartCommand()");
|
||||
|
||||
serviceHandler.sendMessage(serviceHandler.obtainMessage());
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
Log.d(TAG, "onBind()");
|
||||
return null;
|
||||
}
|
||||
|
||||
private final Runnable kickMigrationRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
getBaseContext().startService(new Intent(getBaseContext(), MigrationService.class));
|
||||
}
|
||||
};
|
||||
|
||||
public void handleStartKickingMigration() {
|
||||
Log.d(TAG, "handleStartKickingMigration()");
|
||||
|
||||
intervalTimer = new Timer();
|
||||
TimerTask kickMigrationTask = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
messageHandler.post(kickMigrationRunnable);
|
||||
}
|
||||
};
|
||||
|
||||
intervalTimer.schedule(kickMigrationTask, 0, KICK_INTERVAL_MILLISECONDS);
|
||||
}
|
||||
}
|
||||
@ -36,7 +36,7 @@ import android.widget.EditText;
|
||||
import android.widget.ListView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.crypto.InvalidMacException;
|
||||
import org.anhonesteffort.flock.sync.addressbook.AddressbookSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.addressbook.HidingCardDavCollection;
|
||||
|
||||
@ -40,7 +40,7 @@ import android.widget.ListView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.chiralcode.colorpicker.ColorPicker;
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.sync.calendar.CalendarsSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.calendar.HidingCalDavCollection;
|
||||
import org.anhonesteffort.flock.sync.calendar.HidingCalDavStore;
|
||||
|
||||
@ -27,8 +27,8 @@ import android.content.SharedPreferences;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import org.anhonesteffort.flock.sync.account.AccountStore;
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.sync.addressbook.AddressbookSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.calendar.CalendarsSyncScheduler;
|
||||
@ -42,8 +42,8 @@ public class NotificationDrawer extends BroadcastReceiver {
|
||||
private static final String TAG = "org.anhonesteffort.flock.NotificationDrawer";
|
||||
|
||||
private static final int ID_NOTIFICATION_AUTH = 1020;
|
||||
private static final int ID_NOTIFICATION_SUBSCRIPTION = 1021;
|
||||
private static final int ID_NOTIFICATION_DEBUG_LOG = 1022;
|
||||
private static final int ID_NOTIFICATION_EOL = 1023;
|
||||
|
||||
private static final String PREFERENCES_NAME = "AbstractDavSyncAdapter.PREFERENCES_NAME";
|
||||
private static final String KEY_VOID_AUTH_NOTIFICATIONS = "KEY_VOID_AUTH_NOTIFICATIONS";
|
||||
@ -51,38 +51,39 @@ public class NotificationDrawer extends BroadcastReceiver {
|
||||
private static final String ACTION_STOP_ASKING_FOR_LOGS = "org.anhonesteffort.flock.ACTION_STOP_ASKING_FOR_LOGS";
|
||||
private static final String KEY_STOP_ASKING_FOR_LOGS = "KEY_STOP_ASKING_FOR_LOGS";
|
||||
|
||||
private static SharedPreferences getSharedPreferences(Context context) {
|
||||
return context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
public static void disableAuthNotificationsForRunningAdapters(Context context, Account account) {
|
||||
AddressbookSyncScheduler addressbookSync = new AddressbookSyncScheduler(context);
|
||||
CalendarsSyncScheduler calendarSync = new CalendarsSyncScheduler(context);
|
||||
KeySyncScheduler keySync = new KeySyncScheduler(context);
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_MULTI_PROCESS);
|
||||
SharedPreferences settings = getSharedPreferences(context);
|
||||
|
||||
if (addressbookSync.syncInProgress(account)) {
|
||||
settings.edit().putBoolean(KEY_VOID_AUTH_NOTIFICATIONS + addressbookSync.getAuthority(), true).commit();
|
||||
settings.edit().putBoolean(KEY_VOID_AUTH_NOTIFICATIONS + addressbookSync.getAuthority(), true).apply();
|
||||
Log.w(TAG, "disabled auth notifications for " + addressbookSync.getAuthority());
|
||||
}
|
||||
|
||||
if (calendarSync.syncInProgress(account)) {
|
||||
settings.edit().putBoolean(KEY_VOID_AUTH_NOTIFICATIONS + calendarSync.getAuthority(), true).commit();
|
||||
settings.edit().putBoolean(KEY_VOID_AUTH_NOTIFICATIONS + calendarSync.getAuthority(), true).apply();
|
||||
Log.w(TAG, "disabled auth notifications for " + calendarSync.getAuthority());
|
||||
}
|
||||
|
||||
if (keySync.syncInProgress(account)) {
|
||||
settings.edit().putBoolean(KEY_VOID_AUTH_NOTIFICATIONS + keySync.getAuthority(), true).commit();
|
||||
settings.edit().putBoolean(KEY_VOID_AUTH_NOTIFICATIONS + keySync.getAuthority(), true).apply();
|
||||
Log.w(TAG, "disabled auth notifications for " + keySync.getAuthority());
|
||||
}
|
||||
}
|
||||
|
||||
public static void enableAuthNotifications(Context context, String authority) {
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_MULTI_PROCESS);
|
||||
settings.edit().putBoolean(KEY_VOID_AUTH_NOTIFICATIONS + authority, false).commit();
|
||||
getSharedPreferences(context).edit().putBoolean(KEY_VOID_AUTH_NOTIFICATIONS + authority, false).apply();
|
||||
Log.w(TAG, "enabled auth notification for " + authority);
|
||||
}
|
||||
|
||||
public static boolean isAuthNotificationDisabled(Context context, String authority) {
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_MULTI_PROCESS);
|
||||
|
||||
if (settings.getBoolean(KEY_VOID_AUTH_NOTIFICATIONS + authority, false)) {
|
||||
if (getSharedPreferences(context).getBoolean(KEY_VOID_AUTH_NOTIFICATIONS + authority, false)) {
|
||||
Log.w(TAG, "auth notification is disabled for " + authority);
|
||||
return true;
|
||||
}
|
||||
@ -114,37 +115,30 @@ public class NotificationDrawer extends BroadcastReceiver {
|
||||
getNotificationManager(context).notify(ID_NOTIFICATION_AUTH, notificationBuilder.build());
|
||||
}
|
||||
|
||||
public static void handleNotifyEol(Context context) {
|
||||
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context);
|
||||
Intent clickIntent = new Intent(context, EolActivity.class);
|
||||
|
||||
notificationBuilder.setContentTitle(context.getString(R.string.flock_is_shutting_down));
|
||||
notificationBuilder.setContentText(context.getString(R.string.tap_for_important_details));
|
||||
notificationBuilder.setSmallIcon(R.drawable.flock_actionbar_icon);
|
||||
notificationBuilder.setAutoCancel(true);
|
||||
|
||||
notificationBuilder.setContentIntent(getPendingActivityIntent(context, clickIntent));
|
||||
getNotificationManager(context).notify(ID_NOTIFICATION_EOL, notificationBuilder.build());
|
||||
}
|
||||
|
||||
public static void cancelAuthNotification(Context context) {
|
||||
getNotificationManager(context).cancel(ID_NOTIFICATION_AUTH);
|
||||
}
|
||||
|
||||
public static void showSubscriptionExpiredNotification(Context context) {
|
||||
Log.w(TAG, "showSubscriptionExpiredNotification()");
|
||||
|
||||
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context);
|
||||
Intent clickIntent = new Intent(context, ManageSubscriptionActivity.class);
|
||||
|
||||
notificationBuilder.setContentTitle(context.getString(R.string.notification_flock_subscription_expired));
|
||||
notificationBuilder.setContentText(context.getString(R.string.notification_tap_to_update_subscription));
|
||||
notificationBuilder.setSmallIcon(R.drawable.flock_actionbar_icon);
|
||||
notificationBuilder.setAutoCancel(true);
|
||||
|
||||
Optional<DavAccount> account = DavAccountHelper.getAccount(context);
|
||||
clickIntent.putExtra(ManageSubscriptionActivity.KEY_DAV_ACCOUNT_BUNDLE, account.get().toBundle());
|
||||
|
||||
notificationBuilder.setContentIntent(getPendingActivityIntent(context, clickIntent));
|
||||
getNotificationManager(context).notify(ID_NOTIFICATION_SUBSCRIPTION, notificationBuilder.build());
|
||||
}
|
||||
|
||||
private static boolean isStopAskingForLogsSet(Context context) {
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_MULTI_PROCESS);
|
||||
return settings.getBoolean(KEY_STOP_ASKING_FOR_LOGS, false);
|
||||
return getSharedPreferences(context).getBoolean(KEY_STOP_ASKING_FOR_LOGS, false);
|
||||
}
|
||||
|
||||
private static void setStopAskingForLogs(Context context) {
|
||||
Log.w(TAG, "will stop asking for debug logs :(");
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_MULTI_PROCESS);
|
||||
settings.edit().putBoolean(KEY_STOP_ASKING_FOR_LOGS, true).commit();
|
||||
getSharedPreferences(context).edit().putBoolean(KEY_STOP_ASKING_FOR_LOGS, true).apply();
|
||||
}
|
||||
|
||||
public static void handlePromptForDebugLogIfNotDisabled(Context context) {
|
||||
|
||||
@ -33,9 +33,9 @@ import android.view.MenuItem;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.chiralcode.colorpicker.ColorPickerPreference;
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.sync.account.AccountSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.addressbook.AddressbookSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.calendar.CalendarsSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.key.KeySyncScheduler;
|
||||
@ -57,7 +57,6 @@ public class PreferencesActivity extends PreferenceActivity
|
||||
public static final String KEY_PREF_ADDRESSBOOKS = "pref_addressbooks";
|
||||
|
||||
public static final String KEY_PREF_CATEGORY_ACCOUNT = "pref_category_account";
|
||||
public static final String KEY_PREF_SUBSCRIPTION = "pref_subscription";
|
||||
public static final String KEY_PREF_DELETE_ACCOUNT = "pref_delete_account";
|
||||
|
||||
private StatusHeaderView statusHeader;
|
||||
@ -137,6 +136,8 @@ public class PreferencesActivity extends PreferenceActivity
|
||||
private void initContentObservers() {
|
||||
new AddressbookSyncScheduler(getBaseContext()).registerSelfForBroadcasts();
|
||||
new CalendarsSyncScheduler(getBaseContext()).registerSelfForBroadcasts();
|
||||
new KeySyncScheduler(getBaseContext()).registerSelfForBroadcasts();
|
||||
new AccountSyncScheduler(getBaseContext()).registerSelfForBroadcasts();
|
||||
}
|
||||
|
||||
private void initSyncNowButton() {
|
||||
@ -149,6 +150,7 @@ public class PreferencesActivity extends PreferenceActivity
|
||||
new KeySyncScheduler(getBaseContext()).requestSync();
|
||||
new CalendarsSyncScheduler(getBaseContext()).requestSync();
|
||||
new AddressbookSyncScheduler(getBaseContext()).requestSync();
|
||||
new AccountSyncScheduler(getBaseContext()).requestSync();
|
||||
|
||||
Toast.makeText(getBaseContext(),
|
||||
R.string.sync_requested_will_begin_when_possible,
|
||||
@ -197,29 +199,9 @@ public class PreferencesActivity extends PreferenceActivity
|
||||
if (!DavAccountHelper.isUsingOurServers(getBaseContext()))
|
||||
return;
|
||||
|
||||
Preference manageSubscription = findPreference(KEY_PREF_SUBSCRIPTION);
|
||||
Preference addressbooks = findPreference(KEY_PREF_ADDRESSBOOKS);
|
||||
PreferenceCategory category = (PreferenceCategory) findPreference(KEY_PREF_CATEGORY_CONTACTS);
|
||||
|
||||
if (manageSubscription != null) {
|
||||
manageSubscription.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
Optional<DavAccount> account = DavAccountHelper.getAccount(getBaseContext());
|
||||
if (account.isPresent()) {
|
||||
Intent nextIntent = new Intent(getBaseContext(), ManageSubscriptionActivity.class);
|
||||
nextIntent.putExtra(ManageSubscriptionActivity.KEY_DAV_ACCOUNT_BUNDLE,
|
||||
account.get().toBundle());
|
||||
startActivity(nextIntent);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
if (addressbooks != null)
|
||||
category.removePreference(addressbooks);
|
||||
}
|
||||
@ -229,13 +211,10 @@ public class PreferencesActivity extends PreferenceActivity
|
||||
return;
|
||||
|
||||
PreferenceCategory accountCategory = (PreferenceCategory) findPreference(KEY_PREF_CATEGORY_ACCOUNT);
|
||||
Preference manageSubscription = findPreference(KEY_PREF_SUBSCRIPTION);
|
||||
Preference deleteAccount = findPreference(KEY_PREF_DELETE_ACCOUNT);
|
||||
|
||||
if (manageSubscription != null) {
|
||||
accountCategory.removePreference(manageSubscription);
|
||||
if (deleteAccount != null)
|
||||
accountCategory.removePreference(deleteAccount);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -244,6 +223,7 @@ public class PreferencesActivity extends PreferenceActivity
|
||||
new KeySyncScheduler(getBaseContext()).setSyncInterval(Integer.valueOf((String) newValue));
|
||||
new AddressbookSyncScheduler(getBaseContext()).setSyncInterval(Integer.valueOf((String) newValue));
|
||||
new CalendarsSyncScheduler(getBaseContext()).setSyncInterval(Integer.valueOf((String) newValue));
|
||||
new AccountSyncScheduler(getBaseContext()).setSyncInterval(Integer.valueOf((String) newValue));
|
||||
|
||||
updateSyncIntervalSummary(Optional.of((String) newValue));
|
||||
}
|
||||
|
||||
@ -19,223 +19,31 @@
|
||||
|
||||
package org.anhonesteffort.flock;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import org.anhonesteffort.flock.util.PasswordUtil;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
/**
|
||||
* Programmer: rhodey
|
||||
*/
|
||||
public class RegisterAccountFragment extends Fragment {
|
||||
|
||||
private static final String TAG = "org.anhonesteffort.flock.RegisterAccountFragment";
|
||||
|
||||
private static final String KEY_USERNAME = "KEY_USERNAME";
|
||||
|
||||
protected static final int CODE_ACCOUNT_IMPORTED = 9001;
|
||||
|
||||
private static MessageHandler messageHandler;
|
||||
private SetupActivity setupActivity;
|
||||
private TextWatcher passwordWatcher;
|
||||
private TextWatcher passwordRepeatWatcher;
|
||||
private Optional<String> username = Optional.absent();
|
||||
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
if (savedInstanceState != null)
|
||||
username = Optional.fromNullable(savedInstanceState.getString(KEY_USERNAME));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle savedInstanceState) {
|
||||
super.onSaveInstanceState(savedInstanceState);
|
||||
|
||||
EditText usernameView = (EditText) getActivity().findViewById(R.id.account_username);
|
||||
if (usernameView.getText() != null)
|
||||
savedInstanceState.putString(KEY_USERNAME, usernameView.getText().toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
|
||||
if (activity instanceof SetupActivity)
|
||||
this.setupActivity = (SetupActivity) activity;
|
||||
else
|
||||
throw new ClassCastException(activity.toString() + " not what I expected D: !");
|
||||
|
||||
if (messageHandler == null)
|
||||
messageHandler = new MessageHandler(setupActivity, this);
|
||||
else {
|
||||
messageHandler.setupActivity = setupActivity;
|
||||
messageHandler.importFragment = this;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
ViewGroup container,
|
||||
Bundle savedInstanceState)
|
||||
{
|
||||
View view = inflater.inflate(R.layout.fragment_register_ows_account, container, false);
|
||||
|
||||
initButtons();
|
||||
initForm(view);
|
||||
|
||||
return view;
|
||||
return inflater.inflate(R.layout.fragment_register_ows_account, container, false);
|
||||
}
|
||||
|
||||
private void initButtons() {
|
||||
getActivity().findViewById(R.id.button_next).setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
registerAccountAsync();
|
||||
}
|
||||
|
||||
});
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
getActivity().findViewById(R.id.button_next).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private void initForm(View view) {
|
||||
if (messageHandler != null && messageHandler.serviceStarted) {
|
||||
getActivity().setProgressBarIndeterminateVisibility(true);
|
||||
getActivity().setProgressBarVisibility(true);
|
||||
setupActivity.setNavigationDisabled(true);
|
||||
}
|
||||
|
||||
if (username.isPresent())
|
||||
((EditText)view.findViewById(R.id.account_username)).setText(username.get());
|
||||
|
||||
final EditText passwordTextView = (EditText) view.findViewById(R.id.cipher_passphrase);
|
||||
final EditText passwordRepeatTextView = (EditText) view.findViewById(R.id.cipher_passphrase_repeat);
|
||||
final ProgressBar passwordProgressView = (ProgressBar) view.findViewById(R.id.progress_password_strength);
|
||||
final ProgressBar passwordRepeatProgressView = (ProgressBar) view.findViewById(R.id.progress_password_strength_repeat);
|
||||
|
||||
if (passwordWatcher != null)
|
||||
passwordTextView.removeTextChangedListener(passwordWatcher);
|
||||
if (passwordRepeatWatcher != null)
|
||||
passwordRepeatTextView.removeTextChangedListener(passwordRepeatWatcher);
|
||||
|
||||
passwordWatcher = PasswordUtil.getPasswordStrengthTextWatcher(getActivity(), passwordProgressView);
|
||||
passwordRepeatWatcher = PasswordUtil.getPasswordStrengthTextWatcher(getActivity(), passwordRepeatProgressView);
|
||||
|
||||
passwordTextView.addTextChangedListener(passwordWatcher);
|
||||
passwordRepeatTextView.addTextChangedListener(passwordRepeatWatcher);
|
||||
}
|
||||
|
||||
private void handleRegisterComplete() {
|
||||
Log.d(TAG, "handleRegisterComplete()");
|
||||
setupActivity.updateFragmentUsingState(SetupActivity.STATE_IMPORT_CONTACTS);
|
||||
}
|
||||
|
||||
private void registerAccountAsync() {
|
||||
if (messageHandler != null && messageHandler.serviceStarted)
|
||||
return;
|
||||
else if (messageHandler == null)
|
||||
messageHandler = new MessageHandler(setupActivity, this);
|
||||
|
||||
Bundle result = new Bundle();
|
||||
String username = ((EditText) getActivity().findViewById(R.id.account_username)).getText().toString().trim();
|
||||
String password = ((EditText) getActivity().findViewById(R.id.cipher_passphrase)).getText().toString().trim();
|
||||
String passwordRepeat = ((EditText) getActivity().findViewById(R.id.cipher_passphrase_repeat)).getText().toString().trim();
|
||||
|
||||
if (StringUtils.isEmpty(username)) {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_EMPTY_ACCOUNT_ID);
|
||||
ErrorToaster.handleDisplayToastBundledError(getActivity(), result);
|
||||
return;
|
||||
}
|
||||
|
||||
if (username.contains(" ")) {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SPACES_IN_USERNAME);
|
||||
ErrorToaster.handleDisplayToastBundledError(getActivity(), result);
|
||||
return;
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(password)) {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SHORT_PASSWORD);
|
||||
ErrorToaster.handleDisplayToastBundledError(getActivity(), result);
|
||||
((TextView)getActivity().findViewById(R.id.cipher_passphrase)).setText("");
|
||||
((TextView)getActivity().findViewById(R.id.cipher_passphrase_repeat)).setText("");
|
||||
return;
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(passwordRepeat) || !password.equals(passwordRepeat)) {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_PASSWORDS_DO_NOT_MATCH);
|
||||
ErrorToaster.handleDisplayToastBundledError(getActivity(), result);
|
||||
((TextView)getActivity().findViewById(R.id.cipher_passphrase)).setText("");
|
||||
((TextView)getActivity().findViewById(R.id.cipher_passphrase_repeat)).setText("");
|
||||
return;
|
||||
}
|
||||
|
||||
username = DavAccountHelper.correctUsername(getActivity(), username);
|
||||
Intent importService = new Intent(getActivity(), RegisterAccountService.class);
|
||||
|
||||
importService.putExtra(RegisterAccountService.KEY_MESSENGER, new Messenger(messageHandler));
|
||||
importService.putExtra(RegisterAccountService.KEY_ACCOUNT_ID, username);
|
||||
importService.putExtra(RegisterAccountService.KEY_MASTER_PASSPHRASE, password);
|
||||
|
||||
getActivity().startService(importService);
|
||||
messageHandler.serviceStarted = true;
|
||||
|
||||
setupActivity.setNavigationDisabled(true);
|
||||
getActivity().setProgressBarIndeterminateVisibility(true);
|
||||
getActivity().setProgressBarVisibility(true);
|
||||
}
|
||||
|
||||
public static class MessageHandler extends Handler {
|
||||
|
||||
public SetupActivity setupActivity;
|
||||
public RegisterAccountFragment importFragment;
|
||||
public boolean serviceStarted = false;
|
||||
|
||||
public MessageHandler(SetupActivity setupActivity, RegisterAccountFragment importFragment) {
|
||||
this.setupActivity = setupActivity;
|
||||
this.importFragment = importFragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
messageHandler = null;
|
||||
serviceStarted = false;
|
||||
|
||||
setupActivity.setNavigationDisabled(false);
|
||||
setupActivity.setProgressBarIndeterminateVisibility(false);
|
||||
setupActivity.setProgressBarVisibility(false);
|
||||
|
||||
if (message.arg1 == CODE_ACCOUNT_IMPORTED)
|
||||
importFragment.handleRegisterComplete();
|
||||
|
||||
else if (message.arg1 != ErrorToaster.CODE_SUCCESS) {
|
||||
Bundle errorBundler = new Bundle();
|
||||
|
||||
errorBundler.putInt(ErrorToaster.KEY_STATUS_CODE, message.arg1);
|
||||
ErrorToaster.handleDisplayToastBundledError(setupActivity, errorBundler);
|
||||
|
||||
if (importFragment.getView().findViewById(R.id.account_username) != null) {
|
||||
((TextView)importFragment.getView().findViewById(R.id.account_username)).setText("");
|
||||
((TextView)importFragment.getView().findViewById(R.id.cipher_passphrase)).setText("");
|
||||
((TextView)importFragment.getView().findViewById(R.id.cipher_passphrase_repeat)).setText("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ import android.os.RemoteException;
|
||||
import android.view.View;
|
||||
import android.widget.CompoundButton;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.crypto.InvalidMacException;
|
||||
import org.anhonesteffort.flock.sync.calendar.CalendarsSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.calendar.HidingCalDavCollection;
|
||||
|
||||
@ -136,11 +136,9 @@ public class SelectServiceProviderFragment extends Fragment {
|
||||
if (!radioButtonOws.isChecked()) {
|
||||
radioButtonOws.setChecked(true);
|
||||
radioButtonOther.setChecked(false);
|
||||
serviceDescription.setText(
|
||||
Html.fromHtml(
|
||||
getString(R.string.flock_sync_is_a_service_run_by_open_whisper_systems_available, costPerYearUsd)
|
||||
)
|
||||
);
|
||||
String descriptionText = getString(R.string.flock_sync_is_a_service_run_by_open_whisper_systems_available, costPerYearUsd);
|
||||
descriptionText += "<br/><br/>" + getString(R.string.privacy_and_terms_of_service);
|
||||
serviceDescription.setText(Html.fromHtml(descriptionText));
|
||||
serviceDescription.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
}
|
||||
}
|
||||
@ -167,11 +165,9 @@ public class SelectServiceProviderFragment extends Fragment {
|
||||
if (isChecked) {
|
||||
radioButtonOws.setChecked(true);
|
||||
radioButtonOther.setChecked(false);
|
||||
serviceDescription.setText(
|
||||
Html.fromHtml(
|
||||
getString(R.string.flock_sync_is_a_service_run_by_open_whisper_systems_available, costPerYearUsd)
|
||||
)
|
||||
);
|
||||
String descriptionText = getString(R.string.flock_sync_is_a_service_run_by_open_whisper_systems_available, costPerYearUsd);
|
||||
descriptionText += "<br/><br/>" + getString(R.string.privacy_and_terms_of_service);
|
||||
serviceDescription.setText(Html.fromHtml(descriptionText));
|
||||
serviceDescription.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
}
|
||||
}
|
||||
@ -196,11 +192,9 @@ public class SelectServiceProviderFragment extends Fragment {
|
||||
final TextView serviceDescription = (TextView) fragmentView.findViewById(R.id.sync_service_description);
|
||||
final Double costPerYearUsd = (double) getResources().getInteger(R.integer.cost_per_year_usd);
|
||||
|
||||
serviceDescription.setText(
|
||||
Html.fromHtml(
|
||||
getString(R.string.flock_sync_is_a_service_run_by_open_whisper_systems_available, costPerYearUsd)
|
||||
)
|
||||
);
|
||||
String descriptionText = getString(R.string.flock_sync_is_a_service_run_by_open_whisper_systems_available, costPerYearUsd);
|
||||
descriptionText += "<br/><br/>" + getString(R.string.privacy_and_terms_of_service);
|
||||
serviceDescription.setText(Html.fromHtml(descriptionText));
|
||||
serviceDescription.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,7 +41,6 @@ import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.EncodeHintType;
|
||||
import com.google.zxing.MultiFormatWriter;
|
||||
@ -51,6 +50,7 @@ import com.stripe.exception.StripeException;
|
||||
import com.stripe.model.Receiver;
|
||||
import de.passsy.holocircularprogressbar.HoloCircularProgressBar;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.registration.OwsRegistration;
|
||||
import org.anhonesteffort.flock.registration.model.FlockAccount;
|
||||
@ -325,7 +325,7 @@ public class SendBitcoinActivity extends Activity {
|
||||
String btcReceiverId = preferences.getString(KEY_ID_BTC_RECEIVER, null);
|
||||
|
||||
if (btcReceiverId != null)
|
||||
preferences.edit().remove(KEY_ID_BTC_RECEIVER).commit();
|
||||
preferences.edit().remove(KEY_ID_BTC_RECEIVER).apply();
|
||||
}
|
||||
|
||||
private Receiver handleCreateNewBtcReceiver(Double costUsd) throws StripeException {
|
||||
@ -357,7 +357,7 @@ public class SendBitcoinActivity extends Activity {
|
||||
|
||||
if (btcReceiverId == null) {
|
||||
btcReceiver = handleCreateNewBtcReceiver(costUsd);
|
||||
preferences.edit().putString(KEY_ID_BTC_RECEIVER, btcReceiver.getId()).commit();
|
||||
preferences.edit().putString(KEY_ID_BTC_RECEIVER, btcReceiver.getId()).apply();
|
||||
return btcReceiver;
|
||||
}
|
||||
|
||||
|
||||
@ -33,7 +33,6 @@ import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import ezvcard.VCard;
|
||||
import ezvcard.VCardVersion;
|
||||
import ezvcard.property.StructuredName;
|
||||
@ -45,11 +44,13 @@ import net.fortuna.ical4j.model.property.CalScale;
|
||||
import net.fortuna.ical4j.model.property.Description;
|
||||
import net.fortuna.ical4j.model.property.Version;
|
||||
import net.fortuna.ical4j.util.Calendars;
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.sync.OwsWebDav;
|
||||
import org.anhonesteffort.flock.webdav.ComponentETagPair;
|
||||
import org.anhonesteffort.flock.webdav.InvalidComponentException;
|
||||
import org.anhonesteffort.flock.webdav.PropertyParseException;
|
||||
import org.anhonesteffort.flock.webdav.WebDavConstants;
|
||||
import org.anhonesteffort.flock.webdav.caldav.CalDavCollection;
|
||||
import org.anhonesteffort.flock.webdav.caldav.CalDavStore;
|
||||
import org.anhonesteffort.flock.webdav.carddav.CardDavCollection;
|
||||
@ -236,8 +237,9 @@ public class ServerTestsFragment extends Fragment {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CARDDAV_CURRENT_USER_PRINCIPAL);
|
||||
|
||||
} catch (DavException e) {
|
||||
Log.e(TAG, "carddav current user principal", e);
|
||||
|
||||
if (e.getErrorCode() == DavServletResponse.SC_UNAUTHORIZED)
|
||||
if (e.getErrorCode() == WebDavConstants.SC_UNAUTHORIZED)
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_UNAUTHORIZED);
|
||||
else
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CARDDAV_CURRENT_USER_PRINCIPAL);
|
||||
@ -261,9 +263,9 @@ public class ServerTestsFragment extends Fragment {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CALDAV_CURRENT_USER_PRINCIPAL);
|
||||
|
||||
} catch (DavException e) {
|
||||
Log.e(TAG, "calddav current user principal", e);
|
||||
|
||||
Log.d(TAG, e.toString());
|
||||
if (e.getErrorCode() == DavServletResponse.SC_UNAUTHORIZED)
|
||||
if (e.getErrorCode() == WebDavConstants.SC_UNAUTHORIZED)
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_UNAUTHORIZED);
|
||||
else
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CALDAV_CURRENT_USER_PRINCIPAL);
|
||||
@ -287,10 +289,10 @@ public class ServerTestsFragment extends Fragment {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CARDDAV_ADDRESSBOOK_HOMESET);
|
||||
|
||||
} catch (DavException e) {
|
||||
Log.d(TAG, e.toString());
|
||||
Log.e(TAG, "carddav addressbook homeset", e);
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CARDDAV_ADDRESSBOOK_HOMESET);
|
||||
} catch (PropertyParseException e) {
|
||||
Log.d(TAG, e.toString());
|
||||
Log.e(TAG, "carddav addressbook homeset", e);
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CARDDAV_ADDRESSBOOK_HOMESET);
|
||||
} catch (SSLException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
@ -311,10 +313,10 @@ public class ServerTestsFragment extends Fragment {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CALDAV_CALENDAR_HOMESET);
|
||||
|
||||
} catch (DavException e) {
|
||||
Log.d(TAG, e.toString());
|
||||
Log.e(TAG, "caldav calendar homeset", e);
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CALDAV_CALENDAR_HOMESET);
|
||||
} catch (PropertyParseException e) {
|
||||
Log.d(TAG, e.toString());
|
||||
Log.e(TAG, "caldav calendar homeset", e);
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CALDAV_CALENDAR_HOMESET);
|
||||
} catch (SSLException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
@ -353,10 +355,10 @@ public class ServerTestsFragment extends Fragment {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SUCCESS);
|
||||
|
||||
} catch (DavException e) {
|
||||
Log.d(TAG, e.toString());
|
||||
Log.e(TAG, "caldav create delete collection", e);
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CALDAV_CREATE_DELETE_COLLECTION);
|
||||
} catch (PropertyParseException e) {
|
||||
Log.d(TAG, e.toString());
|
||||
Log.e(TAG, "caldav create delete collection", e);
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CALDAV_CREATE_DELETE_COLLECTION);
|
||||
} catch (SSLException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
@ -422,10 +424,10 @@ public class ServerTestsFragment extends Fragment {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SUCCESS);
|
||||
|
||||
} catch (DavException e) {
|
||||
Log.d(TAG, e.toString());
|
||||
Log.e(TAG, "caldav create edit collection properties", e);
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CALDAV_CREATE_EDIT_COLLECTION_PROPERTIES);
|
||||
} catch (PropertyParseException e) {
|
||||
Log.d(TAG, e.toString());
|
||||
Log.e(TAG, "caldav create edit collection properties", e);
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CALDAV_CREATE_EDIT_COLLECTION_PROPERTIES);
|
||||
} catch (SSLException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
@ -495,13 +497,13 @@ public class ServerTestsFragment extends Fragment {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SUCCESS);
|
||||
|
||||
} catch (DavException e) {
|
||||
Log.d(TAG, e.toString());
|
||||
Log.e(TAG, "carddav create delete contacts", e);
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CARDDAV_CREATE_DELETE_CONTACTS);
|
||||
} catch (PropertyParseException e) {
|
||||
Log.d(TAG, e.toString());
|
||||
Log.e(TAG, "carddav create delete contacts", e);
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CARDDAV_CREATE_DELETE_CONTACTS);
|
||||
} catch (InvalidComponentException e) {
|
||||
Log.d(TAG, e.toString());
|
||||
Log.e(TAG, "carddav create delete contacts", e);
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CARDDAV_CREATE_DELETE_CONTACTS);
|
||||
} catch (SSLException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
@ -579,16 +581,16 @@ public class ServerTestsFragment extends Fragment {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SUCCESS);
|
||||
|
||||
} catch (DavException e) {
|
||||
Log.d(TAG, e.toString());
|
||||
Log.e(TAG, "caldav create delete events", e);
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CALDAV_CREATE_DELETE_EVENTS);
|
||||
} catch (PropertyParseException e) {
|
||||
Log.d(TAG, e.toString());
|
||||
Log.e(TAG, "caldav create delete events", e);
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CALDAV_CREATE_DELETE_EVENTS);
|
||||
} catch (InvalidComponentException e) {
|
||||
Log.d(TAG, e.toString());
|
||||
Log.e(TAG, "caldav create delete events", e);
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CALDAV_CREATE_DELETE_EVENTS);
|
||||
} catch (ConstraintViolationException e) {
|
||||
Log.d(TAG, e.toString());
|
||||
Log.e(TAG, "caldav create delete events", e);
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CALDAV_CREATE_DELETE_EVENTS);
|
||||
} catch (SSLException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
|
||||
@ -24,6 +24,9 @@ import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.widget.Button;
|
||||
@ -31,7 +34,7 @@ import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.example.android.wizardpager.wizard.ui.StepPagerStrip;
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.sync.key.KeySyncScheduler;
|
||||
|
||||
/**
|
||||
@ -96,6 +99,25 @@ public class SetupActivity extends FragmentActivity {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.setup_menu, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.button_send_debug_log:
|
||||
Intent nextIntent = new Intent(getBaseContext(), SendDebugLogActivity.class);
|
||||
startActivity(nextIntent);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void setNavigationDisabled(boolean navigationDisabled) {
|
||||
this.navigationDisabled = navigationDisabled;
|
||||
}
|
||||
|
||||
@ -29,8 +29,7 @@ import android.view.LayoutInflater;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.crypto.InvalidCipherVersionException;
|
||||
import org.anhonesteffort.flock.crypto.KeyHelper;
|
||||
@ -38,10 +37,6 @@ import org.anhonesteffort.flock.registration.RegistrationApi;
|
||||
import org.anhonesteffort.flock.registration.RegistrationApiException;
|
||||
import org.anhonesteffort.flock.sync.addressbook.AddressbookSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.calendar.CalendarsSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.key.DavKeyCollection;
|
||||
import org.anhonesteffort.flock.sync.key.DavKeyStore;
|
||||
import org.anhonesteffort.flock.sync.key.KeySyncScheduler;
|
||||
import org.anhonesteffort.flock.webdav.InvalidComponentException;
|
||||
import org.anhonesteffort.flock.webdav.PropertyParseException;
|
||||
import org.apache.jackrabbit.webdav.DavException;
|
||||
|
||||
@ -66,7 +61,6 @@ public class StatusHeaderView extends LinearLayout {
|
||||
private AsyncTask asyncTaskSubscription;
|
||||
private AsyncTask asyncTaskCard;
|
||||
private AsyncTask asyncTaskMasterPassphrase;
|
||||
private AsyncTask asyncTaskMigration;
|
||||
|
||||
private long timeLastSync = -1;
|
||||
private boolean syncInProgress = false;
|
||||
@ -181,7 +175,6 @@ public class StatusHeaderView extends LinearLayout {
|
||||
syncStatusText = getContext().getString(R.string.notification_flock_subscription_expired);
|
||||
syncStatusDrawable = R.drawable.sad_cloud;
|
||||
if (!subscriptionNotificationShown) {
|
||||
NotificationDrawer.showSubscriptionExpiredNotification(getContext());
|
||||
subscriptionNotificationShown = true;
|
||||
}
|
||||
}
|
||||
@ -189,15 +182,6 @@ public class StatusHeaderView extends LinearLayout {
|
||||
syncStatusText = getContext().getString(R.string.status_header_status_auto_renew_error);
|
||||
syncStatusDrawable = R.drawable.sad_cloud;
|
||||
}
|
||||
else if (MigrationHelperBroadcastReceiver.getUiDisabledForMigration(getContext())) {
|
||||
syncStatusText = getContext().getString(R.string.status_header_status_migration_in_progress);
|
||||
syncStatusDrawable = R.drawable.migration_in_progress;
|
||||
|
||||
timeLastSyncView.setText(R.string.please_wait_this_will_take_a_few_minutes);
|
||||
timeLastSyncView.setVisibility(VISIBLE);
|
||||
|
||||
new KeySyncScheduler(getContext()).requestSync();
|
||||
}
|
||||
else if (!cipherPassphraseIsValid) {
|
||||
syncStatusText = getContext().getString(R.string.status_header_status_encryption_password_incorrect);
|
||||
syncStatusDrawable = R.drawable.sad_cloud;
|
||||
@ -273,11 +257,12 @@ public class StatusHeaderView extends LinearLayout {
|
||||
|
||||
@Override
|
||||
protected Bundle doInBackground(String... params) {
|
||||
Bundle result = new Bundle();
|
||||
RegistrationApi registrationApi = new RegistrationApi(getContext());
|
||||
Bundle result = new Bundle();
|
||||
|
||||
try {
|
||||
|
||||
RegistrationApi registrationApi = new RegistrationApi(getContext());
|
||||
|
||||
if (registrationApi.getAccount(account.get()).getLastStripeChargeFailed())
|
||||
lastChargeFailed = registrationApi.getCard(account.get()).isPresent();
|
||||
|
||||
@ -351,59 +336,6 @@ public class StatusHeaderView extends LinearLayout {
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private void handleUpdateMigrationInProgress() {
|
||||
if ((asyncTaskMigration != null && !asyncTaskMigration.isCancelled()) ||
|
||||
!MigrationHelperBroadcastReceiver.getUiDisabledForMigration(getContext()))
|
||||
return;
|
||||
|
||||
Log.d(TAG, "handleUpdateMigrationInProgress()");
|
||||
|
||||
asyncTaskMigration = new AsyncTask<String, Void, Bundle>() {
|
||||
|
||||
@Override
|
||||
protected Bundle doInBackground(String... params) {
|
||||
boolean migrationInProgress = true;
|
||||
Bundle result = new Bundle();
|
||||
|
||||
try {
|
||||
|
||||
DavKeyStore davKeyStore = DavAccountHelper.getDavKeyStore(getContext(), account.get());
|
||||
Optional<DavKeyCollection> keyCollection = davKeyStore.getCollection();
|
||||
|
||||
if (keyCollection.isPresent())
|
||||
migrationInProgress = !keyCollection.get().isMigrationComplete();
|
||||
|
||||
MigrationHelperBroadcastReceiver.setUiDisabledForMigration(getContext(), migrationInProgress);
|
||||
|
||||
if (!migrationInProgress)
|
||||
MigrationHelperBroadcastReceiver.setMigrationUpdateHandled(getContext());
|
||||
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SUCCESS);
|
||||
|
||||
} catch (InvalidComponentException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
} catch (PropertyParseException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
} catch (DavException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
} catch (IOException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Bundle result) {
|
||||
asyncTaskMigration = null;
|
||||
|
||||
if (result.getInt(ErrorToaster.KEY_STATUS_CODE) != ErrorToaster.CODE_SUCCESS)
|
||||
ErrorToaster.handleDisplayToastBundledError(getContext(), result);
|
||||
}
|
||||
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private final Runnable refreshUiRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@ -429,12 +361,6 @@ public class StatusHeaderView extends LinearLayout {
|
||||
handleUpdateCipherPassphraseIsValid();
|
||||
}
|
||||
};
|
||||
private final Runnable refreshMigrationRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
handleUpdateMigrationInProgress();
|
||||
}
|
||||
};
|
||||
|
||||
public void handleStartPerpetualRefresh() {
|
||||
account = DavAccountHelper.getAccount(getContext());
|
||||
@ -464,12 +390,6 @@ public class StatusHeaderView extends LinearLayout {
|
||||
uiHandler.post(refreshCipherPassphraseRunnable);
|
||||
}
|
||||
};
|
||||
TimerTask migrationTask = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
uiHandler.post(refreshMigrationRunnable);
|
||||
}
|
||||
};
|
||||
|
||||
intervalTimer.schedule(uiTask, 0, 2000);
|
||||
|
||||
@ -480,8 +400,6 @@ public class StatusHeaderView extends LinearLayout {
|
||||
}
|
||||
else
|
||||
intervalTimer.schedule(passphraseTask, 0, 10000);
|
||||
|
||||
intervalTimer.schedule(migrationTask, 0, 10000);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -34,7 +34,7 @@ import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.auth.AccountAuthenticator;
|
||||
import org.anhonesteffort.flock.crypto.KeyStore;
|
||||
import org.anhonesteffort.flock.registration.RegistrationApi;
|
||||
@ -62,7 +62,7 @@ public class UnregisterAccountActivity extends AccountAndKeyRequiredActivity {
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (!accountAndKeyAvailableAndMigrationComplete())
|
||||
if (!accountAndKeyAvailable())
|
||||
return;
|
||||
|
||||
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
|
||||
|
||||
@ -56,18 +56,20 @@ public class AccountAuthenticator extends AbstractAccountAuthenticator {
|
||||
|
||||
public static void setAllowAccountRemoval(Context context, boolean isAllowed) {
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME,
|
||||
Context.MODE_MULTI_PROCESS);
|
||||
settings.edit().putBoolean(KEY_ALLOW_ACCOUNT_REMOVAL, isAllowed).commit();
|
||||
Context.MODE_PRIVATE);
|
||||
settings.edit().putBoolean(KEY_ALLOW_ACCOUNT_REMOVAL, isAllowed).apply();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle getAccountRemovalAllowed(AccountAuthenticatorResponse response, Account account) {
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME,
|
||||
Context.MODE_MULTI_PROCESS);
|
||||
Boolean isAllowed = settings.getBoolean(KEY_ALLOW_ACCOUNT_REMOVAL, false);
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME,
|
||||
Context.MODE_PRIVATE);
|
||||
|
||||
Boolean isAllowed = settings.getBoolean(KEY_ALLOW_ACCOUNT_REMOVAL, false);
|
||||
Bundle resultBundle = new Bundle();
|
||||
|
||||
Bundle resultBundle = new Bundle();
|
||||
resultBundle.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, isAllowed);
|
||||
|
||||
return resultBundle;
|
||||
}
|
||||
|
||||
|
||||
@ -24,14 +24,12 @@ import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Programmer: rhodey
|
||||
*/
|
||||
// TODO: this is kind of hacked together-- can to better.
|
||||
public class DavAccount {
|
||||
|
||||
public static final String SYNC_ACCOUNT_TYPE = "openwhispersystems.org";
|
||||
@ -71,15 +69,17 @@ public class DavAccount {
|
||||
}
|
||||
|
||||
public Optional<String> getCardDavCollectionPath(Context context) {
|
||||
SharedPreferences preferences = context.getSharedPreferences(SYNC_ACCOUNT_TYPE,
|
||||
Context.MODE_MULTI_PROCESS);
|
||||
SharedPreferences preferences =
|
||||
context.getSharedPreferences(SYNC_ACCOUNT_TYPE, Context.MODE_PRIVATE);
|
||||
|
||||
return Optional.fromNullable(preferences.getString(KEY_CARD_DAV_COLLECTION, null));
|
||||
}
|
||||
|
||||
public void setCardDavCollection(Context context, String path) {
|
||||
SharedPreferences preferences = context.getSharedPreferences(SYNC_ACCOUNT_TYPE,
|
||||
Context.MODE_MULTI_PROCESS);
|
||||
preferences.edit().putString(KEY_CARD_DAV_COLLECTION, path).commit();
|
||||
SharedPreferences preferences =
|
||||
context.getSharedPreferences(SYNC_ACCOUNT_TYPE, Context.MODE_PRIVATE);
|
||||
|
||||
preferences.edit().putString(KEY_CARD_DAV_COLLECTION, path).apply();
|
||||
}
|
||||
|
||||
public Account getOsAccount() {
|
||||
@ -89,8 +89,8 @@ public class DavAccount {
|
||||
public Bundle toBundle() {
|
||||
Bundle bundle = new Bundle();
|
||||
|
||||
bundle.putString(KEY_USER_ID, userId);
|
||||
bundle.putString(KEY_AUTH_TOKEN, authToken);
|
||||
bundle.putString(KEY_USER_ID, userId);
|
||||
bundle.putString(KEY_AUTH_TOKEN, authToken);
|
||||
bundle.putString(KEY_DAV_HOST_HREF, davHostHREF);
|
||||
|
||||
return bundle;
|
||||
|
||||
@ -17,10 +17,8 @@
|
||||
* /
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock.sync;
|
||||
package org.anhonesteffort.flock.crypto;
|
||||
|
||||
import org.anhonesteffort.flock.crypto.InvalidMacException;
|
||||
import org.anhonesteffort.flock.crypto.MasterCipher;
|
||||
import org.anhonesteffort.flock.util.Util;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -28,7 +26,9 @@ import java.security.GeneralSecurityException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Programmer: rhodey
|
||||
* rhodey
|
||||
*
|
||||
* NOTE: THIS WAS A MISTAKE, I BROUGHT THIS UPON US AND NOW WE HAVE TO LIVE WITH IT D;
|
||||
*/
|
||||
public class HidingUtil {
|
||||
|
||||
@ -22,7 +22,7 @@ package org.anhonesteffort.flock.crypto;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.util.Base64;
|
||||
import org.anhonesteffort.flock.util.Util;
|
||||
|
||||
@ -60,7 +60,6 @@ public class KeyHelper {
|
||||
public static Optional<MasterCipher> getMasterCipher(Context context)
|
||||
throws IOException
|
||||
{
|
||||
boolean useCipherVersionZero = KeyStore.getUseCipherVersionZero(context);
|
||||
Optional<byte[]> cipherKeyBytes = KeyStore.getCipherKey(context);
|
||||
Optional<byte[]> macKeyBytes = KeyStore.getMacKey(context);
|
||||
|
||||
@ -68,14 +67,9 @@ public class KeyHelper {
|
||||
return Optional.absent();
|
||||
|
||||
SecretKey cipherKey = new SecretKeySpec(cipherKeyBytes.get(), "AES");
|
||||
SecretKey macKey = null;
|
||||
SecretKey macKey = new SecretKeySpec(macKeyBytes.get(), "SHA256");
|
||||
|
||||
if (useCipherVersionZero)
|
||||
macKey = new SecretKeySpec(cipherKeyBytes.get(), "SHA256");
|
||||
else
|
||||
macKey = new SecretKeySpec(macKeyBytes.get(), "SHA256");
|
||||
|
||||
return Optional.of(new MasterCipher(useCipherVersionZero, cipherKey, macKey));
|
||||
return Optional.of(new MasterCipher(cipherKey, macKey));
|
||||
}
|
||||
|
||||
public static Optional<String> buildEncodedSalt(Context context) throws IOException {
|
||||
@ -102,7 +96,7 @@ public class KeyHelper {
|
||||
SecretKey[] masterKeys = KeyUtil.getCipherAndMacKeysForPassphrase(salt.get(), masterPassphrase.get());
|
||||
SecretKey masterCipherKey = masterKeys[0];
|
||||
SecretKey masterMacKey = masterKeys[1];
|
||||
MasterCipher masterCipher = new MasterCipher(false, masterCipherKey, masterMacKey);
|
||||
MasterCipher masterCipher = new MasterCipher(masterCipherKey, masterMacKey);
|
||||
|
||||
byte[] keyMaterial = Util.combine(cipherKey.get(), macKey.get());
|
||||
byte[] encryptedKeyMaterial = masterCipher.encryptAndEncode(keyMaterial);
|
||||
@ -120,12 +114,11 @@ public class KeyHelper {
|
||||
if (!masterPassphrase.isPresent())
|
||||
throw new InvalidMacException("Passphrase unavailable.");
|
||||
|
||||
boolean useCipherVersionZero = KeyStore.getUseCipherVersionZero(context);
|
||||
byte[] salt = Base64.decode(saltAndEncryptedKeyMaterial[0]);
|
||||
SecretKey[] masterKeys = KeyUtil.getCipherAndMacKeysForPassphrase(salt, masterPassphrase.get());
|
||||
SecretKey masterCipherKey = masterKeys[0];
|
||||
SecretKey masterMacKey = masterKeys[1];
|
||||
MasterCipher masterCipher = new MasterCipher(useCipherVersionZero, masterCipherKey, masterMacKey);
|
||||
MasterCipher masterCipher = new MasterCipher(masterCipherKey, masterMacKey);
|
||||
byte[] plaintextKeyMaterial = masterCipher.decodeAndDecrypt(saltAndEncryptedKeyMaterial[1].getBytes());
|
||||
|
||||
boolean saltLengthValid = salt.length == KeyUtil.SALT_LENGTH_BYTES;
|
||||
@ -164,7 +157,7 @@ public class KeyHelper {
|
||||
SecretKey[] masterKeys = KeyUtil.getCipherAndMacKeysForPassphrase(salt.get(), masterPassphrase.get());
|
||||
SecretKey masterCipherKey = masterKeys[0];
|
||||
SecretKey masterMacKey = masterKeys[1];
|
||||
MasterCipher masterCipher = new MasterCipher(false, masterCipherKey, masterMacKey);
|
||||
MasterCipher masterCipher = new MasterCipher(masterCipherKey, masterMacKey);
|
||||
|
||||
try {
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.util.Base64;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -36,47 +36,40 @@ public class KeyStore {
|
||||
private static final String TAG = "org.anhonesteffort.flock.crypto.KeyStore";
|
||||
|
||||
private static final String PREFERENCES_NAME = "org.anhonesteffort.flock.crypto.KeyStore";
|
||||
private static final String KEY_USE_CIPHER_VERSION_ZERO = "KEY_USE_CIPHER_VERSION_ZERO";
|
||||
private static final String KEY_MASTER_PASSPHRASE = "KEY_OLD_MASTER_PASSPHRASE";
|
||||
private static final String KEY_CIPHER_KEY = "KEY_CIPHER_KEY";
|
||||
private static final String KEY_MAC_KEY = "KEY_MAC_KEY";
|
||||
private static final String KEY_KEY_MATERIAL_SALT = "KEY_KEY_MATERIAL_SALT";
|
||||
private static final String KEY_ENCRYPTED_KEY_MATERIAL = "KEY_ENCRYPTED_KEY_MATERIAL";
|
||||
|
||||
protected static boolean getUseCipherVersionZero(Context context) {
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_MULTI_PROCESS);
|
||||
return settings.getBoolean(KEY_USE_CIPHER_VERSION_ZERO, false);
|
||||
private static SharedPreferences getSharedPreferences(Context context) {
|
||||
return context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
public static void setUseCipherVersionZero(Context context, boolean useCipherVersionZero) {
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_MULTI_PROCESS);
|
||||
settings.edit().putBoolean(KEY_USE_CIPHER_VERSION_ZERO, useCipherVersionZero).commit();
|
||||
}
|
||||
|
||||
protected static void saveCipherKey(Context context, byte[] cipherKey) {
|
||||
public static void saveCipherKey(Context context, byte[] cipherKey) {
|
||||
Log.d(TAG, "SAVING CIPHER KEY MATERIAL...");
|
||||
saveBytes(context, KEY_CIPHER_KEY, cipherKey);
|
||||
}
|
||||
|
||||
protected static Optional<byte[]> getCipherKey(Context context) throws IOException {
|
||||
public static Optional<byte[]> getCipherKey(Context context) throws IOException {
|
||||
return retrieveBytes(context, KEY_CIPHER_KEY);
|
||||
}
|
||||
|
||||
protected static void saveMacKey(Context context, byte[] cipherKey) {
|
||||
public static void saveMacKey(Context context, byte[] cipherKey) {
|
||||
Log.d(TAG, "SAVING MAC KEY MATERIAL...");
|
||||
saveBytes(context, KEY_MAC_KEY, cipherKey);
|
||||
}
|
||||
|
||||
protected static Optional<byte[]> getMacKey(Context context) throws IOException {
|
||||
public static Optional<byte[]> getMacKey(Context context) throws IOException {
|
||||
return retrieveBytes(context, KEY_MAC_KEY);
|
||||
}
|
||||
|
||||
protected static void saveKeyMaterialSalt(Context context, byte[] salt) {
|
||||
public static void saveKeyMaterialSalt(Context context, byte[] salt) {
|
||||
Log.d(TAG, "SAVING SALT FOR KEY MATERIAL...");
|
||||
saveBytes(context, KEY_KEY_MATERIAL_SALT, salt);
|
||||
}
|
||||
|
||||
protected static Optional<byte[]> getKeyMaterialSalt(Context context) throws IOException {
|
||||
public static Optional<byte[]> getKeyMaterialSalt(Context context) throws IOException {
|
||||
return retrieveBytes(context, KEY_KEY_MATERIAL_SALT);
|
||||
}
|
||||
|
||||
@ -100,36 +93,32 @@ public class KeyStore {
|
||||
|
||||
public static void invalidateKeyMaterial(Context context) {
|
||||
Log.w(TAG, "INVALIDATING ALL KEY MATERIAL...");
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME,
|
||||
Context.MODE_MULTI_PROCESS);
|
||||
SharedPreferences settings = getSharedPreferences(context);
|
||||
|
||||
settings.edit().remove(KEY_CIPHER_KEY).commit();
|
||||
settings.edit().remove(KEY_MAC_KEY).commit();
|
||||
settings.edit().remove(KEY_KEY_MATERIAL_SALT).commit();
|
||||
settings.edit().remove(KEY_MASTER_PASSPHRASE).commit();
|
||||
settings.edit().remove(KEY_CIPHER_KEY).apply();
|
||||
settings.edit().remove(KEY_MAC_KEY).apply();
|
||||
settings.edit().remove(KEY_KEY_MATERIAL_SALT).apply();
|
||||
settings.edit().remove(KEY_MASTER_PASSPHRASE).apply();
|
||||
}
|
||||
|
||||
private static void saveBytes(Context context, String key, byte[] value) {
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME,
|
||||
Context.MODE_MULTI_PROCESS);
|
||||
SharedPreferences settings = getSharedPreferences(context);
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
|
||||
editor.putString(key, Base64.encodeBytes(value));
|
||||
editor.commit();
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
private static void saveString(Context context, String key, String value) {
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME,
|
||||
Context.MODE_MULTI_PROCESS);
|
||||
SharedPreferences settings = getSharedPreferences(context);
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
|
||||
editor.putString(key, value);
|
||||
editor.commit();
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
private static Optional<byte[]> retrieveBytes(Context context, String key) throws IOException {
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME,
|
||||
Context.MODE_MULTI_PROCESS);
|
||||
SharedPreferences settings = getSharedPreferences(context);
|
||||
String encodedValue = settings.getString(key, null);
|
||||
|
||||
if (encodedValue == null)
|
||||
@ -139,9 +128,7 @@ public class KeyStore {
|
||||
}
|
||||
|
||||
private static Optional<String> retrieveString(Context context, String key) {
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME,
|
||||
Context.MODE_MULTI_PROCESS);
|
||||
return Optional.fromNullable(settings.getString(key, null));
|
||||
return Optional.fromNullable(getSharedPreferences(context).getString(key, null));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -27,7 +27,6 @@ import java.security.GeneralSecurityException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
|
||||
@ -40,14 +40,12 @@ public class MasterCipher {
|
||||
protected static final int MAC_LENGTH_BYTES = 32;
|
||||
protected static final int IV_LENGTH_BYTES = 16;
|
||||
|
||||
private final boolean useCipherVersionZero;
|
||||
private final SecretKey cipherKey;
|
||||
private final SecretKey macKey;
|
||||
|
||||
protected MasterCipher(boolean useCipherVersionZero, SecretKey cipherKey, SecretKey macKey) {
|
||||
this.useCipherVersionZero = useCipherVersionZero;
|
||||
this.cipherKey = cipherKey;
|
||||
this.macKey = macKey;
|
||||
protected MasterCipher(SecretKey cipherKey, SecretKey macKey) {
|
||||
this.cipherKey = cipherKey;
|
||||
this.macKey = macKey;
|
||||
}
|
||||
|
||||
public byte[] encryptAndEncode(byte[] data)
|
||||
@ -61,15 +59,9 @@ public class MasterCipher {
|
||||
|
||||
byte[] iv = encryptingCipher.getIV();
|
||||
byte[] ciphertext = encryptingCipher.doFinal(data);
|
||||
byte[] mac = hmac.doFinal(Util.combine(new byte[] {CURRENT_CIPHER_VERSION}, iv, ciphertext));
|
||||
|
||||
if (useCipherVersionZero) {
|
||||
byte[] mac = hmac.doFinal(Util.combine(iv, ciphertext));
|
||||
return Base64.encodeBytesToBytes(Util.combine(iv, ciphertext, mac));
|
||||
}
|
||||
else {
|
||||
byte[] mac = hmac.doFinal(Util.combine(new byte[] {CURRENT_CIPHER_VERSION}, iv, ciphertext));
|
||||
return Base64.encodeBytesToBytes(Util.combine(new byte[] {CURRENT_CIPHER_VERSION}, iv, ciphertext, mac));
|
||||
}
|
||||
return Base64.encodeBytesToBytes(Util.combine(new byte[] {CURRENT_CIPHER_VERSION}, iv, ciphertext, mac));
|
||||
}
|
||||
|
||||
public String encryptAndEncode(String data)
|
||||
@ -78,39 +70,9 @@ public class MasterCipher {
|
||||
return new String(encryptAndEncode(data.getBytes()));
|
||||
}
|
||||
|
||||
private byte[] decodeAndDecryptCipherVersionZero(byte[] encodedIvCiphertextAndMac)
|
||||
throws InvalidMacException, IOException, GeneralSecurityException
|
||||
{
|
||||
byte[] ivCiphertextAndMac = Base64.decode(encodedIvCiphertextAndMac);
|
||||
if (ivCiphertextAndMac.length <= (IV_LENGTH_BYTES + MAC_LENGTH_BYTES))
|
||||
throw new GeneralSecurityException("invalid length on decoded iv, ciphertext and mac");
|
||||
|
||||
byte[] iv = Arrays.copyOfRange(ivCiphertextAndMac, 0, IV_LENGTH_BYTES);
|
||||
byte[] ciphertext = Arrays.copyOfRange(ivCiphertextAndMac,
|
||||
IV_LENGTH_BYTES,
|
||||
ivCiphertextAndMac.length - MAC_LENGTH_BYTES);
|
||||
byte[] mac = Arrays.copyOfRange(ivCiphertextAndMac,
|
||||
ivCiphertextAndMac.length - MAC_LENGTH_BYTES,
|
||||
ivCiphertextAndMac.length);
|
||||
|
||||
Cipher decryptingCipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
|
||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
||||
Mac hmac = Mac.getInstance("HmacSHA256");
|
||||
|
||||
decryptingCipher.init(Cipher.DECRYPT_MODE, cipherKey, ivSpec);
|
||||
hmac.init(macKey);
|
||||
|
||||
verifyMac(hmac, Util.combine(iv, ciphertext), mac);
|
||||
|
||||
return decryptingCipher.doFinal(ciphertext);
|
||||
}
|
||||
|
||||
public byte[] decodeAndDecrypt(byte[] encodedVersionIvCiphertextAndMac)
|
||||
throws InvalidMacException, IOException, GeneralSecurityException
|
||||
{
|
||||
if (useCipherVersionZero)
|
||||
return decodeAndDecryptCipherVersionZero(encodedVersionIvCiphertextAndMac);
|
||||
|
||||
byte[] versionIvCiphertextAndMac = Base64.decode(encodedVersionIvCiphertextAndMac);
|
||||
if (versionIvCiphertextAndMac.length <= (1 + IV_LENGTH_BYTES + MAC_LENGTH_BYTES))
|
||||
throw new GeneralSecurityException("invalid length on decoded cipherVersion, iv, ciphertext and mac");
|
||||
|
||||
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock.registration;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.http.conn.scheme.Scheme;
|
||||
import org.apache.http.conn.scheme.SchemeRegistry;
|
||||
import org.apache.http.conn.ssl.SSLSocketFactory;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyStore;
|
||||
|
||||
/**
|
||||
* rhodey
|
||||
*/
|
||||
public class HttpClientFactory {
|
||||
|
||||
private final Context context;
|
||||
|
||||
public HttpClientFactory(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public DefaultHttpClient buildClient() throws RegistrationApiException {
|
||||
try {
|
||||
|
||||
AssetManager assetManager = context.getAssets();
|
||||
InputStream keyStoreInputStream = assetManager.open("flock.store");
|
||||
KeyStore trustStore = KeyStore.getInstance("BKS");
|
||||
|
||||
trustStore.load(keyStoreInputStream, "owsflock".toCharArray());
|
||||
|
||||
SSLSocketFactory appSSLSocketFactory = new SSLSocketFactory(trustStore);
|
||||
DefaultHttpClient client = new DefaultHttpClient();
|
||||
SchemeRegistry schemeRegistry = client.getConnectionManager().getSchemeRegistry();
|
||||
Scheme httpsScheme = new Scheme("https", appSSLSocketFactory, 443);
|
||||
|
||||
schemeRegistry.register(httpsScheme);
|
||||
|
||||
return client;
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(getClass().getName(), "caught exception while constructing HttpClient client", e);
|
||||
throw new RegistrationApiException("caught exception while constructing HttpClient client: " + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock.registration;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.anhonesteffort.flock.registration.model.AugmentedFlockAccount;
|
||||
import org.anhonesteffort.flock.registration.model.FlockCardInformation;
|
||||
import org.anhonesteffort.flock.util.MapperUtil;
|
||||
import org.apache.http.HttpResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* rhodey
|
||||
*/
|
||||
public class ModelFactory {
|
||||
|
||||
public static AugmentedFlockAccount buildAccount(HttpResponse response)
|
||||
throws RegistrationApiParseException
|
||||
{
|
||||
try {
|
||||
|
||||
return MapperUtil.getMapper().readValue(
|
||||
response.getEntity().getContent(), AugmentedFlockAccount.class
|
||||
);
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e(ModelFactory.class.getName(), "unable to build account from HTTP response", e);
|
||||
throw new RegistrationApiParseException("unable to build account from HTTP response.");
|
||||
}
|
||||
}
|
||||
|
||||
public static FlockCardInformation buildCard(HttpResponse response)
|
||||
throws RegistrationApiParseException
|
||||
{
|
||||
try {
|
||||
|
||||
return MapperUtil.getMapper().readValue(
|
||||
response.getEntity().getContent(), FlockCardInformation.class
|
||||
);
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e(ModelFactory.class.getName(), "unable to build card information from HTTP response", e);
|
||||
throw new RegistrationApiParseException("unable to build card information from HTTP response.");
|
||||
}
|
||||
}
|
||||
|
||||
public static Boolean buildBoolean(HttpResponse response)
|
||||
throws RegistrationApiParseException
|
||||
{
|
||||
try {
|
||||
|
||||
return MapperUtil.getMapper().readValue(
|
||||
response.getEntity().getContent(), Boolean.class
|
||||
);
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e(ModelFactory.class.getName(), "unable to build boolean from HTTP response", e);
|
||||
throw new RegistrationApiParseException("unable to build boolean from HTTP response.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -33,40 +33,48 @@ import java.util.List;
|
||||
*/
|
||||
public class OwsRegistration {
|
||||
|
||||
protected static final int STATUS_OK = 200;
|
||||
protected static final int STATUS_REDIRECT = 300;
|
||||
protected static final int STATUS_MALFORMED_REQUEST = 400;
|
||||
protected static final int STATUS_UNAUTHORIZED = 401;
|
||||
protected static final int STATUS_PAYMENT_REQUIRED = 402;
|
||||
protected static final int STATUS_RESOURCE_ALREADY_EXISTS = 403;
|
||||
protected static final int STATUS_RESOURCE_NOT_FOUND = 404;
|
||||
protected static final int STATUS_SERVER_ERROR = 500;
|
||||
protected static final int STATUS_SERVICE_UNAVAILABLE = 503;
|
||||
public static final int STATUS_OK = 200;
|
||||
public static final int STATUS_REDIRECT = 300;
|
||||
public static final int STATUS_MALFORMED_REQUEST = 400;
|
||||
public static final int STATUS_UNAUTHORIZED = 401;
|
||||
public static final int STATUS_PAYMENT_REQUIRED = 402;
|
||||
public static final int STATUS_RESOURCE_ALREADY_EXISTS = 403;
|
||||
public static final int STATUS_RESOURCE_NOT_FOUND = 404;
|
||||
public static final int STATUS_RATE_LIMITED = 420;
|
||||
public static final int STATUS_SERVER_ERROR = 500;
|
||||
public static final int STATUS_SERVICE_UNAVAILABLE = 503;
|
||||
|
||||
protected static final String ACCOUNT_COLLECTION = "accounts";
|
||||
protected static final String ACCOUNT_CARD_CONTROLLER = "card";
|
||||
protected static final String ACCOUNT_PLAN_CONTROLLER = "plan";
|
||||
protected static final String PRICING_CONTROLLER = "pricing";
|
||||
|
||||
protected static final String PARAM_ACCOUNT_ID = "id";
|
||||
protected static final String PARAM_ACCOUNT_VERSION = "version";
|
||||
protected static final String PARAM_ACCOUNT_PASSWORD = "password";
|
||||
protected static final String PARAM_STRIPE_CARD_TOKEN = "stripe_card_token";
|
||||
protected static final String PARAM_AUTO_RENEW = "auto_renew";
|
||||
protected static final String PARAM_PLAN_TYPE = "plan_type";
|
||||
protected static final String PARAM_PLAN_ID = "plan_id";
|
||||
protected static final String PARAM_PURCHASE_TOKEN = "purchase_token";
|
||||
|
||||
protected static final String REGISTRATION_API_HOST = "flock-accounts.whispersystems.org";
|
||||
protected static final int REGISTRATION_API_PORT = 443;
|
||||
protected static final String HREF_REGISTRATION_API = "https://" + REGISTRATION_API_HOST + ":" + REGISTRATION_API_PORT;
|
||||
protected static final String HREF_ACCOUNT_COLLECTION = HREF_REGISTRATION_API + "/" + ACCOUNT_COLLECTION + "/";
|
||||
protected static final String HREF_PRICING = HREF_REGISTRATION_API + "/" + PRICING_CONTROLLER + "/";
|
||||
protected static final String HREF_ACCOUNT_COLLECTION = HREF_REGISTRATION_API + "/v2/" + ACCOUNT_COLLECTION;
|
||||
protected static final String HREF_PRICING = HREF_REGISTRATION_API + "/" + PRICING_CONTROLLER;
|
||||
|
||||
public static final String STRIPE_PUBLIC_KEY = "pk_live_EiIuIaXaPPMgjllTlweiDYgJ";
|
||||
|
||||
protected static String getHrefForAccount(String accountId) {
|
||||
return HREF_ACCOUNT_COLLECTION + accountId;
|
||||
return HREF_REGISTRATION_API + "/v2/" + ACCOUNT_COLLECTION + "/" + accountId;
|
||||
}
|
||||
|
||||
protected static String getHrefForCard(String accountId) {
|
||||
return HREF_ACCOUNT_COLLECTION + accountId + "/" + ACCOUNT_CARD_CONTROLLER;
|
||||
return HREF_REGISTRATION_API + "/" + ACCOUNT_COLLECTION + "/" + accountId + "/" + ACCOUNT_CARD_CONTROLLER;
|
||||
}
|
||||
|
||||
protected static String getHrefForPlan(String accountId) {
|
||||
return HREF_REGISTRATION_API + "/v2/" + ACCOUNT_COLLECTION + "/" + accountId + "/" + ACCOUNT_PLAN_CONTROLLER;
|
||||
}
|
||||
|
||||
protected static String getHrefWithParameters(String href, List<NameValuePair> params) {
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock.registration;
|
||||
|
||||
/**
|
||||
* rhodey
|
||||
*/
|
||||
public class PaymentRequiredException extends RegistrationApiException {
|
||||
|
||||
public PaymentRequiredException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@ -20,21 +20,13 @@
|
||||
package org.anhonesteffort.flock.registration;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.AssetManager;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.stripe.exception.CardException;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.registration.model.AugmentedFlockAccount;
|
||||
import org.anhonesteffort.flock.registration.model.FlockCardInformation;
|
||||
import org.anhonesteffort.flock.registration.model.SubscriptionPlan;
|
||||
import org.anhonesteffort.flock.util.Base64;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.NameValuePair;
|
||||
@ -43,17 +35,11 @@ import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.methods.HttpPut;
|
||||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
import org.apache.http.conn.scheme.Scheme;
|
||||
import org.apache.http.conn.scheme.SchemeRegistry;
|
||||
import org.apache.http.conn.ssl.SSLSocketFactory;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.security.KeyStore;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -63,58 +49,28 @@ public class RegistrationApi {
|
||||
|
||||
private static final String TAG = "org.anhonesteffort.flock.registration.RegistrationApi";
|
||||
|
||||
private static final String KEY_CACHED_DAYS_REMAINING = "KEY_CACHED_DAYS_REMAINING";
|
||||
private static final String KEY_CACHED_AUTO_RENEW_ENABLED = "KEY_CACHED_AUTO_RENEW_ENABLED";
|
||||
private static final String KEY_CACHED_LAST_CHARGE_FAILED = "KEY_CACHED_LAST_CHARGE_FAILED";
|
||||
private final DefaultHttpClient httpClient;
|
||||
|
||||
private Context context;
|
||||
private final ObjectMapper mapper;
|
||||
|
||||
public RegistrationApi(Context context) {
|
||||
this.context = context;
|
||||
this.mapper = new ObjectMapper();
|
||||
|
||||
mapper.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
|
||||
public RegistrationApi(Context context) throws RegistrationApiException {
|
||||
this.httpClient = new HttpClientFactory(context).buildClient();
|
||||
}
|
||||
|
||||
private DefaultHttpClient getClient(Context context)
|
||||
throws IOException, RegistrationApiClientException
|
||||
{
|
||||
try {
|
||||
|
||||
AssetManager assetManager = context.getAssets();
|
||||
InputStream keyStoreInputStream = assetManager.open("flock.store");
|
||||
KeyStore trustStore = KeyStore.getInstance("BKS");
|
||||
|
||||
trustStore.load(keyStoreInputStream, "owsflock".toCharArray());
|
||||
|
||||
SSLSocketFactory appSSLSocketFactory = new SSLSocketFactory(trustStore);
|
||||
DefaultHttpClient client = new DefaultHttpClient();
|
||||
SchemeRegistry schemeRegistry = client.getConnectionManager().getSchemeRegistry();
|
||||
Scheme httpsScheme = new Scheme("https", appSSLSocketFactory, 443);
|
||||
|
||||
schemeRegistry.register(httpsScheme);
|
||||
|
||||
return client;
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "caught exception while constructing HttpClient client", e);
|
||||
throw new RegistrationApiClientException("caught exception while constructing HttpClient client: " + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void authorizeRequest(HttpRequestBase httpRequest, DavAccount account) {
|
||||
private static void authorizeRequest(HttpRequestBase httpRequest, DavAccount account) {
|
||||
String encodedAuth = account.getUserId() + ":" + account.getAuthToken();
|
||||
httpRequest.addHeader("Authorization", "Basic " + Base64.encodeBytes(encodedAuth.getBytes()));
|
||||
}
|
||||
|
||||
private void throwExceptionIfNotOK(HttpResponse response) throws RegistrationApiException {
|
||||
private static void throwExceptionIfNotOK(HttpResponse response)
|
||||
throws RegistrationApiException
|
||||
{
|
||||
Log.d(TAG, "response status code: " + response.getStatusLine().getStatusCode());
|
||||
|
||||
switch (response.getStatusLine().getStatusCode()) {
|
||||
case OwsRegistration.STATUS_MALFORMED_REQUEST:
|
||||
throw new RegistrationApiClientException("Registration API returned status malformed request.");
|
||||
Integer status = response.getStatusLine().getStatusCode();
|
||||
|
||||
if (status >= 200 && status < 300)
|
||||
return;
|
||||
|
||||
switch (status) {
|
||||
case OwsRegistration.STATUS_UNAUTHORIZED:
|
||||
throw new AuthorizationException("Registration API returned status unauthorized.");
|
||||
|
||||
@ -125,73 +81,15 @@ public class RegistrationApi {
|
||||
throw new ResourceAlreadyExistsException("Registration API returned status 403, resource already exists.");
|
||||
|
||||
case OwsRegistration.STATUS_PAYMENT_REQUIRED:
|
||||
throw new RegistrationApiClientException("Registration API didn't like the card token we gave it",
|
||||
OwsRegistration.STATUS_PAYMENT_REQUIRED);
|
||||
throw new PaymentRequiredException("Registration API didn't like the card or purchase token we gave it");
|
||||
|
||||
case OwsRegistration.STATUS_SERVICE_UNAVAILABLE:
|
||||
throw new RegistrationApiException("Registration API service is unavailable");
|
||||
|
||||
case OwsRegistration.STATUS_SERVER_ERROR:
|
||||
throw new RegistrationApiException("Registration API returned status 500! 0.o");
|
||||
case OwsRegistration.STATUS_RATE_LIMITED:
|
||||
throw new RegistrationApiException("we are being rate limited for some reason, bummer.");
|
||||
}
|
||||
}
|
||||
|
||||
private AugmentedFlockAccount buildFlockAccount(HttpResponse response)
|
||||
throws RegistrationApiClientException
|
||||
{
|
||||
try {
|
||||
|
||||
return mapper.readValue(response.getEntity().getContent(), AugmentedFlockAccount.class);
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "unable to build account from HTTP response", e);
|
||||
throw new RegistrationApiClientException("unable to build account from HTTP response.");
|
||||
}
|
||||
}
|
||||
|
||||
private FlockCardInformation buildFlockCardInformation(HttpResponse response)
|
||||
throws RegistrationApiClientException
|
||||
{
|
||||
try {
|
||||
|
||||
return mapper.readValue(response.getEntity().getContent(), FlockCardInformation.class);
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "unable to build card information from HTTP response", e);
|
||||
throw new RegistrationApiClientException("unable to build card information from HTTP response.");
|
||||
}
|
||||
}
|
||||
|
||||
public Double getCostPerYearUsd()
|
||||
throws IOException, RegistrationApiException
|
||||
{
|
||||
HttpGet httpGet = new HttpGet(OwsRegistration.HREF_PRICING);
|
||||
DefaultHttpClient httpClient = getClient(context);
|
||||
HttpResponse response = httpClient.execute(httpGet);
|
||||
InputStreamReader reader = new InputStreamReader(response.getEntity().getContent());
|
||||
|
||||
throwExceptionIfNotOK(response);
|
||||
|
||||
return Double.valueOf(CharStreams.toString(reader));
|
||||
}
|
||||
|
||||
public boolean isAuthenticated(DavAccount account)
|
||||
throws IOException, RegistrationApiException
|
||||
{
|
||||
try {
|
||||
|
||||
HttpGet httpGet = new HttpGet(OwsRegistration.getHrefForAccount(account.getUserId()));
|
||||
DefaultHttpClient httpClient = getClient(context);
|
||||
authorizeRequest(httpGet, account);
|
||||
|
||||
HttpResponse response = httpClient.execute(httpGet);
|
||||
throwExceptionIfNotOK(response);
|
||||
|
||||
return true;
|
||||
|
||||
} catch (AuthorizationException e) {
|
||||
return false;
|
||||
}
|
||||
throw new RegistrationApiException(
|
||||
"got bad status: " + response.getStatusLine().getReasonPhrase() + ", " + status, status
|
||||
);
|
||||
}
|
||||
|
||||
public AugmentedFlockAccount createAccount(DavAccount account)
|
||||
@ -199,81 +97,83 @@ public class RegistrationApi {
|
||||
{
|
||||
Log.d(TAG, "createAccount()");
|
||||
|
||||
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(1);
|
||||
List<NameValuePair> nameValuePairs = new LinkedList<>();
|
||||
nameValuePairs.add(new BasicNameValuePair(OwsRegistration.PARAM_ACCOUNT_ID, account.getUserId()));
|
||||
nameValuePairs.add(new BasicNameValuePair(OwsRegistration.PARAM_ACCOUNT_VERSION, Integer.toString(2)));
|
||||
nameValuePairs.add(new BasicNameValuePair(OwsRegistration.PARAM_ACCOUNT_PASSWORD, account.getAuthToken()));
|
||||
|
||||
String HREF = OwsRegistration.getHrefWithParameters(OwsRegistration.HREF_ACCOUNT_COLLECTION,
|
||||
nameValuePairs);
|
||||
HttpPost httpPost = new HttpPost(HREF);
|
||||
DefaultHttpClient httpClient = getClient(context);
|
||||
String accountsControllerHref = OwsRegistration.getHrefWithParameters(
|
||||
OwsRegistration.HREF_ACCOUNT_COLLECTION,
|
||||
nameValuePairs
|
||||
);
|
||||
|
||||
HttpPost httpPost = new HttpPost(accountsControllerHref);
|
||||
HttpResponse response = httpClient.execute(httpPost);
|
||||
|
||||
throwExceptionIfNotOK(response);
|
||||
|
||||
AugmentedFlockAccount flockAccount = buildFlockAccount(response);
|
||||
|
||||
cacheSubscriptionDetails(context,
|
||||
flockAccount.getDaysRemaining(),
|
||||
flockAccount.getAutoRenewEnabled(),
|
||||
flockAccount.getLastStripeChargeFailed());
|
||||
return flockAccount;
|
||||
return ModelFactory.buildAccount(response);
|
||||
}
|
||||
|
||||
|
||||
public AugmentedFlockAccount getAccount(DavAccount account)
|
||||
throws RegistrationApiException, IOException
|
||||
{
|
||||
String HREF = OwsRegistration.getHrefForAccount(account.getUserId());
|
||||
HttpGet httpGet = new HttpGet(HREF);
|
||||
DefaultHttpClient httpClient = getClient(context);
|
||||
String HREF = OwsRegistration.getHrefForAccount(account.getUserId());
|
||||
HttpGet httpGet = new HttpGet(HREF);
|
||||
authorizeRequest(httpGet, account);
|
||||
|
||||
HttpResponse response = httpClient.execute(httpGet);
|
||||
throwExceptionIfNotOK(response);
|
||||
|
||||
AugmentedFlockAccount flockAccount = buildFlockAccount(response);
|
||||
|
||||
cacheSubscriptionDetails(context,
|
||||
flockAccount.getDaysRemaining(),
|
||||
flockAccount.getAutoRenewEnabled(),
|
||||
flockAccount.getLastStripeChargeFailed());
|
||||
return flockAccount;
|
||||
return ModelFactory.buildAccount(response);
|
||||
}
|
||||
|
||||
private static void cacheSubscriptionDetails(Context context,
|
||||
Long daysRemaining,
|
||||
Boolean autoRenewEnabled,
|
||||
Boolean lastChargeFailed)
|
||||
public void setAccountVersion(DavAccount account, Integer version)
|
||||
throws RegistrationApiException, IOException
|
||||
{
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
preferences.edit()
|
||||
.putLong(KEY_CACHED_DAYS_REMAINING, daysRemaining)
|
||||
.putBoolean(KEY_CACHED_AUTO_RENEW_ENABLED, autoRenewEnabled)
|
||||
.putBoolean(KEY_CACHED_LAST_CHARGE_FAILED, lastChargeFailed)
|
||||
.commit();
|
||||
Log.d(TAG, "setAccountVersion()");
|
||||
|
||||
List<NameValuePair> nameValuePairs = new LinkedList<>();
|
||||
nameValuePairs.add(new BasicNameValuePair(OwsRegistration.PARAM_ACCOUNT_VERSION, version.toString()));
|
||||
|
||||
String accountControllerHref = OwsRegistration.getHrefWithParameters(
|
||||
OwsRegistration.getHrefForAccount(account.getUserId()),
|
||||
nameValuePairs
|
||||
);
|
||||
|
||||
HttpPut httpPut = new HttpPut(accountControllerHref);
|
||||
authorizeRequest(httpPut, account);
|
||||
|
||||
HttpResponse response = httpClient.execute(httpPut);
|
||||
throwExceptionIfNotOK(response);
|
||||
}
|
||||
|
||||
public static Optional<Pair<Long, Boolean[]>> getCachedSubscriptionDetails(Context context) {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
if (preferences.getLong(KEY_CACHED_DAYS_REMAINING, -1) == -1)
|
||||
return Optional.absent();
|
||||
public void setAccountPassword(DavAccount account, String newPassword)
|
||||
throws RegistrationApiException, IOException
|
||||
{
|
||||
Log.d(TAG, "setAccountPassword()");
|
||||
|
||||
return Optional.of(new Pair<Long, Boolean[]>(
|
||||
preferences.getLong(KEY_CACHED_DAYS_REMAINING, 0),
|
||||
new Boolean[] {
|
||||
preferences.getBoolean(KEY_CACHED_AUTO_RENEW_ENABLED, false),
|
||||
preferences.getBoolean(KEY_CACHED_LAST_CHARGE_FAILED, true)
|
||||
}
|
||||
));
|
||||
List<NameValuePair> nameValuePairs = new LinkedList<>();
|
||||
nameValuePairs.add(new BasicNameValuePair(OwsRegistration.PARAM_ACCOUNT_PASSWORD, newPassword));
|
||||
|
||||
String accountControllerHref = OwsRegistration.getHrefWithParameters(
|
||||
OwsRegistration.getHrefForAccount(account.getUserId()),
|
||||
nameValuePairs
|
||||
);
|
||||
|
||||
HttpPut httpPut = new HttpPut(accountControllerHref);
|
||||
authorizeRequest(httpPut, account);
|
||||
|
||||
HttpResponse response = httpClient.execute(httpPut);
|
||||
throwExceptionIfNotOK(response);
|
||||
}
|
||||
|
||||
public Optional<FlockCardInformation> getCard(DavAccount account)
|
||||
throws RegistrationApiException, IOException
|
||||
{
|
||||
String HREF = OwsRegistration.getHrefForCard(account.getUserId());
|
||||
HttpGet httpGet = new HttpGet(HREF);
|
||||
DefaultHttpClient httpClient = getClient(context);
|
||||
String cardControllerHref = OwsRegistration.getHrefForCard(account.getUserId());
|
||||
HttpGet httpGet = new HttpGet(cardControllerHref);
|
||||
authorizeRequest(httpGet, account);
|
||||
|
||||
HttpResponse response = httpClient.execute(httpGet);
|
||||
@ -286,99 +186,95 @@ public class RegistrationApi {
|
||||
return Optional.absent();
|
||||
}
|
||||
|
||||
return Optional.of(buildFlockCardInformation(response));
|
||||
return Optional.of(ModelFactory.buildCard(response));
|
||||
}
|
||||
|
||||
public void setAccountVersion(DavAccount account, Integer version)
|
||||
public void setStripeCard(DavAccount account, String stripeCardToken)
|
||||
throws RegistrationApiException, IOException
|
||||
{
|
||||
Log.d(TAG, "setAccountVersion()");
|
||||
|
||||
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(1);
|
||||
nameValuePairs.add(new BasicNameValuePair(OwsRegistration.PARAM_ACCOUNT_VERSION, version.toString()));
|
||||
|
||||
String HREF = OwsRegistration.getHrefWithParameters(OwsRegistration.getHrefForAccount(account.getUserId()), nameValuePairs);
|
||||
HttpPut httpPut = new HttpPut(HREF);
|
||||
DefaultHttpClient httpClient = getClient(context);
|
||||
authorizeRequest(httpPut, account);
|
||||
|
||||
HttpResponse response = httpClient.execute(httpPut);
|
||||
throwExceptionIfNotOK(response);
|
||||
}
|
||||
|
||||
public void setAccountPassword(DavAccount account, String newPassword)
|
||||
throws RegistrationApiException, IOException
|
||||
{
|
||||
Log.d(TAG, "setAccountPassword()");
|
||||
|
||||
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(1);
|
||||
nameValuePairs.add(new BasicNameValuePair(OwsRegistration.PARAM_ACCOUNT_PASSWORD, newPassword));
|
||||
|
||||
String HREF = OwsRegistration.getHrefWithParameters(OwsRegistration.getHrefForAccount(account.getUserId()), nameValuePairs);
|
||||
HttpPut httpPut = new HttpPut(HREF);
|
||||
DefaultHttpClient httpClient = getClient(context);
|
||||
authorizeRequest(httpPut, account);
|
||||
|
||||
HttpResponse response = httpClient.execute(httpPut);
|
||||
throwExceptionIfNotOK(response);
|
||||
}
|
||||
|
||||
public void updateAccountStripeCard(DavAccount account, String stripeCardToken)
|
||||
throws CardException, RegistrationApiException, IOException
|
||||
{
|
||||
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(1);
|
||||
List<NameValuePair> nameValuePairs = new LinkedList<>();
|
||||
nameValuePairs.add(new BasicNameValuePair(OwsRegistration.PARAM_STRIPE_CARD_TOKEN, stripeCardToken));
|
||||
|
||||
String HREF = OwsRegistration.getHrefWithParameters(OwsRegistration.getHrefForAccount(account.getUserId()), nameValuePairs);
|
||||
HttpPut httpPut = new HttpPut(HREF);
|
||||
DefaultHttpClient httpClient = getClient(context);
|
||||
authorizeRequest(httpPut, account);
|
||||
String cardControllerHref = OwsRegistration.getHrefWithParameters(
|
||||
OwsRegistration.getHrefForCard(account.getUserId()),
|
||||
nameValuePairs
|
||||
);
|
||||
|
||||
HttpResponse response = httpClient.execute(httpPut);
|
||||
|
||||
try {
|
||||
|
||||
throwExceptionIfNotOK(response);
|
||||
|
||||
} catch (RegistrationApiClientException e) {
|
||||
if (e.getStatus() == OwsRegistration.STATUS_PAYMENT_REQUIRED)
|
||||
throw new CardException("server rejected card", "hack", "hack", null);
|
||||
else
|
||||
throw e;
|
||||
}
|
||||
|
||||
Optional<Pair<Long, Boolean[]>> subscriptionDetails = getCachedSubscriptionDetails(context);
|
||||
if (!subscriptionDetails.isPresent())
|
||||
return;
|
||||
|
||||
cacheSubscriptionDetails(context,
|
||||
subscriptionDetails.get().first,
|
||||
subscriptionDetails.get().second[0],
|
||||
false);
|
||||
}
|
||||
|
||||
public void setAccountAutoRenew(DavAccount account, Boolean autoRenewEnabled)
|
||||
throws RegistrationApiException, IOException
|
||||
{
|
||||
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(1);
|
||||
nameValuePairs.add(new BasicNameValuePair(OwsRegistration.PARAM_AUTO_RENEW, autoRenewEnabled.toString()));
|
||||
|
||||
String HREF = OwsRegistration.getHrefWithParameters(OwsRegistration.getHrefForAccount(account.getUserId()), nameValuePairs);
|
||||
HttpPut httpPut = new HttpPut(HREF);
|
||||
DefaultHttpClient httpClient = getClient(context);
|
||||
HttpPut httpPut = new HttpPut(cardControllerHref);
|
||||
authorizeRequest(httpPut, account);
|
||||
|
||||
HttpResponse response = httpClient.execute(httpPut);
|
||||
throwExceptionIfNotOK(response);
|
||||
}
|
||||
|
||||
Optional<Pair<Long, Boolean[]>> subscriptionDetails = getCachedSubscriptionDetails(context);
|
||||
if (!subscriptionDetails.isPresent())
|
||||
return;
|
||||
public boolean getIsPlanAutoRenewing(DavAccount account)
|
||||
throws RegistrationApiException, IOException
|
||||
{
|
||||
String planControllerHref = OwsRegistration.getHrefForPlan(account.getUserId());
|
||||
HttpGet httpGet = new HttpGet(planControllerHref);
|
||||
authorizeRequest(httpGet, account);
|
||||
|
||||
cacheSubscriptionDetails(context,
|
||||
subscriptionDetails.get().first,
|
||||
autoRenewEnabled,
|
||||
subscriptionDetails.get().second[1]);
|
||||
HttpResponse response = httpClient.execute(httpGet);
|
||||
throwExceptionIfNotOK(response);
|
||||
|
||||
return ModelFactory.buildBoolean(response);
|
||||
}
|
||||
|
||||
public void putNewGooglePlan(DavAccount account, String planId, String purchaseToken)
|
||||
throws RegistrationApiException, IOException
|
||||
{
|
||||
Log.d(TAG, "putNewGooglePlan()");
|
||||
List<NameValuePair> nameValuePairs = new LinkedList<>();
|
||||
nameValuePairs.add(new BasicNameValuePair(OwsRegistration.PARAM_PLAN_TYPE, Integer.toString(SubscriptionPlan.PLAN_TYPE_GOOGLE)));
|
||||
nameValuePairs.add(new BasicNameValuePair(OwsRegistration.PARAM_PLAN_ID, planId));
|
||||
nameValuePairs.add(new BasicNameValuePair(OwsRegistration.PARAM_PURCHASE_TOKEN, purchaseToken));
|
||||
|
||||
String planControllerHref = OwsRegistration.getHrefWithParameters(
|
||||
OwsRegistration.getHrefForPlan(account.getUserId()),
|
||||
nameValuePairs
|
||||
);
|
||||
|
||||
HttpPut httpPut = new HttpPut(planControllerHref);
|
||||
authorizeRequest(httpPut, account);
|
||||
|
||||
HttpResponse response = httpClient.execute(httpPut);
|
||||
throwExceptionIfNotOK(response);
|
||||
}
|
||||
|
||||
public void migrateBillingToStripeSubscriptionModel(DavAccount account)
|
||||
throws RegistrationApiException, IOException
|
||||
{
|
||||
Log.d(TAG, "migrateBillingToStripeSubscriptionModel()");
|
||||
|
||||
List<NameValuePair> nameValuePairs = new LinkedList<>();
|
||||
nameValuePairs.add(new BasicNameValuePair(
|
||||
OwsRegistration.PARAM_PLAN_TYPE,
|
||||
Integer.toString(SubscriptionPlan.PLAN_TYPE_STRIPE)
|
||||
));
|
||||
|
||||
String planControllerHref = OwsRegistration.getHrefWithParameters(
|
||||
OwsRegistration.getHrefForPlan(account.getUserId()),
|
||||
nameValuePairs
|
||||
);
|
||||
|
||||
HttpPut httpPut = new HttpPut(planControllerHref);
|
||||
authorizeRequest(httpPut, account);
|
||||
|
||||
HttpResponse response = httpClient.execute(httpPut);
|
||||
throwExceptionIfNotOK(response);
|
||||
}
|
||||
|
||||
public void cancelSubscription(DavAccount account)
|
||||
throws RegistrationApiException, IOException
|
||||
{
|
||||
Log.d(TAG, "cancelSubscription()");
|
||||
|
||||
String planController = OwsRegistration.getHrefForPlan(account.getUserId());
|
||||
HttpDelete httpDelete = new HttpDelete(planController);
|
||||
authorizeRequest(httpDelete, account);
|
||||
|
||||
HttpResponse response = httpClient.execute(httpDelete);
|
||||
throwExceptionIfNotOK(response);
|
||||
}
|
||||
|
||||
public void deleteAccount(DavAccount account)
|
||||
@ -386,13 +282,12 @@ public class RegistrationApi {
|
||||
{
|
||||
Log.d(TAG, "deleteAccount()");
|
||||
|
||||
HttpDelete httpDelete = new HttpDelete(OwsRegistration.getHrefForAccount(account.getUserId()));
|
||||
DefaultHttpClient httpClient = getClient(context);
|
||||
String accountControllerHref = OwsRegistration.getHrefForAccount(account.getUserId());
|
||||
HttpDelete httpDelete = new HttpDelete(accountControllerHref);
|
||||
authorizeRequest(httpDelete, account);
|
||||
|
||||
HttpResponse response = httpClient.execute(httpDelete);
|
||||
throwExceptionIfNotOK(response);
|
||||
|
||||
cacheSubscriptionDetails(context, 0L, false, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* *
|
||||
* Copyright (C) 2014 Open Whisper Systems
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* /
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock.registration;
|
||||
|
||||
/**
|
||||
* Programmer: rhodey
|
||||
*/
|
||||
public class RegistrationApiClientException extends RegistrationApiException {
|
||||
|
||||
public RegistrationApiClientException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public RegistrationApiClientException(String message, int status) {
|
||||
super(message, status);
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user