Compare commits

..

44 Commits

Author SHA1 Message Date
Rhodey Orbits
fba6dd7915 update readme 2015-10-20 13:42:46 +02:00
Rhodey Orbits
c28a51a266 bumping to 29 / 0.9.7 2015-07-15 12:14:27 -04:00
Rhodey Orbits
751f221fd3 Flock end-of-life
Flock will shutdown permanently on October 1st, 2015. Upon shutdown, all
active subscriptions will be refunded and account information erased. We
apologize for the inconvenience and thank you for supporting the project.
Contacts and calendars can be exported to standard .vcf and .ics files
through the new 'Export data' feature.

1) disable registration
2) disable subscription management
3) remind daily that EOL is approaching
4) allow export of contacts and calendars from EOL message
2015-07-13 12:55:37 -07:00
Rhodey Orbits
33fe753b91 bumping to 28 / 0.9.6 2015-02-27 19:02:24 -08:00
rhodey
aa4d305d2e Merge pull request #89 from haaja/typo_fix
rhodey forgot "choose" vs "chose" again, fixed.
2015-02-27 18:52:25 -08:00
Rhodey Orbits
f2b6e258ac updated german and japanese translations, added finish, russian, and swedish. FREEBIE 2015-02-27 17:21:38 -08:00
Rhodey Orbits
bbff2a5a34 created ContentProviderOperationQueue to help with max size limits when dealing with ContentProviders. Added full set of tests for ContactFactory and fixed a bug in handling of postal address. Added some tests for EventFactory and fixed a bug related to handling of event reminders where the event title and description were both null. Fixed bug in rendering of the intro on tablets. 2015-02-27 16:35:35 -08:00
Janne Haapsaari
ac1dfce553 strings: fix couple of typos 2015-02-25 16:58:17 +01:00
Rhodey Orbits
9c2fa9e35e added download link for the Flock nonplay flavored apk. FREEBIE 2015-02-23 15:24:11 -08:00
Rhodey Orbits
3f00796888 bumping to 27 / 0.9.5, FREEBIE 2015-02-22 21:02:49 -08:00
Rhodey Orbits
10510f8466 added account deletion countdown to subscription expired notification, improved account registration text, and dropped requirement that the user know their current password to change encryption passwords because the device is considered trusted. 2015-02-22 20:42:01 -08:00
Rhodey Orbits
a556144c2b migration is no longer needed, removed all traces and added a missing test to KeyHelperTest. FREEBIE 2015-02-20 12:24:00 -08:00
Rhodey Orbits
e3055aa853 patched bug related to flock client confusing account expiration dates by adding many tests and removing code duplication. FREEBIE 2015-02-18 16:28:47 -08:00
Rhodey Orbits
10b5d65a47 bumping to version 26 / 0.9.4 FREEBIE 2015-02-16 23:53:52 -08:00
Rhodey Orbits
612f4f3a87 merged updated translations 2015-02-16 17:16:17 -08:00
Rhodey Orbits
b25ab2e878 reorganized gradle build files and project structure. bumped api to 21. bumped support v4 to 21.0.3. pulled needed guava classes into util/guava package and dropped guava dependency. replaced javax.servlet dependency with constants. added sanity checks when working with google play services. created a nonplay flavor and integrated SupplyChain to provide updates and crash reporting. pointed nonplay flavor to modified privacy policy. 2015-02-16 16:35:09 -08:00
Rhodey Orbits
32c06bae83 refactored the org.anhonesteffort.flock.registration and crypto packages for improved tests, added new tests, and deleted old integration tests that should be replaced with unit tests and mocks. fixed binding problem in account sync stuff, updated libpastelog, and bumping to new version. 2015-01-25 23:11:38 -10:00
Rhodey Orbits
ee39f92658 bumping to version 0.9.2 for korean translations on the google pkay store FREEBIE 2015-01-16 13:25:41 -10:00
Rhodey Orbits
a6cb2050c7 merge branch 'issue-58' FREEBIE 2015-01-16 13:14:45 -10:00
Rhodey Orbits
25037e1e8e created SyncWorker interface and switched KeySyncWorker, AddressbookSyncWorker, and CalendarSyncWorker over to this. renamed AbstractDavSyncAdapter to AbstractSyncAdapter for future AccountSyncAdapter. moved hackish account and subscription caching from RegistrationApi into a new sync adapter. reworked ManageSubscriptionActivity into a FragmentActivity with separate fragments for different forms of payment. Implemented new registration server API and integrated google in-app subscriptions.
bumping to version 0.9.1 and pushing to alpha channel
2015-01-16 10:48:21 -10:00
Rhodey Orbits
e0fc90d37d added korean translations (korea) provided by Josh Graham, thanks :) FREEBIE 2014-11-10 16:18:24 -08:00
Rhodey Orbits
fc27816190 resolves issue 73 by accepting any type of redirect when identifying the current user principal through well-known URI request. FREEBIE 2014-11-10 15:02:20 -08:00
rhodey
83ed52ed6e Merge pull request #78 from JoshGraham/patch-1
corrected spelling mistake in strings.xml 'chose' >> 'choose'
2014-11-10 13:18:02 -08:00
JoshGraham
96047afe6a Update strings.xml
Typo?
2014-11-08 01:10:55 -08:00
rhodey
38a2d2f6cd Merge pull request #74 from McLoo/patch-2
Add Transifex chart to README.md
2014-11-05 14:32:39 -08:00
McLoo
444258d9ef Add Transifex chart to README.md
//FREEBIE
2014-10-27 16:04:01 +01:00
Rhodey Orbits
7b47df7f87 bumping to version 0.9.0 FREEBIE 2014-10-23 21:45:28 -07:00
Rhodey Orbits
8d96adcbba better log-catn' for ServerTestsFragment. FREEBIE 2014-10-23 21:39:31 -07:00
Rhodey Orbits
988a45006c added the 'Send debug log' options menu item to SetupActivity. FREEBIE 2014-10-23 20:12:12 -07:00
Rhodey Orbits
3f904b8f7a bumping to version 0.8.9 FREEBIE 2014-10-17 18:30:18 -07:00
Rhodey Orbits
52a282ea27 improved battery and network performance by syncing less often and more selectively. 2014-10-17 17:53:55 -07:00
Rhodey Orbits
51e629d243 moved sync adapters from separate process into the main Flock process, switched all SharedPreferences code to use the MODE_PRIVATE flag instead of MODE_MULTI_PROCESS, and now using SharedPreferences.apply() instead of SharedPreferences.commit() 2014-10-15 19:34:16 -07:00
Rhodey Orbits
726ed177ed added some sanity checks to sync operations such as making sure that SyncResult.stats are initialized to zero and duplicates are not returned by Content Provider queries. touchup to ContactFactory Base64 error handling. FREEBIE 2014-10-15 15:26:26 -07:00
Rhodey Orbits
28ae8b0e48 Merge branch 'contact-photo-compression' FREEBIE??? 2014-10-14 21:40:44 -07:00
Rhodey Orbits
958a76e4f6 Flock now prevents the Android Contact Provider from degrading contact photo quality with unnecessary compression. 2014-10-14 15:55:09 -07:00
rhodey
83012b0163 Merge pull request #67 from techtonik/patch-1
BUILDING.md: Fix repository link and proofread mistakes
2014-10-13 12:38:09 -07:00
Rhodey Orbits
1c02fe4bd2 removed all newline characters from strings.xml files for a more friendly Transifex experience. added missing copyright. FREEBIE 2014-10-13 12:17:52 -07:00
Wenzel Pünter
1af27bddcd Updated German translations pulled from Transifex, provided by scento. 2014-10-13 11:45:07 -07:00
Rhodey Orbits
9abf57d1ff bumping to version 0.8.8 FREEBIE 2014-10-12 10:22:40 -07:00
Rhodey Orbits
30d840e027 temporary patch to prevent issue #70 FREEBIE 2014-10-12 10:21:16 -07:00
Rhodey Orbits
c229bd95dd resolves #69 where aggregation exceptions (separated & joined contacts) were ignored by Flock. 2014-10-11 21:31:32 -07:00
Rhodey Orbits
e8ff70ee6a increased speed of contact and calendar import by applying batch operations more efficiently. 2014-10-09 19:09:42 -07:00
Rhodey Orbits
d46783405e better handling of edge case where contacts or events are incorrectly marked as 'new' by Android content providers. FREEBIE 2014-10-08 15:05:56 -07:00
anatoly techtonik
f220ed894e BUILDING.md: Fix repository link and typos 2014-10-04 14:17:37 +03:00
212 changed files with 10035 additions and 6963 deletions

View File

@ -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

View File

@ -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).
*[![Play Store Badge](https://developer.android.com/images/brand/en_app_rgb_wo_60.png)](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/
[![Transifex](https://www.transifex.com/projects/p/flock/resource/master/chart/image_png)](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.

View File

@ -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
View 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

View File

@ -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.

View File

@ -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
}
]
}

View 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"
}
}

View File

@ -0,0 +1,5 @@
{
"account_id" : "ACCOUNT00",
"card_last_four" : "6666",
"card_expiration" : "10/2020"
}

View File

@ -0,0 +1,7 @@
{
"account_id" : "ACCOUNT00",
"payment_id" : "payment00",
"create_date" : 1391853600000,
"days_credit" : 365,
"cost_usd" : 1.337
}

View 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
}

View File

@ -0,0 +1,5 @@
{
"account_id" : "ACCOUNT00",
"plan_type" : 1,
"plan_id" : "stripe00"
}

View File

@ -0,0 +1,5 @@
{
"account_id" : "nope",
"plan_type" : 0,
"plan_id" : "nope"
}

View File

@ -0,0 +1,4 @@
{
"known" : "known",
"unknown" : "unknown"
}

Binary file not shown.

View File

@ -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()
);
}
}

View File

@ -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));
}
}

View File

@ -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));
}
}

View File

@ -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));
}
}

View File

@ -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);
}
}

View File

@ -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)
);
}
}

View File

@ -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()));
}
}

View File

@ -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()));
}
}

View File

@ -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())
);
}
}

View File

@ -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));
}
}

View File

@ -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));
}
}

View File

@ -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));
}
}

View File

@ -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()));
}
}

View File

@ -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());
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -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());
}
}

View File

@ -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();
}
}

View File

@ -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)
);
}
}

View File

@ -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);
}
}
}

View File

@ -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());
}
}

View File

@ -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);
}
}

View File

@ -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";
}

View File

@ -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());
}
}

View File

@ -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());
}
}

View File

@ -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());
}
}

View File

@ -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());
} */
}

View File

@ -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>

View File

@ -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);
}

View File

@ -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;

View File

@ -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();

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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("");
}

View File

@ -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);

View File

@ -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();
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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();

View File

@ -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();
}
}

View File

@ -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();
}
}

View 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());
}
}

View File

@ -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);
}
}

View 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;
}
}

View File

@ -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) {

View File

@ -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();

View File

@ -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();

View File

@ -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;

View File

@ -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() {

View File

@ -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;

View File

@ -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());
}
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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.");
}
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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) {

View File

@ -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));
}

View File

@ -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("");
}
}
}
}
}

View File

@ -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;

View File

@ -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());
}
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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);
}
}
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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;

View File

@ -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 {

View File

@ -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 {

View File

@ -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));
}
}

View File

@ -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;

View File

@ -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");

View File

@ -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());
}
}
}

View File

@ -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.");
}
}
}

View File

@ -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) {

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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