Compare commits
No commits in common. "master" and "v0.8.5" have entirely different histories.
10
.tx/config
10
.tx/config
@ -1,10 +0,0 @@
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
lang_map = fr_CA:fr-rCA,pt_BR:pt-rBR,pt_PT:pt,zh_CN:zh-rCN,zh_HK:zh-rHK,zh_TW:zh-rTW,da_DK:da-rDK,de_DE:de,tr_TR:tr,fr_FR:fr,es_ES:es,hu_HU:hu,sv_SE:sv-rSE,bg_BG:bg,el_GR:el,kn_IN:kn-rIN,cs_CZ:cs
|
||||
|
||||
[flock.master]
|
||||
file_filter = flock/src/main/res/values-<lang>/strings.xml
|
||||
source_file = flock/src/main/res/values/strings.xml
|
||||
source_lang = en
|
||||
type = ANDROID
|
||||
|
||||
@ -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/WhisperSystems/Flock.git)
|
||||
6. Android studio should detect the presence of a project file and ask you whether to open it. Click "yes".
|
||||
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".
|
||||
7. Default config options should be good enough.
|
||||
8. Project initialisation and build should proceed.
|
||||
|
||||
@ -48,4 +48,6 @@ Code contributions should be sent via github as pull requests, from feature bran
|
||||
Mailing list
|
||||
------------
|
||||
|
||||
Development discussion happens on the [whispersystems mailing list](https://lists.riseup.net/www/info/whispersystems). Send emails to whispersystems@lists.riseup.net
|
||||
Development discussion happens on the whispersystems mailing list.
|
||||
[To join](https://lists.riseup.net/www/info/whispersystems)
|
||||
Send emails to whispersystems@lists.riseup.net
|
||||
|
||||
67
README.md
67
README.md
@ -1,4 +1,5 @@
|
||||
# Flock (Discontinued)
|
||||
Flock
|
||||
=================
|
||||
|
||||
A secure contact and calendar syncing application for Android.
|
||||
|
||||
@ -11,71 +12,39 @@ 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)
|
||||
|
||||
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).
|
||||
Building and 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.
|
||||
|
||||
*[](https://play.google.com/store/apps/details?id=org.anhonesteffort.flock)*
|
||||
Bug tracker
|
||||
-----------
|
||||
|
||||
## Contributing bug reports
|
||||
Have a bug? Please create an issue here on GitHub!
|
||||
|
||||
https://github.com/WhisperSystems/Flock/issues
|
||||
|
||||
## Contributing translations
|
||||
Interested in helping to translate Flock? Contribute here:
|
||||
Mailing list
|
||||
------------
|
||||
|
||||
https://www.transifex.com/projects/p/flock/
|
||||
|
||||
[](https://www.transifex.com/projects/p/flock/)
|
||||
|
||||
## Contributing code
|
||||
Instructions on how to build Flock, as well as on how to setup an IDE to modify it can be found in the [BUILDING.md](BUILDING.md) file.
|
||||
|
||||
If you're new to the Flock codebase, we recommend going through our issues and picking out a simple bug to fix in order to get yourself familiar.
|
||||
|
||||
For larger changes and feature ideas, we ask that you propose it on the mailing list for a high-level discussion before implementation.
|
||||
|
||||
This repository is set up with [BitHub](https://whispersystems.org/blog/bithub/), so you can make money for committing to Flock. The current BitHub price for an accepted pull request is:
|
||||
|
||||
[](https://whispersystems.org/blog/bithub/)
|
||||
|
||||
## Contributing Ideas
|
||||
Have something you want to say about Open Whisper Systems projects or want to be part of the conversation? Get involved in the mailing list!
|
||||
Have a question? Ask on our mailing list!
|
||||
|
||||
whispersystems@lists.riseup.net
|
||||
|
||||
https://lists.riseup.net/www/info/whispersystems
|
||||
|
||||
## Contributing Funds
|
||||
[](https://whispersystems.org/blog/bithub/)
|
||||
Cryptography Notice
|
||||
------------
|
||||
|
||||
You can add funds to BitHub to directly help further development efforts.
|
||||
|
||||
Help
|
||||
====
|
||||
## Support
|
||||
For troubleshooting and questions, please visit our support center!
|
||||
|
||||
http://support.whispersystems.org/
|
||||
|
||||
## Documentation
|
||||
Looking for documentation? Check out the wiki!
|
||||
|
||||
https://github.com/WhisperSystems/Flock/wiki
|
||||
|
||||
# Legal things
|
||||
## Cryptography Notice
|
||||
|
||||
This distribution includes cryptographic software. The country in which you currently reside may have restrictions on the import, possession, use, and/or re-export to another country, of encryption software.
|
||||
BEFORE using any encryption software, please check your country's laws, regulations and policies concerning the import, possession, or use, and re-export of encryption software, to see if this is permitted.
|
||||
This distribution includes cryptographic software. The country in which you currently reside may have restrictions on the import, possession, use, and/or re-export to another country, of encryption software.
|
||||
BEFORE using any encryption software, please check your country's laws, regulations and policies concerning the import, possession, or use, and re-export of encryption software, to see if this is permitted.
|
||||
See <http://www.wassenaar.org/> for more information.
|
||||
|
||||
The U.S. Government Department of Commerce, Bureau of Industry and Security (BIS), has classified this software as Export Commodity Control Number (ECCN) 5D002.C.1, which includes information security software using or performing cryptographic functions with asymmetric algorithms.
|
||||
The U.S. Government Department of Commerce, Bureau of Industry and Security (BIS), has classified this software as Export Commodity Control Number (ECCN) 5D002.C.1, which includes information security software using or performing cryptographic functions with asymmetric algorithms.
|
||||
The form and manner of this distribution makes it eligible for export under the License Exception ENC Technology Software Unrestricted (TSU) exception (see the BIS Export Administration Regulations, Section 740.13) for both object code and source code.
|
||||
|
||||
## License
|
||||
License
|
||||
---------------------
|
||||
|
||||
Copyright 2014 Open Whisper Systems
|
||||
Copyright 2014 Open WhisperSystems
|
||||
|
||||
Licensed under the GPLv3: http://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
19
build.gradle
19
build.gradle
@ -1,18 +1 @@
|
||||
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/' }
|
||||
}
|
||||
}
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
20
eol.txt
20
eol.txt
@ -1,20 +0,0 @@
|
||||
// flock end of life
|
||||
|
||||
1) client update
|
||||
** disable new account registration
|
||||
** disable subscription management
|
||||
** remind daily that eol is approaching
|
||||
** allow export of contacts & calendars to external storage
|
||||
|
||||
2) publish client update and warn in play store details
|
||||
3) ** remove statsd code, deploy new servers, terminate statsd
|
||||
4) update translations while waiting for eol
|
||||
|
||||
5) eol
|
||||
remove apk from play store
|
||||
terminate all flock amazon resources
|
||||
refund active subscriptions
|
||||
cancel active subscriptions
|
||||
close stripe account
|
||||
delete flock api project
|
||||
delete flock payments
|
||||
@ -1,22 +1,34 @@
|
||||
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'
|
||||
|
||||
android {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
||||
compileSdkVersion 21
|
||||
buildToolsVersion '21.1.2'
|
||||
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'
|
||||
|
||||
defaultConfig {
|
||||
applicationId "org.anhonesteffort.flock"
|
||||
versionCode 29
|
||||
versionName "0.9.7"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 21
|
||||
}
|
||||
|
||||
productFlavors {
|
||||
play { }
|
||||
nonplay { }
|
||||
targetSdkVersion 19
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
@ -34,47 +46,35 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile group: 'com.android.support', name: 'support-v4', version: '21.0.3'
|
||||
compile group: 'com.google.guava', name: 'guava', version: '16.0'
|
||||
compile group: 'com.android.support', name: 'support-v4', version: '19.0.1'
|
||||
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.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.android.support:support-v4:703572d3015a088cc5604b7e38885af3d307c829d0c5ceaf8654ff41c71cd160',
|
||||
'com.google.guava:guava:fa917f4f3f6a76375134ba89a40d3a1ce807945a91bbdbe39c31f76e03030868',
|
||||
'com.android.support:support-v4:a4268abd6370c3fd3f94d2a7f9e6e755f5ddd62450cf8bbc62ba789e1274d585',
|
||||
'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',
|
||||
@ -83,7 +83,8 @@ dependencyVerification {
|
||||
'commons-lang:commons-lang:50f11b09f877c294d56f24463f47d28f929cf5044f648661c0f0cfbae9a2f49c',
|
||||
'backport-util-concurrent:backport-util-concurrent:f5759b7fcdfc83a525a036deedcbd32e5b536b625ebc282426f16ca137eb5902',
|
||||
'com.fasterxml.jackson.core:jackson-annotations:0c8c3811322cc84c09a93f34436fe784a1259dd5376a90aec5a73493456f757d',
|
||||
'com.fasterxml.jackson.core:jackson-core:61f84f93e3f901134d7498b50119ee01074f10d59560e45ccd3e1d48cfec493b'
|
||||
'org.slf4j:slf4j-api:fe30825245d2336c859dc38d60c0fc5f3668dbf29cd586828d2b5667ec355b91',
|
||||
'com.fasterxml.jackson.core:jackson-core:61f84f93e3f901134d7498b50119ee01074f10d59560e45ccd3e1d48cfec493b',
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
BIN
flock/libs/gradle-witness.jar
Normal file
BIN
flock/libs/gradle-witness.jar
Normal file
Binary file not shown.
@ -1,24 +0,0 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
{
|
||||
"account_id" : "ACCOUNT00",
|
||||
"card_last_four" : "6666",
|
||||
"card_expiration" : "10/2020"
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
{
|
||||
"account_id" : "ACCOUNT00",
|
||||
"payment_id" : "payment00",
|
||||
"create_date" : 1391853600000,
|
||||
"days_credit" : 365,
|
||||
"cost_usd" : 1.337
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
{
|
||||
"account_id" : "ACCOUNT00",
|
||||
"plan_type" : 2,
|
||||
"plan_id" : "google00",
|
||||
"package_name" : "org.anhonesteffort.flock",
|
||||
"purchase_token" : "purchase00",
|
||||
"expiration_date" : 1391853600000
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
{
|
||||
"account_id" : "ACCOUNT00",
|
||||
"plan_type" : 1,
|
||||
"plan_id" : "stripe00"
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
{
|
||||
"account_id" : "nope",
|
||||
"plan_type" : 0,
|
||||
"plan_id" : "nope"
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
{
|
||||
"known" : "known",
|
||||
"unknown" : "unknown"
|
||||
}
|
||||
Binary file not shown.
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* 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()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,45 +0,0 @@
|
||||
/*
|
||||
* 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));
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock.test.crypto;
|
||||
|
||||
import org.anhonesteffort.flock.crypto.HidingUtil;
|
||||
import org.anhonesteffort.flock.crypto.MasterCipher;
|
||||
import org.anhonesteffort.flock.test.InstrumentationTestCaseWithMocks;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* rhodey
|
||||
*/
|
||||
public class HidingUtilTest extends InstrumentationTestCaseWithMocks {
|
||||
|
||||
private final byte[] PLAINTEXT_STUFF = "plaintext stuff".getBytes();
|
||||
|
||||
private MasterCipher masterCipher;
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
masterCipher = mock(MasterCipher.class);
|
||||
|
||||
when(masterCipher.encryptAndEncode(any(byte[].class))).thenReturn(PLAINTEXT_STUFF);
|
||||
when(masterCipher.decodeAndDecrypt(any(byte[].class))).thenReturn(PLAINTEXT_STUFF);
|
||||
}
|
||||
|
||||
public void testEncryptAndDecrypt() throws Exception {
|
||||
final byte[] ciphertext = HidingUtil.encryptEncodeAndPrefix(masterCipher, PLAINTEXT_STUFF);
|
||||
final byte[] plaintext = HidingUtil.decodeAndDecryptIfNecessary(masterCipher, ciphertext);
|
||||
|
||||
assertTrue(Arrays.equals(PLAINTEXT_STUFF, plaintext));
|
||||
}
|
||||
|
||||
public void testDecryptNonEncrypted() throws Exception {
|
||||
final byte[] plaintext = HidingUtil.decodeAndDecryptIfNecessary(masterCipher, PLAINTEXT_STUFF);
|
||||
|
||||
assertTrue(Arrays.equals(PLAINTEXT_STUFF, plaintext));
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,26 +1,10 @@
|
||||
/*
|
||||
* 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 org.anhonesteffort.flock.util.guava.Optional;
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import org.anhonesteffort.flock.crypto.KeyHelper;
|
||||
import org.anhonesteffort.flock.crypto.KeyStore;
|
||||
@ -43,6 +27,8 @@ 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
|
||||
@ -50,6 +36,32 @@ 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
|
||||
{
|
||||
@ -69,9 +81,9 @@ public class KeyHelperTest extends AndroidTestCase {
|
||||
public void testKeyHelperGenerateKeyMaterial() throws Exception {
|
||||
KeyHelper.generateAndSaveSaltAndKeyMaterial(context);
|
||||
|
||||
Optional<byte[]> resultCipherKeyBytes = KeyStore.getCipherKey(context);
|
||||
Optional<byte[]> resultMacKeyBytes = KeyStore.getMacKey(context);
|
||||
Optional<byte[]> resultSaltBytes = KeyStore.getKeyMaterialSalt(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");
|
||||
|
||||
assertTrue("KeyHelper can generate key material.",
|
||||
resultCipherKeyBytes.get().length > 0 &&
|
||||
@ -90,8 +102,8 @@ public class KeyHelperTest extends AndroidTestCase {
|
||||
final SecretKey testCipherKey = new SecretKeySpec(cipherKeyBytes, "AES");
|
||||
final SecretKey testMacKey = new SecretKeySpec(macKeyBytes, "SHA256");
|
||||
|
||||
KeyStore.saveCipherKey(context, cipherKeyBytes);
|
||||
KeyStore.saveMacKey(context, macKeyBytes);
|
||||
saveBytes(context, "KEY_CIPHER_KEY", cipherKeyBytes);
|
||||
saveBytes(context, "KEY_MAC_KEY", macKeyBytes);
|
||||
|
||||
byte[] encodedKeyHelperResult = KeyHelper.getMasterCipher(context).get().encryptAndEncode(plaintext);
|
||||
byte[] keyHelperResult = Base64.decode(encodedKeyHelperResult);
|
||||
@ -110,10 +122,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};
|
||||
|
||||
KeyStore.saveCipherKey(context, cipherKeyBytes);
|
||||
KeyStore.saveMacKey(context, macKeyBytes);
|
||||
KeyStore.saveKeyMaterialSalt(context, saltBytes);
|
||||
KeyStore.saveMasterPassphrase(context, masterPassphrase);
|
||||
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);
|
||||
|
||||
Optional<String> encodedSalt = KeyHelper.buildEncodedSalt(context);
|
||||
Optional<String> encryptedKeyMaterial = KeyHelper.buildEncryptedKeyMaterial(context);
|
||||
@ -123,37 +135,17 @@ public class KeyHelperTest extends AndroidTestCase {
|
||||
};
|
||||
|
||||
KeyStore.invalidateKeyMaterial(context);
|
||||
KeyStore.saveMasterPassphrase(context, masterPassphrase);
|
||||
saveString(context, "KEY_OLD_MASTER_PASSPHRASE", masterPassphrase);
|
||||
|
||||
KeyHelper.importSaltAndEncryptedKeyMaterial(context, saltAndEncryptedKeyMaterial);
|
||||
|
||||
Optional<byte[]> resultCipherKeyBytes = KeyStore.getCipherKey(context);
|
||||
Optional<byte[]> resultMacKeyBytes = KeyStore.getMacKey(context);
|
||||
Optional<byte[]> resultSaltBytes = KeyStore.getKeyMaterialSalt(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");
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,48 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,105 +0,0 @@
|
||||
/*
|
||||
* 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)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,98 +0,0 @@
|
||||
/*
|
||||
* 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()));
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,99 +0,0 @@
|
||||
/*
|
||||
* 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()));
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,68 +0,0 @@
|
||||
/*
|
||||
* 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())
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,63 +0,0 @@
|
||||
/*
|
||||
* 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));
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,118 +0,0 @@
|
||||
/*
|
||||
* 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));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock.test.sync.account;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import org.anhonesteffort.flock.registration.model.AugmentedFlockAccount;
|
||||
import org.anhonesteffort.flock.registration.model.FlockCardInformation;
|
||||
import org.anhonesteffort.flock.registration.model.SubscriptionPlan;
|
||||
import org.anhonesteffort.flock.sync.account.AccountStore;
|
||||
import org.anhonesteffort.flock.test.registration.model.AugmentedFlockAccountTest;
|
||||
import org.anhonesteffort.flock.test.registration.model.FlockCardInformationTest;
|
||||
import org.anhonesteffort.flock.test.registration.model.SubscriptionPlanTest;
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
|
||||
/**
|
||||
* rhodey
|
||||
*/
|
||||
public class AccountStoreTest extends AndroidTestCase {
|
||||
|
||||
public void testGetSetLastStripeChargeFailed() throws Exception {
|
||||
AccountStore.setLastChargeFailed(getContext(), true);
|
||||
assertTrue(AccountStore.getLastChargeFailed(getContext()));
|
||||
|
||||
AccountStore.setLastChargeFailed(getContext(), false);
|
||||
assertTrue(!AccountStore.getLastChargeFailed(getContext()));
|
||||
}
|
||||
|
||||
public void testGetSetAutoRenew() throws Exception {
|
||||
AccountStore.setAutoRenew(getContext(), true);
|
||||
assertTrue(AccountStore.getAutoRenew(getContext()));
|
||||
|
||||
AccountStore.setAutoRenew(getContext(), false);
|
||||
assertTrue(!AccountStore.getAutoRenew(getContext()));
|
||||
}
|
||||
|
||||
public void testSetGetDaysRemainingPositive() throws Exception {
|
||||
final Long daysRemaining = 1337L;
|
||||
|
||||
AccountStore.setDaysRemaining(getContext(), daysRemaining);
|
||||
assertTrue(AccountStore.getDaysRemaining(getContext()).get().equals(daysRemaining));
|
||||
}
|
||||
|
||||
public void testSetGetDaysRemainingNegative() throws Exception {
|
||||
final Long daysRemaining = -1337L;
|
||||
|
||||
AccountStore.setDaysRemaining(getContext(), daysRemaining);
|
||||
assertTrue(AccountStore.getDaysRemaining(getContext()).get().equals(daysRemaining));
|
||||
}
|
||||
|
||||
public void testSetGetSubscriptionPlanNone() throws Exception {
|
||||
final SubscriptionPlan planNone = SubscriptionPlanTest.nonePlan();
|
||||
|
||||
AccountStore.setSubscriptionPlan(getContext(), planNone);
|
||||
assertTrue(AccountStore.getSubscriptionPlanType(getContext()).equals(planNone.getPlanType()));
|
||||
assertTrue(AccountStore.getSubscriptionPlan(getContext()).equals(planNone));
|
||||
}
|
||||
|
||||
public void testSetGetSubscriptionPlanStripe() throws Exception {
|
||||
final SubscriptionPlan stripePlan = SubscriptionPlanTest.stripePlan();
|
||||
|
||||
AccountStore.setSubscriptionPlan(getContext(), stripePlan);
|
||||
assertTrue(AccountStore.getSubscriptionPlanType(getContext()).equals(stripePlan.getPlanType()));
|
||||
assertTrue(AccountStore.getSubscriptionPlan(getContext()).equals(stripePlan));
|
||||
}
|
||||
|
||||
public void testSetGetSubscriptionPlanGoogle() throws Exception {
|
||||
final SubscriptionPlan googlePlan = SubscriptionPlanTest.googlePlan();
|
||||
|
||||
AccountStore.setSubscriptionPlan(getContext(), googlePlan);
|
||||
assertTrue(AccountStore.getSubscriptionPlanType(getContext()).equals(googlePlan.getPlanType()));
|
||||
assertTrue(AccountStore.getSubscriptionPlan(getContext()).equals(googlePlan));
|
||||
}
|
||||
|
||||
public void testSetGetCardInformationPresent() throws Exception {
|
||||
final FlockCardInformation card = FlockCardInformationTest.card();
|
||||
|
||||
AccountStore.setCardInformation(getContext(), Optional.of(card));
|
||||
assertTrue(AccountStore.getCardInformation(getContext()).get().equals(card));
|
||||
}
|
||||
|
||||
public void testSetGetCardInformationAbsent() throws Exception {
|
||||
AccountStore.setCardInformation(getContext(), Optional.<FlockCardInformation>absent());
|
||||
assertTrue(!AccountStore.getCardInformation(getContext()).isPresent());
|
||||
}
|
||||
|
||||
public void testUpdateStore() throws Exception {
|
||||
final AugmentedFlockAccount account = AugmentedFlockAccountTest.accountNoPlan();
|
||||
|
||||
AccountStore.updateStore(getContext(), account);
|
||||
|
||||
assertTrue(AccountStore.getLastChargeFailed(getContext()) == account.getLastStripeChargeFailed());
|
||||
assertTrue(AccountStore.getAutoRenew(getContext()) == account.getAutoRenewEnabled());
|
||||
assertTrue(AccountStore.getDaysRemaining(getContext()).get().equals(account.getDaysRemaining()));
|
||||
assertTrue(AccountStore.getSubscriptionPlanType(getContext()).equals(account.getSubscriptionPlan().getPlanType()));
|
||||
assertTrue(AccountStore.getSubscriptionPlan(getContext()).equals(account.getSubscriptionPlan()));
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,84 @@
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,147 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,131 @@
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,44 +0,0 @@
|
||||
/*
|
||||
* 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)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,76 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
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";
|
||||
|
||||
}
|
||||
@ -0,0 +1,145 @@
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,86 @@
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,90 @@
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
package org.anhonesteffort.flock.test.webdav.carddav;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.test.webdav.DavTestParams;
|
||||
import org.anhonesteffort.flock.webdav.carddav.CardDavCollection;
|
||||
import org.anhonesteffort.flock.webdav.carddav.CardDavStore;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
|
||||
/**
|
||||
* Programmer: rhodey
|
||||
* Date: 2/4/14
|
||||
*/
|
||||
public class CardDavStoreTest extends AndroidTestCase {
|
||||
|
||||
private CardDavStore cardDavStore;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
cardDavStore = new CardDavStore(DavTestParams.WEBDAV_HOST,
|
||||
DavTestParams.USERNAME,
|
||||
DavTestParams.PASSWORD,
|
||||
Optional.<String>absent(),
|
||||
Optional.<String>absent());
|
||||
}
|
||||
|
||||
public void testDavCurrentUserPrincipal() throws Exception {
|
||||
Optional<String> currentUserPrincipal = cardDavStore.getCurrentUserPrincipal();
|
||||
assertTrue("DAV:current-user-principal should be something.", currentUserPrincipal.isPresent());
|
||||
}
|
||||
|
||||
public void testGetCollections() throws Exception {
|
||||
final String DEFAULT_COLLECTION_OWNER = "/principals/__uids__/" + URLEncoder.encode(DavTestParams.USERNAME) + "/";
|
||||
|
||||
CardDavCollection collection = cardDavStore.getCollections().get(0);
|
||||
|
||||
assertEquals("Default addressbook collection must be owned by " + DavTestParams.USERNAME,
|
||||
collection.getOwnerHref().get(),
|
||||
DEFAULT_COLLECTION_OWNER);
|
||||
}
|
||||
|
||||
// TODO: implement address book creation in Darwin Calendar Server so this can be tested.
|
||||
/*
|
||||
@Test
|
||||
public void addGetRemoveSimpleCollection() throws Exception {
|
||||
Optional<String> addressbookHomeSet = cardDavStore.getAddressbookHomeSet();
|
||||
assertTrue("Address book home set property must be found.", addressbookHomeSet.isPresent());
|
||||
|
||||
final String COLLECTION_PATH = addressbookHomeSet.get().concat("test-collection/");
|
||||
|
||||
cardDavStore.addCollection(COLLECTION_PATH);
|
||||
assertTrue("Added collection must be found in the store.", cardDavStore.getCollection(COLLECTION_PATH).isPresent());
|
||||
|
||||
cardDavStore.removeCollection(COLLECTION_PATH);
|
||||
assertTrue("Removed collection must not be found in the store.", !cardDavStore.getCollection(COLLECTION_PATH).isPresent());
|
||||
} */
|
||||
|
||||
}
|
||||
@ -1,6 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.anhonesteffort.flock" >
|
||||
package="org.anhonesteffort.flock"
|
||||
android:versionCode="13"
|
||||
android:versionName="0.8.5" >
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="16"
|
||||
android:targetSdkVersion="19" />
|
||||
|
||||
<uses-permission android:name="android.permission.USE_CREDENTIALS"/>
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
|
||||
@ -19,9 +25,6 @@
|
||||
<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"
|
||||
@ -107,6 +110,13 @@
|
||||
</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">
|
||||
@ -116,18 +126,12 @@
|
||||
</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.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>
|
||||
<activity android:name="org.anhonesteffort.flock.MigrationReleaseNotesActivity" />
|
||||
|
||||
<service android:name="org.anhonesteffort.flock.auth.AccountAuthenticatorService">
|
||||
<intent-filter>
|
||||
@ -143,7 +147,8 @@
|
||||
android:label="KeySyncService" />
|
||||
|
||||
<service android:name="org.anhonesteffort.flock.sync.key.KeySyncService"
|
||||
android:exported="true" >
|
||||
android:exported="true"
|
||||
android:process=":flock" >
|
||||
<intent-filter>
|
||||
<action android:name="android.content.SyncAdapter"/>
|
||||
</intent-filter>
|
||||
@ -151,22 +156,9 @@
|
||||
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:exported="true"
|
||||
android:process=":flock" >
|
||||
<intent-filter>
|
||||
<action android:name="android.content.SyncAdapter"/>
|
||||
</intent-filter>
|
||||
@ -177,7 +169,8 @@
|
||||
</service>
|
||||
|
||||
<service android:name="org.anhonesteffort.flock.sync.calendar.CalendarsSyncService"
|
||||
android:exported="true" >
|
||||
android:exported="true"
|
||||
android:process=":flock" >
|
||||
<intent-filter>
|
||||
<action android:name="android.content.SyncAdapter"/>
|
||||
</intent-filter>
|
||||
@ -206,7 +199,7 @@
|
||||
<service android:name="org.anhonesteffort.flock.ChangeEncryptionPasswordService"
|
||||
android:exported="false"/>
|
||||
|
||||
<service android:name="org.anhonesteffort.flock.ExportService"
|
||||
<service android:name="org.anhonesteffort.flock.MigrationService"
|
||||
android:exported="false"/>
|
||||
|
||||
<receiver android:name="org.anhonesteffort.flock.sync.SyncBooter">
|
||||
@ -215,17 +208,13 @@
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name="org.anhonesteffort.flock.NotificationDrawer">
|
||||
<receiver android:name="org.anhonesteffort.flock.MigrationHelperBroadcastReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="org.anhonesteffort.flock.ACTION_STOP_ASKING_FOR_LOGS"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<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.INTENT_ALARM_24_HOURS"/>
|
||||
<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"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
|
||||
@ -1,144 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.vending.billing;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
/**
|
||||
* InAppBillingService is the service that provides in-app billing version 3 and beyond.
|
||||
* This service provides the following features:
|
||||
* 1. Provides a new API to get details of in-app items published for the app including
|
||||
* price, type, title and description.
|
||||
* 2. The purchase flow is synchronous and purchase information is available immediately
|
||||
* after it completes.
|
||||
* 3. Purchase information of in-app purchases is maintained within the Google Play system
|
||||
* till the purchase is consumed.
|
||||
* 4. An API to consume a purchase of an inapp item. All purchases of one-time
|
||||
* in-app items are consumable and thereafter can be purchased again.
|
||||
* 5. An API to get current purchases of the user immediately. This will not contain any
|
||||
* consumed purchases.
|
||||
*
|
||||
* All calls will give a response code with the following possible values
|
||||
* RESULT_OK = 0 - success
|
||||
* RESULT_USER_CANCELED = 1 - user pressed back or canceled a dialog
|
||||
* RESULT_BILLING_UNAVAILABLE = 3 - this billing API version is not supported for the type requested
|
||||
* RESULT_ITEM_UNAVAILABLE = 4 - requested SKU is not available for purchase
|
||||
* RESULT_DEVELOPER_ERROR = 5 - invalid arguments provided to the API
|
||||
* RESULT_ERROR = 6 - Fatal error during the API action
|
||||
* RESULT_ITEM_ALREADY_OWNED = 7 - Failure to purchase since item is already owned
|
||||
* RESULT_ITEM_NOT_OWNED = 8 - Failure to consume since item is not owned
|
||||
*/
|
||||
interface IInAppBillingService {
|
||||
/**
|
||||
* Checks support for the requested billing API version, package and in-app type.
|
||||
* Minimum API version supported by this interface is 3.
|
||||
* @param apiVersion the billing version which the app is using
|
||||
* @param packageName the package name of the calling app
|
||||
* @param type type of the in-app item being purchased "inapp" for one-time purchases
|
||||
* and "subs" for subscription.
|
||||
* @return RESULT_OK(0) on success, corresponding result code on failures
|
||||
*/
|
||||
int isBillingSupported(int apiVersion, String packageName, String type);
|
||||
|
||||
/**
|
||||
* Provides details of a list of SKUs
|
||||
* Given a list of SKUs of a valid type in the skusBundle, this returns a bundle
|
||||
* with a list JSON strings containing the productId, price, title and description.
|
||||
* This API can be called with a maximum of 20 SKUs.
|
||||
* @param apiVersion billing API version that the Third-party is using
|
||||
* @param packageName the package name of the calling app
|
||||
* @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST"
|
||||
* @return Bundle containing the following key-value pairs
|
||||
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
|
||||
* failure as listed above.
|
||||
* "DETAILS_LIST" with a StringArrayList containing purchase information
|
||||
* in JSON format similar to:
|
||||
* '{ "productId" : "exampleSku", "type" : "inapp", "price" : "$5.00",
|
||||
* "title : "Example Title", "description" : "This is an example description" }'
|
||||
*/
|
||||
Bundle getSkuDetails(int apiVersion, String packageName, String type, in Bundle skusBundle);
|
||||
|
||||
/**
|
||||
* Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU,
|
||||
* the type, a unique purchase token and an optional developer payload.
|
||||
* @param apiVersion billing API version that the app is using
|
||||
* @param packageName package name of the calling app
|
||||
* @param sku the SKU of the in-app item as published in the developer console
|
||||
* @param type the type of the in-app item ("inapp" for one-time purchases
|
||||
* and "subs" for subscription).
|
||||
* @param developerPayload optional argument to be sent back with the purchase information
|
||||
* @return Bundle containing the following key-value pairs
|
||||
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
|
||||
* failure as listed above.
|
||||
* "BUY_INTENT" - PendingIntent to start the purchase flow
|
||||
*
|
||||
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow
|
||||
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
|
||||
* If the purchase is successful, the result data will contain the following key-value pairs
|
||||
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
|
||||
* failure as listed above.
|
||||
* "INAPP_PURCHASE_DATA" - String in JSON format similar to
|
||||
* '{"orderId":"12999763169054705758.1371079406387615",
|
||||
* "packageName":"com.example.app",
|
||||
* "productId":"exampleSku",
|
||||
* "purchaseTime":1345678900000,
|
||||
* "purchaseToken" : "122333444455555",
|
||||
* "developerPayload":"example developer payload" }'
|
||||
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
|
||||
* was signed with the private key of the developer
|
||||
* TODO: change this to app-specific keys.
|
||||
*/
|
||||
Bundle getBuyIntent(int apiVersion, String packageName, String sku, String type,
|
||||
String developerPayload);
|
||||
|
||||
/**
|
||||
* Returns the current SKUs owned by the user of the type and package name specified along with
|
||||
* purchase information and a signature of the data to be validated.
|
||||
* This will return all SKUs that have been purchased in V3 and managed items purchased using
|
||||
* V1 and V2 that have not been consumed.
|
||||
* @param apiVersion billing API version that the app is using
|
||||
* @param packageName package name of the calling app
|
||||
* @param type the type of the in-app items being requested
|
||||
* ("inapp" for one-time purchases and "subs" for subscription).
|
||||
* @param continuationToken to be set as null for the first call, if the number of owned
|
||||
* skus are too many, a continuationToken is returned in the response bundle.
|
||||
* This method can be called again with the continuation token to get the next set of
|
||||
* owned skus.
|
||||
* @return Bundle containing the following key-value pairs
|
||||
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
|
||||
* failure as listed above.
|
||||
* "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs
|
||||
* "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information
|
||||
* "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures
|
||||
* of the purchase information
|
||||
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
|
||||
* next set of in-app purchases. Only set if the
|
||||
* user has more owned skus than the current list.
|
||||
*/
|
||||
Bundle getPurchases(int apiVersion, String packageName, String type, String continuationToken);
|
||||
|
||||
/**
|
||||
* Consume the last purchase of the given SKU. This will result in this item being removed
|
||||
* from all subsequent responses to getPurchases() and allow re-purchase of this item.
|
||||
* @param apiVersion billing API version that the app is using
|
||||
* @param packageName package name of the calling app
|
||||
* @param purchaseToken token in the purchase information JSON that identifies the purchase
|
||||
* to be consumed
|
||||
* @return 0 if consumption succeeded. Appropriate error values for failures.
|
||||
*/
|
||||
int consumePurchase(int apiVersion, String packageName, String purchaseToken);
|
||||
}
|
||||
@ -28,7 +28,7 @@ import android.widget.ArrayAdapter;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.crypto.InvalidMacException;
|
||||
import org.anhonesteffort.flock.sync.HidingDavCollection;
|
||||
import org.anhonesteffort.flock.sync.LocalComponentStore;
|
||||
@ -120,7 +120,7 @@ public abstract class AbstractDavCollectionArrayAdapter<T extends HidingDavColle
|
||||
collectionRowView.setBackgroundResource(0);
|
||||
}
|
||||
|
||||
viewHolder.displayName.setText(R.string.display_name_missing);
|
||||
viewHolder.displayName.setText(R.string.display_name_unavailable);
|
||||
|
||||
try {
|
||||
|
||||
@ -132,6 +132,8 @@ public abstract class AbstractDavCollectionArrayAdapter<T extends HidingDavColle
|
||||
Optional<String> displayName = remoteCollections[position].getHiddenDisplayName();
|
||||
if (displayName.isPresent())
|
||||
viewHolder.displayName.setText(displayName.get());
|
||||
else
|
||||
viewHolder.displayName.setText(R.string.display_name_missing);
|
||||
|
||||
if (hasSyncOption) {
|
||||
if (localStore.getCollection(remoteCollections[position].getPath()).isPresent())
|
||||
|
||||
@ -35,7 +35,7 @@ import android.widget.CompoundButton;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import com.google.common.base.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 (accountAndKeyAvailable())
|
||||
if (accountAndKeyAvailableAndMigrationComplete())
|
||||
initButtons();
|
||||
|
||||
return fragmentView;
|
||||
@ -105,7 +105,7 @@ public abstract class AbstractMyCollectionsFragment extends AccountAndKeyRequire
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (!accountAndKeyAvailable())
|
||||
if (!accountAndKeyAvailableAndMigrationComplete())
|
||||
return;
|
||||
|
||||
activity = getActivity();
|
||||
|
||||
@ -25,7 +25,7 @@ import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.crypto.KeyHelper;
|
||||
import org.anhonesteffort.flock.crypto.MasterCipher;
|
||||
@ -97,7 +97,16 @@ public class AccountAndKeyRequiredActivity extends Activity {
|
||||
masterCipher = handleGetMasterCipherOrFail(this);
|
||||
}
|
||||
|
||||
protected boolean accountAndKeyAvailable() {
|
||||
protected boolean accountAndKeyAvailableAndMigrationComplete() {
|
||||
if (MigrationHelperBroadcastReceiver.getUiDisabledForMigration(getBaseContext())) {
|
||||
Toast.makeText(getBaseContext(),
|
||||
R.string.migration_in_progress_please_wait,
|
||||
Toast.LENGTH_LONG).show();
|
||||
|
||||
finish();
|
||||
return false;
|
||||
}
|
||||
|
||||
return account != null && masterCipher != null;
|
||||
}
|
||||
|
||||
|
||||
@ -21,6 +21,7 @@ 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;
|
||||
@ -41,7 +42,16 @@ public class AccountAndKeyRequiredFragment extends Fragment {
|
||||
masterCipher = AccountAndKeyRequiredActivity.handleGetMasterCipherOrFail(getActivity());
|
||||
}
|
||||
|
||||
protected boolean accountAndKeyAvailable() {
|
||||
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;
|
||||
}
|
||||
|
||||
return account != null && masterCipher != null;
|
||||
}
|
||||
|
||||
|
||||
@ -29,7 +29,7 @@ import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@ -93,6 +93,7 @@ public class CalendarCopyService extends Service implements CalendarCopiedListen
|
||||
private void handleCopyComplete() {
|
||||
Log.d(TAG, "handleCopyComplete()");
|
||||
|
||||
new CalendarsSyncScheduler(getBaseContext()).requestSync();
|
||||
stopForeground(false);
|
||||
stopSelf();
|
||||
}
|
||||
@ -104,13 +105,17 @@ public class CalendarCopyService extends Service implements CalendarCopiedListen
|
||||
if (countEventCopiesFailed == 0) {
|
||||
notificationBuilder
|
||||
.setProgress(0, 0, false)
|
||||
.setContentText(getString(R.string.notification_import_complete_copied_events, countEventsCopied));
|
||||
.setContentText(getString(R.string.notification_import_complete_copied) +
|
||||
" " + countEventsCopied + " " + getString(R.string.events) +
|
||||
getString(R.string.period));
|
||||
}
|
||||
else {
|
||||
notificationBuilder
|
||||
.setProgress(0, 0, false)
|
||||
.setContentText(getString(R.string.notification_import_complete_copied_events_failed,
|
||||
countEventsCopied, countEventCopiesFailed));
|
||||
.setContentText(getString(R.string.notification_import_complete_copied) +
|
||||
" " + countEventsCopied + " " + getString(R.string.events) +
|
||||
", " + countEventCopiesFailed + " " + getString(R.string.failed) +
|
||||
getString(R.string.period));
|
||||
}
|
||||
|
||||
notifyManager.notify(ID_CALENDAR_COPY_NOTIFICATION, notificationBuilder.build());
|
||||
@ -121,7 +126,8 @@ public class CalendarCopyService extends Service implements CalendarCopiedListen
|
||||
Log.d(TAG, "handleEventCopied() events copied: " + countEventsCopied);
|
||||
|
||||
notificationBuilder
|
||||
.setContentText(getString(R.string.notification_importing_events_from, fromAccount.name))
|
||||
.setContentText(getString(R.string.notification_importing_events_from) +
|
||||
" " + fromAccount.name)
|
||||
.setProgress(countEventsToCopy,
|
||||
countEventsCopied + countEventCopiesFailed,
|
||||
false);
|
||||
@ -156,7 +162,8 @@ public class CalendarCopyService extends Service implements CalendarCopiedListen
|
||||
}
|
||||
|
||||
notificationBuilder
|
||||
.setContentText(getString(R.string.notification_importing_events_from, fromAccount.name))
|
||||
.setContentText(getString(R.string.notification_importing_events_from) +
|
||||
" " + fromAccount.name)
|
||||
.setProgress(countEventsToCopy,
|
||||
countEventsCopied + countEventCopiesFailed,
|
||||
false);
|
||||
|
||||
@ -34,7 +34,7 @@ import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import com.google.common.base.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 (!accountAndKeyAvailable())
|
||||
if (!accountAndKeyAvailableAndMigrationComplete())
|
||||
return;
|
||||
|
||||
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
|
||||
@ -116,6 +116,7 @@ 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();
|
||||
|
||||
@ -135,10 +136,11 @@ public class ChangeEncryptionPasswordActivity extends AccountAndKeyRequiredActiv
|
||||
}
|
||||
|
||||
Optional<String> savedPassphrase = KeyStore.getMasterPassphrase(getBaseContext());
|
||||
if (!savedPassphrase.isPresent()) {
|
||||
if (!savedPassphrase.isPresent() || !savedPassphrase.get().equals(cipherPassphrase)) {
|
||||
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;
|
||||
@ -147,7 +149,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, savedPassphrase.get());
|
||||
changeService.putExtra(ChangeEncryptionPasswordService.KEY_OLD_MASTER_PASSPHRASE, cipherPassphrase);
|
||||
changeService.putExtra(ChangeEncryptionPasswordService.KEY_NEW_MASTER_PASSPHRASE, newCipherPassphrase);
|
||||
changeService.putExtra(ChangeEncryptionPasswordService.KEY_ACCOUNT, account.toBundle());
|
||||
|
||||
@ -175,7 +177,8 @@ public class ChangeEncryptionPasswordActivity extends AccountAndKeyRequiredActiv
|
||||
errorBundler.putInt(ErrorToaster.KEY_STATUS_CODE, message.arg1);
|
||||
ErrorToaster.handleDisplayToastBundledError(getBaseContext(), errorBundler);
|
||||
|
||||
if (findViewById(R.id.new_cipher_passphrase) != null) {
|
||||
if (findViewById(R.id.cipher_passphrase) != null) {
|
||||
((TextView)findViewById(R.id.cipher_passphrase)).setText("");
|
||||
((TextView)findViewById(R.id.new_cipher_passphrase)).setText("");
|
||||
((TextView)findViewById(R.id.new_cipher_passphrase_repeat)).setText("");
|
||||
}
|
||||
|
||||
@ -15,13 +15,15 @@ import android.os.RemoteException;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
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.KeyUtil;
|
||||
import org.anhonesteffort.flock.registration.RegistrationApi;
|
||||
import org.anhonesteffort.flock.registration.RegistrationApiException;
|
||||
import org.anhonesteffort.flock.sync.AbstractDavSyncAdapter;
|
||||
import org.anhonesteffort.flock.sync.key.DavKeyCollection;
|
||||
import org.anhonesteffort.flock.sync.key.DavKeyStore;
|
||||
import org.anhonesteffort.flock.webdav.PropertyParseException;
|
||||
@ -99,15 +101,15 @@ public class ChangeEncryptionPasswordService extends Service {
|
||||
|
||||
private void handleChangeOwsAuthToken(Bundle result, String passphrase) {
|
||||
Log.d(TAG, "handleChangeOwsAuthToken()");
|
||||
RegistrationApi registrationApi = new RegistrationApi(getBaseContext());
|
||||
|
||||
try {
|
||||
|
||||
RegistrationApi registrationApi = new RegistrationApi(getBaseContext());
|
||||
String newAuthToken = KeyUtil.getAuthTokenForPassphrase(passphrase);
|
||||
String newAuthToken = KeyUtil.getAuthTokenForPassphrase(passphrase);
|
||||
|
||||
registrationApi.setAccountPassword(account, newAuthToken);
|
||||
DavAccountHelper.setAccountPassword(getBaseContext(), newAuthToken);
|
||||
NotificationDrawer.disableAuthNotificationsForRunningAdapters(getBaseContext(), account.getOsAccount());
|
||||
AbstractDavSyncAdapter.disableAuthNotificationsForRunningAdapters(getBaseContext(), account.getOsAccount());
|
||||
|
||||
account = new DavAccount(account.getUserId(), newAuthToken, account.getDavHostHREF());
|
||||
|
||||
|
||||
@ -39,6 +39,7 @@ import android.util.Pair;
|
||||
import org.anhonesteffort.flock.sync.addressbook.AddressbookSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.addressbook.ContactCopiedListener;
|
||||
import org.anhonesteffort.flock.sync.addressbook.LocalContactCollection;
|
||||
import org.anhonesteffort.flock.webdav.InvalidComponentException;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@ -89,8 +90,8 @@ public class ContactCopyService extends Service implements ContactCopiedListener
|
||||
Log.d(TAG, "handleContactCopied() contacts copied: " + countContactsCopied);
|
||||
|
||||
notificationBuilder
|
||||
.setContentText(getString(R.string.notification_importing_contacts_from,
|
||||
((fromAccount != null) ? fromAccount.name : getString(R.string.local_storage))))
|
||||
.setContentText(getString(R.string.notification_importing_contacts_from) +
|
||||
" " + ((fromAccount != null) ? fromAccount.name : getString(R.string.local_storage)))
|
||||
.setProgress(countContactsToCopy, countContactsCopied + countContactCopiesFailed, false);
|
||||
|
||||
notifyManager.notify(1023, notificationBuilder.build());
|
||||
@ -123,7 +124,8 @@ public class ContactCopyService extends Service implements ContactCopiedListener
|
||||
}
|
||||
|
||||
notificationBuilder
|
||||
.setContentText(getString(R.string.notification_importing_contacts_from, fromAccount.name))
|
||||
.setContentText(getString(R.string.notification_importing_contacts_from) +
|
||||
" " + fromAccount.name)
|
||||
.setProgress(countContactsToCopy, countContactsCopied + countContactCopiesFailed, false);
|
||||
|
||||
notifyManager.notify(1023, notificationBuilder.build());
|
||||
@ -132,6 +134,7 @@ public class ContactCopyService extends Service implements ContactCopiedListener
|
||||
private void handleCopyComplete() {
|
||||
Log.d(TAG, "handleCopyComplete()");
|
||||
|
||||
new AddressbookSyncScheduler(getBaseContext()).requestSync();
|
||||
stopForeground(false);
|
||||
stopSelf();
|
||||
}
|
||||
@ -143,13 +146,17 @@ public class ContactCopyService extends Service implements ContactCopiedListener
|
||||
if (countContactCopiesFailed == 0) {
|
||||
notificationBuilder
|
||||
.setProgress(0, 0, false)
|
||||
.setContentText(getString(R.string.notification_import_complete_copied_contacts, countContactsCopied));
|
||||
.setContentText(getString(R.string.notification_import_complete_copied) +
|
||||
" " + countContactsCopied + " " + getString(R.string.contacts) +
|
||||
getString(R.string.period));
|
||||
}
|
||||
else {
|
||||
notificationBuilder
|
||||
.setProgress(0, 0, false)
|
||||
.setContentText(getString(R.string.notification_import_complete_copied_contacts_failed,
|
||||
countContactsCopied, countContactCopiesFailed));
|
||||
.setContentText(getString(R.string.notification_import_complete_copied) +
|
||||
" " + countContactsCopied + " " + getString(R.string.contacts) + ", " +
|
||||
countContactCopiesFailed + " " +
|
||||
getString(R.string.failed) + getString(R.string.period));
|
||||
}
|
||||
|
||||
notifyManager.notify(1023, notificationBuilder.build());
|
||||
@ -192,6 +199,9 @@ public class ContactCopyService extends Service implements ContactCopiedListener
|
||||
|
||||
fromCollection.copyToAccount(copyPair.second, this);
|
||||
|
||||
} catch (InvalidComponentException e) {
|
||||
ErrorToaster.handleShowError(getBaseContext(), e);
|
||||
return;
|
||||
} catch (RemoteException e) {
|
||||
ErrorToaster.handleShowError(getBaseContext(), e);
|
||||
return;
|
||||
|
||||
@ -33,7 +33,7 @@ import android.view.Window;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.sync.addressbook.AddressbookSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.calendar.CalendarsSyncScheduler;
|
||||
|
||||
@ -15,11 +15,13 @@ import android.os.RemoteException;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
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.KeyUtil;
|
||||
import org.anhonesteffort.flock.sync.AbstractDavSyncAdapter;
|
||||
import org.anhonesteffort.flock.webdav.PropertyParseException;
|
||||
import org.apache.jackrabbit.webdav.DavException;
|
||||
|
||||
@ -137,7 +139,7 @@ public class CorrectPasswordService extends Service {
|
||||
|
||||
if (DavAccountHelper.isAuthenticated(getBaseContext(), account)) {
|
||||
DavAccountHelper.setAccountPassword(getBaseContext(), account.getAuthToken());
|
||||
NotificationDrawer.cancelAuthNotification(getBaseContext());
|
||||
AbstractDavSyncAdapter.cancelAuthNotification(getBaseContext());
|
||||
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SUCCESS);
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ import android.accounts.AccountManager;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import com.google.common.base.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_PRIVATE);
|
||||
return context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_MULTI_PROCESS);
|
||||
}
|
||||
|
||||
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).apply();
|
||||
getSharedPreferences(context).edit().putString(KEY_DAV_USERNAME, username).commit();
|
||||
}
|
||||
|
||||
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).apply();
|
||||
getSharedPreferences(context).edit().putString(KEY_DAV_PASSWORD, password).commit();
|
||||
}
|
||||
|
||||
public static void invalidateAccountPassword(Context context) {
|
||||
getSharedPreferences(context).edit().remove(KEY_DAV_PASSWORD).apply();
|
||||
getSharedPreferences(context).edit().remove(KEY_DAV_PASSWORD).commit();
|
||||
}
|
||||
|
||||
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).apply();
|
||||
getSharedPreferences(context).edit().putString(KEY_DAV_HOST, href).commit();
|
||||
}
|
||||
|
||||
public static void invalidateAccount(Context context) {
|
||||
getSharedPreferences(context).edit().remove(KEY_DAV_HOST).apply();
|
||||
getSharedPreferences(context).edit().remove(KEY_DAV_USERNAME).apply();
|
||||
getSharedPreferences(context).edit().remove(KEY_DAV_PASSWORD).apply();
|
||||
getSharedPreferences(context).edit().remove(KEY_DAV_HOST).commit();
|
||||
getSharedPreferences(context).edit().remove(KEY_DAV_USERNAME).commit();
|
||||
getSharedPreferences(context).edit().remove(KEY_DAV_PASSWORD).commit();
|
||||
}
|
||||
|
||||
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() == WebDavConstants.SC_UNAUTHORIZED)
|
||||
else if (e.getErrorCode() == DavServletResponse.SC_UNAUTHORIZED)
|
||||
return false;
|
||||
else
|
||||
throw e;
|
||||
|
||||
@ -33,8 +33,10 @@ import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import com.google.common.base.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;
|
||||
|
||||
@ -51,7 +53,7 @@ public class DeleteAllContactsActivity extends AccountAndKeyRequiredActivity {
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (!accountAndKeyAvailable())
|
||||
if (!accountAndKeyAvailableAndMigrationComplete())
|
||||
return;
|
||||
|
||||
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
|
||||
@ -109,6 +111,8 @@ 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();
|
||||
|
||||
@ -0,0 +1,750 @@
|
||||
/*
|
||||
* *
|
||||
* Copyright (C) 2014 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* /
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.stripe.exception.CardException;
|
||||
import com.stripe.exception.StripeException;
|
||||
import com.stripe.model.Token;
|
||||
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.registration.OwsRegistration;
|
||||
import org.anhonesteffort.flock.registration.RegistrationApi;
|
||||
import org.anhonesteffort.flock.registration.RegistrationApiException;
|
||||
import org.anhonesteffort.flock.registration.model.AugmentedFlockAccount;
|
||||
import org.anhonesteffort.flock.registration.model.FlockAccount;
|
||||
import org.anhonesteffort.flock.registration.model.FlockCardInformation;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Programmer: rhodey
|
||||
*/
|
||||
public class EditAutoRenewActivity extends Activity {
|
||||
|
||||
private static final String TAG = "org.anhonesteffort.flock.EditAutoRenewActivity";
|
||||
|
||||
public static final String KEY_DAV_ACCOUNT_BUNDLE = "KEY_DAV_ACCOUNT_BUNDLE";
|
||||
public static final String KEY_FLOCK_ACCOUNT_BUNDLE = "KEY_FLOCK_ACCOUNT_BUNDLE";
|
||||
public static final String KEY_CARD_INFORMATION_BUNDLE = "KEY_CARD_INFORMATION_BUNDLE";
|
||||
|
||||
private DavAccount davAccount;
|
||||
private AsyncTask asyncTask;
|
||||
private TextWatcher cardNumberTextWatcher;
|
||||
private TextWatcher cardExpirationTextWatcher;
|
||||
|
||||
private Optional<FlockAccount> flockAccount = Optional.absent();
|
||||
private Optional<FlockCardInformation> cardInformation = Optional.absent();
|
||||
|
||||
private int lastCardExpirationLength = 0;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
|
||||
requestWindowFeature(Window.FEATURE_PROGRESS);
|
||||
|
||||
setContentView(R.layout.activity_edit_auto_renew);
|
||||
getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getActionBar().setTitle(R.string.button_edit_payment_details);
|
||||
|
||||
if (savedInstanceState != null && !savedInstanceState.isEmpty()) {
|
||||
if (!DavAccount.build(savedInstanceState.getBundle(KEY_DAV_ACCOUNT_BUNDLE)).isPresent()) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
davAccount = DavAccount.build(savedInstanceState.getBundle(KEY_DAV_ACCOUNT_BUNDLE)).get();
|
||||
flockAccount = FlockAccount.build(savedInstanceState.getBundle(KEY_FLOCK_ACCOUNT_BUNDLE));
|
||||
cardInformation = FlockCardInformation.build(savedInstanceState.getBundle(KEY_CARD_INFORMATION_BUNDLE));
|
||||
}
|
||||
else if (getIntent().getExtras() != null) {
|
||||
if (!DavAccount.build(getIntent().getExtras().getBundle(KEY_DAV_ACCOUNT_BUNDLE)).isPresent()) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
davAccount = DavAccount.build(getIntent().getExtras().getBundle(KEY_DAV_ACCOUNT_BUNDLE)).get();
|
||||
flockAccount = FlockAccount.build(getIntent().getExtras().getBundle(KEY_FLOCK_ACCOUNT_BUNDLE));
|
||||
cardInformation = FlockCardInformation.build(getIntent().getExtras().getBundle(KEY_CARD_INFORMATION_BUNDLE));
|
||||
}
|
||||
|
||||
initCostPerYear();
|
||||
}
|
||||
|
||||
private void initCostPerYear() {
|
||||
TextView costPerYearView = (TextView) findViewById(R.id.cost_per_year);
|
||||
double costPerYearUsd = (double) getResources().getInteger(R.integer.cost_per_year_usd);
|
||||
|
||||
costPerYearView.setText(getString(R.string.usd_per_year, costPerYearUsd));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
((CheckBox) findViewById(R.id.checkbox_enable_auto_renew)).setChecked(false);
|
||||
handleDisableForm();
|
||||
|
||||
handleGetAccountAndCardAsync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle savedInstanceState) {
|
||||
savedInstanceState.putBundle(KEY_DAV_ACCOUNT_BUNDLE, davAccount.toBundle());
|
||||
|
||||
if (flockAccount.isPresent())
|
||||
savedInstanceState.putBundle(KEY_FLOCK_ACCOUNT_BUNDLE, flockAccount.get().toBundle());
|
||||
|
||||
if (cardInformation.isPresent())
|
||||
savedInstanceState.putBundle(KEY_CARD_INFORMATION_BUNDLE, cardInformation.get().toBundle());
|
||||
|
||||
super.onSaveInstanceState(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
|
||||
if (asyncTask != null && !asyncTask.isCancelled())
|
||||
asyncTask.cancel(true);
|
||||
|
||||
((EditText) findViewById(R.id.card_number)).setText("");
|
||||
((EditText) findViewById(R.id.card_expiration)).setText("");
|
||||
}
|
||||
|
||||
private void handleDisableForm() {
|
||||
Log.d(TAG, "handleDisableForm()");
|
||||
|
||||
Button verifyAndSaveButton = (Button) findViewById(R.id.button_verify_and_save);
|
||||
EditText cardExpirationView = (EditText) findViewById(R.id.card_expiration);
|
||||
EditText cardNumberView = (EditText) findViewById(R.id.card_number);
|
||||
TextView cardCVCView = (TextView) findViewById(R.id.card_cvc);
|
||||
|
||||
cardNumberView.setFocusable(false);
|
||||
cardNumberView.setFocusableInTouchMode(false);
|
||||
cardExpirationView.setFocusable(false);
|
||||
cardExpirationView.setFocusableInTouchMode(false);
|
||||
cardCVCView.setFocusable(false);
|
||||
cardCVCView.setFocusableInTouchMode(false);
|
||||
|
||||
cardNumberView.setText("");
|
||||
cardExpirationView.setText("");
|
||||
cardCVCView.setText("");
|
||||
|
||||
cardNumberView.setOnTouchListener(null);
|
||||
cardExpirationView.setOnTouchListener(null);
|
||||
cardCVCView.setOnTouchListener(null);
|
||||
|
||||
verifyAndSaveButton.setText(R.string.button_save);
|
||||
verifyAndSaveButton.setOnClickListener(null);
|
||||
}
|
||||
|
||||
private void handleInitFormAsAutoRenewDisabled() {
|
||||
Log.d(TAG, "handleInitFormFormAsAutoRenewDisabled()");
|
||||
|
||||
Button verifyAndSaveButton = (Button) findViewById(R.id.button_verify_and_save);
|
||||
EditText cardExpirationView = (EditText) findViewById(R.id.card_expiration);
|
||||
EditText cardNumberView = (EditText) findViewById(R.id.card_number);
|
||||
TextView cardCVCView = (TextView) findViewById(R.id.card_cvc);
|
||||
|
||||
cardNumberView.setFocusable(false);
|
||||
cardNumberView.setFocusableInTouchMode(false);
|
||||
cardExpirationView.setFocusable(false);
|
||||
cardExpirationView.setFocusableInTouchMode(false);
|
||||
cardCVCView.setFocusable(false);
|
||||
cardCVCView.setFocusableInTouchMode(false);
|
||||
|
||||
cardNumberView.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN)
|
||||
((CheckBox) findViewById(R.id.checkbox_enable_auto_renew)).setChecked(true);
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
cardExpirationView.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN)
|
||||
((CheckBox) findViewById(R.id.checkbox_enable_auto_renew)).setChecked(true);
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
cardCVCView.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN)
|
||||
((CheckBox) findViewById(R.id.checkbox_enable_auto_renew)).setChecked(true);
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
cardNumberView.setText("");
|
||||
cardExpirationView.setText("");
|
||||
cardCVCView.setText("");
|
||||
|
||||
verifyAndSaveButton.setText(R.string.button_save);
|
||||
verifyAndSaveButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
handleSaveAutoRenewAndFinish(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleEnableForm() {
|
||||
Log.d(TAG, "handleEnableForm()");
|
||||
|
||||
EditText cardExpirationView = (EditText) findViewById(R.id.card_expiration);
|
||||
EditText cardNumberView = (EditText) findViewById(R.id.card_number);
|
||||
TextView cardCVCView = (TextView) findViewById(R.id.card_cvc);
|
||||
|
||||
cardNumberView.setFocusable(true);
|
||||
cardNumberView.setFocusableInTouchMode(true);
|
||||
cardExpirationView.setFocusable(true);
|
||||
cardExpirationView.setFocusableInTouchMode(true);
|
||||
cardCVCView.setFocusable(true);
|
||||
cardCVCView.setFocusableInTouchMode(true);
|
||||
|
||||
cardNumberView.setOnTouchListener(null);
|
||||
cardExpirationView.setOnTouchListener(null);
|
||||
cardCVCView.setOnTouchListener(null);
|
||||
}
|
||||
|
||||
private void handleInitFormForEditing() {
|
||||
Log.d(TAG, "handleInitFormForEditing()");
|
||||
|
||||
Button verifyAndSaveButton = (Button) findViewById(R.id.button_verify_and_save);
|
||||
|
||||
handleEnableForm();
|
||||
initCardNumberHelper();
|
||||
initCardExpirationHelper();
|
||||
|
||||
verifyAndSaveButton.setText(R.string.button_verify_and_save);
|
||||
verifyAndSaveButton.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
handleVerifyCardAndFinish();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private void handleInitFormForFixingError() {
|
||||
Log.d(TAG, "handleInitFormForFixingError()");
|
||||
|
||||
EditText cardNumberView = (EditText) findViewById(R.id.card_number);
|
||||
EditText cardExpirationView = (EditText) findViewById(R.id.card_expiration);
|
||||
TextView cardCVCView = (TextView) findViewById(R.id.card_cvc);
|
||||
Button verifyAndSaveButton = (Button) findViewById(R.id.button_verify_and_save);
|
||||
|
||||
if (StringUtils.isEmpty(cardNumberView.getText().toString()))
|
||||
cardNumberView.setText("**** **** **** " + cardInformation.get().getCardLastFour());
|
||||
|
||||
cardNumberView.setError(getString(R.string.error_your_card_could_not_be_verified));
|
||||
|
||||
if (StringUtils.isEmpty(cardExpirationView.getText().toString()))
|
||||
cardExpirationView.setText(cardInformation.get().getCardExpiration());
|
||||
|
||||
cardCVCView.setText("");
|
||||
|
||||
handleEnableForm();
|
||||
initCardNumberHelper();
|
||||
initCardExpirationHelper();
|
||||
|
||||
verifyAndSaveButton.setText(R.string.button_verify_and_save);
|
||||
verifyAndSaveButton.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
handleVerifyCardAndFinish();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private void handleInitFormForViewingSuccess() {
|
||||
Log.d(TAG, "handleInitFormForViewingSuccess()");
|
||||
|
||||
Button verifyAndSaveButton = (Button) findViewById(R.id.button_verify_and_save);
|
||||
EditText cardNumberView = (EditText) findViewById(R.id.card_number);
|
||||
EditText cardExpirationView = (EditText) findViewById(R.id.card_expiration);
|
||||
TextView cardCVCView = (TextView) findViewById(R.id.card_cvc);
|
||||
|
||||
handleEnableForm();
|
||||
|
||||
cardNumberView.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN)
|
||||
handleInitFormForEditing();
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
cardExpirationView.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN)
|
||||
handleInitFormForEditing();
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
cardCVCView.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN)
|
||||
handleInitFormForEditing();
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (StringUtils.isEmpty(cardNumberView.getText().toString()))
|
||||
cardNumberView.setText("**** **** **** " + cardInformation.get().getCardLastFour());
|
||||
|
||||
if (StringUtils.isEmpty(cardExpirationView.getText().toString()))
|
||||
cardExpirationView.setText(cardInformation.get().getCardExpiration());
|
||||
|
||||
cardCVCView.setText("");
|
||||
|
||||
verifyAndSaveButton.setText(R.string.button_save);
|
||||
verifyAndSaveButton.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
handleSaveAutoRenewAndFinish(true);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private void initCardNumberHelper() {
|
||||
Log.d(TAG, "initCardNumberHelper()");
|
||||
|
||||
final EditText cardNumberView = (EditText) findViewById(R.id.card_number);
|
||||
final EditText cardExpirationView = (EditText) findViewById(R.id.card_expiration);
|
||||
final CheckBox autoRenewIsEnabled = (CheckBox) findViewById(R.id.checkbox_enable_auto_renew);
|
||||
|
||||
cardNumberView.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (cardNumberView.getText() != null &&
|
||||
cardNumberView.getText().toString().contains("*"))
|
||||
{
|
||||
cardNumberView.setText("");
|
||||
}
|
||||
|
||||
if (autoRenewIsEnabled.isChecked())
|
||||
handleInitFormForEditing();
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (cardNumberTextWatcher != null)
|
||||
cardNumberView.removeTextChangedListener(cardNumberTextWatcher);
|
||||
|
||||
cardNumberTextWatcher = new TextWatcher() {
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
String cardNumber = s.toString().replace(" ", "");
|
||||
String formattedCardNumber = "";
|
||||
|
||||
for (int i = 0; i < cardNumber.length(); i++) {
|
||||
if (i > 0 && i % 4 == 0)
|
||||
formattedCardNumber += " ";
|
||||
|
||||
formattedCardNumber += cardNumber.charAt(i);
|
||||
}
|
||||
|
||||
cardNumberView.removeTextChangedListener(this);
|
||||
cardNumberView.setText(formattedCardNumber);
|
||||
cardNumberView.setSelection(formattedCardNumber.length());
|
||||
cardNumberView.addTextChangedListener(this);
|
||||
|
||||
if (!cardNumber.contains("*") && cardNumber.length() == 16)
|
||||
cardExpirationView.requestFocus();
|
||||
}
|
||||
};
|
||||
|
||||
cardNumberView.addTextChangedListener(cardNumberTextWatcher);
|
||||
}
|
||||
|
||||
private void initCardExpirationHelper() {
|
||||
Log.d(TAG, "initCardExpirationHelper()");
|
||||
|
||||
final EditText cardExpirationView = (EditText) findViewById(R.id.card_expiration);
|
||||
final EditText cardCvcView = (EditText) findViewById(R.id.card_cvc);
|
||||
final CheckBox autoRenewIsEnabled = (CheckBox) findViewById(R.id.checkbox_enable_auto_renew);
|
||||
|
||||
cardExpirationView.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (autoRenewIsEnabled.isChecked())
|
||||
handleInitFormForEditing();
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (cardExpirationTextWatcher != null)
|
||||
cardExpirationView.removeTextChangedListener(cardExpirationTextWatcher);
|
||||
|
||||
cardExpirationTextWatcher = new TextWatcher() {
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
String formattedCardExpiration = s.toString();
|
||||
|
||||
if (lastCardExpirationLength <= formattedCardExpiration.length() &&
|
||||
formattedCardExpiration.length() == 2)
|
||||
{
|
||||
formattedCardExpiration = formattedCardExpiration + "/";
|
||||
}
|
||||
|
||||
lastCardExpirationLength = formattedCardExpiration.length();
|
||||
|
||||
cardExpirationView.removeTextChangedListener(this);
|
||||
cardExpirationView.setText(formattedCardExpiration);
|
||||
cardExpirationView.setSelection(formattedCardExpiration.length());
|
||||
cardExpirationView.addTextChangedListener(this);
|
||||
|
||||
if (formattedCardExpiration.length() == 5)
|
||||
cardCvcView.requestFocus();
|
||||
}
|
||||
};
|
||||
|
||||
cardExpirationView.addTextChangedListener(cardExpirationTextWatcher);
|
||||
}
|
||||
|
||||
private void handleRefreshForm(boolean isCallback) {
|
||||
Log.d(TAG, "handleRefreshForm() is callback >> " + isCallback);
|
||||
|
||||
CheckBox autoRenewIsEnabled = (CheckBox) findViewById(R.id.checkbox_enable_auto_renew);
|
||||
|
||||
if (!isCallback)
|
||||
autoRenewIsEnabled.setChecked(flockAccount.get().getAutoRenewEnabled());
|
||||
|
||||
autoRenewIsEnabled.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
handleRefreshForm(true);
|
||||
}
|
||||
});
|
||||
|
||||
if (!autoRenewIsEnabled.isChecked())
|
||||
handleInitFormAsAutoRenewDisabled();
|
||||
else {
|
||||
if (!flockAccount.get().getLastStripeChargeFailed() && cardInformation.isPresent())
|
||||
handleInitFormForViewingSuccess();
|
||||
else if (flockAccount.get().getLastStripeChargeFailed() && cardInformation.isPresent())
|
||||
handleInitFormForFixingError();
|
||||
else
|
||||
handleInitFormForEditing();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSaveAutoRenewAndFinish(final Boolean autoRenewIsEnabled) {
|
||||
if (asyncTask != null)
|
||||
return;
|
||||
|
||||
asyncTask = new AsyncTask<Void, Void, Bundle>() {
|
||||
boolean autoRenewChanged = false;
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
Log.d(TAG, "handleSaveAutoRenewAndFinish()");
|
||||
setProgressBarIndeterminateVisibility(true);
|
||||
setProgressBarVisibility(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bundle doInBackground(Void... params) {
|
||||
Bundle result = new Bundle();
|
||||
RegistrationApi registrationApi = new RegistrationApi(getBaseContext());
|
||||
|
||||
if (flockAccount.get().getAutoRenewEnabled() == autoRenewIsEnabled) {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SUCCESS);
|
||||
return result;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
registrationApi.setAccountAutoRenew(davAccount, autoRenewIsEnabled);
|
||||
flockAccount = Optional.of((FlockAccount) registrationApi.getAccount(davAccount));
|
||||
autoRenewChanged = true;
|
||||
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SUCCESS);
|
||||
|
||||
} catch (RegistrationApiException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
} catch (IOException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Bundle result) {
|
||||
asyncTask = null;
|
||||
setProgressBarIndeterminateVisibility(false);
|
||||
setProgressBarVisibility(false);
|
||||
|
||||
if (result.getInt(ErrorToaster.KEY_STATUS_CODE) == ErrorToaster.CODE_SUCCESS) {
|
||||
if (autoRenewChanged)
|
||||
Toast.makeText(getBaseContext(),
|
||||
R.string.autorenew_saved,
|
||||
Toast.LENGTH_LONG).show();
|
||||
|
||||
finish();
|
||||
}
|
||||
else
|
||||
ErrorToaster.handleDisplayToastBundledError(getBaseContext(), result);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private void handleVerifyCardAndFinish() {
|
||||
if (asyncTask != null)
|
||||
return;
|
||||
|
||||
asyncTask = new AsyncTask<Void, Void, Bundle>() {
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
Log.d(TAG, "handleVerifyCardAndFinish()");
|
||||
setProgressBarIndeterminateVisibility(true);
|
||||
setProgressBarVisibility(true);
|
||||
}
|
||||
|
||||
private String handleGetStripeCardTokenId(String cardNumber,
|
||||
String cardExpiration,
|
||||
String cardCVC)
|
||||
throws StripeException
|
||||
{
|
||||
String[] expiration = cardExpiration.split("/");
|
||||
Integer expirationMonth = Integer.valueOf(expiration[0]);
|
||||
Integer expirationYear;
|
||||
|
||||
if (expiration[1].length() == 4)
|
||||
expirationYear = Integer.valueOf(expiration[1]);
|
||||
else
|
||||
expirationYear = Integer.valueOf(expiration[1]) + 2000;
|
||||
|
||||
java.util.Map<String, Object> cardParams = new HashMap<String, Object>();
|
||||
java.util.Map<String, Object> tokenParams = new HashMap<String, Object>();
|
||||
|
||||
cardParams.put("number", cardNumber.replace(" ", ""));
|
||||
cardParams.put("exp_month", expirationMonth);
|
||||
cardParams.put("exp_year", expirationYear);
|
||||
cardParams.put("cvc", cardCVC);
|
||||
|
||||
tokenParams.put("card", cardParams);
|
||||
|
||||
return Token.create(tokenParams, OwsRegistration.STRIPE_PUBLIC_KEY).getId();
|
||||
}
|
||||
|
||||
private void handlePutStripeTokenToServer(String stripeTokenId)
|
||||
throws IOException, RegistrationApiException, CardException
|
||||
{
|
||||
RegistrationApi registrationApi = new RegistrationApi(getBaseContext());
|
||||
registrationApi.updateAccountStripeCard(davAccount, stripeTokenId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bundle doInBackground(Void... params) {
|
||||
Bundle result = new Bundle();
|
||||
String cardNumber = ((TextView)findViewById(R.id.card_number)).getText().toString();
|
||||
String cardExpiration = ((TextView)findViewById(R.id.card_expiration)).getText().toString();
|
||||
String cardCVC = ((TextView)findViewById(R.id.card_cvc)).getText().toString();
|
||||
|
||||
if (StringUtils.isEmpty(cardNumber)) {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_CARD_NUMBER_INVALID);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(cardExpiration) || cardExpiration.split("/").length != 2) {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_CARD_EXPIRATION_INVALID);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(cardCVC) || cardCVC.length() < 1) {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_CARD_CVC_INVALID);
|
||||
return result;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
String stripeTokenId = handleGetStripeCardTokenId(cardNumber, cardExpiration, cardCVC);
|
||||
handlePutStripeTokenToServer(stripeTokenId);
|
||||
|
||||
if (!flockAccount.get().getAutoRenewEnabled())
|
||||
new RegistrationApi(getBaseContext()).setAccountAutoRenew(davAccount, true);
|
||||
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SUCCESS);
|
||||
|
||||
} catch (CardException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
} catch (StripeException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
} catch (RegistrationApiException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
} catch (IOException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Bundle result) {
|
||||
asyncTask = null;
|
||||
setProgressBarIndeterminateVisibility(false);
|
||||
setProgressBarVisibility(false);
|
||||
|
||||
if (result.getInt(ErrorToaster.KEY_STATUS_CODE) == ErrorToaster.CODE_SUCCESS) {
|
||||
Toast.makeText(getBaseContext(), R.string.card_verified_and_saved, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
}
|
||||
|
||||
else {
|
||||
handleInitFormForEditing();
|
||||
ErrorToaster.handleDisplayToastBundledError(getBaseContext(), result);
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private void handleGetAccountAndCardAsync() {
|
||||
if (flockAccount.isPresent() && cardInformation.isPresent()) {
|
||||
handleRefreshForm(false);
|
||||
return;
|
||||
}
|
||||
|
||||
asyncTask = new AsyncTask<String, Void, Bundle>() {
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
Log.d(TAG, "handleGetAccountAndCardAsync()");
|
||||
setProgressBarIndeterminateVisibility(true);
|
||||
setProgressBarVisibility(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bundle doInBackground(String... params) {
|
||||
Bundle result = new Bundle();
|
||||
RegistrationApi registrationApi = new RegistrationApi(getBaseContext());
|
||||
|
||||
try {
|
||||
|
||||
if (!flockAccount.isPresent()) {
|
||||
|
||||
AugmentedFlockAccount augmentedAccount = registrationApi.getAccount(davAccount);
|
||||
flockAccount = Optional.of((FlockAccount) augmentedAccount);
|
||||
}
|
||||
|
||||
if (!cardInformation.isPresent())
|
||||
cardInformation = registrationApi.getCard(davAccount);
|
||||
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SUCCESS);
|
||||
|
||||
} catch (RegistrationApiException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
} catch (IOException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Bundle result) {
|
||||
asyncTask = null;
|
||||
setProgressBarIndeterminateVisibility(false);
|
||||
setProgressBarVisibility(false);
|
||||
|
||||
if (result.getInt(ErrorToaster.KEY_STATUS_CODE) == ErrorToaster.CODE_SUCCESS)
|
||||
handleRefreshForm(false);
|
||||
else
|
||||
ErrorToaster.handleDisplayToastBundledError(getBaseContext(), result);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
}
|
||||
@ -1,101 +0,0 @@
|
||||
/*
|
||||
* *
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* /
|
||||
*/
|
||||
package org.anhonesteffort.flock;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* rhodey
|
||||
*/
|
||||
public class EolNotifier extends BroadcastReceiver {
|
||||
|
||||
public static final String INTENT_ALARM_24_HOURS = "org.anhonesteffort.flock.INTENT_ALARM_24_HOURS";
|
||||
public static final String KEY_TIME_LAST_ALARM = "KEY_TIME_LAST_ALARM";
|
||||
|
||||
private static final String TAG = EolNotifier.class.getSimpleName();
|
||||
|
||||
private Long getMsSinceLastAlarm(Context context) {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
Long timeLastAlarm = preferences.getLong(KEY_TIME_LAST_ALARM, -1);
|
||||
|
||||
if (timeLastAlarm < 0 || timeLastAlarm > System.currentTimeMillis())
|
||||
return AlarmManager.INTERVAL_DAY;
|
||||
|
||||
return System.currentTimeMillis() - timeLastAlarm;
|
||||
}
|
||||
|
||||
private void handleDeviceBooted(Context context) {
|
||||
Long msSinceLastAlarm = getMsSinceLastAlarm(context);
|
||||
Long msTillNextAlarm = AlarmManager.INTERVAL_DAY - msSinceLastAlarm;
|
||||
|
||||
if (msTillNextAlarm < 0)
|
||||
msTillNextAlarm = 0L;
|
||||
|
||||
Intent alarmIntent = new Intent(INTENT_ALARM_24_HOURS);
|
||||
PendingIntent pendingAlarm = PendingIntent.getBroadcast(context, 0, alarmIntent, 0);
|
||||
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
||||
|
||||
alarmManager.setInexactRepeating(
|
||||
AlarmManager.RTC,
|
||||
System.currentTimeMillis() + msTillNextAlarm,
|
||||
AlarmManager.INTERVAL_DAY,
|
||||
pendingAlarm
|
||||
);
|
||||
|
||||
Log.d(TAG, "scheduled 24 hour alarm to begin firing repeatedly in " + msTillNextAlarm + "ms");
|
||||
}
|
||||
|
||||
private void handleAlarmFired(Context context) {
|
||||
Log.d(TAG, "EOL alarm fired");
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
preferences.edit().putLong(KEY_TIME_LAST_ALARM, System.currentTimeMillis()).apply();
|
||||
NotificationDrawer.handleNotifyEol(context);
|
||||
}
|
||||
|
||||
private void scheduleAlarmIfNotExists(Context context) {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
if (preferences.getLong(KEY_TIME_LAST_ALARM, -1) == -1L)
|
||||
handleDeviceBooted(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent.getAction().equals(Intent.ACTION_MY_PACKAGE_REPLACED)) {
|
||||
Intent nextIntent = new Intent(context, EolActivity.class);
|
||||
nextIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
nextIntent.putExtra(EolActivity.EXTRA_BACK_DISABLED, true);
|
||||
context.startActivity(nextIntent);
|
||||
scheduleAlarmIfNotExists(context);
|
||||
}
|
||||
else if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED))
|
||||
handleDeviceBooted(context);
|
||||
else if (intent.getAction().equals(INTENT_ALARM_24_HOURS))
|
||||
handleAlarmFired(context);
|
||||
else
|
||||
Log.e(TAG, "received broadcast intent with unknown action " + intent.getAction());
|
||||
}
|
||||
|
||||
}
|
||||
@ -31,15 +31,13 @@ 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.PaymentRequiredException;
|
||||
import org.anhonesteffort.flock.registration.RegistrationApiParseException;
|
||||
import org.anhonesteffort.flock.registration.RegistrationApiClientException;
|
||||
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;
|
||||
@ -93,24 +91,20 @@ 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 RegistrationApiParseException)
|
||||
else if (e instanceof RegistrationApiClientException)
|
||||
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() == WebDavConstants.SC_UNAUTHORIZED)
|
||||
if (ex.getErrorCode() == DavServletResponse.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);
|
||||
@ -125,7 +119,7 @@ public class ErrorToaster {
|
||||
|
||||
else if (e instanceof InvalidComponentException) {
|
||||
InvalidComponentException ex = (InvalidComponentException) e;
|
||||
if (ex instanceof InvalidRemoteComponentException)
|
||||
if (ex.isServersFault())
|
||||
bundle.putInt(KEY_STATUS_CODE, CODE_DAV_SERVER_ERROR);
|
||||
else
|
||||
bundle.putInt(KEY_STATUS_CODE, CODE_DAV_CLIENT_ERROR);
|
||||
@ -168,7 +162,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);
|
||||
@ -271,10 +265,6 @@ public class ErrorToaster {
|
||||
handleShowAccountManagerError(context);
|
||||
break;
|
||||
|
||||
case CODE_GOOGLE_PLAY_ERROR:
|
||||
handleShowGooglePlayError(context);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -387,8 +377,4 @@ 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,462 +0,0 @@
|
||||
/*
|
||||
* *
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* /
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import net.fortuna.ical4j.data.CalendarOutputter;
|
||||
import net.fortuna.ical4j.model.Calendar;
|
||||
import net.fortuna.ical4j.model.Property;
|
||||
import net.fortuna.ical4j.model.ValidationException;
|
||||
import net.fortuna.ical4j.model.component.VEvent;
|
||||
import net.fortuna.ical4j.model.property.Name;
|
||||
import net.fortuna.ical4j.model.property.Version;
|
||||
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.sync.AbstractLocalComponentCollection;
|
||||
import org.anhonesteffort.flock.sync.InvalidLocalComponentException;
|
||||
import org.anhonesteffort.flock.sync.addressbook.AddressbookSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.addressbook.ContactFactory;
|
||||
import org.anhonesteffort.flock.sync.addressbook.LocalAddressbookStore;
|
||||
import org.anhonesteffort.flock.sync.addressbook.LocalContactCollection;
|
||||
import org.anhonesteffort.flock.sync.calendar.CalendarsSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.calendar.LocalCalendarStore;
|
||||
import org.anhonesteffort.flock.sync.calendar.LocalEventCollection;
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import ezvcard.VCard;
|
||||
import ezvcard.VCardVersion;
|
||||
import ezvcard.io.text.VCardWriter;
|
||||
import ezvcard.property.Photo;
|
||||
import ezvcard.property.Uid;
|
||||
|
||||
/**
|
||||
* Programmer: rhodey
|
||||
*/
|
||||
public class ExportService extends Service {
|
||||
|
||||
private static final String TAG = ExportService.class.getSimpleName();
|
||||
|
||||
private static final int NOTIFY_ID = 1025;
|
||||
|
||||
private ServiceHandler serviceHandler;
|
||||
private NotificationManager notifyManager;
|
||||
private NotificationCompat.Builder notificationBuilder;
|
||||
|
||||
private int countFailedContactExports = 0;
|
||||
private int countFailedEventExports = 0;
|
||||
|
||||
private enum EndState {
|
||||
SUCCESS, PROMPT_LOGIN,
|
||||
PROMPT_MAKE_SPACE, PROMPT_RESTART
|
||||
}
|
||||
private EndState endState = null;
|
||||
|
||||
private void handleContactExportFailed() {
|
||||
countFailedContactExports++;
|
||||
Log.d(TAG, "contact export failed, counter: " + countFailedContactExports);
|
||||
}
|
||||
|
||||
private void handleEventExportFailed() {
|
||||
countFailedEventExports++;
|
||||
Log.d(TAG, "event export failed, counter: " + countFailedEventExports);
|
||||
}
|
||||
|
||||
private void handleInitializeNotification() {
|
||||
notificationBuilder
|
||||
.setProgress(0, 0, true)
|
||||
.setContentTitle(getString(R.string.export))
|
||||
.setContentText(getString(R.string.exporting_contacts_and_calendars))
|
||||
.setSmallIcon(R.drawable.flock_actionbar_icon);
|
||||
|
||||
startForeground(NOTIFY_ID, notificationBuilder.build());
|
||||
}
|
||||
|
||||
private Optional<LocalContactCollection> getAddressbook(ContentProviderClient client, DavAccount account) {
|
||||
LocalAddressbookStore addressbookStore = new LocalAddressbookStore(getBaseContext(), client, account);
|
||||
List<LocalContactCollection> addressbooks = addressbookStore.getCollections();
|
||||
|
||||
if (addressbooks.isEmpty()) return Optional.absent();
|
||||
else return Optional.of(addressbooks.get(0));
|
||||
}
|
||||
|
||||
private List<LocalEventCollection> getCalendars(ContentProviderClient client, Account account)
|
||||
throws RemoteException
|
||||
{
|
||||
LocalCalendarStore calendarStore = new LocalCalendarStore(client, account);
|
||||
return calendarStore.getCollections();
|
||||
}
|
||||
|
||||
private Optional<File> createExternalFile(String filename) {
|
||||
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
Log.w(TAG, "external media not mounted?");
|
||||
return Optional.absent();
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
File file = new File(Environment.getExternalStorageDirectory(), filename);
|
||||
|
||||
if (file.exists()) return Optional.of(file);
|
||||
else if (file.createNewFile()) return Optional.of(file);
|
||||
|
||||
return Optional.absent();
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "unable to create file " + filename, e);
|
||||
return Optional.absent();
|
||||
}
|
||||
}
|
||||
|
||||
private List<File> createFilesForCollections(List<AbstractLocalComponentCollection<?>> collections) {
|
||||
List<File> files = new LinkedList<>();
|
||||
Optional<File> file = null;
|
||||
int calCount = 1;
|
||||
|
||||
for (AbstractLocalComponentCollection collection : collections) {
|
||||
if (collection instanceof LocalContactCollection)
|
||||
file = createExternalFile(getString(R.string.flock_contacts_vcf));
|
||||
else {
|
||||
file = createExternalFile(getString(R.string.flock_calendar_ical, calCount));
|
||||
calCount++;
|
||||
}
|
||||
if (file.isPresent())
|
||||
files.add(file.get());
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
private void simulateExport(AbstractLocalComponentCollection<?> collection, File output)
|
||||
throws IOException, RemoteException
|
||||
{
|
||||
FileOutputStream stream = new FileOutputStream(output, false);
|
||||
|
||||
try {
|
||||
|
||||
for (int i = 0; i < collection.getComponentIds().size(); i++)
|
||||
stream.write(new byte[512]);
|
||||
|
||||
} finally {
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isStorageSpaceAvailable(List<AbstractLocalComponentCollection<?>> collections, List<File> files)
|
||||
throws RemoteException
|
||||
{
|
||||
if (files.size() != collections.size()) {
|
||||
Log.w(TAG, "collection count and output file count differ");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
for (int i = 0; i < collections.size(); i++)
|
||||
simulateExport(collections.get(i), files.get(i));
|
||||
return true;
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "error during export simulation, not enough space?", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleExportContacts(LocalContactCollection addressbook, File output)
|
||||
throws RemoteException, IOException
|
||||
{
|
||||
VCardWriter vCardWriter = new VCardWriter(output, false, VCardVersion.V3_0);
|
||||
|
||||
try {
|
||||
for (Long contactId : addressbook.getComponentIds()) {
|
||||
try {
|
||||
|
||||
Optional<VCard> vCard = addressbook.getComponent(contactId);
|
||||
if (vCard.isPresent()) {
|
||||
vCard.get().removeProperties(Uid.class);
|
||||
vCard.get().removeProperties(Photo.class);
|
||||
vCard.get().removeExtendedProperty(ContactFactory.PROPERTY_STARRED);
|
||||
vCardWriter.write(vCard.get());
|
||||
} else {
|
||||
Log.w(TAG, "couldn't find " + contactId + " in addressbook");
|
||||
}
|
||||
|
||||
} catch (InvalidLocalComponentException e) {
|
||||
handleContactExportFailed();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
vCardWriter.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleExportCalendars(List<LocalEventCollection> eventCollections, List<File> outputs)
|
||||
throws ValidationException, RemoteException, IOException
|
||||
{
|
||||
CalendarOutputter calendarWriter = new CalendarOutputter(false);
|
||||
for (int i = 0; i < eventCollections.size(); i++) {
|
||||
LocalEventCollection eventCollection = eventCollections.get(i);
|
||||
List<Long> eventIds = eventCollection.getComponentIds();
|
||||
Calendar calendar = new Calendar();
|
||||
FileOutputStream output = new FileOutputStream(outputs.get(i), false);
|
||||
|
||||
Optional<String> displayName = eventCollection.getDisplayName();
|
||||
if (displayName.isPresent() && !displayName.get().isEmpty())
|
||||
calendar.getProperties().add(new Name(displayName.get()));
|
||||
|
||||
try {
|
||||
|
||||
for (Long eventId : eventIds) {
|
||||
try {
|
||||
|
||||
Optional<Calendar> event = eventCollection.getComponent(eventId);
|
||||
if (event.isPresent()) {
|
||||
VEvent vEvent = (VEvent) event.get().getComponent(VEvent.VEVENT);
|
||||
if (vEvent != null) {
|
||||
if (vEvent.getProperty(Property.ORGANIZER) != null)
|
||||
vEvent.getProperties().remove(vEvent.getProperty(Property.ORGANIZER));
|
||||
calendar.getComponents().add(vEvent);
|
||||
}
|
||||
else
|
||||
Log.w(TAG, "couldn't parse VEVENT from local calendar component");
|
||||
} else {
|
||||
Log.w(TAG, "couldn't find " + eventId + " in calendar " + eventCollection.getPath());
|
||||
}
|
||||
|
||||
} catch (InvalidLocalComponentException e) {
|
||||
handleEventExportFailed();
|
||||
}
|
||||
}
|
||||
|
||||
calendar.getProperties().add(Version.VERSION_2_0);
|
||||
calendarWriter.output(calendar, output);
|
||||
|
||||
} finally {
|
||||
output.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleIndexFilesWithMediaScanner(List<File> files) {
|
||||
for (File file : files) {
|
||||
sendBroadcast(new Intent(
|
||||
Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,
|
||||
Uri.fromFile(file)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
private void handleExportComplete(EndState endState) {
|
||||
Log.d(TAG, "HANDLE EXPORT COMPLETE: " + endState);
|
||||
this.endState = endState;
|
||||
stopForeground(false);
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
private void handlePromptLoginAndRetry() {
|
||||
Log.w(TAG, "HANDLE PROMPT LOGIN AND RETRY");
|
||||
|
||||
Intent clickIntent = new Intent(getBaseContext(), CorrectPasswordActivity.class);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(getBaseContext(), 0, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
notificationBuilder
|
||||
.setAutoCancel(true)
|
||||
.setProgress(0, 0, false)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setContentTitle(getString(R.string.export_failed))
|
||||
.setContentText(getString(R.string.tap_to_login_then_retry_export));
|
||||
notifyManager.notify(NOTIFY_ID, notificationBuilder.build());
|
||||
}
|
||||
|
||||
private void handlePromptClearSpaceAndRetry() {
|
||||
Log.w(TAG, "HANDLE PROMPT CLEAR SPACE AND RETRY");
|
||||
notificationBuilder
|
||||
.setProgress(0, 0, false)
|
||||
.setContentTitle(getString(R.string.export_failed))
|
||||
.setContentText(getString(R.string.try_making_more_storage_space_available));
|
||||
notifyManager.notify(NOTIFY_ID, notificationBuilder.build());
|
||||
}
|
||||
|
||||
private void handleUnrecoverableError() {
|
||||
Log.w(TAG, "HANDLE UNRECOVERABLE ERROR");
|
||||
notificationBuilder
|
||||
.setProgress(0, 0, false)
|
||||
.setContentTitle(getString(R.string.export_failed))
|
||||
.setContentText(getString(R.string.try_a_separate_export_app_if_error_continues));
|
||||
notifyManager.notify(NOTIFY_ID, notificationBuilder.build());
|
||||
}
|
||||
|
||||
private void handleStartExport() {
|
||||
Log.d(TAG, "HANDLE START EXPORT");
|
||||
handleInitializeNotification();
|
||||
|
||||
try {
|
||||
Optional<DavAccount> account = DavAccountHelper.getAccount(getBaseContext());
|
||||
ContentProviderClient contactClient = getBaseContext().getContentResolver()
|
||||
.acquireContentProviderClient(AddressbookSyncScheduler.CONTENT_AUTHORITY);
|
||||
ContentProviderClient calendarClient = getBaseContext().getContentResolver()
|
||||
.acquireContentProviderClient(CalendarsSyncScheduler.CONTENT_AUTHORITY);
|
||||
|
||||
if (account.isPresent()) {
|
||||
try {
|
||||
|
||||
Optional<LocalContactCollection> addressbook = getAddressbook(contactClient, account.get());
|
||||
List<LocalEventCollection> calendars = getCalendars(calendarClient, account.get().getOsAccount());
|
||||
List<AbstractLocalComponentCollection<?>> collections = new LinkedList<>();
|
||||
|
||||
if (!addressbook.isPresent()) {
|
||||
throw new RemoteException("addressbook missing, what is going on?");
|
||||
}
|
||||
|
||||
collections.add(addressbook.get());
|
||||
collections.addAll(calendars);
|
||||
List<File> outputFiles = createFilesForCollections(collections);
|
||||
|
||||
if (isStorageSpaceAvailable(collections, outputFiles)) {
|
||||
File contactsFile = outputFiles.remove(0);
|
||||
handleExportContacts(addressbook.get(), contactsFile);
|
||||
handleExportCalendars(calendars, outputFiles);
|
||||
outputFiles.add(contactsFile);
|
||||
handleIndexFilesWithMediaScanner(outputFiles);
|
||||
handleExportComplete(EndState.SUCCESS);
|
||||
return;
|
||||
} else {
|
||||
handleExportComplete(EndState.PROMPT_MAKE_SPACE);
|
||||
return;
|
||||
}
|
||||
|
||||
} catch (ValidationException e) {
|
||||
Log.e(TAG, "WTF ical4j", e);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "why android?", e);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "why android?", e);
|
||||
}
|
||||
} else {
|
||||
handleExportComplete(EndState.PROMPT_LOGIN);
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "caught unexpected runtime exception", e);
|
||||
}
|
||||
|
||||
handleExportComplete(EndState.PROMPT_RESTART);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
Log.d(TAG, "ON DESTROY");
|
||||
|
||||
switch (endState) {
|
||||
case PROMPT_LOGIN:
|
||||
handlePromptLoginAndRetry();
|
||||
break;
|
||||
|
||||
case PROMPT_MAKE_SPACE:
|
||||
handlePromptClearSpaceAndRetry();
|
||||
break;
|
||||
|
||||
case PROMPT_RESTART:
|
||||
handleUnrecoverableError();
|
||||
break;
|
||||
}
|
||||
|
||||
if (endState != EndState.SUCCESS) {
|
||||
Toast.makeText(getBaseContext(), R.string.export_failed, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (countFailedContactExports == 0 && countFailedEventExports == 0) {
|
||||
notificationBuilder
|
||||
.setProgress(0, 0, false)
|
||||
.setContentTitle(getString(R.string.export_complete))
|
||||
.setContentText(getString(R.string.export_completed_successfully));
|
||||
} else {
|
||||
notificationBuilder
|
||||
.setProgress(0, 0, false)
|
||||
.setContentTitle(getString(R.string.export_complete))
|
||||
.setContentText(getString(
|
||||
R.string.failed_to_copy_contacts_and_events,
|
||||
countFailedContactExports,
|
||||
countFailedEventExports
|
||||
));
|
||||
}
|
||||
|
||||
notifyManager.notify(NOTIFY_ID, notificationBuilder.build());
|
||||
Toast.makeText(getBaseContext(), R.string.export_complete, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
HandlerThread thread = new HandlerThread(getClass().getSimpleName(), HandlerThread.NORM_PRIORITY);
|
||||
thread.start();
|
||||
|
||||
serviceHandler = new ServiceHandler(thread.getLooper());
|
||||
notifyManager = (NotificationManager)getBaseContext().getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationBuilder = new NotificationCompat.Builder(getBaseContext());
|
||||
}
|
||||
|
||||
private final class ServiceHandler extends Handler {
|
||||
|
||||
public ServiceHandler(Looper looper) {
|
||||
super(looper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
handleStartExport();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
serviceHandler.sendMessage(serviceHandler.obtainMessage());
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@ -5,15 +5,18 @@ import android.app.Service;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import com.google.common.base.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;
|
||||
|
||||
@ -53,6 +56,15 @@ 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);
|
||||
@ -62,8 +74,14 @@ public abstract class ImportAccountService extends Service {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_DAV_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
keyCollection.get().setMigrationComplete(getBaseContext());
|
||||
MigrationHelperBroadcastReceiver.setMigrationUpdateHandled(getBaseContext());
|
||||
MigrationHelperBroadcastReceiver.setUiDisabledForMigration(getBaseContext(), false);
|
||||
}
|
||||
|
||||
} catch (InvalidComponentException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
} catch (PropertyParseException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
} catch (DavException e) {
|
||||
|
||||
@ -41,8 +41,8 @@ import android.widget.ListView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.chiralcode.colorpicker.ColorPicker;
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.sync.calendar.LocalCalendarStore;
|
||||
import org.anhonesteffort.flock.sync.calendar.LocalEventCollection;
|
||||
|
||||
@ -82,7 +82,7 @@ public class ImportCalendarsFragment extends AccountAndKeyRequiredFragment
|
||||
{
|
||||
View fragmentView = inflater.inflate(R.layout.fragment_simple_list, container, false);
|
||||
|
||||
if (!accountAndKeyAvailable())
|
||||
if (!accountAndKeyAvailableAndMigrationComplete())
|
||||
return fragmentView;
|
||||
|
||||
initButtons();
|
||||
@ -94,7 +94,7 @@ public class ImportCalendarsFragment extends AccountAndKeyRequiredFragment
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (!accountAndKeyAvailable())
|
||||
if (!accountAndKeyAvailableAndMigrationComplete())
|
||||
return ;
|
||||
|
||||
initializeList();
|
||||
@ -115,7 +115,9 @@ public class ImportCalendarsFragment extends AccountAndKeyRequiredFragment
|
||||
Log.d(TAG, "handleBackgroundImportStarted()");
|
||||
|
||||
if (selectedCalendars.size() > 0) {
|
||||
String toastMessage = getString(R.string.started_background_import_of_calendars, selectedCalendars.size());
|
||||
String toastMessage = getString(R.string.started_background_import_of) + " " +
|
||||
selectedCalendars.size() + " " +
|
||||
getString(R.string.calendars);
|
||||
Toast.makeText(getActivity(), toastMessage, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
|
||||
@ -75,7 +75,7 @@ public class ImportContactsFragment extends AccountAndKeyRequiredFragment
|
||||
{
|
||||
View fragmentView = inflater.inflate(R.layout.fragment_simple_list, container, false);
|
||||
|
||||
if (!accountAndKeyAvailable())
|
||||
if (!accountAndKeyAvailableAndMigrationComplete())
|
||||
return fragmentView;
|
||||
|
||||
initButtons();
|
||||
@ -87,7 +87,7 @@ public class ImportContactsFragment extends AccountAndKeyRequiredFragment
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (!accountAndKeyAvailable())
|
||||
if (!accountAndKeyAvailableAndMigrationComplete())
|
||||
return ;
|
||||
|
||||
initializeList();
|
||||
@ -178,7 +178,8 @@ public class ImportContactsFragment extends AccountAndKeyRequiredFragment
|
||||
contactCount += copyAccount.contact_count;
|
||||
|
||||
if (selectedAccounts.size() > 0) {
|
||||
String toastMessage = getString(R.string.started_background_import_of_contacts, contactCount);
|
||||
String toastMessage = getString(R.string.started_background_import_of) + " " +
|
||||
contactCount + " " + getString(R.string.contacts);
|
||||
Toast.makeText(getActivity(), toastMessage, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
|
||||
@ -36,7 +36,7 @@ import android.widget.EditText;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.util.PasswordUtil;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
@ -33,7 +33,7 @@ import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
@ -134,6 +134,10 @@ public class ImportOwsAccountFragment extends Fragment {
|
||||
Toast.makeText(getActivity(),
|
||||
R.string.notification_flock_subscription_expired,
|
||||
Toast.LENGTH_LONG).show();
|
||||
|
||||
Intent nextIntent = new Intent(getActivity(), ManageSubscriptionActivity.class);
|
||||
nextIntent.putExtra(ManageSubscriptionActivity.KEY_DAV_ACCOUNT_BUNDLE, account.toBundle());
|
||||
startActivity(nextIntent);
|
||||
}
|
||||
|
||||
private void handleImportAccountAsync() {
|
||||
|
||||
@ -14,7 +14,8 @@ import android.os.RemoteException;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.crypto.InvalidMacException;
|
||||
import org.anhonesteffort.flock.crypto.KeyHelper;
|
||||
|
||||
@ -22,12 +22,9 @@ 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
|
||||
@ -52,9 +49,7 @@ public class IntroductionFragment extends Fragment {
|
||||
Bundle savedInstanceState)
|
||||
{
|
||||
View view = inflater.inflate(R.layout.fragment_intro, container, false);
|
||||
|
||||
initButtons();
|
||||
initDescription(view);
|
||||
|
||||
return view;
|
||||
}
|
||||
@ -69,11 +64,4 @@ public class IntroductionFragment extends Fragment {
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private void initDescription(View fragmentView) {
|
||||
final TextView appDescription = (TextView) fragmentView.findViewById(R.id.flock_description);
|
||||
|
||||
appDescription.setText(Html.fromHtml(getString(R.string.flock_syncs_your_contacts_and_calendars_between_multiple_devices)));
|
||||
appDescription.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.sync.calendar.LocalEventCollection;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -0,0 +1,398 @@
|
||||
/*
|
||||
* *
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* *
|
||||
* Copyright (C) 2014 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* /
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.sync.addressbook.AddressbookSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.addressbook.AddressbookSyncWorker;
|
||||
import org.anhonesteffort.flock.sync.calendar.CalendarsSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.key.DavKeyCollection;
|
||||
import org.anhonesteffort.flock.sync.key.KeySyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.key.KeySyncWorker;
|
||||
|
||||
/**
|
||||
* rhodey.
|
||||
*/
|
||||
public class MigrationHelperBroadcastReceiver extends BroadcastReceiver {
|
||||
|
||||
private static final String TAG = MigrationHelperBroadcastReceiver.class.getSimpleName();
|
||||
|
||||
private static final String KEY_MIGRATION_UPDATED_HANDLED = "MigrationHelperBroadcastReceiver.KEY_MIGRATION_UPDATED_HANDLED";
|
||||
private static final String KEY_UI_DISABLED_FOR_MIGRATION = "MigrationHelperBroadcastReceiver.KEY_UI_DISABLED_FOR_MIGRATION";
|
||||
|
||||
public static void setMigrationUpdateHandled(Context context) {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
preferences.edit().putBoolean(KEY_MIGRATION_UPDATED_HANDLED, true).commit();
|
||||
}
|
||||
|
||||
private static boolean getMigrationUpdateHandled(Context context) {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
return preferences.getBoolean(KEY_MIGRATION_UPDATED_HANDLED, false);
|
||||
}
|
||||
|
||||
public static void setUiDisabledForMigration(Context context,
|
||||
boolean uiDisabled)
|
||||
{
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
preferences.edit().putBoolean(KEY_UI_DISABLED_FOR_MIGRATION, uiDisabled).commit();
|
||||
}
|
||||
|
||||
public static boolean getUiDisabledForMigration(Context context) {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
return preferences.getBoolean(KEY_UI_DISABLED_FOR_MIGRATION, false);
|
||||
}
|
||||
|
||||
private void handleActionMyPackageReplaced(Context context) {
|
||||
Log.d(TAG, "handleActionMyPackageReplaced");
|
||||
|
||||
if (MigrationHelperBroadcastReceiver.getMigrationUpdateHandled(context)) {
|
||||
Log.d(TAG, "migration already handled for this update, nothing to do.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!DavAccountHelper.isUsingOurServers(context)) {
|
||||
Intent nextIntent = new Intent(context, MigrationReleaseNotesActivity.class);
|
||||
nextIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(nextIntent);
|
||||
}
|
||||
|
||||
new KeySyncScheduler(context).requestSync();
|
||||
MigrationHelperBroadcastReceiver.setUiDisabledForMigration(context, true);
|
||||
MigrationHelperBroadcastReceiver.setMigrationUpdateHandled(context);
|
||||
|
||||
Optional<Account> account = DavAccountHelper.getOsAccount(context);
|
||||
if (account.isPresent()) {
|
||||
new CalendarsSyncScheduler(context).cancelPendingSyncs(account.get());
|
||||
new AddressbookSyncScheduler(context).cancelPendingSyncs(account.get());
|
||||
|
||||
new CalendarsSyncScheduler(context).setSyncEnabled(account.get(), false);
|
||||
new AddressbookSyncScheduler(context).setSyncEnabled(account.get(), false);
|
||||
}
|
||||
else
|
||||
Log.w(TAG, "account not present at handleActionMyPackageReplaced()");
|
||||
}
|
||||
|
||||
private void handleActionMigrationStarted(Context context) {
|
||||
Log.d(TAG, "handleActionMigrationStarted");
|
||||
MigrationHelperBroadcastReceiver.setUiDisabledForMigration(context, true);
|
||||
}
|
||||
|
||||
private void handleActionMigrationComplete(Context context) {
|
||||
Log.d(TAG, "handleActionMigrationComplete");
|
||||
MigrationHelperBroadcastReceiver.setUiDisabledForMigration(context, false);
|
||||
}
|
||||
|
||||
private void handleActionKeyMaterialImported(Context context) {
|
||||
Log.d(TAG, "handleActionKeyMaterialImported");
|
||||
|
||||
if (!DavKeyCollection.weStartedMigration(context))
|
||||
MigrationHelperBroadcastReceiver.setUiDisabledForMigration(context, false);
|
||||
}
|
||||
|
||||
private void handleActionPushCreatedContacts(Context context) {
|
||||
Log.d(TAG, "handleActionPushCreatedContacts");
|
||||
MigrationService.hackOnActionPushCreatedContacts(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Log.d(TAG, "onReceive()");
|
||||
|
||||
if (intent == null || intent.getAction() == null) {
|
||||
Log.e(TAG, "received null intent or intent with null action.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (intent.getAction().equals(Intent.ACTION_MY_PACKAGE_REPLACED))
|
||||
handleActionMyPackageReplaced(context);
|
||||
else if (intent.getAction().equals(AddressbookSyncWorker.ACTION_PUSH_CREATED_CONTACTS))
|
||||
handleActionPushCreatedContacts(context);
|
||||
else if (intent.getPackage() == null ||
|
||||
!intent.getPackage().equals(MigrationHelperBroadcastReceiver.class.getPackage().getName()))
|
||||
{
|
||||
Log.e(TAG, "received intent from untrusted package.");
|
||||
}
|
||||
else if (intent.getAction().equals(MigrationService.ACTION_MIGRATION_STARTED))
|
||||
handleActionMigrationStarted(context);
|
||||
else if (intent.getAction().equals(MigrationService.ACTION_MIGRATION_COMPLETE))
|
||||
handleActionMigrationComplete(context);
|
||||
else if (intent.getAction().equals(KeySyncWorker.ACTION_KEY_MATERIAL_IMPORTED))
|
||||
handleActionKeyMaterialImported(context);
|
||||
else
|
||||
Log.e(TAG, "received intent with unwanted action.");
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
* 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
|
||||
@ -16,47 +16,44 @@
|
||||
* 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 EolActivity extends Activity {
|
||||
|
||||
public static final String EXTRA_BACK_DISABLED = "EolActivity.EXTRA_BACK_DISABLED";
|
||||
public class MigrationReleaseNotesActivity extends Activity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.eol_activity);
|
||||
setContentView(R.layout.migration_release_notes);
|
||||
getActionBar().setDisplayHomeAsUpEnabled(false);
|
||||
getActionBar().setTitle(R.string.shutting_down);
|
||||
getActionBar().setTitle(R.string.release_notes);
|
||||
|
||||
initButtons();
|
||||
}
|
||||
|
||||
private void initButtons() {
|
||||
findViewById(R.id.button_export).setOnClickListener(new View.OnClickListener() {
|
||||
findViewById(R.id.button_ok).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();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,898 @@
|
||||
/*
|
||||
* *
|
||||
* Copyright (C) 2014 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* /
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.app.Service;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.OperationApplicationException;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.crypto.KeyHelper;
|
||||
import org.anhonesteffort.flock.crypto.KeyStore;
|
||||
import org.anhonesteffort.flock.crypto.MasterCipher;
|
||||
import org.anhonesteffort.flock.registration.RegistrationApi;
|
||||
import org.anhonesteffort.flock.registration.RegistrationApiException;
|
||||
import org.anhonesteffort.flock.sync.AbstractLocalComponentCollection;
|
||||
import org.anhonesteffort.flock.sync.addressbook.AddressbookSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.addressbook.HidingCardDavStore;
|
||||
import org.anhonesteffort.flock.sync.addressbook.LocalAddressbookStore;
|
||||
import org.anhonesteffort.flock.sync.addressbook.LocalContactCollection;
|
||||
import org.anhonesteffort.flock.sync.calendar.CalendarsSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.calendar.HidingCalDavStore;
|
||||
import org.anhonesteffort.flock.sync.calendar.LocalCalendarStore;
|
||||
import org.anhonesteffort.flock.sync.calendar.LocalEventCollection;
|
||||
import org.anhonesteffort.flock.sync.key.DavKeyCollection;
|
||||
import org.anhonesteffort.flock.sync.key.DavKeyStore;
|
||||
import org.anhonesteffort.flock.webdav.InvalidComponentException;
|
||||
import org.anhonesteffort.flock.webdav.PropertyParseException;
|
||||
import org.anhonesteffort.flock.webdav.caldav.CalDavStore;
|
||||
import org.anhonesteffort.flock.webdav.carddav.CardDavStore;
|
||||
import org.apache.jackrabbit.webdav.DavException;
|
||||
import org.apache.jackrabbit.webdav.DavServletResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Date;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
|
||||
/**
|
||||
* rhodey
|
||||
*/
|
||||
public class MigrationService extends Service {
|
||||
|
||||
public static final String ACTION_MIGRATION_STARTED = "org.anhonesteffort.flock.MigrationService.ACTION_MIGRATION_STARTED";
|
||||
public static final String ACTION_MIGRATION_COMPLETE = "org.anhonesteffort.flock.MigrationService.ACTION_MIGRATION_COMPLETE";
|
||||
|
||||
private static final String TAG = MigrationService.class.getSimpleName();
|
||||
private static final String PREFERENCES_NAME = "MigrationService.PREFERENCES_NAME";
|
||||
|
||||
private static final String KEY_STATE = "MigrationService.KEY_STATE";
|
||||
private static final String KEY_TIME_FIRST_CALENDAR_SYNC = "MigrationService.KEY_TIME_FIRST_CALENDAR_SYNC";
|
||||
private static final String KEY_TIME_FIRST_ADDRESSBOOK_SYNC = "MigrationService.KEY_TIME_FIRST_ADDRESSBOOK_SYNC";
|
||||
|
||||
private static final int STATE_STARTED_MIGRATION = 1;
|
||||
private static final int STATE_SYNCED_WITH_REMOTE = 2;
|
||||
private static final int STATE_DELETED_KEY_COLLECTION = 3;
|
||||
private static final int STATE_GENERATED_NEW_KEYS = 4;
|
||||
private static final int STATE_REPLACED_KEY_COLLECTION = 5;
|
||||
private static final int STATE_REPLACED_KEYS = 6;
|
||||
private static final int STATE_DELETED_REMOTE_CALENDARS_AND_ADDRESSBOOKS = 7;
|
||||
private static final int STATE_REPLACED_REMOTE_CALENDARS = 8;
|
||||
private static final int STATE_REPLACED_REMOTE_ADDRESSBOOKS = 9;
|
||||
private static final int STATE_READY_TO_REPLACE_EVENTS_AND_CONTACTS = 10;
|
||||
private static final int STATE_REPLACED_EVENTS_AND_CONTACTS = 11;
|
||||
private static final int STATE_MIGRATION_COMPLETE = 12;
|
||||
|
||||
private static final long KICK_INTERVAL_MILLISECONDS = 5000;
|
||||
|
||||
private final Handler messageHandler = new Handler();
|
||||
private ServiceHandler serviceHandler;
|
||||
private Timer intervalTimer;
|
||||
private NotificationManager notifyManager;
|
||||
private NotificationCompat.Builder notificationBuilder;
|
||||
|
||||
private DavAccount account;
|
||||
private MasterCipher masterCipher;
|
||||
|
||||
private void setState(int state) {
|
||||
Log.d(TAG, "setState() >> " + state);
|
||||
|
||||
SharedPreferences settings =
|
||||
getBaseContext().getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
|
||||
settings.edit().putInt(KEY_STATE, state).commit();
|
||||
handleUpdateNotificationUsingState();
|
||||
}
|
||||
|
||||
private static int getState(Context context) {
|
||||
SharedPreferences settings =
|
||||
context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
|
||||
Log.d(TAG, "getState() >> " + settings.getInt(KEY_STATE, 0));
|
||||
|
||||
return settings.getInt(KEY_STATE, 0);
|
||||
}
|
||||
|
||||
public static void hackOnActionPushCreatedContacts(Context context) {
|
||||
if (getState(context) == STATE_READY_TO_REPLACE_EVENTS_AND_CONTACTS) {
|
||||
Log.d(TAG, "just finished pushing new contacts during replace events and contacts state" +
|
||||
", marking early finish of contact sync.");
|
||||
new AddressbookSyncScheduler(context).setTimeLastSync(new Date().getTime());
|
||||
}
|
||||
}
|
||||
|
||||
private void recordTimeFirstSync(String key, long timeMilliseconds) {
|
||||
Log.d(TAG, "recordTimeFirstSync() >> " + key + " " + timeMilliseconds);
|
||||
|
||||
SharedPreferences settings =
|
||||
getBaseContext().getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
|
||||
settings.edit().putLong(key, timeMilliseconds).commit();
|
||||
}
|
||||
|
||||
private long getTimeFirstSync(String key) {
|
||||
SharedPreferences settings =
|
||||
getBaseContext().getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
|
||||
return settings.getLong(key, -1);
|
||||
}
|
||||
|
||||
private void handleInitializeNotification() {
|
||||
Log.d(TAG, "handleInitializeNotification()");
|
||||
|
||||
notificationBuilder.setContentTitle(getString(R.string.migrating_to_new_version))
|
||||
.setContentText(getString(R.string.preparing_to_upgrade_sync_protocol))
|
||||
.setProgress(0, 0, true)
|
||||
.setSmallIcon(R.drawable.flock_actionbar_icon);
|
||||
|
||||
startForeground(1030, notificationBuilder.build());
|
||||
}
|
||||
|
||||
private void handleAskUserToEnableSync() {
|
||||
Log.d(TAG, "handleAskUserToEnableSync()");
|
||||
|
||||
notificationBuilder.setContentTitle(getString(R.string.please_enable_sync))
|
||||
.setContentText(getString(R.string.please_enable_anrdoid_sync_to_complete_migration))
|
||||
.setProgress(0, 0, false)
|
||||
.setSmallIcon(R.drawable.alert_warning_light);
|
||||
|
||||
startForeground(1030, notificationBuilder.build());
|
||||
}
|
||||
|
||||
private void handleUpdateNotificationUsingState() {
|
||||
Log.d(TAG, "handleUpdateNotificationUsingState() >> " + getState(getBaseContext()));
|
||||
|
||||
notificationBuilder.setContentTitle(getString(R.string.migrating_to_new_version))
|
||||
.setSmallIcon(R.drawable.flock_actionbar_icon)
|
||||
.setProgress(0, 0, true);
|
||||
|
||||
switch (getState(getBaseContext())) {
|
||||
case STATE_STARTED_MIGRATION:
|
||||
notificationBuilder.setContentText(getString(R.string.checking_sync_for_new_contacts_and_calendar));
|
||||
break;
|
||||
|
||||
case STATE_SYNCED_WITH_REMOTE:
|
||||
case STATE_DELETED_KEY_COLLECTION:
|
||||
case STATE_GENERATED_NEW_KEYS:
|
||||
case STATE_REPLACED_KEY_COLLECTION:
|
||||
notificationBuilder.setContentText(getString(R.string.generating_new_encryption_secrets));
|
||||
break;
|
||||
|
||||
case STATE_REPLACED_KEYS:
|
||||
case STATE_DELETED_REMOTE_CALENDARS_AND_ADDRESSBOOKS:
|
||||
case STATE_REPLACED_REMOTE_CALENDARS:
|
||||
notificationBuilder.setContentText(getString(R.string.replacing_addressbooks_and_calendars));
|
||||
break;
|
||||
|
||||
case STATE_REPLACED_REMOTE_ADDRESSBOOKS:
|
||||
case STATE_READY_TO_REPLACE_EVENTS_AND_CONTACTS:
|
||||
notificationBuilder.setContentText(getString(R.string.replacing_old_contacts_and_events));
|
||||
break;
|
||||
|
||||
case STATE_REPLACED_EVENTS_AND_CONTACTS:
|
||||
notificationBuilder.setContentText(getString(R.string.finalizing_migration));
|
||||
break;
|
||||
|
||||
case STATE_MIGRATION_COMPLETE:
|
||||
notificationBuilder.setContentTitle(getString(R.string.migration_complete));
|
||||
notificationBuilder.setContentText(getString(R.string.please_update_flock_on_all_your_devices));
|
||||
break;
|
||||
}
|
||||
|
||||
notifyManager.notify(1030, notificationBuilder.build());
|
||||
}
|
||||
|
||||
private void handleMigrationComplete() {
|
||||
Log.d(TAG, "handleMigrationComplete()");
|
||||
|
||||
if (intervalTimer != null)
|
||||
intervalTimer.cancel();
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setPackage(MigrationHelperBroadcastReceiver.class.getPackage().getName());
|
||||
intent.setAction(ACTION_MIGRATION_COMPLETE);
|
||||
sendBroadcast(intent);
|
||||
|
||||
stopForeground(false);
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
Log.d(TAG, "onDestroy()");
|
||||
|
||||
if (getState(getBaseContext()) == STATE_MIGRATION_COMPLETE) {
|
||||
notificationBuilder
|
||||
.setSmallIcon(R.drawable.flock_actionbar_icon)
|
||||
.setContentTitle(getString(R.string.migration_complete))
|
||||
.setContentText(getString(R.string.please_update_flock_on_all_your_devices))
|
||||
.setProgress(0, 0, false);
|
||||
|
||||
notifyManager.notify(1030, notificationBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleEnableAllSyncAdapters() {
|
||||
new CalendarsSyncScheduler(getBaseContext()).setSyncEnabled(account.getOsAccount(), true);
|
||||
new AddressbookSyncScheduler(getBaseContext()).setSyncEnabled(account.getOsAccount(), true);
|
||||
}
|
||||
|
||||
private void handleDisableAllSyncAdapters() {
|
||||
new CalendarsSyncScheduler(getBaseContext()).setSyncEnabled(account.getOsAccount(), false);
|
||||
new AddressbookSyncScheduler(getBaseContext()).setSyncEnabled(account.getOsAccount(), false);
|
||||
|
||||
new CalendarsSyncScheduler(getBaseContext()).cancelPendingSyncs(account.getOsAccount());
|
||||
new AddressbookSyncScheduler(getBaseContext()).cancelPendingSyncs(account.getOsAccount());
|
||||
}
|
||||
|
||||
private void handleException(Exception e) {
|
||||
if (e instanceof IOException) {
|
||||
IOException ex = (IOException) e;
|
||||
|
||||
if (ex instanceof SocketException ||
|
||||
ex instanceof UnknownHostException ||
|
||||
ex instanceof SocketTimeoutException)
|
||||
{
|
||||
Log.d(TAG, "experienced connection error during migration, will continue trying.", e);
|
||||
}
|
||||
else
|
||||
Log.d(TAG, "caught unknown IOException during migration >> " + e.toString(), e);
|
||||
}
|
||||
else
|
||||
Log.d(TAG, "caught exception during migration >> " + e.toString(), e);
|
||||
}
|
||||
|
||||
private void handleStartMigration() {
|
||||
Log.d(TAG, "handleStartMigration()");
|
||||
|
||||
handleInitializeNotification();
|
||||
handleStartKickingMigration();
|
||||
|
||||
KeyStore.setUseCipherVersionZero(getBaseContext(), true);
|
||||
|
||||
if (DavAccountHelper.isUsingOurServers(getBaseContext())) {
|
||||
try {
|
||||
|
||||
new RegistrationApi(getBaseContext()).setAccountVersion(account, 2);
|
||||
|
||||
} catch (RegistrationApiException e) {
|
||||
handleException(e);
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setPackage(MigrationHelperBroadcastReceiver.class.getPackage().getName());
|
||||
intent.setAction(ACTION_MIGRATION_STARTED);
|
||||
sendBroadcast(intent);
|
||||
|
||||
setState(STATE_STARTED_MIGRATION);
|
||||
}
|
||||
|
||||
private void handleSyncWithRemote() {
|
||||
Log.d(TAG, "handleSyncWithRemote()");
|
||||
|
||||
handleEnableAllSyncAdapters();
|
||||
if (!ContentResolver.getMasterSyncAutomatically()) {
|
||||
handleAskUserToEnableSync();
|
||||
return;
|
||||
}
|
||||
handleUpdateNotificationUsingState();
|
||||
|
||||
Long firstRecordedCalendarSync = getTimeFirstSync(KEY_TIME_FIRST_CALENDAR_SYNC);
|
||||
Optional<Long> timeLastCalendarSync = new CalendarsSyncScheduler(getBaseContext()).getTimeLastSync();
|
||||
|
||||
if (firstRecordedCalendarSync <= 0) {
|
||||
if (!timeLastCalendarSync.isPresent())
|
||||
recordTimeFirstSync(KEY_TIME_FIRST_CALENDAR_SYNC, new Date().getTime());
|
||||
else
|
||||
recordTimeFirstSync(KEY_TIME_FIRST_CALENDAR_SYNC, timeLastCalendarSync.get());
|
||||
|
||||
new CalendarsSyncScheduler(getBaseContext()).requestSync();
|
||||
}
|
||||
|
||||
Long firstRecordedAddressbookSync = getTimeFirstSync(KEY_TIME_FIRST_ADDRESSBOOK_SYNC);
|
||||
Optional<Long> timeLastAddressbookSync = new AddressbookSyncScheduler(getBaseContext()).getTimeLastSync();
|
||||
|
||||
if (firstRecordedAddressbookSync <= 0) {
|
||||
if (!timeLastAddressbookSync.isPresent())
|
||||
recordTimeFirstSync(KEY_TIME_FIRST_ADDRESSBOOK_SYNC, new Date().getTime());
|
||||
else
|
||||
recordTimeFirstSync(KEY_TIME_FIRST_ADDRESSBOOK_SYNC, timeLastAddressbookSync.get());
|
||||
|
||||
new AddressbookSyncScheduler(getBaseContext()).requestSync();
|
||||
}
|
||||
|
||||
if (firstRecordedCalendarSync > 0 && firstRecordedAddressbookSync > 0) {
|
||||
if (timeLastCalendarSync.isPresent() && timeLastAddressbookSync.isPresent()) {
|
||||
if (timeLastCalendarSync.get() > firstRecordedCalendarSync &&
|
||||
timeLastAddressbookSync.get() > firstRecordedAddressbookSync)
|
||||
{
|
||||
if (new CalendarsSyncScheduler(getBaseContext()).syncInProgress(account.getOsAccount()) ||
|
||||
new AddressbookSyncScheduler(getBaseContext()).syncInProgress(account.getOsAccount()))
|
||||
{
|
||||
Log.w(TAG, "finished syncing with remote but waiting for active syncs to complete.");
|
||||
handleDisableAllSyncAdapters();
|
||||
return;
|
||||
}
|
||||
|
||||
KeyStore.setUseCipherVersionZero(getBaseContext(), false);
|
||||
handleDisableAllSyncAdapters();
|
||||
|
||||
recordTimeFirstSync(KEY_TIME_FIRST_CALENDAR_SYNC, -1);
|
||||
recordTimeFirstSync(KEY_TIME_FIRST_ADDRESSBOOK_SYNC, -1);
|
||||
|
||||
setState(STATE_SYNCED_WITH_REMOTE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDeleteKeyCollection() {
|
||||
Log.d(TAG, "handleDeleteKeyCollection()");
|
||||
|
||||
try {
|
||||
|
||||
DavKeyStore keyStore = DavAccountHelper.getDavKeyStore(getBaseContext(), account);
|
||||
|
||||
try {
|
||||
|
||||
Optional<String> calendarHomeSet = keyStore.getCalendarHomeSet();
|
||||
keyStore.removeCollection(calendarHomeSet.get().concat(DavKeyStore.PATH_KEY_COLLECTION));
|
||||
|
||||
setState(STATE_DELETED_KEY_COLLECTION);
|
||||
|
||||
} catch (PropertyParseException e) {
|
||||
handleException(e);
|
||||
} catch (DavException e) {
|
||||
handleException(e);
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
} finally {
|
||||
keyStore.closeHttpConnection();
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleGenerateNewKeys() {
|
||||
Log.d(TAG, "handleGenerateNewKeys()");
|
||||
|
||||
try {
|
||||
|
||||
KeyHelper.generateAndSaveSaltAndKeyMaterial(getBaseContext());
|
||||
masterCipher = KeyHelper.getMasterCipher(getBaseContext()).get();
|
||||
|
||||
setState(STATE_GENERATED_NEW_KEYS);
|
||||
|
||||
} catch (GeneralSecurityException e) {
|
||||
handleException(e);
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCreateKeyCollection() {
|
||||
Log.d(TAG, "handleCreateKeyCollection()");
|
||||
|
||||
try {
|
||||
|
||||
DavKeyStore.createCollection(getBaseContext(), account);
|
||||
|
||||
setState(STATE_REPLACED_KEY_COLLECTION);
|
||||
|
||||
} catch (PropertyParseException e) {
|
||||
handleException(e);
|
||||
} catch (DavException e) {
|
||||
|
||||
if (e.getErrorCode() == DavServletResponse.SC_FORBIDDEN) {
|
||||
Log.w(TAG, "caught 403 when trying to create key collection, assuming already exists.");
|
||||
setState(STATE_REPLACED_KEY_COLLECTION);
|
||||
}
|
||||
else
|
||||
handleException(e);
|
||||
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleReplaceKeys() {
|
||||
Log.d(TAG, "handleReplaceKeys()");
|
||||
|
||||
try {
|
||||
|
||||
DavKeyStore keyStore = DavAccountHelper.getDavKeyStore(getBaseContext(), account);
|
||||
|
||||
try {
|
||||
|
||||
Optional<DavKeyCollection> keyCollection = keyStore.getCollection();
|
||||
|
||||
if (!keyCollection.isPresent()) {
|
||||
Log.e(TAG, "missing key collection, reverting state to regenerate keys!");
|
||||
setState(STATE_SYNCED_WITH_REMOTE);
|
||||
return;
|
||||
}
|
||||
|
||||
keyCollection.get().setMigrationStarted(getBaseContext());
|
||||
|
||||
Optional<String> localKeyMaterialSalt = KeyHelper.buildEncodedSalt(getBaseContext());
|
||||
Optional<String> localEncryptedKeyMaterial = KeyStore.getEncryptedKeyMaterial(getBaseContext());
|
||||
|
||||
if (localKeyMaterialSalt.isPresent() && localEncryptedKeyMaterial.isPresent()) {
|
||||
keyCollection.get().setKeyMaterialSalt(localKeyMaterialSalt.get());
|
||||
keyCollection.get().setEncryptedKeyMaterial(localEncryptedKeyMaterial.get());
|
||||
|
||||
setState(STATE_REPLACED_KEYS);
|
||||
}
|
||||
else {
|
||||
Log.e(TAG, "missing key material, reverting state to regenerate keys!");
|
||||
setState(STATE_SYNCED_WITH_REMOTE);
|
||||
}
|
||||
|
||||
} catch (InvalidComponentException e) {
|
||||
handleException(e);
|
||||
} catch (PropertyParseException e) {
|
||||
handleException(e);
|
||||
} catch (DavException e) {
|
||||
handleException(e);
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
} finally {
|
||||
keyStore.closeHttpConnection();
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDeleteRemoteCalendarsAndAddressbooks() {
|
||||
Log.d(TAG, "handleDeleteRemoteCalendarsAndAddressbooks()");
|
||||
|
||||
try {
|
||||
|
||||
CalDavStore remoteCalendarStore = DavAccountHelper.getCalDavStore(getBaseContext(), account);
|
||||
CardDavStore remoteAddressbookStore = DavAccountHelper.getCardDavStore(getBaseContext(), account);
|
||||
|
||||
try {
|
||||
|
||||
LocalCalendarStore localCalendarStore = new LocalCalendarStore(getBaseContext(), account.getOsAccount());
|
||||
LocalAddressbookStore localAddressbookStore = new LocalAddressbookStore(getBaseContext(), account);
|
||||
|
||||
for (LocalEventCollection localCollection : localCalendarStore.getCollections()) {
|
||||
if (remoteCalendarStore.getCollection(localCollection.getPath()).isPresent()) {
|
||||
Log.d(TAG, "deleting remote caldav collection at >> " + localCollection.getPath());
|
||||
remoteCalendarStore.removeCollection(localCollection.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
for (LocalContactCollection localCollection : localAddressbookStore.getCollections()) {
|
||||
if (remoteAddressbookStore.getCollection(localCollection.getPath()).isPresent()) {
|
||||
Log.d(TAG, "deleting remote carddav collection at >> " + localCollection.getPath());
|
||||
remoteAddressbookStore.removeCollection(localCollection.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
setState(STATE_DELETED_REMOTE_CALENDARS_AND_ADDRESSBOOKS);
|
||||
|
||||
} catch (RemoteException e) {
|
||||
handleException(e);
|
||||
} catch (DavException e) {
|
||||
handleException(e);
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
} finally {
|
||||
remoteCalendarStore.closeHttpConnection();
|
||||
remoteAddressbookStore.closeHttpConnection();
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleReplaceRemoteCalendars() {
|
||||
Log.d(TAG, "handleReplaceRemoteCalendars()");
|
||||
|
||||
try {
|
||||
|
||||
HidingCalDavStore remoteCalendarStore = DavAccountHelper.getHidingCalDavStore(getBaseContext(), account, masterCipher);
|
||||
|
||||
try {
|
||||
|
||||
LocalCalendarStore localCalendarStore = new LocalCalendarStore(getBaseContext(), account.getOsAccount());
|
||||
|
||||
for (LocalEventCollection localCollection : localCalendarStore.getCollections()) {
|
||||
if (!remoteCalendarStore.getCollection(localCollection.getPath()).isPresent()) {
|
||||
Log.d(TAG, "creating remote caldav collection at >> " + localCollection.getPath());
|
||||
|
||||
Optional<String> displayName = localCollection.getDisplayName();
|
||||
Optional<Integer> color = localCollection.getColor();
|
||||
|
||||
if (displayName.isPresent() && color.isPresent())
|
||||
remoteCalendarStore.addCollection(localCollection.getPath(), displayName.get(), color.get());
|
||||
else if (displayName.isPresent()) {
|
||||
remoteCalendarStore.addCollection(localCollection.getPath(),
|
||||
displayName.get(),
|
||||
getBaseContext().getResources().getColor(R.color.flocktheme_color));
|
||||
}
|
||||
else
|
||||
remoteCalendarStore.addCollection(localCollection.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
setState(STATE_REPLACED_REMOTE_CALENDARS);
|
||||
|
||||
} catch (RemoteException e) {
|
||||
handleException(e);
|
||||
} catch (DavException e) {
|
||||
|
||||
if (e.getErrorCode() != DavServletResponse.SC_FORBIDDEN)
|
||||
handleException(e);
|
||||
|
||||
} catch (GeneralSecurityException e) {
|
||||
handleException(e);
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
} finally {
|
||||
remoteCalendarStore.releaseConnections();
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleReplaceRemoteAddressbooks() {
|
||||
Log.d(TAG, "handleReplaceRemoteAddressbooks()");
|
||||
|
||||
try {
|
||||
|
||||
HidingCardDavStore remoteAddressbookStore = DavAccountHelper.getHidingCardDavStore(getBaseContext(), account, masterCipher);
|
||||
|
||||
try {
|
||||
|
||||
LocalAddressbookStore localAddressbookStore = new LocalAddressbookStore(getBaseContext(), account);
|
||||
|
||||
for (LocalContactCollection localCollection : localAddressbookStore.getCollections()) {
|
||||
if (!remoteAddressbookStore.getCollection(localCollection.getPath()).isPresent()) {
|
||||
Log.d(TAG, "creating remote carddav collection at >> " + localCollection.getPath());
|
||||
|
||||
Optional<String> displayName = localCollection.getDisplayName();
|
||||
if (displayName.isPresent())
|
||||
remoteAddressbookStore.addCollection(localCollection.getPath(), displayName.get());
|
||||
else
|
||||
remoteAddressbookStore.addCollection(localCollection.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
setState(STATE_REPLACED_REMOTE_ADDRESSBOOKS);
|
||||
|
||||
} catch (DavException e) {
|
||||
|
||||
if (e.getErrorCode() != DavServletResponse.SC_FORBIDDEN)
|
||||
handleException(e);
|
||||
|
||||
} catch (GeneralSecurityException e) {
|
||||
handleException(e);
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
} finally {
|
||||
remoteAddressbookStore.releaseConnections();
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleMarkAllComponentsAsNew(AbstractLocalComponentCollection<?> localCollection)
|
||||
throws RemoteException, OperationApplicationException
|
||||
{
|
||||
Log.d(TAG, "handleMarkAllComponentsAsNew() >> " + localCollection.getPath());
|
||||
|
||||
for (Long componentId : localCollection.getComponentIds()) {
|
||||
localCollection.queueForMigration(componentId);
|
||||
localCollection.commitPendingOperations();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleMarkAllLocalEventsAndContactsAsNew() {
|
||||
Log.d(TAG, "handleMarkAllLocalEventsAndContactsAsNew()");
|
||||
|
||||
LocalCalendarStore localCalendarStore = new LocalCalendarStore(getBaseContext(), account.getOsAccount());
|
||||
LocalAddressbookStore localAddressbookStore = new LocalAddressbookStore(getBaseContext(), account);
|
||||
|
||||
try {
|
||||
|
||||
for (LocalEventCollection collection : localCalendarStore.getCollections()) {
|
||||
Log.d(TAG, "marking all components as new in collection >> " + collection.getPath());
|
||||
handleMarkAllComponentsAsNew(collection);
|
||||
}
|
||||
|
||||
for (LocalContactCollection collection : localAddressbookStore.getCollections()) {
|
||||
Log.d(TAG, "marking all components as new in collection >> " + collection.getPath());
|
||||
handleMarkAllComponentsAsNew(collection);
|
||||
}
|
||||
|
||||
setState(STATE_READY_TO_REPLACE_EVENTS_AND_CONTACTS);
|
||||
|
||||
} catch (OperationApplicationException e) {
|
||||
handleException(e);
|
||||
} catch (RemoteException e) {
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleReplaceEventsAndContacts() {
|
||||
Log.d(TAG, "handleReplaceEventsAndContacts()");
|
||||
|
||||
handleEnableAllSyncAdapters();
|
||||
if (!ContentResolver.getMasterSyncAutomatically()) {
|
||||
handleAskUserToEnableSync();
|
||||
return;
|
||||
}
|
||||
handleUpdateNotificationUsingState();
|
||||
|
||||
Long firstRecordedCalendarSync = getTimeFirstSync(KEY_TIME_FIRST_CALENDAR_SYNC);
|
||||
Optional<Long> timeLastCalendarSync = new CalendarsSyncScheduler(getBaseContext()).getTimeLastSync();
|
||||
|
||||
if (firstRecordedCalendarSync <= 0) {
|
||||
if (!timeLastCalendarSync.isPresent())
|
||||
recordTimeFirstSync(KEY_TIME_FIRST_CALENDAR_SYNC, new Date().getTime());
|
||||
else
|
||||
recordTimeFirstSync(KEY_TIME_FIRST_CALENDAR_SYNC, timeLastCalendarSync.get());
|
||||
|
||||
new CalendarsSyncScheduler(getBaseContext()).requestSync();
|
||||
}
|
||||
|
||||
Long firstRecordedAddressbookSync = getTimeFirstSync(KEY_TIME_FIRST_ADDRESSBOOK_SYNC);
|
||||
Optional<Long> timeLastAddressbookSync = new AddressbookSyncScheduler(getBaseContext()).getTimeLastSync();
|
||||
|
||||
if (firstRecordedAddressbookSync <= 0) {
|
||||
if (!timeLastAddressbookSync.isPresent())
|
||||
recordTimeFirstSync(KEY_TIME_FIRST_ADDRESSBOOK_SYNC, new Date().getTime());
|
||||
else
|
||||
recordTimeFirstSync(KEY_TIME_FIRST_ADDRESSBOOK_SYNC, timeLastAddressbookSync.get());
|
||||
|
||||
new AddressbookSyncScheduler(getBaseContext()).requestSync();
|
||||
}
|
||||
|
||||
if (firstRecordedCalendarSync > 0 && firstRecordedAddressbookSync > 0) {
|
||||
if (timeLastCalendarSync.isPresent() && timeLastAddressbookSync.isPresent()) {
|
||||
if (timeLastCalendarSync.get() > firstRecordedCalendarSync &&
|
||||
timeLastAddressbookSync.get() > firstRecordedAddressbookSync)
|
||||
{
|
||||
setState(STATE_REPLACED_EVENTS_AND_CONTACTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSetMigrationComplete() {
|
||||
Log.d(TAG, "handleSetMigrationComplete()");
|
||||
|
||||
try {
|
||||
|
||||
DavKeyStore davKeyStore = DavAccountHelper.getDavKeyStore(getBaseContext(), account);
|
||||
|
||||
try {
|
||||
|
||||
Optional<DavKeyCollection> keyCollection = davKeyStore.getCollection();
|
||||
|
||||
if (keyCollection.isPresent()) {
|
||||
keyCollection.get().setMigrationComplete(getBaseContext());
|
||||
setState(STATE_MIGRATION_COMPLETE);
|
||||
}
|
||||
else {
|
||||
Log.e(TAG, "missing key collection, reverting state to regenerate keys!");
|
||||
setState(STATE_SYNCED_WITH_REMOTE);
|
||||
}
|
||||
|
||||
} catch (InvalidComponentException e) {
|
||||
handleException(e);
|
||||
} catch (PropertyParseException e) {
|
||||
handleException(e);
|
||||
} catch (DavException e) {
|
||||
handleException(e);
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
} finally {
|
||||
davKeyStore.closeHttpConnection();
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleStartOrResumeMigration() {
|
||||
Log.d(TAG, "handleStartOrResumeMigration()");
|
||||
|
||||
switch (getState(getBaseContext())) {
|
||||
case STATE_STARTED_MIGRATION:
|
||||
handleSyncWithRemote();
|
||||
if (getState(getBaseContext()) == STATE_STARTED_MIGRATION)
|
||||
break;
|
||||
|
||||
case STATE_SYNCED_WITH_REMOTE:
|
||||
handleDeleteKeyCollection();
|
||||
if (getState(getBaseContext()) <= STATE_SYNCED_WITH_REMOTE)
|
||||
break;
|
||||
|
||||
case STATE_DELETED_KEY_COLLECTION:
|
||||
handleGenerateNewKeys();
|
||||
if (getState(getBaseContext()) <= STATE_DELETED_KEY_COLLECTION)
|
||||
break;
|
||||
|
||||
case STATE_GENERATED_NEW_KEYS:
|
||||
handleCreateKeyCollection();
|
||||
if (getState(getBaseContext()) <= STATE_GENERATED_NEW_KEYS)
|
||||
break;
|
||||
|
||||
case STATE_REPLACED_KEY_COLLECTION:
|
||||
handleReplaceKeys();
|
||||
if (getState(getBaseContext()) <= STATE_REPLACED_KEY_COLLECTION)
|
||||
break;
|
||||
|
||||
case STATE_REPLACED_KEYS:
|
||||
handleDeleteRemoteCalendarsAndAddressbooks();
|
||||
if (getState(getBaseContext()) <= STATE_REPLACED_KEYS)
|
||||
break;
|
||||
|
||||
case STATE_DELETED_REMOTE_CALENDARS_AND_ADDRESSBOOKS:
|
||||
handleReplaceRemoteCalendars();
|
||||
if (getState(getBaseContext()) <= STATE_DELETED_REMOTE_CALENDARS_AND_ADDRESSBOOKS)
|
||||
break;
|
||||
|
||||
case STATE_REPLACED_REMOTE_CALENDARS:
|
||||
handleReplaceRemoteAddressbooks();
|
||||
if (getState(getBaseContext()) <= STATE_REPLACED_REMOTE_CALENDARS)
|
||||
break;
|
||||
|
||||
case STATE_REPLACED_REMOTE_ADDRESSBOOKS:
|
||||
handleMarkAllLocalEventsAndContactsAsNew();
|
||||
if (getState(getBaseContext()) <= STATE_REPLACED_REMOTE_ADDRESSBOOKS)
|
||||
break;
|
||||
|
||||
case STATE_READY_TO_REPLACE_EVENTS_AND_CONTACTS:
|
||||
handleReplaceEventsAndContacts();
|
||||
if (getState(getBaseContext()) <= STATE_READY_TO_REPLACE_EVENTS_AND_CONTACTS)
|
||||
break;
|
||||
|
||||
case STATE_REPLACED_EVENTS_AND_CONTACTS:
|
||||
handleSetMigrationComplete();
|
||||
if (getState(getBaseContext()) <= STATE_REPLACED_EVENTS_AND_CONTACTS)
|
||||
break;
|
||||
|
||||
case STATE_MIGRATION_COMPLETE:
|
||||
handleMigrationComplete();
|
||||
break;
|
||||
|
||||
default:
|
||||
handleStartMigration();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
HandlerThread thread = new HandlerThread("MigrationService", HandlerThread.NORM_PRIORITY);
|
||||
thread.start();
|
||||
|
||||
Looper serviceLooper = thread.getLooper();
|
||||
|
||||
serviceHandler = new ServiceHandler(serviceLooper);
|
||||
notifyManager = (NotificationManager)getBaseContext().getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationBuilder = new NotificationCompat.Builder(getBaseContext());
|
||||
|
||||
try {
|
||||
|
||||
Optional<DavAccount> account = DavAccountHelper.getAccount(getBaseContext());
|
||||
Optional<MasterCipher> masterCipher = KeyHelper.getMasterCipher(getBaseContext());
|
||||
|
||||
if (account.isPresent() && masterCipher.isPresent()) {
|
||||
this.account = account.get();
|
||||
this.masterCipher = masterCipher.get();
|
||||
}
|
||||
else
|
||||
Log.e(TAG, "ACCOUNT NOT PRESENT xxx 0.O");
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "exception while getting MasterCipher >> " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private final class ServiceHandler extends Handler {
|
||||
|
||||
public ServiceHandler(Looper looper) {
|
||||
super(looper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
Log.d(TAG, "handleMessage()");
|
||||
|
||||
if (account != null && masterCipher != null)
|
||||
handleStartOrResumeMigration();
|
||||
else
|
||||
Log.e(TAG, "missing account or master cipher! xxx");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
Log.d(TAG, "onStartCommand()");
|
||||
|
||||
serviceHandler.sendMessage(serviceHandler.obtainMessage());
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
Log.d(TAG, "onBind()");
|
||||
return null;
|
||||
}
|
||||
|
||||
private final Runnable kickMigrationRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
getBaseContext().startService(new Intent(getBaseContext(), MigrationService.class));
|
||||
}
|
||||
};
|
||||
|
||||
public void handleStartKickingMigration() {
|
||||
Log.d(TAG, "handleStartKickingMigration()");
|
||||
|
||||
intervalTimer = new Timer();
|
||||
TimerTask kickMigrationTask = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
messageHandler.post(kickMigrationRunnable);
|
||||
}
|
||||
};
|
||||
|
||||
intervalTimer.schedule(kickMigrationTask, 0, KICK_INTERVAL_MILLISECONDS);
|
||||
}
|
||||
}
|
||||
@ -36,7 +36,7 @@ import android.widget.EditText;
|
||||
import android.widget.ListView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.crypto.InvalidMacException;
|
||||
import org.anhonesteffort.flock.sync.addressbook.AddressbookSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.addressbook.HidingCardDavCollection;
|
||||
|
||||
@ -40,7 +40,7 @@ import android.widget.ListView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.chiralcode.colorpicker.ColorPicker;
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.sync.calendar.CalendarsSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.calendar.HidingCalDavCollection;
|
||||
import org.anhonesteffort.flock.sync.calendar.HidingCalDavStore;
|
||||
|
||||
@ -1,185 +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.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
|
||||
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;
|
||||
import org.anhonesteffort.flock.sync.key.KeySyncScheduler;
|
||||
|
||||
/**
|
||||
* rhodey
|
||||
*/
|
||||
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_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";
|
||||
|
||||
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 = getSharedPreferences(context);
|
||||
|
||||
if (addressbookSync.syncInProgress(account)) {
|
||||
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).apply();
|
||||
Log.w(TAG, "disabled auth notifications for " + calendarSync.getAuthority());
|
||||
}
|
||||
|
||||
if (keySync.syncInProgress(account)) {
|
||||
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) {
|
||||
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) {
|
||||
if (getSharedPreferences(context).getBoolean(KEY_VOID_AUTH_NOTIFICATIONS + authority, false)) {
|
||||
Log.w(TAG, "auth notification is disabled for " + authority);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static NotificationManager getNotificationManager(Context context) {
|
||||
return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
}
|
||||
|
||||
private static PendingIntent getPendingActivityIntent(Context context, Intent intent) {
|
||||
return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
|
||||
public static void handleInvalidatePasswordAndShowAuthNotification(Context context) {
|
||||
Log.w(TAG, "handleInvalidatePasswordAndShowAuthNotification()");
|
||||
|
||||
DavAccountHelper.invalidateAccountPassword(context);
|
||||
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context);
|
||||
Intent clickIntent = new Intent(context, CorrectPasswordActivity.class);
|
||||
|
||||
notificationBuilder.setContentTitle(context.getString(R.string.notification_flock_login_error));
|
||||
notificationBuilder.setContentText(context.getString(R.string.notification_tap_to_correct_password));
|
||||
notificationBuilder.setSmallIcon(R.drawable.flock_actionbar_icon);
|
||||
notificationBuilder.setAutoCancel(true);
|
||||
|
||||
notificationBuilder.setContentIntent(getPendingActivityIntent(context, clickIntent));
|
||||
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);
|
||||
}
|
||||
|
||||
private static boolean isStopAskingForLogsSet(Context context) {
|
||||
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 :(");
|
||||
getSharedPreferences(context).edit().putBoolean(KEY_STOP_ASKING_FOR_LOGS, true).apply();
|
||||
}
|
||||
|
||||
public static void handlePromptForDebugLogIfNotDisabled(Context context) {
|
||||
if (isStopAskingForLogsSet(context)) {
|
||||
Log.w(TAG, "user doesn't care to send us logs, not going to ask.");
|
||||
return;
|
||||
}
|
||||
|
||||
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context);
|
||||
Intent sendLogIntent = new Intent(context, SendDebugLogActivity.class);
|
||||
Intent stopAskingIntent = new Intent(ACTION_STOP_ASKING_FOR_LOGS);
|
||||
|
||||
String contentText = context.getString(R.string.something_strange_happened_send_us_your_debug_log);
|
||||
NotificationCompat.BigTextStyle textStyle = new NotificationCompat.BigTextStyle().bigText(contentText);
|
||||
|
||||
notificationBuilder.setSmallIcon(R.drawable.flock_actionbar_icon);
|
||||
notificationBuilder.setContentTitle(context.getString(R.string.flock_debug_log));
|
||||
notificationBuilder.setContentText(contentText);
|
||||
notificationBuilder.setStyle(textStyle);
|
||||
notificationBuilder.setAutoCancel(true);
|
||||
|
||||
notificationBuilder.addAction(R.drawable.navigation_cancel,
|
||||
context.getString(R.string.dont_ask_again),
|
||||
PendingIntent.getBroadcast(context, 0, stopAskingIntent, PendingIntent.FLAG_UPDATE_CURRENT));
|
||||
|
||||
notificationBuilder.addAction(R.drawable.social_send_now,
|
||||
context.getString(R.string.send_log),
|
||||
getPendingActivityIntent(context, sendLogIntent));
|
||||
|
||||
getNotificationManager(context).notify(ID_NOTIFICATION_DEBUG_LOG, notificationBuilder.build());
|
||||
}
|
||||
|
||||
public static void cancelDebugLogPrompt(Context context) {
|
||||
getNotificationManager(context).cancel(ID_NOTIFICATION_DEBUG_LOG);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent.getAction().equals(ACTION_STOP_ASKING_FOR_LOGS)) {
|
||||
setStopAskingForLogs(context);
|
||||
cancelDebugLogPrompt(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -33,9 +33,9 @@ import android.view.MenuItem;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.chiralcode.colorpicker.ColorPickerPreference;
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import org.anhonesteffort.flock.sync.account.AccountSyncScheduler;
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.sync.addressbook.AddressbookSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.calendar.CalendarsSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.key.KeySyncScheduler;
|
||||
@ -57,6 +57,7 @@ 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;
|
||||
@ -88,21 +89,13 @@ public class PreferencesActivity extends PreferenceActivity
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
Intent nextIntent = null;
|
||||
|
||||
switch (item.getItemId()) {
|
||||
case R.id.button_delete_all_contacts:
|
||||
nextIntent = new Intent(getBaseContext(), DeleteAllContactsActivity.class);
|
||||
break;
|
||||
|
||||
case R.id.button_send_debug_log:
|
||||
nextIntent = new Intent(getBaseContext(), SendDebugLogActivity.class);
|
||||
Intent nextIntent = new Intent(getBaseContext(), DeleteAllContactsActivity.class);
|
||||
startActivity(nextIntent);
|
||||
break;
|
||||
}
|
||||
|
||||
if (nextIntent != null)
|
||||
startActivity(nextIntent);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -136,8 +129,6 @@ 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() {
|
||||
@ -150,7 +141,6 @@ 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,
|
||||
@ -182,7 +172,7 @@ public class PreferencesActivity extends PreferenceActivity
|
||||
|
||||
if (value.isPresent()) {
|
||||
String colorName = colorUtils.getColorNameFromHex(value.get());
|
||||
calendarColorPreference.setSummary(getString(R.string.new_calendars_will_be, colorName));
|
||||
calendarColorPreference.setSummary(getString(R.string.new_calendars_will_be) + " '" + colorName + "'");
|
||||
}
|
||||
else {
|
||||
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
|
||||
@ -191,7 +181,7 @@ public class PreferencesActivity extends PreferenceActivity
|
||||
String colorName = colorUtils.getColorNameFromHex(calendarColor);
|
||||
|
||||
if (colorName != null)
|
||||
calendarColorPreference.setSummary(getString(R.string.new_calendars_will_be, colorName));
|
||||
calendarColorPreference.setSummary(getString(R.string.new_calendars_will_be) + " '" + colorName + "'");
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,9 +189,29 @@ 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);
|
||||
}
|
||||
@ -211,10 +221,13 @@ 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 (deleteAccount != null)
|
||||
if (manageSubscription != null) {
|
||||
accountCategory.removePreference(manageSubscription);
|
||||
accountCategory.removePreference(deleteAccount);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -223,7 +236,6 @@ public class PreferencesActivity extends PreferenceActivity
|
||||
new KeySyncScheduler(getBaseContext()).setSyncInterval(Integer.valueOf((String) newValue));
|
||||
new AddressbookSyncScheduler(getBaseContext()).setSyncInterval(Integer.valueOf((String) newValue));
|
||||
new CalendarsSyncScheduler(getBaseContext()).setSyncInterval(Integer.valueOf((String) newValue));
|
||||
new AccountSyncScheduler(getBaseContext()).setSyncInterval(Integer.valueOf((String) newValue));
|
||||
|
||||
updateSyncIntervalSummary(Optional.of((String) newValue));
|
||||
}
|
||||
|
||||
@ -19,31 +19,223 @@
|
||||
|
||||
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)
|
||||
{
|
||||
return inflater.inflate(R.layout.fragment_register_ows_account, container, false);
|
||||
View view = inflater.inflate(R.layout.fragment_register_ows_account, container, false);
|
||||
|
||||
initButtons();
|
||||
initForm(view);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
getActivity().findViewById(R.id.button_next).setVisibility(View.GONE);
|
||||
private void initButtons() {
|
||||
getActivity().findViewById(R.id.button_next).setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
registerAccountAsync();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private void initForm(View view) {
|
||||
if (messageHandler != null && messageHandler.serviceStarted) {
|
||||
getActivity().setProgressBarIndeterminateVisibility(true);
|
||||
getActivity().setProgressBarVisibility(true);
|
||||
setupActivity.setNavigationDisabled(true);
|
||||
}
|
||||
|
||||
if (username.isPresent())
|
||||
((EditText)view.findViewById(R.id.account_username)).setText(username.get());
|
||||
|
||||
final EditText passwordTextView = (EditText) view.findViewById(R.id.cipher_passphrase);
|
||||
final EditText passwordRepeatTextView = (EditText) view.findViewById(R.id.cipher_passphrase_repeat);
|
||||
final ProgressBar passwordProgressView = (ProgressBar) view.findViewById(R.id.progress_password_strength);
|
||||
final ProgressBar passwordRepeatProgressView = (ProgressBar) view.findViewById(R.id.progress_password_strength_repeat);
|
||||
|
||||
if (passwordWatcher != null)
|
||||
passwordTextView.removeTextChangedListener(passwordWatcher);
|
||||
if (passwordRepeatWatcher != null)
|
||||
passwordRepeatTextView.removeTextChangedListener(passwordRepeatWatcher);
|
||||
|
||||
passwordWatcher = PasswordUtil.getPasswordStrengthTextWatcher(getActivity(), passwordProgressView);
|
||||
passwordRepeatWatcher = PasswordUtil.getPasswordStrengthTextWatcher(getActivity(), passwordRepeatProgressView);
|
||||
|
||||
passwordTextView.addTextChangedListener(passwordWatcher);
|
||||
passwordRepeatTextView.addTextChangedListener(passwordRepeatWatcher);
|
||||
}
|
||||
|
||||
private void handleRegisterComplete() {
|
||||
Log.d(TAG, "handleRegisterComplete()");
|
||||
setupActivity.updateFragmentUsingState(SetupActivity.STATE_IMPORT_CONTACTS);
|
||||
}
|
||||
|
||||
private void registerAccountAsync() {
|
||||
if (messageHandler != null && messageHandler.serviceStarted)
|
||||
return;
|
||||
else if (messageHandler == null)
|
||||
messageHandler = new MessageHandler(setupActivity, this);
|
||||
|
||||
Bundle result = new Bundle();
|
||||
String username = ((EditText) getActivity().findViewById(R.id.account_username)).getText().toString().trim();
|
||||
String password = ((EditText) getActivity().findViewById(R.id.cipher_passphrase)).getText().toString().trim();
|
||||
String passwordRepeat = ((EditText) getActivity().findViewById(R.id.cipher_passphrase_repeat)).getText().toString().trim();
|
||||
|
||||
if (StringUtils.isEmpty(username)) {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_EMPTY_ACCOUNT_ID);
|
||||
ErrorToaster.handleDisplayToastBundledError(getActivity(), result);
|
||||
return;
|
||||
}
|
||||
|
||||
if (username.contains(" ")) {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SPACES_IN_USERNAME);
|
||||
ErrorToaster.handleDisplayToastBundledError(getActivity(), result);
|
||||
return;
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(password)) {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SHORT_PASSWORD);
|
||||
ErrorToaster.handleDisplayToastBundledError(getActivity(), result);
|
||||
((TextView)getActivity().findViewById(R.id.cipher_passphrase)).setText("");
|
||||
((TextView)getActivity().findViewById(R.id.cipher_passphrase_repeat)).setText("");
|
||||
return;
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(passwordRepeat) || !password.equals(passwordRepeat)) {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_PASSWORDS_DO_NOT_MATCH);
|
||||
ErrorToaster.handleDisplayToastBundledError(getActivity(), result);
|
||||
((TextView)getActivity().findViewById(R.id.cipher_passphrase)).setText("");
|
||||
((TextView)getActivity().findViewById(R.id.cipher_passphrase_repeat)).setText("");
|
||||
return;
|
||||
}
|
||||
|
||||
username = DavAccountHelper.correctUsername(getActivity(), username);
|
||||
Intent importService = new Intent(getActivity(), RegisterAccountService.class);
|
||||
|
||||
importService.putExtra(RegisterAccountService.KEY_MESSENGER, new Messenger(messageHandler));
|
||||
importService.putExtra(RegisterAccountService.KEY_ACCOUNT_ID, username);
|
||||
importService.putExtra(RegisterAccountService.KEY_MASTER_PASSPHRASE, password);
|
||||
|
||||
getActivity().startService(importService);
|
||||
messageHandler.serviceStarted = true;
|
||||
|
||||
setupActivity.setNavigationDisabled(true);
|
||||
getActivity().setProgressBarIndeterminateVisibility(true);
|
||||
getActivity().setProgressBarVisibility(true);
|
||||
}
|
||||
|
||||
public static class MessageHandler extends Handler {
|
||||
|
||||
public SetupActivity setupActivity;
|
||||
public RegisterAccountFragment importFragment;
|
||||
public boolean serviceStarted = false;
|
||||
|
||||
public MessageHandler(SetupActivity setupActivity, RegisterAccountFragment importFragment) {
|
||||
this.setupActivity = setupActivity;
|
||||
this.importFragment = importFragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
messageHandler = null;
|
||||
serviceStarted = false;
|
||||
|
||||
setupActivity.setNavigationDisabled(false);
|
||||
setupActivity.setProgressBarIndeterminateVisibility(false);
|
||||
setupActivity.setProgressBarVisibility(false);
|
||||
|
||||
if (message.arg1 == CODE_ACCOUNT_IMPORTED)
|
||||
importFragment.handleRegisterComplete();
|
||||
|
||||
else if (message.arg1 != ErrorToaster.CODE_SUCCESS) {
|
||||
Bundle errorBundler = new Bundle();
|
||||
|
||||
errorBundler.putInt(ErrorToaster.KEY_STATUS_CODE, message.arg1);
|
||||
ErrorToaster.handleDisplayToastBundledError(setupActivity, errorBundler);
|
||||
|
||||
if (importFragment.getView().findViewById(R.id.account_username) != null) {
|
||||
((TextView)importFragment.getView().findViewById(R.id.account_username)).setText("");
|
||||
((TextView)importFragment.getView().findViewById(R.id.cipher_passphrase)).setText("");
|
||||
((TextView)importFragment.getView().findViewById(R.id.cipher_passphrase_repeat)).setText("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ import android.os.RemoteException;
|
||||
import android.view.View;
|
||||
import android.widget.CompoundButton;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.crypto.InvalidMacException;
|
||||
import org.anhonesteffort.flock.sync.calendar.CalendarsSyncScheduler;
|
||||
import org.anhonesteffort.flock.sync.calendar.HidingCalDavCollection;
|
||||
|
||||
@ -136,9 +136,11 @@ public class SelectServiceProviderFragment extends Fragment {
|
||||
if (!radioButtonOws.isChecked()) {
|
||||
radioButtonOws.setChecked(true);
|
||||
radioButtonOther.setChecked(false);
|
||||
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.setText(
|
||||
Html.fromHtml(
|
||||
getString(R.string.flock_sync_is_a_service_run_by_open_whisper_systems_available, costPerYearUsd)
|
||||
)
|
||||
);
|
||||
serviceDescription.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
}
|
||||
}
|
||||
@ -165,9 +167,11 @@ public class SelectServiceProviderFragment extends Fragment {
|
||||
if (isChecked) {
|
||||
radioButtonOws.setChecked(true);
|
||||
radioButtonOther.setChecked(false);
|
||||
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.setText(
|
||||
Html.fromHtml(
|
||||
getString(R.string.flock_sync_is_a_service_run_by_open_whisper_systems_available, costPerYearUsd)
|
||||
)
|
||||
);
|
||||
serviceDescription.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
}
|
||||
}
|
||||
@ -192,9 +196,11 @@ 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);
|
||||
|
||||
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.setText(
|
||||
Html.fromHtml(
|
||||
getString(R.string.flock_sync_is_a_service_run_by_open_whisper_systems_available, costPerYearUsd)
|
||||
)
|
||||
);
|
||||
serviceDescription.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,6 +41,7 @@ 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;
|
||||
@ -50,7 +51,6 @@ 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).apply();
|
||||
preferences.edit().remove(KEY_ID_BTC_RECEIVER).commit();
|
||||
}
|
||||
|
||||
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()).apply();
|
||||
preferences.edit().putString(KEY_ID_BTC_RECEIVER, btcReceiver.getId()).commit();
|
||||
return btcReceiver;
|
||||
}
|
||||
|
||||
|
||||
@ -1,85 +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.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.whispersystems.libpastelog.SubmitLogFragment;
|
||||
|
||||
/**
|
||||
* rhodey
|
||||
*/
|
||||
public class SendDebugLogActivity extends FragmentActivity implements SubmitLogFragment.OnLogSubmittedListener {
|
||||
|
||||
private static final String TAG = "org.anhonesteffort.flock.SendDebugLogActivity";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.simple_fragment_activity);
|
||||
getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getActionBar().setTitle(R.string.send_debug_log);
|
||||
|
||||
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
|
||||
Fragment sendLogFragment = SubmitLogFragment.newInstance(getString(R.string.support_email_address),
|
||||
getString(R.string.flock_debug_log));
|
||||
|
||||
fragmentTransaction.replace(R.id.fragment_view, sendLogFragment);
|
||||
fragmentTransaction.commit();
|
||||
|
||||
NotificationDrawer.cancelDebugLogPrompt(getBaseContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
Log.d(TAG, "onSuccess()");
|
||||
Toast.makeText(getBaseContext(), R.string.thanks, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure() {
|
||||
Log.d(TAG, "onFailure()");
|
||||
Toast.makeText(getBaseContext(), R.string.error_connection_error, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel() {
|
||||
Log.d(TAG, "onCancel()");
|
||||
finish();
|
||||
}
|
||||
}
|
||||
@ -33,6 +33,7 @@ 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;
|
||||
@ -44,13 +45,11 @@ 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;
|
||||
@ -237,9 +236,8 @@ 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() == WebDavConstants.SC_UNAUTHORIZED)
|
||||
if (e.getErrorCode() == DavServletResponse.SC_UNAUTHORIZED)
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_UNAUTHORIZED);
|
||||
else
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CARDDAV_CURRENT_USER_PRINCIPAL);
|
||||
@ -263,9 +261,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);
|
||||
|
||||
if (e.getErrorCode() == WebDavConstants.SC_UNAUTHORIZED)
|
||||
Log.d(TAG, e.toString());
|
||||
if (e.getErrorCode() == DavServletResponse.SC_UNAUTHORIZED)
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_UNAUTHORIZED);
|
||||
else
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CALDAV_CURRENT_USER_PRINCIPAL);
|
||||
@ -289,10 +287,10 @@ public class ServerTestsFragment extends Fragment {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CARDDAV_ADDRESSBOOK_HOMESET);
|
||||
|
||||
} catch (DavException e) {
|
||||
Log.e(TAG, "carddav addressbook homeset", e);
|
||||
Log.d(TAG, e.toString());
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CARDDAV_ADDRESSBOOK_HOMESET);
|
||||
} catch (PropertyParseException e) {
|
||||
Log.e(TAG, "carddav addressbook homeset", e);
|
||||
Log.d(TAG, e.toString());
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CARDDAV_ADDRESSBOOK_HOMESET);
|
||||
} catch (SSLException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
@ -313,10 +311,10 @@ public class ServerTestsFragment extends Fragment {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CALDAV_CALENDAR_HOMESET);
|
||||
|
||||
} catch (DavException e) {
|
||||
Log.e(TAG, "caldav calendar homeset", e);
|
||||
Log.d(TAG, e.toString());
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CALDAV_CALENDAR_HOMESET);
|
||||
} catch (PropertyParseException e) {
|
||||
Log.e(TAG, "caldav calendar homeset", e);
|
||||
Log.d(TAG, e.toString());
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CALDAV_CALENDAR_HOMESET);
|
||||
} catch (SSLException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
@ -355,10 +353,10 @@ public class ServerTestsFragment extends Fragment {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SUCCESS);
|
||||
|
||||
} catch (DavException e) {
|
||||
Log.e(TAG, "caldav create delete collection", e);
|
||||
Log.d(TAG, e.toString());
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CALDAV_CREATE_DELETE_COLLECTION);
|
||||
} catch (PropertyParseException e) {
|
||||
Log.e(TAG, "caldav create delete collection", e);
|
||||
Log.d(TAG, e.toString());
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CALDAV_CREATE_DELETE_COLLECTION);
|
||||
} catch (SSLException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
@ -424,10 +422,10 @@ public class ServerTestsFragment extends Fragment {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SUCCESS);
|
||||
|
||||
} catch (DavException e) {
|
||||
Log.e(TAG, "caldav create edit collection properties", e);
|
||||
Log.d(TAG, e.toString());
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CALDAV_CREATE_EDIT_COLLECTION_PROPERTIES);
|
||||
} catch (PropertyParseException e) {
|
||||
Log.e(TAG, "caldav create edit collection properties", e);
|
||||
Log.d(TAG, e.toString());
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CALDAV_CREATE_EDIT_COLLECTION_PROPERTIES);
|
||||
} catch (SSLException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
@ -497,13 +495,13 @@ public class ServerTestsFragment extends Fragment {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SUCCESS);
|
||||
|
||||
} catch (DavException e) {
|
||||
Log.e(TAG, "carddav create delete contacts", e);
|
||||
Log.d(TAG, e.toString());
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CARDDAV_CREATE_DELETE_CONTACTS);
|
||||
} catch (PropertyParseException e) {
|
||||
Log.e(TAG, "carddav create delete contacts", e);
|
||||
Log.d(TAG, e.toString());
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CARDDAV_CREATE_DELETE_CONTACTS);
|
||||
} catch (InvalidComponentException e) {
|
||||
Log.e(TAG, "carddav create delete contacts", e);
|
||||
Log.d(TAG, e.toString());
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CARDDAV_CREATE_DELETE_CONTACTS);
|
||||
} catch (SSLException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
@ -581,16 +579,16 @@ public class ServerTestsFragment extends Fragment {
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SUCCESS);
|
||||
|
||||
} catch (DavException e) {
|
||||
Log.e(TAG, "caldav create delete events", e);
|
||||
Log.d(TAG, e.toString());
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CALDAV_CREATE_DELETE_EVENTS);
|
||||
} catch (PropertyParseException e) {
|
||||
Log.e(TAG, "caldav create delete events", e);
|
||||
Log.d(TAG, e.toString());
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CALDAV_CREATE_DELETE_EVENTS);
|
||||
} catch (InvalidComponentException e) {
|
||||
Log.e(TAG, "caldav create delete events", e);
|
||||
Log.d(TAG, e.toString());
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CALDAV_CREATE_DELETE_EVENTS);
|
||||
} catch (ConstraintViolationException e) {
|
||||
Log.e(TAG, "caldav create delete events", e);
|
||||
Log.d(TAG, e.toString());
|
||||
result.putInt(ErrorToaster.KEY_STATUS_CODE, CODE_ERROR_CALDAV_CREATE_DELETE_EVENTS);
|
||||
} catch (SSLException e) {
|
||||
ErrorToaster.handleBundleError(e, result);
|
||||
|
||||
@ -24,9 +24,6 @@ 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;
|
||||
@ -34,7 +31,7 @@ import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.example.android.wizardpager.wizard.ui.StepPagerStrip;
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.sync.key.KeySyncScheduler;
|
||||
|
||||
/**
|
||||
@ -99,25 +96,6 @@ public class SetupActivity extends FragmentActivity {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.setup_menu, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.button_send_debug_log:
|
||||
Intent nextIntent = new Intent(getBaseContext(), SendDebugLogActivity.class);
|
||||
startActivity(nextIntent);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void setNavigationDisabled(boolean navigationDisabled) {
|
||||
this.navigationDisabled = navigationDisabled;
|
||||
}
|
||||
|
||||
@ -29,14 +29,20 @@ import android.view.LayoutInflater;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import org.anhonesteffort.flock.auth.DavAccount;
|
||||
import org.anhonesteffort.flock.crypto.InvalidCipherVersionException;
|
||||
import org.anhonesteffort.flock.crypto.KeyHelper;
|
||||
import org.anhonesteffort.flock.registration.RegistrationApi;
|
||||
import org.anhonesteffort.flock.registration.RegistrationApiException;
|
||||
import org.anhonesteffort.flock.sync.AbstractDavSyncAdapter;
|
||||
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;
|
||||
|
||||
@ -61,6 +67,7 @@ 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;
|
||||
@ -167,7 +174,7 @@ public class StatusHeaderView extends LinearLayout {
|
||||
syncStatusText = getContext().getString(R.string.status_header_status_account_login_failed);
|
||||
syncStatusDrawable = R.drawable.sad_cloud;
|
||||
if (!authNotificationShown) {
|
||||
NotificationDrawer.handleInvalidatePasswordAndShowAuthNotification(getContext());
|
||||
AbstractDavSyncAdapter.showAuthNotificationAndInvalidatePassword(getContext());
|
||||
authNotificationShown = true;
|
||||
}
|
||||
}
|
||||
@ -175,6 +182,7 @@ public class StatusHeaderView extends LinearLayout {
|
||||
syncStatusText = getContext().getString(R.string.notification_flock_subscription_expired);
|
||||
syncStatusDrawable = R.drawable.sad_cloud;
|
||||
if (!subscriptionNotificationShown) {
|
||||
AbstractDavSyncAdapter.showSubscriptionExpiredNotification(getContext());
|
||||
subscriptionNotificationShown = true;
|
||||
}
|
||||
}
|
||||
@ -182,6 +190,15 @@ 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;
|
||||
@ -257,12 +274,11 @@ public class StatusHeaderView extends LinearLayout {
|
||||
|
||||
@Override
|
||||
protected Bundle doInBackground(String... params) {
|
||||
Bundle result = new Bundle();
|
||||
Bundle result = new Bundle();
|
||||
RegistrationApi registrationApi = new RegistrationApi(getContext());
|
||||
|
||||
try {
|
||||
|
||||
RegistrationApi registrationApi = new RegistrationApi(getContext());
|
||||
|
||||
if (registrationApi.getAccount(account.get()).getLastStripeChargeFailed())
|
||||
lastChargeFailed = registrationApi.getCard(account.get()).isPresent();
|
||||
|
||||
@ -336,6 +352,59 @@ 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() {
|
||||
@ -361,6 +430,12 @@ public class StatusHeaderView extends LinearLayout {
|
||||
handleUpdateCipherPassphraseIsValid();
|
||||
}
|
||||
};
|
||||
private final Runnable refreshMigrationRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
handleUpdateMigrationInProgress();
|
||||
}
|
||||
};
|
||||
|
||||
public void handleStartPerpetualRefresh() {
|
||||
account = DavAccountHelper.getAccount(getContext());
|
||||
@ -390,6 +465,12 @@ public class StatusHeaderView extends LinearLayout {
|
||||
uiHandler.post(refreshCipherPassphraseRunnable);
|
||||
}
|
||||
};
|
||||
TimerTask migrationTask = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
uiHandler.post(refreshMigrationRunnable);
|
||||
}
|
||||
};
|
||||
|
||||
intervalTimer.schedule(uiTask, 0, 2000);
|
||||
|
||||
@ -400,6 +481,8 @@ public class StatusHeaderView extends LinearLayout {
|
||||
}
|
||||
else
|
||||
intervalTimer.schedule(passphraseTask, 0, 10000);
|
||||
|
||||
intervalTimer.schedule(migrationTask, 0, 10000);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -34,7 +34,7 @@ import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import com.google.common.base.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 (!accountAndKeyAvailable())
|
||||
if (!accountAndKeyAvailableAndMigrationComplete())
|
||||
return;
|
||||
|
||||
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
|
||||
|
||||
@ -56,20 +56,18 @@ public class AccountAuthenticator extends AbstractAccountAuthenticator {
|
||||
|
||||
public static void setAllowAccountRemoval(Context context, boolean isAllowed) {
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME,
|
||||
Context.MODE_PRIVATE);
|
||||
settings.edit().putBoolean(KEY_ALLOW_ACCOUNT_REMOVAL, isAllowed).apply();
|
||||
Context.MODE_MULTI_PROCESS);
|
||||
settings.edit().putBoolean(KEY_ALLOW_ACCOUNT_REMOVAL, isAllowed).commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle getAccountRemovalAllowed(AccountAuthenticatorResponse response, Account account) {
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME,
|
||||
Context.MODE_PRIVATE);
|
||||
|
||||
Boolean isAllowed = settings.getBoolean(KEY_ALLOW_ACCOUNT_REMOVAL, false);
|
||||
Bundle resultBundle = new Bundle();
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME,
|
||||
Context.MODE_MULTI_PROCESS);
|
||||
Boolean isAllowed = settings.getBoolean(KEY_ALLOW_ACCOUNT_REMOVAL, false);
|
||||
|
||||
Bundle resultBundle = new Bundle();
|
||||
resultBundle.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, isAllowed);
|
||||
|
||||
return resultBundle;
|
||||
}
|
||||
|
||||
|
||||
@ -24,12 +24,14 @@ import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import com.google.common.base.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";
|
||||
@ -69,17 +71,15 @@ public class DavAccount {
|
||||
}
|
||||
|
||||
public Optional<String> getCardDavCollectionPath(Context context) {
|
||||
SharedPreferences preferences =
|
||||
context.getSharedPreferences(SYNC_ACCOUNT_TYPE, Context.MODE_PRIVATE);
|
||||
|
||||
SharedPreferences preferences = context.getSharedPreferences(SYNC_ACCOUNT_TYPE,
|
||||
Context.MODE_MULTI_PROCESS);
|
||||
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_PRIVATE);
|
||||
|
||||
preferences.edit().putString(KEY_CARD_DAV_COLLECTION, path).apply();
|
||||
SharedPreferences preferences = context.getSharedPreferences(SYNC_ACCOUNT_TYPE,
|
||||
Context.MODE_MULTI_PROCESS);
|
||||
preferences.edit().putString(KEY_CARD_DAV_COLLECTION, path).commit();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@ -22,7 +22,7 @@ package org.anhonesteffort.flock.crypto;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.util.Base64;
|
||||
import org.anhonesteffort.flock.util.Util;
|
||||
|
||||
@ -60,6 +60,7 @@ 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);
|
||||
|
||||
@ -67,9 +68,14 @@ public class KeyHelper {
|
||||
return Optional.absent();
|
||||
|
||||
SecretKey cipherKey = new SecretKeySpec(cipherKeyBytes.get(), "AES");
|
||||
SecretKey macKey = new SecretKeySpec(macKeyBytes.get(), "SHA256");
|
||||
SecretKey macKey = null;
|
||||
|
||||
return Optional.of(new MasterCipher(cipherKey, macKey));
|
||||
if (useCipherVersionZero)
|
||||
macKey = new SecretKeySpec(cipherKeyBytes.get(), "SHA256");
|
||||
else
|
||||
macKey = new SecretKeySpec(macKeyBytes.get(), "SHA256");
|
||||
|
||||
return Optional.of(new MasterCipher(useCipherVersionZero, cipherKey, macKey));
|
||||
}
|
||||
|
||||
public static Optional<String> buildEncodedSalt(Context context) throws IOException {
|
||||
@ -96,7 +102,7 @@ public class KeyHelper {
|
||||
SecretKey[] masterKeys = KeyUtil.getCipherAndMacKeysForPassphrase(salt.get(), masterPassphrase.get());
|
||||
SecretKey masterCipherKey = masterKeys[0];
|
||||
SecretKey masterMacKey = masterKeys[1];
|
||||
MasterCipher masterCipher = new MasterCipher(masterCipherKey, masterMacKey);
|
||||
MasterCipher masterCipher = new MasterCipher(false, masterCipherKey, masterMacKey);
|
||||
|
||||
byte[] keyMaterial = Util.combine(cipherKey.get(), macKey.get());
|
||||
byte[] encryptedKeyMaterial = masterCipher.encryptAndEncode(keyMaterial);
|
||||
@ -114,11 +120,12 @@ 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(masterCipherKey, masterMacKey);
|
||||
MasterCipher masterCipher = new MasterCipher(useCipherVersionZero, masterCipherKey, masterMacKey);
|
||||
byte[] plaintextKeyMaterial = masterCipher.decodeAndDecrypt(saltAndEncryptedKeyMaterial[1].getBytes());
|
||||
|
||||
boolean saltLengthValid = salt.length == KeyUtil.SALT_LENGTH_BYTES;
|
||||
@ -157,7 +164,7 @@ public class KeyHelper {
|
||||
SecretKey[] masterKeys = KeyUtil.getCipherAndMacKeysForPassphrase(salt.get(), masterPassphrase.get());
|
||||
SecretKey masterCipherKey = masterKeys[0];
|
||||
SecretKey masterMacKey = masterKeys[1];
|
||||
MasterCipher masterCipher = new MasterCipher(masterCipherKey, masterMacKey);
|
||||
MasterCipher masterCipher = new MasterCipher(false, masterCipherKey, masterMacKey);
|
||||
|
||||
try {
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Log;
|
||||
|
||||
import org.anhonesteffort.flock.util.guava.Optional;
|
||||
import com.google.common.base.Optional;
|
||||
import org.anhonesteffort.flock.util.Base64;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -36,40 +36,47 @@ 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";
|
||||
|
||||
private static SharedPreferences getSharedPreferences(Context context) {
|
||||
return context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
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);
|
||||
}
|
||||
|
||||
public static void saveCipherKey(Context context, byte[] cipherKey) {
|
||||
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) {
|
||||
Log.d(TAG, "SAVING CIPHER KEY MATERIAL...");
|
||||
saveBytes(context, KEY_CIPHER_KEY, cipherKey);
|
||||
}
|
||||
|
||||
public static Optional<byte[]> getCipherKey(Context context) throws IOException {
|
||||
protected static Optional<byte[]> getCipherKey(Context context) throws IOException {
|
||||
return retrieveBytes(context, KEY_CIPHER_KEY);
|
||||
}
|
||||
|
||||
public static void saveMacKey(Context context, byte[] cipherKey) {
|
||||
protected static void saveMacKey(Context context, byte[] cipherKey) {
|
||||
Log.d(TAG, "SAVING MAC KEY MATERIAL...");
|
||||
saveBytes(context, KEY_MAC_KEY, cipherKey);
|
||||
}
|
||||
|
||||
public static Optional<byte[]> getMacKey(Context context) throws IOException {
|
||||
protected static Optional<byte[]> getMacKey(Context context) throws IOException {
|
||||
return retrieveBytes(context, KEY_MAC_KEY);
|
||||
}
|
||||
|
||||
public static void saveKeyMaterialSalt(Context context, byte[] salt) {
|
||||
protected static void saveKeyMaterialSalt(Context context, byte[] salt) {
|
||||
Log.d(TAG, "SAVING SALT FOR KEY MATERIAL...");
|
||||
saveBytes(context, KEY_KEY_MATERIAL_SALT, salt);
|
||||
}
|
||||
|
||||
public static Optional<byte[]> getKeyMaterialSalt(Context context) throws IOException {
|
||||
protected static Optional<byte[]> getKeyMaterialSalt(Context context) throws IOException {
|
||||
return retrieveBytes(context, KEY_KEY_MATERIAL_SALT);
|
||||
}
|
||||
|
||||
@ -93,32 +100,36 @@ public class KeyStore {
|
||||
|
||||
public static void invalidateKeyMaterial(Context context) {
|
||||
Log.w(TAG, "INVALIDATING ALL KEY MATERIAL...");
|
||||
SharedPreferences settings = getSharedPreferences(context);
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME,
|
||||
Context.MODE_MULTI_PROCESS);
|
||||
|
||||
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();
|
||||
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();
|
||||
}
|
||||
|
||||
private static void saveBytes(Context context, String key, byte[] value) {
|
||||
SharedPreferences settings = getSharedPreferences(context);
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME,
|
||||
Context.MODE_MULTI_PROCESS);
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
|
||||
editor.putString(key, Base64.encodeBytes(value));
|
||||
editor.apply();
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
private static void saveString(Context context, String key, String value) {
|
||||
SharedPreferences settings = getSharedPreferences(context);
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME,
|
||||
Context.MODE_MULTI_PROCESS);
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
|
||||
editor.putString(key, value);
|
||||
editor.apply();
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
private static Optional<byte[]> retrieveBytes(Context context, String key) throws IOException {
|
||||
SharedPreferences settings = getSharedPreferences(context);
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME,
|
||||
Context.MODE_MULTI_PROCESS);
|
||||
String encodedValue = settings.getString(key, null);
|
||||
|
||||
if (encodedValue == null)
|
||||
@ -128,7 +139,9 @@ public class KeyStore {
|
||||
}
|
||||
|
||||
private static Optional<String> retrieveString(Context context, String key) {
|
||||
return Optional.fromNullable(getSharedPreferences(context).getString(key, null));
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME,
|
||||
Context.MODE_MULTI_PROCESS);
|
||||
return Optional.fromNullable(settings.getString(key, null));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -27,6 +27,7 @@ import java.security.GeneralSecurityException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
|
||||
@ -40,12 +40,14 @@ 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(SecretKey cipherKey, SecretKey macKey) {
|
||||
this.cipherKey = cipherKey;
|
||||
this.macKey = macKey;
|
||||
protected MasterCipher(boolean useCipherVersionZero, SecretKey cipherKey, SecretKey macKey) {
|
||||
this.useCipherVersionZero = useCipherVersionZero;
|
||||
this.cipherKey = cipherKey;
|
||||
this.macKey = macKey;
|
||||
}
|
||||
|
||||
public byte[] encryptAndEncode(byte[] data)
|
||||
@ -59,9 +61,15 @@ 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));
|
||||
|
||||
return Base64.encodeBytesToBytes(Util.combine(new byte[] {CURRENT_CIPHER_VERSION}, iv, ciphertext, mac));
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
public String encryptAndEncode(String data)
|
||||
@ -70,9 +78,39 @@ 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");
|
||||
|
||||
@ -1,67 +0,0 @@
|
||||
/*
|
||||
* 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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock.registration;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.anhonesteffort.flock.registration.model.AugmentedFlockAccount;
|
||||
import org.anhonesteffort.flock.registration.model.FlockCardInformation;
|
||||
import org.anhonesteffort.flock.util.MapperUtil;
|
||||
import org.apache.http.HttpResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* rhodey
|
||||
*/
|
||||
public class ModelFactory {
|
||||
|
||||
public static AugmentedFlockAccount buildAccount(HttpResponse response)
|
||||
throws RegistrationApiParseException
|
||||
{
|
||||
try {
|
||||
|
||||
return MapperUtil.getMapper().readValue(
|
||||
response.getEntity().getContent(), AugmentedFlockAccount.class
|
||||
);
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e(ModelFactory.class.getName(), "unable to build account from HTTP response", e);
|
||||
throw new RegistrationApiParseException("unable to build account from HTTP response.");
|
||||
}
|
||||
}
|
||||
|
||||
public static FlockCardInformation buildCard(HttpResponse response)
|
||||
throws RegistrationApiParseException
|
||||
{
|
||||
try {
|
||||
|
||||
return MapperUtil.getMapper().readValue(
|
||||
response.getEntity().getContent(), FlockCardInformation.class
|
||||
);
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e(ModelFactory.class.getName(), "unable to build card information from HTTP response", e);
|
||||
throw new RegistrationApiParseException("unable to build card information from HTTP response.");
|
||||
}
|
||||
}
|
||||
|
||||
public static Boolean buildBoolean(HttpResponse response)
|
||||
throws RegistrationApiParseException
|
||||
{
|
||||
try {
|
||||
|
||||
return MapperUtil.getMapper().readValue(
|
||||
response.getEntity().getContent(), Boolean.class
|
||||
);
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e(ModelFactory.class.getName(), "unable to build boolean from HTTP response", e);
|
||||
throw new RegistrationApiParseException("unable to build boolean from HTTP response.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -33,48 +33,40 @@ import java.util.List;
|
||||
*/
|
||||
public class OwsRegistration {
|
||||
|
||||
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 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;
|
||||
|
||||
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_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 PARAM_AUTO_RENEW = "auto_renew";
|
||||
|
||||
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 + "/v2/" + ACCOUNT_COLLECTION;
|
||||
protected static final String HREF_PRICING = HREF_REGISTRATION_API + "/" + PRICING_CONTROLLER;
|
||||
protected static final String HREF_ACCOUNT_COLLECTION = HREF_REGISTRATION_API + "/" + 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_REGISTRATION_API + "/v2/" + ACCOUNT_COLLECTION + "/" + accountId;
|
||||
return HREF_ACCOUNT_COLLECTION + accountId;
|
||||
}
|
||||
|
||||
protected static String getHrefForCard(String accountId) {
|
||||
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;
|
||||
return HREF_ACCOUNT_COLLECTION + accountId + "/" + ACCOUNT_CARD_CONTROLLER;
|
||||
}
|
||||
|
||||
protected static String getHrefWithParameters(String href, List<NameValuePair> params) {
|
||||
|
||||
@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.anhonesteffort.flock.registration;
|
||||
|
||||
/**
|
||||
* rhodey
|
||||
*/
|
||||
public class PaymentRequiredException extends RegistrationApiException {
|
||||
|
||||
public PaymentRequiredException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@ -20,13 +20,21 @@
|
||||
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;
|
||||
@ -35,11 +43,17 @@ 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.util.LinkedList;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.security.KeyStore;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -49,28 +63,58 @@ public class RegistrationApi {
|
||||
|
||||
private static final String TAG = "org.anhonesteffort.flock.registration.RegistrationApi";
|
||||
|
||||
private final DefaultHttpClient httpClient;
|
||||
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";
|
||||
|
||||
public RegistrationApi(Context context) throws RegistrationApiException {
|
||||
this.httpClient = new HttpClientFactory(context).buildClient();
|
||||
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);
|
||||
}
|
||||
|
||||
private static void authorizeRequest(HttpRequestBase httpRequest, DavAccount account) {
|
||||
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) {
|
||||
String encodedAuth = account.getUserId() + ":" + account.getAuthToken();
|
||||
httpRequest.addHeader("Authorization", "Basic " + Base64.encodeBytes(encodedAuth.getBytes()));
|
||||
}
|
||||
|
||||
private static void throwExceptionIfNotOK(HttpResponse response)
|
||||
throws RegistrationApiException
|
||||
{
|
||||
private void throwExceptionIfNotOK(HttpResponse response) throws RegistrationApiException {
|
||||
Log.d(TAG, "response status code: " + response.getStatusLine().getStatusCode());
|
||||
|
||||
Integer status = response.getStatusLine().getStatusCode();
|
||||
switch (response.getStatusLine().getStatusCode()) {
|
||||
case OwsRegistration.STATUS_MALFORMED_REQUEST:
|
||||
throw new RegistrationApiClientException("Registration API returned status malformed request.");
|
||||
|
||||
if (status >= 200 && status < 300)
|
||||
return;
|
||||
|
||||
switch (status) {
|
||||
case OwsRegistration.STATUS_UNAUTHORIZED:
|
||||
throw new AuthorizationException("Registration API returned status unauthorized.");
|
||||
|
||||
@ -81,15 +125,73 @@ public class RegistrationApi {
|
||||
throw new ResourceAlreadyExistsException("Registration API returned status 403, resource already exists.");
|
||||
|
||||
case OwsRegistration.STATUS_PAYMENT_REQUIRED:
|
||||
throw new PaymentRequiredException("Registration API didn't like the card or purchase token we gave it");
|
||||
throw new RegistrationApiClientException("Registration API didn't like the card token we gave it",
|
||||
OwsRegistration.STATUS_PAYMENT_REQUIRED);
|
||||
|
||||
case OwsRegistration.STATUS_RATE_LIMITED:
|
||||
throw new RegistrationApiException("we are being rate limited for some reason, bummer.");
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
throw new RegistrationApiException(
|
||||
"got bad status: " + response.getStatusLine().getReasonPhrase() + ", " + status, status
|
||||
);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public AugmentedFlockAccount createAccount(DavAccount account)
|
||||
@ -97,83 +199,81 @@ public class RegistrationApi {
|
||||
{
|
||||
Log.d(TAG, "createAccount()");
|
||||
|
||||
List<NameValuePair> nameValuePairs = new LinkedList<>();
|
||||
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(1);
|
||||
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 accountsControllerHref = OwsRegistration.getHrefWithParameters(
|
||||
OwsRegistration.HREF_ACCOUNT_COLLECTION,
|
||||
nameValuePairs
|
||||
);
|
||||
String HREF = OwsRegistration.getHrefWithParameters(OwsRegistration.HREF_ACCOUNT_COLLECTION,
|
||||
nameValuePairs);
|
||||
HttpPost httpPost = new HttpPost(HREF);
|
||||
DefaultHttpClient httpClient = getClient(context);
|
||||
|
||||
HttpPost httpPost = new HttpPost(accountsControllerHref);
|
||||
HttpResponse response = httpClient.execute(httpPost);
|
||||
|
||||
throwExceptionIfNotOK(response);
|
||||
|
||||
return ModelFactory.buildAccount(response);
|
||||
}
|
||||
AugmentedFlockAccount flockAccount = buildFlockAccount(response);
|
||||
|
||||
cacheSubscriptionDetails(context,
|
||||
flockAccount.getDaysRemaining(),
|
||||
flockAccount.getAutoRenewEnabled(),
|
||||
flockAccount.getLastStripeChargeFailed());
|
||||
return flockAccount;
|
||||
}
|
||||
|
||||
public AugmentedFlockAccount getAccount(DavAccount account)
|
||||
throws RegistrationApiException, IOException
|
||||
{
|
||||
String HREF = OwsRegistration.getHrefForAccount(account.getUserId());
|
||||
HttpGet httpGet = new HttpGet(HREF);
|
||||
String HREF = OwsRegistration.getHrefForAccount(account.getUserId());
|
||||
HttpGet httpGet = new HttpGet(HREF);
|
||||
DefaultHttpClient httpClient = getClient(context);
|
||||
authorizeRequest(httpGet, account);
|
||||
|
||||
HttpResponse response = httpClient.execute(httpGet);
|
||||
throwExceptionIfNotOK(response);
|
||||
|
||||
return ModelFactory.buildAccount(response);
|
||||
AugmentedFlockAccount flockAccount = buildFlockAccount(response);
|
||||
|
||||
cacheSubscriptionDetails(context,
|
||||
flockAccount.getDaysRemaining(),
|
||||
flockAccount.getAutoRenewEnabled(),
|
||||
flockAccount.getLastStripeChargeFailed());
|
||||
return flockAccount;
|
||||
}
|
||||
|
||||
public void setAccountVersion(DavAccount account, Integer version)
|
||||
throws RegistrationApiException, IOException
|
||||
private static void cacheSubscriptionDetails(Context context,
|
||||
Long daysRemaining,
|
||||
Boolean autoRenewEnabled,
|
||||
Boolean lastChargeFailed)
|
||||
{
|
||||
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);
|
||||
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();
|
||||
}
|
||||
|
||||
public void setAccountPassword(DavAccount account, String newPassword)
|
||||
throws RegistrationApiException, IOException
|
||||
{
|
||||
Log.d(TAG, "setAccountPassword()");
|
||||
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();
|
||||
|
||||
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);
|
||||
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)
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
public Optional<FlockCardInformation> getCard(DavAccount account)
|
||||
throws RegistrationApiException, IOException
|
||||
{
|
||||
String cardControllerHref = OwsRegistration.getHrefForCard(account.getUserId());
|
||||
HttpGet httpGet = new HttpGet(cardControllerHref);
|
||||
String HREF = OwsRegistration.getHrefForCard(account.getUserId());
|
||||
HttpGet httpGet = new HttpGet(HREF);
|
||||
DefaultHttpClient httpClient = getClient(context);
|
||||
authorizeRequest(httpGet, account);
|
||||
|
||||
HttpResponse response = httpClient.execute(httpGet);
|
||||
@ -186,95 +286,99 @@ public class RegistrationApi {
|
||||
return Optional.absent();
|
||||
}
|
||||
|
||||
return Optional.of(ModelFactory.buildCard(response));
|
||||
return Optional.of(buildFlockCardInformation(response));
|
||||
}
|
||||
|
||||
public void setStripeCard(DavAccount account, String stripeCardToken)
|
||||
public void setAccountVersion(DavAccount account, Integer version)
|
||||
throws RegistrationApiException, IOException
|
||||
{
|
||||
List<NameValuePair> nameValuePairs = new LinkedList<>();
|
||||
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);
|
||||
nameValuePairs.add(new BasicNameValuePair(OwsRegistration.PARAM_STRIPE_CARD_TOKEN, stripeCardToken));
|
||||
|
||||
String cardControllerHref = OwsRegistration.getHrefWithParameters(
|
||||
OwsRegistration.getHrefForCard(account.getUserId()),
|
||||
nameValuePairs
|
||||
);
|
||||
String HREF = OwsRegistration.getHrefWithParameters(OwsRegistration.getHrefForAccount(account.getUserId()), nameValuePairs);
|
||||
HttpPut httpPut = new HttpPut(HREF);
|
||||
DefaultHttpClient httpClient = getClient(context);
|
||||
authorizeRequest(httpPut, account);
|
||||
|
||||
HttpPut httpPut = new HttpPut(cardControllerHref);
|
||||
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);
|
||||
authorizeRequest(httpPut, account);
|
||||
|
||||
HttpResponse response = httpClient.execute(httpPut);
|
||||
throwExceptionIfNotOK(response);
|
||||
}
|
||||
|
||||
public boolean getIsPlanAutoRenewing(DavAccount account)
|
||||
throws RegistrationApiException, IOException
|
||||
{
|
||||
String planControllerHref = OwsRegistration.getHrefForPlan(account.getUserId());
|
||||
HttpGet httpGet = new HttpGet(planControllerHref);
|
||||
authorizeRequest(httpGet, account);
|
||||
Optional<Pair<Long, Boolean[]>> subscriptionDetails = getCachedSubscriptionDetails(context);
|
||||
if (!subscriptionDetails.isPresent())
|
||||
return;
|
||||
|
||||
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);
|
||||
cacheSubscriptionDetails(context,
|
||||
subscriptionDetails.get().first,
|
||||
autoRenewEnabled,
|
||||
subscriptionDetails.get().second[1]);
|
||||
}
|
||||
|
||||
public void deleteAccount(DavAccount account)
|
||||
@ -282,12 +386,13 @@ public class RegistrationApi {
|
||||
{
|
||||
Log.d(TAG, "deleteAccount()");
|
||||
|
||||
String accountControllerHref = OwsRegistration.getHrefForAccount(account.getUserId());
|
||||
HttpDelete httpDelete = new HttpDelete(accountControllerHref);
|
||||
HttpDelete httpDelete = new HttpDelete(OwsRegistration.getHrefForAccount(account.getUserId()));
|
||||
DefaultHttpClient httpClient = getClient(context);
|
||||
authorizeRequest(httpDelete, account);
|
||||
|
||||
HttpResponse response = httpClient.execute(httpDelete);
|
||||
throwExceptionIfNotOK(response);
|
||||
}
|
||||
|
||||
cacheSubscriptionDetails(context, 0L, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user