Compare commits

...

2061 Commits

Author SHA1 Message Date
b028ff9d8e Add github sync for i2p.i2p-bote
Some checks failed
Sync Primary Repository to GitHub Mirror / sync (push) Has been cancelled
2025-05-10 18:50:16 -04:00
0775c4dd49 Merge pull request #119 from i2p/release-0.4.8
I2P-Bote 0.4.8
2019-05-16 13:29:06 +01:00
9fd4f6cb03 Update versions and history for 0.4.8 2019-05-16 12:21:08 +01:00
edb086cc1d Update translations after string change 2019-05-16 12:19:57 +01:00
7d5b9a62d4 Update translation strings after changes 2019-05-16 12:18:08 +01:00
86479e543f Update I2P-Bote connection error page to match Bote Android 2019-05-16 12:14:55 +01:00
32ae4d0cf4 Remove old redundant entries from plugin.config
The values are canonically set in webapp/build.gradle, and are
unnecessary here.
2019-05-16 11:35:54 +01:00
c835216b98 Update bug reports URL 2019-05-16 11:29:14 +01:00
2b7e6985ff Translation updates 2019-05-16 11:07:25 +01:00
e989db7470 Upgrade to I2P 0.9.40 2019-05-16 11:06:40 +01:00
1c4401590b Upgrade Gradle wrapper to 5.4.1 2019-05-16 09:10:55 +01:00
d5383c056a Merge pull request #118 from mikalv/master
Release commit for 0.6.1
2019-05-15 21:22:48 +01:00
eb9e88d51d Release commit for 0.6.1 2019-05-14 00:50:10 +02:00
4f142e6737 Merge pull request #112 from eyedeekay/master
Add eyedeekay to bootstrap peers, remove kytv
2019-05-10 00:38:45 +02:00
1df6f61513 Merge pull request #115 from i2p/bug-fixes
Bug fixes
2019-05-09 16:54:07 +01:00
idk
348010573a update node list to include my node(idk) and mark kytv's node as inactive 2019-04-25 14:23:48 -04:00
6dc3c49a01 Set log level for i2p.bote.* in Android to DEBUG in BoteService 2019-04-24 12:50:54 +01:00
2e65d25b02 Test against OpenJDK 12 2019-04-24 12:14:02 +01:00
da2e65a4e8 Fix NPE when reading the local destination key from the key file
Base64.decode() returns null on error. Additionally, since I2P 0.9.14,
leading and trailing whitespace will cause an error.
2019-04-24 12:10:26 +01:00
9019f40252 Merge pull request #111 from i2p/i2p-0.9.39
Upgrade to I2P 0.9.39
2019-04-22 20:09:38 +01:00
4b1eb5f841 Upgrade to I2P 0.9.39 2019-04-22 09:21:29 +01:00
18c05f57a5 Copy ElGamalAESEngine from the router library
The engine was moved out of the I2P core library in 0.9.38, but we still
depend on it for ElGamal2048-DSA1024 addresses and RelayRequest
encryption.
2019-04-22 08:57:33 +01:00
fafab53af2 Merge pull request #110 from i2p/build-system-updates-2
Build system updates 2
2019-04-21 23:34:50 +01:00
6f610b40fb Update Java 7 build support with changes from i2p.i2p 2019-04-21 16:49:04 +01:00
2451f1503a Upgrade Tomcat to 8.5.40 2019-04-21 16:49:04 +01:00
697f31fb41 Upgrade to supported Gretty plugin 2019-04-20 19:50:05 +01:00
2351462b84 Upgrade Android test runner support libraries 2019-04-20 19:20:46 +01:00
a4ac435a78 Upgrade Android multidex support library 2019-04-20 19:15:38 +01:00
ecf4437412 Upgrade general dependencies 2019-04-20 17:01:36 +01:00
c9178f6595 Upgrade Android Gradle build tools 2019-04-20 16:21:18 +01:00
20dee0c93b Fix Android dependencies after build system updates 2019-04-20 16:20:53 +01:00
06c16f7184 Merge pull request #109 from mikalv/master
Translation updates and permissions fix
2019-04-19 22:43:00 +01:00
19d9bdf146 Use same uid as package name 2019-03-23 03:40:33 +01:00
945dd02bc4 A fix to make bote connect to the router on newer android api's. 2019-03-23 03:36:46 +01:00
3cee668040 Translations update 2019-03-23 03:35:06 +01:00
86b6d9d16b Translations update 2019-03-23 03:33:26 +01:00
9f83d07d73 Merge pull request #107 from i2p/build-system-updates
Build system updates
2019-02-16 18:05:31 +00:00
40a3ec9b4e Install newer gettext in Travis CI
gettext 0.19.8-6 fixes a bug where newer JDKs weren't detected. The
trusty build environment doesn't support the latest gettext Debian
package, as it requires a newer dpkg. However, upgrading to a 0.19 build
is sufficient, as then we can generate source files with msgfmt instead
of class files.
2019-02-16 17:18:14 +00:00
86bec4d2e1 Mark message classes from older msgfmt versions as generated output 2019-02-16 16:04:00 +00:00
e71b3c416c Update Travis CI config to test against more JDKs 2019-02-16 15:19:58 +00:00
d5ef16da5b Fix building and testing with JDK 11 2019-02-16 15:17:46 +00:00
2a64794a05 Set up cross-compilation for JDK 9+ 2019-02-16 15:17:39 +00:00
ebe99afe57 Upgrade Gradle wrapper to 5.2.1 2019-02-16 13:43:26 +00:00
9ed3867ccb Merge pull request #101 from i2p/release-0.4.7
I2P-Bote 0.4.7
2018-04-09 07:41:57 -06:00
ec526ccbe1 Update versions and history for 0.4.7 2018-04-09 07:15:39 -06:00
cb2b9801c8 Upgrade Tomcat to 8.5.23 2018-04-09 06:50:36 -06:00
dcf6d8d982 Upgrade I2P dependencies 2018-04-08 14:30:32 +01:00
c0926d0002 Set default store hops to 2 2018-04-08 14:30:00 +01:00
292517003a Merge pull request #100 from i2p/imap4-fixes
IMAP4 fixes
2018-04-08 13:33:59 +01:00
0454319313 Merge pull request #99 from i2p/build-script-fixes
Build script fixes
2018-04-08 13:25:17 +01:00
f1facc975b Don't bitshift uidValidity and modSeq out of range 2018-04-07 22:50:33 +01:00
e9dd6d0877 Use default IMAP namespace for users 2018-04-07 22:38:02 +01:00
4589b7ad38 Remove stray line in build script 2018-04-07 20:53:17 +01:00
e1c721dc78 Migrate from bootClasspath to bootstrapClasspath 2018-04-07 20:53:17 +01:00
5b095b257d Merge pull request #97 from i2p/james-server-3.0.1
Migrate to James Server 3.0.1
2018-04-07 20:51:23 +01:00
c4bf87717c Upgrade Gradle wrapper to 4.6 2018-04-07 20:21:02 +01:00
1906bcbc1f Add support for older msgfmt versions to webapps build script 2018-04-07 18:59:30 +01:00
32d634e350 Migrate to James Server 3.0.1 2018-04-07 17:24:28 +01:00
28bed3c325 Initialise APIs during construction instead of after tunnel building
IMAP and SMTP should be useable while an I2P connection is being set up.
2018-04-07 02:38:46 +01:00
93efffae1b Revert more webapp dependencies to use the "compile" configuration
The plugin build script needs to be able to enumerate all dependencies that need
to be packed, which isn't possible with the "implementation" configuration.
2017-12-07 14:17:07 +13:00
5a9220c47c Revert several webapp dependencies to use the "compile" configuration
They are needed by the JSP compilation step.
2017-12-04 12:28:32 +13:00
3b0ad4ee13 Define API dependencies of core library 2017-12-04 12:14:57 +13:00
44b0fdf75a Add Travis CI build script
Copied from b8eeb72044/.travis.yml
2017-12-04 11:44:48 +13:00
b9bafe2f1a Upgrade I2P dependencies 2017-12-04 11:37:36 +13:00
11fd617fee Upgrade BouncyCastle to 1.58
Includes workaround for https://github.com/bcgit/bc-java/issues/262
2017-12-04 11:29:46 +13:00
5172b704fd Upgrade Android dependencies 2017-12-04 09:17:18 +13:00
52a5c70671 Upgrade to Gradle 4.1 and Android Gradle Tools 3.0.1
Required dropping gradle-witness because it does not support modern Gradle.
2017-12-04 08:50:41 +13:00
c082a50cad Use a common I2P dependency version for core and android 2017-12-04 08:36:27 +13:00
766d5ef00f Fix compilation without bootstrap classpath set 2017-12-04 08:12:37 +13:00
96784b88bb Use new plugins DSL to apply Gretty plugin
Fixes a classpath interaction issue between Gretty and the Android subproject.
2017-08-20 06:13:01 +12:00
f2c152baf8 Tidy up README 2017-05-07 16:06:16 +12:00
d7157423ae Add build tasks and instructions for standalone WAR
Closes #92.
2017-05-07 16:02:51 +12:00
95d7180400 Add missing runtime dependency to fat WAR 2017-05-07 15:51:20 +12:00
7363b658f8 Build separate thin WAR for use in I2P plugin 2017-05-07 15:19:42 +12:00
287ff45915 Merge Android README into main README, add plugin build instructions 2017-05-07 13:03:26 +12:00
9b275e59ce Add .gitignore 2017-05-07 13:03:08 +12:00
b8b9171c08 Fetch James Server archive during execution phase instead of configuration phase 2017-05-07 13:02:17 +12:00
9430125217 Fetch James Server archive over HTTPS 2017-05-07 13:01:52 +12:00
fbf5b43b9e Enable building the plugin without having the Android SDK 2017-05-07 13:00:45 +12:00
6566ad1510 I2P-Bote 0.4.6 2017-05-03 21:25:46 +00:00
169d2061d0 Leave Tomcat JARs out of plugin 2017-05-03 21:16:19 +00:00
3a1298369c Only set i2cp.domainSocket if using I2P Android 2017-04-29 12:09:10 +00:00
024930873f Add Gradle wrapper 2017-04-08 01:17:24 +00:00
2d92022994 Changes to support Jetty 9
- Upgrade servlet and JSP dependencies
- Require Java 1.7, I2P 0.9.30, Jetty 9
2017-04-02 06:40:55 +00:00
8686741693 Fix project version configuration 2017-04-02 05:34:06 +00:00
f9a40374da Upgrade I2P dependencies 2017-04-02 05:20:16 +00:00
f741a7f966 Move old build.xml out of the way
Leaving it in the repository until the Win32 packaging is migrated.
2017-04-02 04:56:18 +00:00
c0baa7278e Update history 2017-04-02 04:53:37 +00:00
720a42ed4d Drop unused libs 2017-04-02 04:46:07 +00:00
155e0bfd0d Remove XPI2P support from the I2P plugin Gradle plugin
It works, but the format is long-deprecated and should not be used for new
plugins.
2017-04-02 04:39:22 +00:00
dbeaf665a2 Generate plugin update files identical to installers (for now) 2017-03-27 11:16:57 +00:00
215120fed0 Upgrade test dependencies 2017-03-27 04:44:33 +00:00
678cf1a5be Move I2PPlugin application to start of webapp build file 2017-03-27 04:44:25 +00:00
fb1a7ddebe Static functions 2017-03-27 04:03:33 +00:00
e056828e3f More sensible name for plugin key directory 2017-03-27 04:00:23 +00:00
ed37609e80 Update translations 2017-03-26 07:41:28 +00:00
45ed12fe7d Update webapp messages after changes 2017-03-26 07:19:35 +00:00
7cdb3cac17 Update translations 2017-03-26 07:17:15 +00:00
f3866de65d Update messages path in Transifex config 2017-03-26 07:16:20 +00:00
0234afec0e Migrate message compilation and bundling to Gradle 2017-03-26 07:07:34 +00:00
13798b3bf2 Migrate poupdate command to Gradle 2017-03-13 11:14:01 +00:00
beadf2917e Move translation files into webapp 2017-03-13 08:16:17 +00:00
50c2316537 Move translation helpers from core to webapp 2017-03-13 01:41:47 +00:00
2987ec6901 Un-translate theme names
This is a minor usability regression, and may be fixed later, but is necessary
for now to enable correct translation handling between webapp and android.
2017-03-13 01:32:31 +00:00
2de7962a37 Extract peer stats translations from core, expose reachability 2017-03-13 01:24:59 +00:00
37ebdf5ced Connect plugin signing inputs to plugin.zip output 2017-03-12 09:58:27 +00:00
ea9fbb5b27 Remove remaining usages of Util._t() from GeneralHelper 2017-03-12 09:57:51 +00:00
bb99f1196f Bugfix 2017-03-12 06:14:17 +00:00
a70bdee405 Move invalid address error translation from core to webapp 2017-03-12 05:10:06 +00:00
9fb28d6724 Upgrade support libraries to 25.2.0 2017-03-12 04:47:38 +00:00
6f0cff9ca5 Suppress Support version lint
Lint is incorrectly requiring multidex to have the same version as other support
libraries.
2017-03-12 04:36:33 +00:00
259106a369 Upgrade to newer maintained version of ViewPagerIndicator
Fixes an incompatibility with API 23+
2017-03-12 03:06:00 +00:00
76dfa45fce Move getHumanReadableSize to JSPHelper 2017-03-12 02:43:37 +00:00
4f506027f4 Escape apostrophes in Android strings 2017-03-12 00:28:53 +00:00
32ca9e1988 Move "bad vanity chars" error message out of core 2017-02-19 21:21:24 +00:00
97b7c6a4d9 Work around SCrypt pack200 issue 2017-02-19 21:19:09 +00:00
26208ddeb4 Move BanList strings from core to webapp 2017-02-19 15:49:07 +00:00
fb9aae2928 Move send error translations from core to webapp 2017-02-19 15:34:18 +00:00
e0edf870b8 Bugfix 2017-02-19 15:14:44 +00:00
a49360a2cb Subclass PasswordException for specific causes 2017-02-19 15:10:21 +00:00
2b47c1f6b3 Move StatusListener strings from core to android 2017-02-19 12:22:17 +00:00
9d1dcabdf2 Don't translate crypto names 2017-02-18 16:04:08 +00:00
0b7c98820a Update translations 2017-02-13 00:30:12 +00:00
597205bd09 Use static string 2017-02-12 23:59:47 +00:00
6b0f66376e Bugfix: use system default locale 2017-02-12 23:54:20 +00:00
3ab3d2faac Update list of Android UI languages 2017-02-12 23:34:14 +00:00
af6abb5b21 New Android translations 2017-02-12 23:23:33 +00:00
1c2e7d886a Update Android translations 2017-02-12 23:20:36 +00:00
ebde691774 Merge Transifex config files 2017-02-12 23:19:16 +00:00
1184bcaba4 Enable encrypted files to be selected for importing 2017-02-12 23:15:21 +00:00
de9c8f9036 Android address book import/export 2017-02-12 23:06:50 +00:00
ee783dc31b Extract Android data import/export logic into parent classes 2017-02-12 22:30:41 +00:00
1536ffc68c Add address book export/import test 2017-02-12 21:15:53 +00:00
23760ee341 Make the address book exportable 2017-02-12 20:52:41 +00:00
7770214150 Extract import/export file logic from identities into a parent class 2017-02-12 20:41:44 +00:00
9865927b30 Extract AddressBook <-> Properties conversion into separate methods 2017-02-12 20:04:47 +00:00
2f2c67d7f3 app:theme -> android:theme (and layout reformatting) 2017-02-12 18:41:43 +00:00
102c62cffa Remove checks for API >= 10, since minSdkVersion = 10 2017-02-12 18:17:13 +00:00
45242e9079 Fix NPE when adding attachment (ticket #1730) 2017-02-12 17:43:46 +00:00
b9f175f4b9 Replace anonymous DialogFragment subclasses with full subclasses 2017-02-12 01:17:37 +00:00
1f2aa72f1a Upgrade Android dependencies 2017-02-12 00:39:59 +00:00
d3b2d33860 Compile against SDK 25
- Bump compileSdkVersion to 25
- Upgrade build tools to 25.0.2
- Upgrade support libraries to 25.1.1
2017-02-12 00:05:10 +00:00
fd492ec00c Upgrade Android test harness 2017-02-11 23:31:29 +00:00
7a0d7b3acc Migrate to support library PreferenceFragmentCompat 2017-02-11 23:27:55 +00:00
b9da2b27a8 Enable multidex 2017-02-11 15:43:14 +00:00
1b00976ff9 Compile against SDK 23
- Bump compileSdkVersion to 23
- Upgrade build tools to 23.0.3
- Upgrade support libraries to 23.4.0
- Replace html-textview code with upstream library that supports SDK 23
2017-02-11 00:14:27 +00:00
a8c5ae8586 Upgrade Android Gradle build tools 2017-02-10 23:16:00 +00:00
4a5cb919de Exclude jstl.jar from I2P plugin 2017-01-21 15:56:38 +00:00
0e7e7ed03a Update Gradle build scripts after prop 2017-01-21 15:56:23 +00:00
d1a2b92bca propagate from branch 'i2p.i2p-bote' (head c1349d682349dd701b9f0b18624293e7acca9ee3)
to branch 'i2p.i2p-bote.gradle' (head ff5f091c01e0dee5a95632846781536d11ff6324)
2017-01-20 18:13:21 +00:00
31cc63bf84 0.4.5 2017-01-20 18:06:17 +00:00
10f951a03b New translations 2017-01-20 15:44:21 +00:00
a1c5af227c Updated translations 2017-01-20 15:43:48 +00:00
2e53c3d5bc Update history 2017-01-20 15:40:22 +00:00
05c915743c Add a Content Security Policy 2017-01-20 15:38:44 +00:00
f1d790c5ff Escape title in header 2017-01-20 15:38:17 +00:00
75ed190892 Escape attachment filenames when showing an email
Thanks BearDog for raising the issue!
2017-01-20 15:37:57 +00:00
fce00ccc7a Add CSRF checking to GET requests on setPassword.jsp 2017-01-20 15:17:23 +00:00
cd1f3e4b98 Catch IAEs when sorting a Folder (ticket #1931) 2017-01-15 19:10:55 +00:00
23a9a0c0db Add CSRF checking to GET requests on submitIdentity.jsp 2017-01-08 14:42:34 +00:00
8cfff96e15 Pull in bundle-messages.sh improvements 2017-01-08 10:49:23 +00:00
61fe334d79 0.4.4 2016-11-28 09:59:45 +00:00
4985e07307 Enforce same-origin policy for POST 2016-11-28 09:14:17 +00:00
c1b40076df Update ignores 2016-11-28 03:39:25 +00:00
a2c98d83f0 Update translations 2016-11-28 03:33:22 +00:00
3234f05c6b Update translation strings 2016-11-27 10:00:04 +00:00
dc7eb02636 Updated translations 2016-11-27 09:52:04 +00:00
1e0f561126 Add missing POST check 2016-11-27 09:47:55 +00:00
8bd02eba47 Show a more helpful screen for CSRF errors 2016-11-27 07:54:13 +00:00
200756a13b Hook CSRF logging into I2P logs 2016-11-27 07:33:46 +00:00
57d12ca32f Add version to CSS URLs so browsers refetch after upgrades 2016-11-27 06:35:05 +00:00
90818cf1f4 Distinguish error messages from info messages in Material theme 2016-11-27 06:13:12 +00:00
3052824d1b Tweak error message to avoid XSS filter (ticket #1815) 2016-11-27 04:04:06 +00:00
b4b52dffa0 Move HungryHobo to "past developers" :( 2016-11-27 02:45:24 +00:00
de720d01c4 Added CSRF guards to all forms
Thanks Beardog for raising the issue!
2016-11-27 02:10:36 +00:00
694de2e4f6 Updated translation strings and translations 2016-11-24 02:54:11 +00:00
435ca2d12b Add links to source code in FAQ 2016-11-24 02:48:43 +00:00
849fbc26d4 Added new translations 2016-11-24 02:42:33 +00:00
37ce138788 Update history and about 2016-11-24 01:56:17 +00:00
c716b93509 Upgrade JavaMail 2016-11-21 07:52:39 +00:00
e2a9f0b323 Tweak I2P plugin DSL to reflect plugin layout 2016-11-21 02:34:19 +00:00
1662da85b7 Add javax.servlet and javax.servlet.jsp to plugin exclusions 2016-11-20 11:04:08 +00:00
4a2b3a8047 Get the Android app building
- Upgrade to I2P 0.9.27
- Rewrite ECUtils to work with both BouncyCastle and SpongyCastle
- Upgrade Mockito to 2.2.+
- Fix ProGuard rules
- Fix viewpagerindicator hash (author accidentally overwrote it on MC)
2016-11-20 09:28:10 +00:00
ac458948e3 Missing file from previous commit 2016-11-20 03:16:53 +00:00
cba49fb0a5 Move common local libs into separate subproject
Required so that Android can access the dependencies without complaining
about them being included twice during dexing:
http://tools.android.com/tech-docs/new-build-system/tips#TOC-Handling-transitive-dependencies-for-local-artifacts-jars-and-aar-
2016-11-20 03:15:35 +00:00
690f22d60d Set up android subproject 2016-10-25 00:02:49 +00:00
dbd99ab539 Remove duplicate files 2016-10-24 23:52:21 +00:00
61126ef640 propagate from branch 'i2p.i2p-bote.android' (head 17ebdec164206fcce5445bd62feb5524a4f4c53a)
to branch 'i2p.i2p-bote.gradle' (head d3b12e514a4f1f218ec8b5dda1f39835a748fc64)
2016-10-24 23:25:38 +00:00
cb5165bd11 Implement I2P plugin build system as a custom Gradle Task 2016-10-24 04:52:59 +00:00
fd56283aea Stop filter clobbering image files 2016-10-24 04:22:31 +00:00
625c6df67e Precompile JSPs 2016-08-22 14:20:44 +00:00
0db69bbe5c Exclude test suite runner from Gradle tests to remove duplicates 2016-08-22 13:33:32 +00:00
f384358723 Add Gradle build scripts
Gradle Witness rev: 10f1269c0aafdc1d478efc005ed48f3a47d44278
2016-08-22 10:10:54 +00:00
9a51d7c071 Split source into core (used for Android) and webapp (used for plugin) 2016-08-22 07:00:48 +00:00
89c7648a27 Dynamically load router classes to remove dependency on router.jar 2016-08-20 14:14:23 +00:00
5936667c0b Refactor SeedlessParameters 2016-08-20 13:32:17 +00:00
2c2a7b703b Use new I2CP domain socket API
Requires an I2P client library based on I2P 0.9.26 (once that is released)
2016-05-29 05:15:43 +00:00
09beea3022 Upgrade Android Gradle plugin 2016-05-29 05:14:47 +00:00
7dd85757f7 More upstream changes 2016-05-29 05:14:25 +00:00
caf03e2d44 Fixes due to i2p.i2p-bote changes 2016-05-29 05:13:55 +00:00
b73a52f48f Use new I2CP domain socket API
Requires I2P Android 0.9.26
2016-05-29 05:11:48 +00:00
4b2c54a29a 0.4.3
Checked in late, forgot to do so after actual release
2016-05-08 12:33:10 +00:00
7e9bbe614a Updated ignores 2016-01-28 03:44:14 +00:00
f5aed6719a New FAQ translations 2016-01-28 03:44:03 +00:00
4f649b32d3 Updated translations 2016-01-28 03:42:01 +00:00
c6d523f9b8 Updated history 2016-01-25 15:43:02 +00:00
ae0c0df63d Temporarily bundle older working Mime4J
The particular Apache James development build I picked included a newer Mime4J
that the server code couldn't actually use...
2016-01-25 15:25:37 +00:00
9c65660a66 Enable I2CP tunnel options to be configured in i2pbote.config (ticket #1708) 2016-01-24 23:06:32 +00:00
ed1419cd55 Remove unused import 2016-01-24 22:47:14 +00:00
2e0568fa66 Only bind IMAP to the configured address and port (ticket #1680) 2016-01-24 20:33:36 +00:00
701266ca98 Add commons-logging.jar from James to updater and installer 2016-01-24 18:59:09 +00:00
07b920d11e Bump required I2P version to 0.9.24 and re-enable IMAP 2016-01-24 05:02:42 +00:00
e0366db73e JavaDoc fix 2016-01-24 04:59:35 +00:00
c5e62dfcb3 Fix ECUtils method 2016-01-23 21:44:50 +00:00
ab099ff3a3 Click on truncated identity key to see details.
Allow user to click on truncated identity key to see full identity.

Explanation:
As a noob, I spent 15 minutes clicking around, reading FAQ, documentation, etc because I couldn't figure out how to get my full address.
This should not require any thought at all.
2015-12-11 10:42:56 +00:00
c4588e673a 0.4.2 2015-11-19 06:12:16 +00:00
d1ef0161d9 Updated translation strings 2015-11-19 05:00:04 +00:00
ebacda4751 Remove SU3 files with "ant clean" 2015-11-19 04:43:27 +00:00
70cb8a778a Make contact's text field editable, display it in address book (#1667) 2015-11-18 11:09:14 +00:00
413ca2326d Updated translations 2015-11-17 09:31:15 +00:00
2e7ff07988 Updated translations 2015-11-15 06:20:45 +00:00
3ce9e87c60 AIOOBE fixes 2015-11-15 05:26:55 +00:00
11d679f5fd NPE fix 2015-11-15 05:04:12 +00:00
0835cfa793 Remove unused imports 2015-11-15 04:57:59 +00:00
035a4b609a CME fix 2015-11-15 04:49:08 +00:00
5b5aaae565 Fix Date format string for Java 6 and Android (ticket #1644) 2015-11-01 21:18:33 +00:00
1f6a407509 Updated translations 2015-11-01 11:12:51 +00:00
428aabe996 Added kay to contributors list 2015-11-01 11:12:42 +00:00
134c6ffb09 Removed HH's Bote address from FAQ (ticket #1642) 2015-11-01 11:12:06 +00:00
e35616ca1e Updated translations 2015-11-01 10:35:38 +00:00
767611f2c3 Fix migrator 2015-11-01 10:24:42 +00:00
92ae32cbb8 Use Notifications API to notify user of new emails 2015-11-01 04:13:27 +00:00
732a868581 Theme fixes, include NotoSans font 2015-10-18 11:03:34 +00:00
61ab5290be Added reminder 2015-10-17 13:38:27 +00:00
ca88452f01 Copy over code from I2P's CryptoCheck so I don't have to bump min-i2p-version 2015-10-17 13:37:20 +00:00
cb23a49234 Use I2P's test for unlimited crypto 2015-10-17 13:33:00 +00:00
2cf86f5f71 Linkify my name on about page 2015-10-17 13:21:34 +00:00
43196e5f08 Tweaked background color 2015-10-17 13:18:15 +00:00
187cfe77f2 Material theme improvements 2015-10-17 13:01:26 +00:00
172bd92aab Updated translations 2015-10-17 03:10:25 +00:00
245af96a99 Added notification and guide about unlimited strength crypto policy files 2015-10-17 02:36:03 +00:00
5cedc505c5 Implement snackbar 2015-10-17 00:32:54 +00:00
0d138b0b3d Material design: tweaked sidebar dimensions and style 2015-10-06 08:10:17 +00:00
0e6de14a63 Fixed JavaDoc generation 2015-09-28 12:20:00 +00:00
1e9a8e0e62 merge of '59904abe9efefe8ad0524f7992af7d78b0a02647'
and '62ca447c6d28866636b9699e1d361af029b8e2fa'
2015-09-28 12:16:38 +00:00
d3cb0ab3f6 0.4.1
Checked in late, forgot to do so after actual release
2015-09-28 12:16:31 +00:00
dev
30beafd36f Fixed #1661 and #1663.
Fix supplied by kay@mail.i2p.
2015-09-22 03:06:29 +00:00
dev
692f8334c2 Applied patch fixing a NPE when network and dht aren't present.
Patch supplied by kay@mail.i2p in #1662.
2015-09-22 02:53:15 +00:00
dev
40afba4093 Applied replacing _() with _t() from kay@mail.i2p in #1665. 2015-09-22 02:50:40 +00:00
e5fa4706db Fix loading of user guide and FAQ translations (#1641) 2015-09-12 15:40:16 +00:00
ecdd2ee426 Added new user guide translation 2015-09-12 14:30:22 +00:00
784a716f0a Updated translations 2015-09-12 14:29:53 +00:00
afdde691b4 CME fix 2015-09-02 01:33:49 +00:00
65d9439e03 Add owner to new ticket URL 2015-08-23 05:59:08 +00:00
8d19c4c78a Fixed CME 2015-08-21 02:22:35 +00:00
8731a72887 0.4 2015-08-21 02:20:25 +00:00
9e1b42134c Remove build numbers from version 2015-08-21 02:20:02 +00:00
17b4d035eb Update last checked time for manual checks too 2015-08-20 23:58:49 +00:00
d967d50c51 Fix email checking 2015-08-20 23:50:17 +00:00
42770cf01b Updated ignores 2015-08-20 11:53:10 +00:00
c910e17f6c Fix bug with sending anon emails 2015-08-20 11:52:09 +00:00
d408242173 Test OutboxProcessor.sendEmail(), proves existence of bug with anon emails 2015-08-20 11:26:34 +00:00
c52429f393 Add Mockito to unittest deps 2015-08-20 11:24:58 +00:00
aac9df446f 0.3 2015-08-15 23:33:58 +00:00
da40d97185 Temporarily disable IMAP due to class loader issues
Apache Commons Logging can't find the Log4J classes, because (I think) it is
not looking in the plugin's class loader (the classes are visible in I2P-Bote).
2015-08-15 23:23:51 +00:00
e661e4306c Updated ignores 2015-08-15 02:09:45 +00:00
18005cdfb3 Updated translations 2015-08-15 02:01:58 +00:00
1a7e8daf8e Fix app icon so it is bundled 2015-08-15 01:58:23 +00:00
771153c4d7 Add app icon 2015-08-15 01:55:14 +00:00
f1e750cef1 Change author to me (for feedback) 2015-08-15 01:54:54 +00:00
52e30278b5 Bump min I2P version to 0.9.18 (first to include my SU3 plugin cert) 2015-08-15 01:44:51 +00:00
4f1b81510f Add SU3 update URL 2015-08-15 01:32:26 +00:00
0c4d1dc5c3 Changed plugin signer and update URL 2015-08-15 01:28:38 +00:00
c1934b4576 Added missing image 2015-08-15 01:27:32 +00:00
0edd014c6b Deduplicate dependency verification 2015-08-15 01:26:47 +00:00
27e02ce527 Fix connection checking 2015-08-14 01:30:55 +00:00
9aa14a705e Only show "Check email" button for identities when connected 2015-08-14 00:56:50 +00:00
219de6e499 Adjust identities column widths on old themes 2015-08-14 00:56:19 +00:00
d07f790dbb Fix NPE in identity creation by properly saving identity config 2015-08-13 23:35:00 +00:00
8abfc8345b Deduplication 2015-08-13 12:39:37 +00:00
3cbcfaa331 Add config option for including local deps in update
Local deps are now moved out of update, because with the impending change of
plugin signer, all users will have all current JARs.
2015-08-13 12:00:06 +00:00
63b63a9f65 Bugfix 2015-08-13 11:29:42 +00:00
29f5db767d Extract common packing macro, delete .jar files from plugin (JIC) 2015-08-13 11:19:09 +00:00
90545b7046 Add config option for including remote deps in update
Remote deps are now moved out of update, because with the impending change of
plugin signer, all users will have all current JARs.
2015-08-13 11:00:06 +00:00
37b64f25c0 Fix hash verification 2015-08-12 12:10:49 +00:00
5f7bb2d7d1 Verify hashes of remote dependencies 2015-08-08 14:09:58 +00:00
69fc637bd7 Refactor remote dependency checking 2015-08-08 13:50:43 +00:00
cb0b0822a3 Upgrade to JavaMail 1.5.4, make it a remote dependency 2015-08-08 13:47:57 +00:00
f83f7cd085 Updated translations 2015-08-05 13:01:19 +00:00
bab92e082f Upgrade BouncyCastle to 1.52 2015-08-05 12:56:10 +00:00
d1e7eeb7bc Updated translation strings 2015-08-05 01:52:23 +00:00
a5409b0e43 Added new translations 2015-08-05 01:43:08 +00:00
742c697150 Updated translations 2015-08-05 01:42:37 +00:00
42c6269cda Improve recipient handling in email composition 2015-08-05 01:38:21 +00:00
03012e5323 Get IMAP STARTTLS working 2015-08-04 16:23:58 +00:00
97825a3d60 Upgrade to 20150627 snapshot of Apache James 2015-08-04 16:18:29 +00:00
dc5d066775 Get tests to pass 2015-08-03 13:21:57 +00:00
c2bc331b55 Add Email Date header tests 2015-08-03 13:19:17 +00:00
8f29c7d6fb Force loading of BouncyCastly so tests pass 2015-08-03 13:18:49 +00:00
6b530a364b Add direct link to creating tickets for I2P-Bote 2015-07-31 04:01:41 +00:00
3f60643c2e Updated history 2015-07-23 09:34:49 +00:00
3539b5a13b Don't move metadata file if we couldn't move email file (#1196) 2015-07-23 02:37:25 +00:00
e37f70506d Don't assume that email metadata file exists (#1196) 2015-07-23 02:27:09 +00:00
4196486799 Fix NPE (#937) 2015-07-22 21:16:18 +00:00
6dbecc0f6e Unused import 2015-07-22 12:30:51 +00:00
d209c9f94b Delete malformed packets from disk (#1287)
Incomplete email packets are unaffected because IncompleteEmailFolder overrides
the default behavior.
2015-07-22 11:44:49 +00:00
6e528dae6a Fix CME caused by no synchronization on kBuckets (#1586) 2015-07-21 10:09:32 +00:00
90f9de4861 Updated guide translations 2015-07-21 09:01:58 +00:00
f7b3499e93 Updated translations 2015-07-21 08:47:31 +00:00
7ee9db3153 Updated CHANGELOG 2015-06-21 06:41:02 +00:00
9b01bef92c 0.6
i2p.i2p-bote: 2bf81924e31a6c73f97f37a67ee877147d908cc2
i2p.i2p tag: i2p-0.9.20
2015-06-21 06:40:46 +00:00
b79696402a Fixed default warning visibility 2015-06-21 03:27:20 +00:00
65575d3125 Updated translations 2015-06-21 02:52:24 +00:00
e949864c47 Test SetupActivity UI 2015-06-21 02:33:05 +00:00
0703112831 Test IntroActivity UI
Doesn't verify result codes yet (is it possible to do so?)
2015-06-20 12:03:20 +00:00
6a1f96dd4f Static imports 2015-06-20 12:01:06 +00:00
ebcebb9f98 Use Espresso-Intents to check started Activities 2015-06-20 11:30:03 +00:00
4605158227 Start testing EmailListActivity UI 2015-06-20 10:32:18 +00:00
0b1c992739 Clean up inbox after tests 2015-06-20 09:32:08 +00:00
9f6e37f2f1 Fix Espresso dependencies 2015-06-20 09:31:46 +00:00
088ff3c883 Upgraded Espresso 2015-06-20 00:35:19 +00:00
626a25b965 Upgraded libraries 2015-06-19 10:53:25 +00:00
3269866b79 Update identities loaders on changes
Requires i2p.i2p-bote 2bf81924e31a6c73f97f37a67ee877147d908cc2
2015-06-05 10:42:10 +00:00
5901565d74 Listen to identity updates too 2015-06-05 10:08:39 +00:00
f98990ad65 Added IdentitiesListener 2015-06-05 09:37:10 +00:00
74cd4d7142 Make first identity default by default 2015-06-05 07:27:26 +00:00
d3d92c0df7 Use selected identity as default for new emails 2015-06-05 06:29:04 +00:00
449f2d171d Require password to access privacy and app protection settings 2015-06-05 05:56:25 +00:00
80e2bb2559 Updated CHANGELOG 2015-06-05 04:31:41 +00:00
51901525da Fix lock size in drawer 2015-06-05 04:30:35 +00:00
6ff1e4ae3e Make identicon size in drawer a multiple of nine 2015-06-05 04:30:12 +00:00
6f1d6e99eb propagate from branch 'i2p.i2p-bote.android.dev' (head 2a826ee95560ec9a0e9fc61f1bc8534229a9cda9)
to branch 'i2p.i2p-bote.android' (head be56b90da9cf777c5291d91ff6fb18294e6582d0)
2015-06-05 02:44:43 +00:00
47a24f8544 Better app protection icon 2015-06-05 02:42:13 +00:00
4cb93b8c8a Upgrade to Iconics 1.0.2 and MaterialDrawer 3.0.6 to fix bugs 2015-06-05 02:41:58 +00:00
4e25e59d30 Upgraded support libraries 2015-06-04 01:23:18 +00:00
f858f6a90e New help translations for in 2015-06-04 01:17:31 +00:00
4efdfcc612 Updated translations 2015-06-04 01:17:23 +00:00
904993ea77 Upgrade to client library v0.7 2015-06-04 01:09:06 +00:00
c6c6a26ac2 Drop now-unused drawables 2015-06-04 00:46:00 +00:00
6dc2161306 Move FABs to Iconics 2015-06-04 00:45:29 +00:00
79ebb2013d Fix defStyle handling 2015-06-04 00:44:36 +00:00
87908149c4 Move menus to Iconics 2015-06-04 00:22:20 +00:00
bef97cb259 Use Iconics for intro swipe icon 2015-06-03 22:50:38 +00:00
f8a6640beb Add padding to settings icons, tweak size 2015-06-03 12:58:28 +00:00
330931a03f Move Preferences to Iconics 2015-06-03 12:44:12 +00:00
c104dcd320 Added padding to IconicsDrawables measured from original drawables 2015-06-03 11:54:27 +00:00
cfc751aae2 Add explicit dependency on Android-Iconics 2015-06-02 23:28:32 +00:00
dc3f152079 Use Android-Iconics for most icons
Some icon sizes increase slightly. Fix waiting on
https://github.com/mikepenz/Android-Iconics/issues/36
https://github.com/mikepenz/MaterialDrawer/issues/384
2015-06-02 23:28:01 +00:00
d0495cfc14 Add fake "locked" profile to unlock app from drawer
Currently broken with an NPE, waiting on
https://github.com/mikepenz/MaterialDrawer/issues/381
2015-06-02 11:21:18 +00:00
f007a044db Display correct new email count for per-identity views 2015-06-02 04:52:30 +00:00
ef2edee93e Tweak how identities are displayed in the drawer 2015-06-01 14:08:37 +00:00
bebf8404bd Add "All mail" view 2015-06-01 13:59:13 +00:00
3923d70d07 Use resource IDs directly for drawer folder icons 2015-06-01 13:34:39 +00:00
fc9c4a04ac Remove unnecessary cast 2015-06-01 13:31:06 +00:00
bb9dc8a90f Enable users to filter folders by identity 2015-06-01 13:29:09 +00:00
e66f3f736d Move DrawerFolderLoader callbacks into an inner class 2015-06-01 11:27:25 +00:00
0945caf8eb Migrate to com.mikepenz:materialdrawer
Net status updates in the drawer are broken, waiting on
https://github.com/mikepenz/MaterialDrawer/issues/378
2015-06-01 10:59:28 +00:00
13e6b530d3 Moved to minSdk 10 and support libs 22.2.0 to use com.mikepenz:materialdrawer 2015-06-01 10:55:48 +00:00
e4e6fc5ec2 Updated translations 2015-05-29 11:58:37 +00:00
ef223e09b5 Use FAB for new identity action 2015-05-29 11:57:10 +00:00
4016b2d019 Updated translations after string push 2015-05-29 09:39:43 +00:00
6fbf145864 Updated CHANGELOG 2015-05-29 09:33:56 +00:00
10abb14758 Icons for preference categories 2015-05-29 09:28:04 +00:00
7915177805 Rename general settings category to network 2015-05-29 09:15:43 +00:00
0811ad125c Implement screen security, combine with password in app protection category 2015-05-29 08:20:06 +00:00
72693a3d51 Split out privacy settings into separate category 2015-05-29 06:40:26 +00:00
ad40a22b00 Extract I2P router config to advanced settings 2015-05-29 06:07:44 +00:00
8547ae4028 Lint 2015-05-29 03:47:19 +00:00
00110bfafe Configurable language, part 3 2015-05-29 03:25:33 +00:00
4af2b881e6 Configurable language, part 2 2015-05-29 03:14:35 +00:00
335b3f4e14 Configurable language, part 1 2015-05-29 02:54:40 +00:00
28c4b20b78 Nav drawer style fixes 2015-05-29 02:30:46 +00:00
6d6d2cdd00 AppCompat v22.1.*: drawable tinting 2015-05-29 02:26:03 +00:00
43bcda77b1 Correct nav drawer styling 2015-05-29 01:52:06 +00:00
3fd0a2b915 Lint 2015-05-29 01:51:39 +00:00
9235be5df2 Don't add settings twice 2015-05-29 00:09:46 +00:00
7864d62892 Pull reseed certs into Bote so the internal router can reseed 2015-05-29 00:06:33 +00:00
8d097f0712 Updated CHANGELOG 2015-05-28 12:28:03 +00:00
0da3adda55 AppCompat v22.1.*: Material design dialogs 2015-05-28 12:22:28 +00:00
a1de624d0a Deprecations 2015-05-28 12:06:42 +00:00
4199c3de8f ActionBarActivity -> AppCompatActivity 2015-05-28 12:01:53 +00:00
229e495a0f Dropped old layout 2015-05-28 11:59:41 +00:00
eb519d6255 Rebuilt libscrypt.so, included armeabi-v7a
Inclusion of armeabi-v7a is necessary because the I2P Android client library
contains a libjbigi.so for armeabi-v7a, and that causes Android to not load the
armeabi libscrypt.so, slowing down Bote.

Source: https://github.com/spaggetti/scrypt
NDK: android-ndk-r10d
2015-05-28 11:43:34 +00:00
ba745bae31 Layout tweak 2015-05-28 11:40:20 +00:00
51416659fb Bugfix 2015-05-28 11:25:55 +00:00
e54027e52b Updated translations 2015-05-28 11:21:50 +00:00
972d64db35 Missing file from previous commit 2015-05-28 11:21:15 +00:00
2f2e44c413 Added Transifex config, renamed id -> in 2015-05-28 11:21:01 +00:00
91c3590155 Migrate settings to support-v4-preferencefragment, split out identities 2015-05-28 11:01:57 +00:00
474a47d0d4 Re-enable drawer toggle (accidentally removed during Toolbar migration) 2015-05-28 01:53:22 +00:00
59f26a3522 Upgraded build tools, bumped target SDK 2015-05-27 11:39:42 +00:00
ff838bbc33 Upgraded support libraries 2015-05-27 11:38:27 +00:00
60bc70219f Prevent NPE 2015-05-27 11:26:38 +00:00
8b98f38d5e Updated TODO with Silent Store checklist (useful reference) 2015-05-27 11:24:24 +00:00
11bd90a1d5 Fully clean I2P source 2015-05-27 11:23:10 +00:00
d290665a17 New user guide translation for es 2015-03-20 09:31:16 +00:00
e2e2233b89 User guide fixes (thx strel!) 2015-03-20 09:25:31 +00:00
29190552d9 Updated translations 2015-03-20 09:24:29 +00:00
371ae89bc3 Preparation for moving StrongTls to i2p.jar:
- Removed last of pre-existing code
- Moved to net.i2p.util package
- Relicensed to public domain
- Improved interface
2015-03-19 19:49:55 +00:00
a34da62789 Overhauled StrongTls with 2015-era protocols and ciphers 2015-03-19 00:51:35 +00:00
8ff2c84cd4 Discovered that one of the old bootstrapping nodes is kytv, now resurrected 2015-03-16 10:12:19 +00:00
96140a49c5 New bootstrap peers (thanks str4d and The_Tin_Hat!) 2015-03-16 06:06:18 +00:00
b3f9ea8a0b Updated TODO 2015-03-12 21:45:10 +00:00
034660d4af Updated translations 2015-03-10 11:28:10 +00:00
bbda61316e Formatting changes by Transifex, and some string fixes 2015-03-10 11:22:45 +00:00
297a26cc63 Added user guide and FAQ to Transifex config 2015-03-10 11:22:26 +00:00
c9686adaef More formatting fixes for Transifex 2015-03-10 10:57:59 +00:00
82d31e4813 Formatting fixes for Transifex 2015-03-10 10:49:55 +00:00
63c17045b6 Multi-line Spanish translation 2015-03-10 10:40:06 +00:00
0566e42f21 New translation of FAQ into Spanish (thanks trolly!) 2015-03-10 04:16:22 +00:00
26333ee610 Upgrade to client library 0.5.1 which includes LogWriter 2015-03-02 11:15:28 +00:00
286ce0977f Necessary LogWriter changes after update to 0.9.18 2015-03-02 04:08:40 +00:00
5437999960 Upgrade Material-ish Progress library to 1.4 2015-03-02 02:21:03 +00:00
3315d6e6c2 Updated I2P client library to 0.5 2015-03-02 00:54:18 +00:00
2514e276c0 Update SKD build tools and dependencies, enable Java 1.7 features 2015-03-02 00:40:39 +00:00
6b246fb0d7 Updated README 2015-02-24 21:23:19 +00:00
c8d6703c86 Update TODO 2015-02-24 20:47:41 +00:00
a21f67d429 Fixes 2015-02-15 10:52:36 +00:00
d7ae6d51e5 FAQ navigation 2015-02-15 10:23:13 +00:00
271eb004b2 Android themed wait.gif 2015-02-15 09:21:46 +00:00
c65e3f8f7e UI option for global identity check 2015-02-15 09:21:30 +00:00
dd7f4055a1 Prevent potential NPE 2015-02-15 09:20:52 +00:00
9b0836249d Bugfix 2015-02-15 07:13:17 +00:00
0585f211fb Implement checking individual identities 2015-02-15 03:25:40 +00:00
4df989d63a Don't disable default selection for new identities when only one exists 2015-02-14 22:33:59 +00:00
a76789cf30 Button layout fix 2015-02-14 22:33:34 +00:00
ae55336e06 Updated translations 2015-02-14 22:32:50 +00:00
437c5ddbac Backend implementation of per-identity relay settings, global check exclusion 2015-02-14 21:51:01 +00:00
63a574f578 Show which nav item is currently selected 2015-02-13 02:21:53 +00:00
42c3e0d369 Match nav drawer list item highlight to Material design spec 2015-02-13 02:01:13 +00:00
5d89032b90 Corrections from Material design spec 2015-02-13 01:55:57 +00:00
fc738142fa Moved getBooleanParameter() and getIntParameter() to Util 2015-02-07 19:51:39 +00:00
4e9b0e93cb Extract String constants 2015-02-06 10:00:05 +00:00
8912cfcf7d Include HTML files in WAR 2015-02-05 10:33:06 +00:00
73e22a3d34 Reference ticket numbers in TODO 2015-02-05 09:36:42 +00:00
dfa1dfbd74 Settings headings 2015-02-04 11:21:27 +00:00
eff95792ac Updated translations 2015-02-04 11:00:52 +00:00
bf53778367 String fix 2015-02-04 11:00:29 +00:00
74bde7aac6 HTML5 cleanups 2015-02-04 01:14:44 +00:00
848ec8cb14 Added not-yet-implemented features to TODO 2015-02-04 00:56:29 +00:00
a72bd3fb04 FAQ formatting 2015-02-03 14:22:22 +00:00
87ce7646f6 FAQ pagetitle 2015-02-03 14:19:29 +00:00
e2c003503a Reformatted FAQ 2015-02-03 14:16:46 +00:00
4f9d717725 List tweaks 2015-02-03 12:58:16 +00:00
f638d8e43a Limit user guide navigation width 2015-02-03 12:30:11 +00:00
2377acf15c Reorganized user guide sections 2015-02-03 12:29:19 +00:00
f11af022b2 User guide navigation 2015-02-03 12:25:31 +00:00
9ef5a15650 Removed unnecessary installation details, renumbered headings 2015-02-03 11:59:50 +00:00
02bb52dc67 Title fixes 2015-02-03 11:51:41 +00:00
bd9a0bd6b4 Consolidated credits 2015-02-03 11:32:04 +00:00
fe7516c24f Reworked user guide intro 2015-02-03 08:43:54 +00:00
97c567a105 User guide fixes 2015-02-03 04:55:16 +00:00
62308d053d Translation string fixes 2015-02-03 04:55:02 +00:00
f4ebc330a1 Translation fixes 2015-02-03 04:54:42 +00:00
93cabe4f44 Updated translation strings 2015-02-03 04:28:37 +00:00
5222861962 Proper theme names 2015-02-03 04:15:55 +00:00
9c09c739d2 Fixed bundle-messages.sh 2015-02-03 04:15:46 +00:00
8b3770f33d New translations 2015-02-03 03:52:46 +00:00
cd5d8b0ac2 Updated translations 2015-02-03 03:52:32 +00:00
596aa39d5d Updated Transifex config 2015-02-03 03:52:25 +00:00
212ca673b0 Updated TODO 2015-02-03 03:41:01 +00:00
9833590266 Move HTML files to subdir in preparation for Transifex 2015-02-03 03:40:50 +00:00
77242920bd Updated French translation of user guide 2015-02-03 03:33:22 +00:00
19a8c09a8d User guide tweaks 2015-02-03 03:33:00 +00:00
2634398ea4 Renamed user guide HTML 2015-02-02 22:41:19 +00:00
2fac3e9eae Tidied up user guide HTML 2015-02-02 22:40:04 +00:00
182bafb524 Enable HTML files to be themed 2015-02-02 22:32:52 +00:00
d7c673e4be Charts on network status page 2015-02-02 03:17:28 +00:00
33273675da Mark new relay peers instead of showing 0% reachability 2015-02-01 04:43:45 +00:00
7b405764fb NPE fix (ticket #1367) 2015-01-30 06:03:01 +00:00
750305fef1 Attachments icon 2015-01-30 05:38:57 +00:00
269f0d07e9 Improved info and error message displaying 2015-01-29 05:03:26 +00:00
7aae76ef4b Fixes after XSSFilter 2015-01-29 04:39:03 +00:00
302414a53d Enable different page title from browser title, more sidenav icons 2015-01-29 04:38:21 +00:00
22732d33c1 Missing bcprov changes 2015-01-28 23:54:55 +00:00
b113dd3e51 Font changes 2015-01-28 08:04:53 +00:00
ca0ac05614 Import identities 2015-01-28 06:47:47 +00:00
54ac8425b5 Export identities 2015-01-28 04:33:03 +00:00
67e36d834e Escape XML 2015-01-28 04:25:51 +00:00
48c2294517 HTML5 <time> 2015-01-27 02:48:12 +00:00
e3e694d0c9 Enable XSSFilter on all inputs except passwords 2015-01-27 01:43:12 +00:00
06fc60af43 Material tweak for new emails 2015-01-27 01:41:33 +00:00
9cd8bddf8a Bugfix 2015-01-27 01:30:29 +00:00
6c4ab4b1b3 NPE fix when attempting to change identity name to blank 2015-01-27 01:14:33 +00:00
3155308054 New theme based on the Material design of the Bote Android app 2015-01-26 10:32:42 +00:00
538a5b2e06 Change addressbook.gif to addressbook.png 2015-01-26 10:02:05 +00:00
dd126df6c7 Inputs to buttons 2015-01-26 07:41:43 +00:00
a1edb3173c Remove onclick from header, the inbox is easily accessed from the side nav 2015-01-26 04:56:34 +00:00
edc51acedb An article around email content 2015-01-26 04:24:07 +00:00
0394b591a7 Extra page title for use in new theme 2015-01-26 03:45:03 +00:00
9a5a161342 CSS tweak 2015-01-25 10:19:05 +00:00
ec3acae530 Replaced linebreak with margin 2015-01-25 09:32:05 +00:00
7fea66978b Remove "Public Address Directory" from side nav until it has a link 2015-01-25 09:21:16 +00:00
b7abb03b32 HTML5, preparations for new theme 2015-01-25 09:18:40 +00:00
958a072d9b HTML5 doctype and charset 2015-01-24 12:31:42 +00:00
b9d778f8fa Spelling 2015-01-13 03:29:01 +00:00
2ccbe875a8 Updated CHANGELOG 2015-01-13 03:26:17 +00:00
50b36e49b4 0.5
i2p.i2p-bote: 19d3dc33a474b094b84043cc3c36e186cf3d2338
i2p.i2p tag: i2p-0.9.17
2015-01-13 03:24:19 +00:00
4c8a33ec46 Fixed occasional crash 2015-01-13 02:52:42 +00:00
25533c4236 Formatting 2015-01-13 02:48:35 +00:00
1b8340b292 Fix CAB inserting above Toolbar
This causes the CAB to overlay the nav drawer too. Waiting on a fix:
https://stackoverflow.com/q/27663853/3317191
2015-01-13 00:43:38 +00:00
19329dad6e Loading and empty views for address book 2015-01-13 00:40:40 +00:00
227e6e9700 Checksum and license updates 2015-01-12 22:50:30 +00:00
0e580b8ebb Moved widgets to i2p.bote.android.widget 2015-01-12 22:42:46 +00:00
1a7302bb73 Loading and empty views for emails list 2015-01-12 22:34:09 +00:00
004987db71 Handle attachments with incorrect MIME types 2015-01-12 10:08:22 +00:00
e7042dc5a7 Detect file MIME types 2015-01-12 07:55:51 +00:00
bc6df473be Ask for plugin SU3 keystore password 2015-01-12 07:34:17 +00:00
04ac90d478 Prevent NPE on Android if the state gets weird (it has happened) 2015-01-11 12:25:45 +00:00
2c6c43c3ac Updated translations 2015-01-11 09:51:50 +00:00
7e8421c63b Added ignores 2015-01-11 05:51:22 +00:00
d8d1caaf2a Updated translations 2015-01-11 02:42:32 +00:00
6b8e41ba37 NPE fix 2015-01-09 23:20:44 +00:00
ef51264d4a Missing file 2015-01-09 02:21:52 +00:00
af21384fc1 Updated translations 2015-01-09 02:21:14 +00:00
c8ed705f1f Warn about large attachment sizes 2015-01-08 23:23:05 +00:00
9bde6402f4 Ensure ContactsCompletionView text is visible on all devices 2015-01-08 22:52:41 +00:00
7873cb40d3 New translations for help pages 2015-01-08 22:10:15 +00:00
b6e1242cc3 Updated translations 2015-01-08 22:09:33 +00:00
b947f9ba88 Use ripple effect on API 21+ 2015-01-08 12:52:39 +00:00
ae8f87c7da Protect ECUtils class methods 2015-01-08 11:54:53 +00:00
dab92a2227 Help page for identities 2015-01-08 11:45:28 +00:00
72fed007f7 Reworked about page for translations 2015-01-08 10:44:14 +00:00
fb58e63f04 Updated changelog 2015-01-08 09:37:10 +00:00
460e8d9280 Updated CHANGELOG 2015-01-08 02:54:48 +00:00
7130eae4d9 Help and About pages 2015-01-07 23:15:32 +00:00
fc8932646f Re-add list dividers 2015-01-07 05:53:16 +00:00
57cf36bed5 Missing file 2015-01-07 05:30:44 +00:00
4e4c57c321 Cache whether we have sent an email or not 2015-01-07 05:09:06 +00:00
45e4cba708 Unused import 2015-01-07 04:31:41 +00:00
2619d88c85 Migrate AuthenticatedListFragments to RecyclerView 2015-01-07 04:22:43 +00:00
7acbb22887 Missing file 2015-01-07 02:56:36 +00:00
ed600b13ee Fixed bug 2015-01-06 19:48:18 +00:00
03926923e2 Color comment 2015-01-06 11:33:18 +00:00
1dabe02684 Fixed selection state bug 2015-01-06 10:48:48 +00:00
0d15417432 Nav drawer folder selection styling 2015-01-06 10:39:27 +00:00
0763d2ea0a Migrated folder list to RecyclerView 2015-01-05 12:15:15 +00:00
5502561454 Pull layout into XML 2015-01-05 06:25:50 +00:00
38fc0ade86 View holder pattern 2015-01-05 06:18:22 +00:00
e9d55fb8ad Cleanups 2015-01-05 05:42:05 +00:00
ff1984e349 Updated translations 2015-01-05 04:34:20 +00:00
16f56f598d Updated CHANGELOG 2015-01-05 04:33:09 +00:00
e4350f1da2 Bugfix: on legacy devices, Views that are GONE are still clickable 2015-01-05 04:29:43 +00:00
8bb9ceacf0 Put identities inside category on legacy devices
A PreferenceCategory in settings_headers_legacy.xml was not being rendered, so
it is added manually instead.
2015-01-05 04:14:59 +00:00
1071116b04 Dynamic Preferences for legacy devices 2015-01-05 04:04:23 +00:00
dc844ab12a Implement legacy identity headers in Setting 2015-01-05 01:36:17 +00:00
7d3cd077d1 Use SpongyCastle for crypto
Requires i2p.i2p-bote rev 474ac06823cf555f824fdf914fe40247e38eab55
2015-01-04 22:21:06 +00:00
ced8dee1dc No need to stub out UpdateChecker any more 2015-01-04 22:18:10 +00:00
3591d25f72 Migrate i2p.bote.crypto AES and EC to JCA; upgrade to BouncyCastle 1.51
This change was necessary to fix the crypto on Android, where SpongyCastly must
be used instead (because of classloader conflicts). The new ECUtils class
contains the remaining code that could not be made provider-agnostic. In
particular, the EmailDestination format for EC requires the use of BouncyCastle
point encoding.

The full BouncyCastle library is now bundled, because it must be added as a
Security Provider to use the JCA system, and this requires a valid signature.

JCA is used for AES operations instead of I2P's built-in AES engine because the
PKCS#7 padding class could not be accessed on its own in a provider-agnostic
manner. The resulting code is also cleaner.
2015-01-04 22:16:16 +00:00
1f1b328f70 Close InputStream in Util.getPartSize() 2015-01-04 05:00:08 +00:00
08748f0c69 Updated translations 2015-01-02 13:28:46 +00:00
15d8bb214f Updated TODO 2015-01-02 13:20:34 +00:00
b6cdf3aa33 FloatingActionBar 1.5.1 2015-01-02 13:20:22 +00:00
ee9e33c938 Separate multiple attachments 2015-01-02 04:41:07 +00:00
4bb1cc949e Removed mistaken string 2015-01-02 04:34:27 +00:00
bc9d067670 Updated translations 2015-01-02 04:31:44 +00:00
2fb6fdab4f Replace copy image with button, extract strings 2015-01-02 04:30:02 +00:00
4b0e78a383 New delivered icon, tweaked email listitem layout 2015-01-02 02:57:15 +00:00
93dc161e97 Updated CHANGELOG 2015-01-02 00:09:45 +00:00
a012a0ca41 Wording 2015-01-02 00:07:06 +00:00
b003143ad7 Layout tweak 2015-01-02 00:05:08 +00:00
fbcbdd8f95 Updated translations 2015-01-01 23:15:56 +00:00
d041bee10a Save attachments to Downloads folder 2015-01-01 23:13:00 +00:00
d40dce7dda Moved copyStream() to BoteHelper 2015-01-01 23:11:48 +00:00
01a50f72ca Missing file from previous commit 2015-01-01 11:49:07 +00:00
cfd62accee Stubbed out attachment saving 2015-01-01 11:48:45 +00:00
1d2f728bb5 Fixed AttachmentProvider.getType() 2015-01-01 11:02:16 +00:00
c1f2c8f6f6 AttachmentProvider.getType() tests 2015-01-01 10:57:17 +00:00
c3c2314ecd Fixed attachment creation in tests 2015-01-01 10:36:33 +00:00
36f104bab4 Start making tests pass 2014-12-31 03:31:13 +00:00
3c81bd0e8f AttachmentProviderTests 2014-12-31 02:11:36 +00:00
a303dcc36f FloatingActionBar 1.4.0 2014-12-30 12:07:49 +00:00
fdb2a903c8 Updated verification hashes 2014-12-30 11:54:45 +00:00
321aba11cc Testing dependencies etc. 2014-12-30 11:50:28 +00:00
3fc665e5ad Layout changes 2014-12-30 01:22:46 +00:00
e021bd3fa1 Implemented AttachmentProvider, removed ContentAttachment.getUri() 2014-12-29 10:50:04 +00:00
b05dcd8ebe Prep work for viewing attachments
Todo: implement a ContentProvider for attachments
2014-12-29 00:05:00 +00:00
5ffdf2018d Added missing files 2014-12-29 00:01:41 +00:00
5520a36e60 Padding fix 2014-12-28 23:59:13 +00:00
612419cd32 Add/remove attachments in new email, list attachments in view email 2014-12-28 20:16:06 +00:00
07ad5bdd08 Updated attachment icon on email list items 2014-12-28 11:48:12 +00:00
823491ae4d Pull out Util method for getting size of a Part 2014-12-28 11:42:23 +00:00
6856773636 Correct the Attachment interface 2014-12-28 11:42:02 +00:00
2cc3df7c1f Generalize attachments 2014-12-28 04:52:27 +00:00
cbfd318cf3 Move log tag to Constants 2014-12-27 23:14:34 +00:00
d64613c0fc Updated translations 2014-12-27 13:47:58 +00:00
b97fe8f5b5 Updated CHANGELOG 2014-12-27 13:47:10 +00:00
e310a3b54e Add support for Cc: and Bcc: 2014-12-27 13:46:19 +00:00
0deaf2aecd Updated CHANGELOG 2014-12-25 10:57:37 +00:00
ab767a03d2 Selectable email content (API 11+) 2014-12-25 10:56:38 +00:00
5b0d631bd3 NPE fix 2014-12-23 21:57:58 +00:00
b4e154e0f4 Fixed string 2014-12-23 11:08:56 +00:00
d0377970d8 Updated translations 2014-12-23 11:08:50 +00:00
852bad8d61 Moved JSPs to correct location
For i2pbote.war the location doesn't matter, because the JSPs are compiled. For
the Eclipse testnet, the JSPs are not compiled and cannot be located because
access to files in WEB-INF is restricted.
2014-12-23 10:47:11 +00:00
a1a89dd2bc Updated TODO 2014-12-22 00:23:06 +00:00
51ab2e7aaa Updated CHANGELOG 2014-12-19 22:00:18 +00:00
124fdeb623 0.4
i2p.i2p-bote: 455772b2a5e9f7ed13bdd6bb883d2a471092627f
i2p.i2p tag: i2p-0.9.17
2014-12-19 21:59:40 +00:00
cdf91568ad Moved release signing into separate buildscript 2014-12-19 20:48:10 +00:00
780171dd86 Upgraded build tools to 21.1.1 2014-12-19 20:40:57 +00:00
36b7ca0714 Upgraded support libraries to 21.0.3 2014-12-19 20:38:58 +00:00
2c8d7209b3 Permission to vibrate 2014-12-19 20:10:52 +00:00
45172e410e Updated translations 2014-12-19 20:08:35 +00:00
d91401ef0b Updated translations 2014-12-19 04:27:06 +00:00
00c6af0c2b Updated TODO 2014-12-19 04:06:32 +00:00
d8785d5c28 New email notification fixes 2014-12-19 04:06:14 +00:00
4f357db8ec Use I2PSocketManagerFactory.createDisconnectedManager() for better connectError 2014-12-17 23:54:31 +00:00
07c9fdfebb Fix settings menu Intents in debug build 2014-12-17 02:39:50 +00:00
92a22c447a Consistent titles 2014-12-17 02:33:56 +00:00
88106ab5f2 Visually break up emails in ViewPager 2014-12-17 02:08:42 +00:00
014932d06f Notify users that Bote needs to be connected to check emails 2014-12-17 02:05:25 +00:00
4d7f5bfe41 Proper error page 2014-12-17 01:53:05 +00:00
4124e2e336 Fixed new email notifications 2014-12-16 11:44:11 +00:00
e8a927e30a Don't modify UI from email-sending thread 2014-12-16 08:13:51 +00:00
bcfd6193c3 New translations for ro 2014-12-16 06:11:11 +00:00
05064a34bf Updated translations 2014-12-16 06:09:23 +00:00
2216f64864 More robust email checking 2014-12-16 06:07:55 +00:00
877b02cc04 Layout fix 2014-12-13 10:56:30 +00:00
4ca77a366d Updated TODO 2014-12-13 04:53:02 +00:00
80f08c0435 Updated CHANGELOG 2014-12-13 04:31:53 +00:00
f45eae9c8f Updated translations 2014-12-13 04:29:29 +00:00
e52de5d9ef Copy EmailDestinations to clipboard 2014-12-13 04:27:32 +00:00
f151f55a2a Only call connectNow() if status==DELAY; always call when I2P status==ACTIVE 2014-12-12 23:08:58 +00:00
848ecbef9a Updated CHANGELOG 2014-12-10 12:54:41 +00:00
02e00d7fb3 Prevent NPE 2014-12-10 12:16:48 +00:00
c38feed198 Updated translations 2014-12-10 11:17:26 +00:00
dc588fe2b7 Updated CHANGELOG 2014-12-10 11:13:03 +00:00
b3350ce395 Missing file from previous commit 2014-12-10 11:08:41 +00:00
65f08049cf Labels in address book FAM (using FAB library 1.3.0) 2014-12-10 11:08:06 +00:00
9411805eec Bumped gradle plugin to 1.0.0 2014-12-09 12:21:18 +00:00
6a38ff1d68 Enable debug versions to be installed alongside release versions 2014-12-04 23:48:12 +00:00
2230f9b0ed Fixed NPE 2014-12-04 22:57:58 +00:00
56811ad9b1 Updated CHANGELOG 2014-12-04 22:56:17 +00:00
3b42807509 0.3
i2p.i2p-bote: bd9c18f038fda8cb52bf9a1f27c65db19b826106
i2p.i2p tag: i2p-0.9.17
2014-12-02 00:39:28 +00:00
d917fb554b Upgrade to client library 0.4, use new helper bind method with new Intents 2014-12-01 10:04:41 +00:00
a3b595b4fa Updated translations 2014-12-01 04:09:20 +00:00
10f0593f0c Updated translations 2014-11-30 11:46:32 +00:00
9dee45cba3 Users don't need to see the local Destination 2014-11-30 11:43:38 +00:00
aee45ce2c5 Upgraded support libraries to 21.0.2 2014-11-30 11:37:33 +00:00
39f04b7f52 Tag strings for translation 2014-11-30 11:36:59 +00:00
67563a2adb Updated TODO 2014-11-26 22:37:01 +00:00
7463470609 Pie charts on network info page 2014-11-26 22:30:53 +00:00
be67702e61 Extract common code for viewing identities and contacts
Side-effect: view contact page now shows QR code
2014-11-25 04:45:13 +00:00
d9b1aa30b3 Use same layout for viewing identities and contacts 2014-11-25 04:12:06 +00:00
07e2067ee6 Updated Gradle Witness
Source: https://github.com/WhisperSystems/gradle-witness
Git commit: 10f1269c0aafdc1d478efc005ed48f3a47d44278
2014-11-25 03:51:13 +00:00
1df677daa5 Zoom QR code on click 2014-11-25 03:46:37 +00:00
6c10e0934c QR code scanning 2014-11-25 03:09:05 +00:00
287358277c Icon for scanning QR codes 2014-11-25 02:21:24 +00:00
c8f9671b62 Move add contact action to FAM 2014-11-25 01:31:52 +00:00
872fa2e99c Don't allow EmailDestination of an existing contact to change 2014-11-25 01:11:35 +00:00
0ba87f00cc NFC: check received message, receive on API 9 2014-11-25 00:49:33 +00:00
743055a121 Updated translations 2014-11-19 20:06:31 +00:00
35030b1d86 Updated CHANGELOG 2014-11-19 07:17:02 +00:00
00aad9fd57 View identity: Generate and show QR code 2014-11-18 11:14:33 +00:00
93f4caeb4c Divider styles 2014-11-18 09:56:05 +00:00
5c1a96237f 0.3-rc5
i2p.i2p-bote: bd9c18f038fda8cb52bf9a1f27c65db19b826106
i2p.i2p:      cb66382d9716f7d9cd9441830face910705253e0
2014-11-18 09:50:42 +00:00
a6df621472 Specify version in build.gradle 2014-11-18 07:00:07 +00:00
f530515e2e New email: If the user has typed anything, confirm when they navigate away 2014-11-18 06:55:46 +00:00
03870e17d8 Missing file 2014-11-18 06:46:40 +00:00
d3df8b4929 Better name handling for email replies 2014-11-18 04:00:21 +00:00
8b5b7507c6 Helpers to get the name for an EmailDestination 2014-11-18 04:00:00 +00:00
11acd45092 Don't include Anonymous in Reply All 2014-11-18 03:28:19 +00:00
05c65c6a40 Increase identicon density 2014-11-18 03:10:31 +00:00
98f7cad19c Better handling of identicon backgrounds 2014-11-18 03:07:01 +00:00
4fa16cfdef View contact: layout changes, removed ShareActionProvider (for now) 2014-11-18 02:11:25 +00:00
be88c7b074 Layout cleanup 2014-11-18 02:11:10 +00:00
60daaa3323 View identity: layout changes, removed ShareActionProvider (for now) 2014-11-18 01:58:22 +00:00
5ff770bc31 Use assertArrayEquals 2014-11-17 22:39:04 +00:00
4107467c64 Mockery thread synchronization 2014-11-17 22:23:20 +00:00
05def741fb Fix BCC header removal bug 2014-11-17 21:41:57 +00:00
82ac0d59d8 Unused imports 2014-11-17 21:25:00 +00:00
10445e1082 junit.framework -> org.junit 2014-11-17 21:15:00 +00:00
5ffdc62e7f Action bar title fixes 2014-11-17 04:09:52 +00:00
ef96c63e51 View email text styles and sizes 2014-11-17 04:01:46 +00:00
7a06c59584 Divider between email header and content 2014-11-17 03:40:09 +00:00
d2d1e95684 Max navigation drawer width 2014-11-17 01:03:55 +00:00
620daab952 View email layout tweaks 2014-11-17 01:03:36 +00:00
0c20f936d5 Show identicon for anonymous sent emails 2014-11-16 21:48:15 +00:00
d82bb57504 More uniform identicons 2014-11-16 21:39:31 +00:00
48db57f1c0 Make identicon visible in contact token 2014-11-16 20:16:31 +00:00
adb14287a8 Email list item layout fix 2014-11-16 20:16:10 +00:00
8bb939c3a5 Material design: text appearance 2014-11-15 01:47:53 +00:00
80a5e0dfd6 Toolbar for settings 2014-11-11 04:34:43 +00:00
5208cb4d88 SHA256 hash for net.i2p.android.ext:floatingactionbutton:1.1.0 2014-11-08 10:39:16 +00:00
ce4eb711b4 Use client library 0.3 2014-11-02 01:49:01 +00:00
a9ac5b6a0a Use mavenLocal() repository instead of a local file repo 2014-11-02 01:42:21 +00:00
a1401e8f37 Upgraded build tools to 21.0.2 (fixes a compile bug) 2014-10-30 23:14:51 +00:00
ff0d4f1d67 Upgraded build tools to 21.0.0 (needed to fix a resource merging bug) 2014-10-30 07:40:06 +00:00
395187ba3f New Gradle Witness build
Includes a fix for Gradle 2.+ that is not yet pulled in:
https://github.com/WhisperSystems/gradle-witness/pull/3
2014-10-30 07:23:05 +00:00
4a432bf51c Use our own, backwards-compatible version of the FloatingActionBar library 2014-10-30 07:22:03 +00:00
90f0ecab0e Upgraded gradle wrapper and build tools 2014-10-30 07:21:13 +00:00
e93e200327 Removed old translations 2014-10-30 07:20:22 +00:00
329e8e9201 Material design: contact chips 2014-10-27 06:35:42 +00:00
10d46edd06 Fixed deprecation 2014-10-26 20:45:36 +00:00
622c9b8d6f FloatingActionBar 1.1.0 2014-10-26 01:16:42 +00:00
60e6b1cd8d Materail design: Floating action button for new email action
This commit breaks automatic builds that pull dependencies from Maven Central,
because com.getbase:floatingactionbutton:1.0.0 has a minSdkVersion of 14. Local
testing indicates the library operates fine on API 10.

Builders will need to pull the source, change the minSdkVersion to 9, and then
run `gradle installArchives` to build and install the library locally. Then add
mavenLocal() to allprojects{repositories{}} in build.gradle.

An issue has been raised requesting official support for API 9+:
https://github.com/futuresimple/android-floating-action-button/issues/11
2014-10-23 03:54:01 +00:00
6a266bc2d1 Material design: Left side nav should open over toolbar 2014-10-22 21:32:29 +00:00
d9e3c179f3 Updated TODO 2014-10-22 11:31:03 +00:00
e1550147cd Identicons for EmailDestinations with no associated picture
Identicon generation code adapted from Android-Identicons
Source: https://github.com/saiimons/Android-Identicons
License: Apache License, Version 2.0
2014-10-22 11:28:05 +00:00
9ab2118110 Material theme: side nav width, toolbar height 2014-10-21 20:00:13 +00:00
11fbae85c4 Change email checker indicator colors 2014-10-21 11:18:11 +00:00
7b4d7de2a5 New network status icons 2014-10-20 10:54:28 +00:00
1f074bce74 Changed outbox icon 2014-10-20 10:00:49 +00:00
956469e7ce Material theme: nav drawer icons 2014-10-20 04:53:41 +00:00
5e762d8063 0.3-rc4 2014-10-20 03:29:20 +00:00
0add04fab0 Material theme: text appearance 2014-10-20 00:35:38 +00:00
2230ab2574 Changed accent color 2014-10-19 23:50:15 +00:00
77ebb06e7d Close drawer when opening address book or network status 2014-10-19 23:44:46 +00:00
c70ad18979 Line accidentally removed 2014-10-19 23:35:03 +00:00
0380b5c287 Material theme: main toolbar 2014-10-19 23:22:39 +00:00
486b06eec0 Material theme: contact list 2014-10-19 11:01:59 +00:00
a2d5d0e8c9 Material theme: email list 2014-10-19 10:52:00 +00:00
5818ab1551 Missing line 2014-10-19 10:11:30 +00:00
44bb19b6a8 Material theme: folder list 2014-10-19 10:06:20 +00:00
2f55af22f9 Material theme: new styles 2014-10-19 09:44:31 +00:00
d7ac0f7a42 Material theme: new icons 2014-10-19 09:41:08 +00:00
e5f9630d99 Upgraded to API 21 2014-10-19 09:39:28 +00:00
2ebf8b7155 Picture styling for identity and contact view pages 2014-10-18 05:06:28 +00:00
fb2e275f06 Updated translations 2014-10-16 04:09:37 +00:00
7b9716834a Use released client library 0.2 2014-09-30 01:51:46 +00:00
8cb3435d22 Updated translations 2014-09-29 04:07:35 +00:00
72ca8e737f Use new helper method in client library 2014-09-26 12:40:17 +00:00
8556aceae5 Plurals fix 2014-09-26 03:23:05 +00:00
1408601ea2 Use client library 0.2 2014-09-26 01:54:36 +00:00
d307e60dad Use the new i2p.i2p-bote directory structure 2014-09-26 01:54:12 +00:00
a968f5c6f4 Updated history 2014-09-25 05:54:49 +00:00
d6564680a5 Update to techdoc by HungryHobo 2014-09-25 05:54:31 +00:00
ec41d7d82e Switch to Maven directory structure without losing history 2014-09-25 05:50:39 +00:00
e48e76483b Different sent date formats for different ages of email 2014-09-25 04:17:59 +00:00
40efa738b2 Updated translations 2014-09-25 02:04:51 +00:00
d8741fa691 Only show incomplete email count in inbox 2014-09-25 01:57:57 +00:00
8a2da003e1 Don't create a new fragment when selecting the current folder in nav drawer 2014-09-25 01:56:17 +00:00
b9c4783a94 Placeholder colors for SwipeRefreshLayout 2014-09-25 01:32:10 +00:00
1ab29db2bc Nav drawer design guidelines 2014-09-25 01:31:45 +00:00
cb8502bd25 Plural string fixes 2014-09-25 01:07:42 +00:00
de64f09c50 Plural strings 2014-09-25 00:45:10 +00:00
db85557308 Updated translations 2014-09-24 14:23:25 +00:00
fa7b63e9d9 Settings improvements 2014-09-24 14:16:49 +00:00
7a92548fcf Updated TODO 2014-09-24 12:58:36 +00:00
27fcd7c31c Initial CHANGELOG 2014-09-24 12:57:38 +00:00
2a18db4859 Check email action; only allow checking email from inbox and when authenticated 2014-09-24 12:56:59 +00:00
f20b3f6743 Make empty inbox swipeable 2014-09-24 12:41:38 +00:00
402ab84a31 Switch to android.support.v4.widget.SwipeRefreshLayout 2014-09-23 11:52:07 +00:00
8a29b9dd0d Mark locations of code incompatible with newer I2P Destinations 2014-09-21 05:32:43 +00:00
6c53344f51 A zero-length password is an uncached password
This brings PasswordCache in line with FileEncryptionUtil.
2014-09-21 05:31:08 +00:00
c22ae24786 Updated translations 2014-09-19 14:02:12 +00:00
2219dbc8d7 Remove unused string 2014-09-19 14:00:25 +00:00
0c3d2bab8d Password check in edit contact page (because it could be opened by an Intent) 2014-09-19 13:58:55 +00:00
e061f17864 Use AuthenticatedListFragment for AddressBookFragment 2014-09-19 13:49:46 +00:00
768162aeb6 Methods that should be private 2014-09-19 13:40:07 +00:00
7d51dc59a3 Split out authentication into abstract class 2014-09-19 13:38:49 +00:00
b808607050 Show keyboard with password dialog 2014-09-18 22:39:01 +00:00
59a6d586d1 "Log out" is a better user hint than "Clear password" 2014-09-18 22:27:42 +00:00
e303214a50 Clarify how to log in 2014-09-18 22:23:38 +00:00
b8d33ddd43 New translations for pl 2014-09-18 21:06:44 +00:00
a917655852 Updated translations 2014-09-18 21:06:26 +00:00
4dd9156eac Missing lock and unlock icons 2014-09-18 06:22:25 +00:00
b31f503fa4 Update folder list on set/clear password (for unread email count) 2014-09-18 06:20:39 +00:00
2768297be9 Add and remove interface for password listeners 2014-09-18 06:11:01 +00:00
f9640c6fb2 Better action management 2014-09-18 05:56:07 +00:00
ba2b9bad8b Remove pop-up login request from email list 2014-09-18 04:36:13 +00:00
e4f2b9b960 "Log in" and "Clear password" actions on email list 2014-09-18 04:27:13 +00:00
0420aa2943 Improved errors 2014-08-30 09:37:30 +00:00
d86ab3466f Fixed NPE 2014-08-30 07:37:14 +00:00
be4e346ff0 Updated translation strings 2014-08-28 14:50:31 +00:00
5fc1d706f9 Updated translations 2014-08-28 13:04:16 +00:00
eba5b4f415 Bugfix: initialize list after authentication 2014-08-28 12:59:44 +00:00
e6348ace18 Forward and Reply all 2014-08-28 12:46:05 +00:00
bf439f213f Hide fingerprints from UI until they are reliable 2014-08-28 10:59:24 +00:00
18de25d75d Fingerprint field for contacts, not filled yet
The regular Fingerprint doesn't work here because it requires the salt, which is
not part of the public Destination. The salt is published with the Destination
in the public address book, and could be conveyed between Bote apps via NFC or
QR code, but that doesn't work for Destinations published in other spaces.
2014-08-28 06:37:58 +00:00
8013783b30 Added view contact activity 2014-08-28 05:42:10 +00:00
a9734ae393 Missing disableForegroundNdefPush() 2014-08-28 03:57:02 +00:00
0e5ae34f82 Show identity fingerprint in current locale if possible 2014-08-28 03:52:25 +00:00
ce74614ba5 Don't allow password dialogs to be canceled by the back button 2014-08-28 03:16:07 +00:00
ecd2667281 Authenticate for identity creation 2014-08-28 03:15:50 +00:00
b6e74b7458 Comment fixes 2014-08-28 03:03:15 +00:00
502e9ddfba Italics for Anonymous senders 2014-08-28 01:53:29 +00:00
d6bc72d9d5 Layout tools fix 2014-08-27 22:17:57 +00:00
3240d04e44 Show recipient for emails listed in Outbox and Sent 2014-08-27 05:27:03 +00:00
29025191f6 Updated TODO 2014-08-27 05:26:39 +00:00
6c8fbeb3be Updated TODO 2014-08-27 04:26:48 +00:00
740197e439 Added TODO 2014-08-26 11:55:12 +00:00
d1db7c14fc View identity:
- Show fingerprint (en)
- Generate QR code from identity key
2014-08-26 05:57:56 +00:00
a352c138e8 0.3-rc3 2014-08-23 01:53:37 +00:00
7d2cc86920 Docstring fix 2014-08-23 01:43:52 +00:00
99d2dc2c62 Disable hidden mode for internal router (participation still disabled) 2014-08-23 01:41:42 +00:00
b22b8eea50 0.3-rc2 2014-08-23 01:25:46 +00:00
24ab394cb4 New translations for zh 2014-08-22 11:24:04 +00:00
c9269ca4bf Updated translations 2014-08-22 11:23:36 +00:00
11909ce6df Use client library 0.1.1 2014-08-21 12:13:10 +00:00
53a540d0b7 Compiled libscrypt.so for x86 and mips using NDK r10 2014-08-20 23:13:26 +00:00
f9f4c17cd2 Recompiled libscrypt.so with NDK r10 to fix text relocations 2014-08-20 22:49:49 +00:00
d53f5c0ac7 0.3-rc1 2014-08-20 11:27:37 +00:00
48134404a2 Added comments to translation strings 2014-08-20 10:59:47 +00:00
d0247e7ca2 Feature graphic for Google Play 2014-08-20 06:40:54 +00:00
1b5691734e Fixed bug caused by API changes 2014-08-20 05:28:53 +00:00
398e4f6bad New translations: id, nb, sq 2014-08-20 05:27:43 +00:00
4148bad064 Missing change 2014-08-20 05:26:26 +00:00
6e632a4748 Don't let a user accidentally exit the setup wizard if they chose to use it 2014-08-20 05:26:10 +00:00
f316933fde Don't show Share menu for new contacts 2014-08-20 05:07:29 +00:00
208c69fb15 Hide old password field in SetPasswordFragment if none is set 2014-08-18 11:56:11 +00:00
ee926ff23b More TODO items 2014-08-18 11:55:26 +00:00
6b990f6feb Request password in onActivityCreated(), not onResume()
This prevents the dialog being re-displayed when returning to EmailListFragment
from another Activity.
2014-08-18 08:58:49 +00:00
46d42210ad Password handling:
- Init lists once in onResume (handles passwords entered higher in stack)
- Destroy any existing data in UI if cached password is cleared
2014-08-17 12:40:46 +00:00
dcec72a5db Password restrictions 2014-08-17 12:02:30 +00:00
bafe5293ac Modularize password entry 2014-08-17 11:36:17 +00:00
b8cb30aff2 Don't leak incomplete email count outside password 2014-08-17 10:47:20 +00:00
cf4dfad946 Make it easier to use a local copy/build of the client library 2014-08-15 01:49:45 +00:00
6e4c52b746 Remove spaces and newlines 2014-08-07 11:56:43 +00:00
cc59ed9adb Remove possible prefixes 2014-08-07 11:46:58 +00:00
ca8399104d Updated translations 2014-08-07 07:00:23 +00:00
3c0a431f09 Remove UpdateChecker
Plugins have been auto-updated by the router for a while now
2014-08-03 10:47:11 +00:00
392ba71037 Another Tomcat 7 fix, see rev. 5e9c9148 2014-08-03 10:42:32 +00:00
e39abcc145 Use SecureFile instead of Util.makePrivate()
* Replace Util.makePrivate() with SecureFile and SecureFileOutputStream
* Make derivparams a SecureFile
2014-08-02 16:46:17 +00:00
a44af32ebf New translations for ru 2014-07-26 02:46:20 +00:00
16c23e9506 Updated translations 2014-07-26 02:45:35 +00:00
e030e9ed5f 0.2.10 2014-07-23 21:36:12 +00:00
1896fccf1f Fix NullPointerExceptions 2014-07-23 21:25:31 +00:00
ca19e9d9e0 Use a separate SessionKeyManager instead of the router's 2014-07-23 21:24:39 +00:00
729a4ac99f Upgrade to 20140722 snapshot of Apache James 2014-07-23 21:22:15 +00:00
ea4f5f50eb I2P Android now using Enum for State 2014-07-17 01:03:07 +00:00
8128d0a075 jstl.jar is a more reliable test, from source only jarBote will prepare it 2014-07-16 05:16:25 +00:00
a5d9e6a3ed Switch to I2CP over domain sockets for I2P Android 2014-07-16 04:13:19 +00:00
729f59e729 Support I2CP connections over Unix domain sockets
Only supported on Android for now.
2014-07-16 04:05:35 +00:00
4172d483cc Use the I2P Android client library
The build script looks for the client library in Maven Central. It is possible              
to use a local .aar by placing it in a folder "aars" in the base folder, and
uncommenting the flatDir{} section in the base build.gradle.
2014-07-16 01:45:14 +00:00
9283840793 Better control of what artifacts are built by what task 2014-07-15 06:31:05 +00:00
d896833aa8 Updated TODO 2014-07-15 06:28:18 +00:00
4430b1c8e0 Cleaner build script 2014-07-11 05:20:35 +00:00
1d351132b2 Updated translations 2014-07-10 22:43:26 +00:00
173bc97301 New translations for fr 2014-07-10 14:45:13 +00:00
b2d3fd969e String improvement (thx Towatowa441) 2014-07-10 14:43:14 +00:00
f6bebfa51d Don't abort on lint errors (this is the 0.2 release commit) 2014-07-10 02:32:20 +00:00
b6010798ad Fixed overflowing text on small screens 2014-07-10 02:29:58 +00:00
854b2c67e6 0.2 2014-07-10 02:17:02 +00:00
a661f48dd0 Made intro_4.svg more Bote-like 2014-07-10 02:00:55 +00:00
73e4413508 Missing checkin 2014-07-10 01:33:37 +00:00
28186b0d43 Updated translations 2014-07-10 01:23:16 +00:00
31628c133b Reverted mistaken commit 2014-07-10 00:58:39 +00:00
494f36ed8d Updated translations for changed strings 2014-07-10 00:49:37 +00:00
29f4c1bd5e Improved intro text 2014-07-10 00:45:35 +00:00
244a91fb1a Fixes 2014-07-10 00:45:00 +00:00
ef98b16e15 Adding in another intro page 2014-07-09 09:13:48 +00:00
fc991d6a98 Adding in another intro page 2014-07-09 09:08:49 +00:00
b235f36cd0 Swap order of intro pages 2014-07-09 08:21:16 +00:00
c38569b4ac Swap order of intro pages 2014-07-09 08:19:16 +00:00
7f2e332248 Updated TODO 2014-07-09 04:56:15 +00:00
eb774b0194 Exclude x86_64 native binaries from scrypt.jar 2014-07-09 04:55:39 +00:00
6327d1f524 Fix email selection by clicking picture 2014-07-09 04:45:19 +00:00
0470939c14 ScrollView around intro pages that might overflow text 2014-07-09 01:25:36 +00:00
21dfd34ff6 Adapted images from the FSF Email Self-Defense guide for use in intro 2014-07-09 01:22:30 +00:00
bd56715ed4 Updated translations 2014-07-08 22:04:42 +00:00
5a84ceccc3 Error message has no info (yet), revert to e.toString() 2014-07-08 13:02:32 +00:00
68ad7757c5 Extracted more strings 2014-07-08 12:39:10 +00:00
22e27fdfe5 Updated translations 2014-07-08 12:32:19 +00:00
d66e277528 Updated ignores 2014-07-08 12:25:33 +00:00
8158bcec27 Clearer launcher icon, new notification icon 2014-07-08 12:23:36 +00:00
63b347292d Fixed locale hiding in replies 2014-07-08 00:22:04 +00:00
17fbe74fb1 New translations for es 2014-07-08 00:07:40 +00:00
eeeb214041 Extracted remaining strings 2014-07-08 00:05:40 +00:00
bc9f4d759a Make Inbox incomplete emails header unselectable 2014-07-08 00:05:00 +00:00
b508c3822b New translations for de 2014-07-07 23:29:15 +00:00
5b480d83c8 Updated license info 2014-07-07 23:14:54 +00:00
08c9005070 Use MultiSelectionUtil from Android Samples for CHOICE_MODE_MULTIPLE_MODAL
Fixed:
- Background highlighting

Broken:
- Selecting emails by clicking pictures
- Tick appearing over picture of selected emails
2014-07-07 13:35:11 +00:00
c05228c206 Ellipse character 2014-07-07 07:42:47 +00:00
33d35010f2 Don't connectNow() on RUNNING, too soon to know that I2CP is ready 2014-07-07 01:38:19 +00:00
4d795e264e Revert to only showing intro/setup on first run 2014-07-07 00:28:42 +00:00
78b66b5509 Bugfix after net.i2p.data.Base64.decode() errors were cleaned up 2014-07-07 00:28:22 +00:00
399aa163ea Unified layout margins 2014-07-06 22:57:07 +00:00
9a0f5a5ee1 Stub out UpdateChecker 2014-07-06 22:18:32 +00:00
cdf01bc9e7 Bugfixes, run I2PBote.shutDown() in separate thread 2014-07-06 14:39:15 +00:00
c8212b7df9 Use NetworkStatusListener for nav drawer status info 2014-07-06 14:33:01 +00:00
5cdcae1416 Missing network status change 2014-07-06 14:26:47 +00:00
ce4136a224 Update service Notification text via NetworkStatusListener 2014-07-06 13:06:14 +00:00
348262e7a2 NetworkStatusListener implementation 2014-07-06 13:05:39 +00:00
64be0e5240 Persistent notification while Bote is running 2014-07-06 10:37:25 +00:00
47a2c31196 Support different I2P Android package names 2014-07-06 09:27:10 +00:00
19e58d223c Improved support for landscape and small screens in intro and setup 2014-07-06 06:17:59 +00:00
21d87a953b Support different I2P Android package names 2014-07-06 03:14:49 +00:00
869042fb93 Updated TODO 2014-07-06 02:58:03 +00:00
9b45832a5d Pull out hard-coded strings, inform user if importer found no identities 2014-07-06 02:56:42 +00:00
adb290658a Inform caller if importer found no identities 2014-07-06 02:55:44 +00:00
c04fc24445 Implement encrypted identities export 2014-07-06 02:28:40 +00:00
7eaf913a36 Updated TODO 2014-07-06 02:07:46 +00:00
89fb236ba1 Easy Share the EmailDestination of identities and contacts 2014-07-06 02:05:56 +00:00
0b6c96f48b Implement append strategy for duplicates 2014-07-05 23:50:35 +00:00
4762f8486e Implement identity appending on import 2014-07-05 23:50:02 +00:00
52a717d7e3 Import identities from FileDescriptor (no appending possible yet) 2014-07-05 04:23:19 +00:00
5dc2572989 Environment.DIRECTORY_DOCUMENTS is only available in API 19+ 2014-07-05 04:14:07 +00:00
9b1f29be64 Import identities UI 2014-07-05 04:12:56 +00:00
9aecce7cc4 Fixed reading contact Destination from text file 2014-07-05 01:42:48 +00:00
a889246066 Export identities UI 2014-07-04 23:53:21 +00:00
24e66a08fd Prepare for export dialogs 2014-07-04 10:55:36 +00:00
044d190c09 Export identities to Documents folder 2014-07-04 06:21:56 +00:00
e9ffb4643b Export identities to file (no encryption possible yet) 2014-07-04 06:21:17 +00:00
382f75f4a4 Pulled out Identities <-> Properties conversion into separate methods 2014-07-04 00:23:14 +00:00
dcba9109c8 Updated checksum for com.mcxiaoke.viewpagerindicator:library:2.4.1 2014-07-03 13:18:25 +00:00
9c552e0a8a Fixed README 2014-07-03 12:44:15 +00:00
af6ceddbd5 Send identity destinations to other devices via NFC 2014-07-03 12:32:03 +00:00
35d103ee58 Send address book contacts between devices via NFC 2014-07-03 12:23:35 +00:00
b61a15b4dd Updated TODO 2014-07-03 12:22:45 +00:00
4a6fc262e7 Show number of incomplete emails 2014-07-02 09:23:58 +00:00
697b29d189 Show number of incomplete emails at top of Inbox 2014-07-02 06:55:59 +00:00
4cd24cd579 Expose number of incomplete emails 2014-07-02 06:38:02 +00:00
7607e2d1f3 Ensure that I2P system vars are set in any Activity that uses I2PBote
Sometimes the Android runtime kills off the Bote process to save memory, and
recreates it when the user next opens it. If the user was on an Activity that
was not EmailListActivity, then when recreated the I2P system vars would not be
set, and the first call to I2PBote.getInstance() would create an instance with
invalid paths. This was non-fatal - killing Bote and restarting it would fix
the problem - but was bad UX, because from the user's PoV all their emails and
data had disappeared.
2014-07-02 03:01:28 +00:00
4d736205b3 Separate message to explain why Bote waits for 3 minutes 2014-07-02 02:36:43 +00:00
8f8fd90851 Move app initialization out of EmailListActivity so it can be used elsewhere 2014-07-02 01:39:50 +00:00
6b11bae3a0 Show contact pictures in recipients dropdown 2014-07-02 00:38:47 +00:00
d3ddca0d4d Static verification of remote dependencies using Gradle Witness
https://github.com/WhisperSystems/gradle-witness
2014-07-01 05:13:50 +00:00
7397c04538 Updated android gradle plugin version 2014-07-01 05:12:55 +00:00
1ce9c6072e Updated README 2014-06-29 02:58:46 +00:00
d647f310e3 Enable building Bote against I2P source
Requires I2P source revision 116c7da67b5bb23b91f9fb55980ceb9865ae7d0a or later
(ie. cannot be built against 0.9.13 source, must use an install for that).
2014-06-28 05:59:52 +00:00
27c086a2dc Signing instructions 2014-06-28 04:21:35 +00:00
ce8e26c109 Improved handling of signing keys 2014-06-28 03:58:22 +00:00
2736ba7627 Temporary fix for broken developer preview of Support Library 2014-06-28 03:37:23 +00:00
076f595abf Fixed jar dependency problems, updated ignores 2014-06-28 02:39:53 +00:00
df7a35934a README fixes 2014-06-28 02:16:11 +00:00
4bb52983be Describe how to pull dependencies via Tor 2014-06-24 05:52:00 +00:00
15f3b54000 License info 2014-06-23 02:11:52 +00:00
32ce92d7cb Updated license.txt 2014-06-23 01:47:07 +00:00
a6bb88567f Parent activity meta-data for 4.0 and below 2014-06-20 08:56:30 +00:00
ec12db7cec 0.1.1 2014-06-20 08:44:43 +00:00
731bde55f3 Prevent NPE with FragmentManager not being initialized 2014-06-20 08:20:10 +00:00
b7295e983a Link to EditIdentityActivity on legacy devices 2014-06-20 02:10:56 +00:00
3fe669a738 Incremented version after dev release 2014-06-19 04:07:33 +00:00
f457ec3de5 Release signing 2014-06-19 04:07:08 +00:00
b5540e1711 Fixed some warnings 2014-06-19 03:32:32 +00:00
b684aaa535 Setup finished page, moved setup buttons to bottom of screen 2014-06-19 03:08:36 +00:00
ac61de07ff Fixed string 2014-06-19 02:48:31 +00:00
73acb4b689 Setup wizard for new users 2014-06-19 02:46:35 +00:00
b30e31fc81 Introduction to Bote for new users 2014-06-19 01:00:22 +00:00
79c3a622ea Committed missing override files for newer APIs 2014-06-19 00:35:02 +00:00
34107e6a62 Fixed ignores 2014-06-19 00:32:55 +00:00
d5864f6258 Added README with build instructions 2014-06-19 00:15:38 +00:00
4f8979d279 Automatic format changed from Android Studio 2014-06-18 20:10:24 +00:00
ad397605a6 Fixed B64 encoding of Bitmaps 2014-06-16 23:15:10 +00:00
5b347e878a Adjust email content margins 2014-06-16 06:30:28 +00:00
522be91528 Save pictures for identities 2014-06-16 06:30:10 +00:00
96bb3a0719 Enable saving picture for EmailIdentity 2014-06-16 05:41:11 +00:00
5cebc0b2ee Added EmailIdentity.setPictureBase64() 2014-06-16 04:57:32 +00:00
1c0cbff55a Fixed getPictureForAddress() 2014-06-16 02:27:39 +00:00
aa954b03ec Fixed wrong ImageView 2014-06-16 02:27:14 +00:00
6dcd309418 Picture layout fixes 2014-06-16 02:10:30 +00:00
947056d344 Updated to Android Studio 0.6.1 2014-06-16 00:51:21 +00:00
fafabe37bc Added default router.config based on I2P Android 2014-06-14 11:07:32 +00:00
237124df1e Removed Eclipse files 2014-06-14 07:36:03 +00:00
50faef08be Pull to check email 2014-06-14 07:11:35 +00:00
f97c96916f Fixed build process 2014-06-13 10:28:25 +00:00
0b689a5a54 Gradle build and settings files, updated ignores 2014-06-13 01:28:36 +00:00
2340a5f265 Reorganized directories 2014-06-13 01:12:43 +00:00
9691f3d169 Added gradle wrapper 2014-06-13 01:07:38 +00:00
c84870ccb0 Fixed duplicate string name 2014-06-13 01:03:03 +00:00
d8637a3b27 Move "New Identity" action to list item 2014-06-12 08:15:34 +00:00
50e909e806 Mark email subject input field to prevent newlines 2014-06-12 04:30:30 +00:00
879c5cc5a5 Quote subject and content when replying to emails 2014-06-12 04:23:09 +00:00
b2439f73d1 Pass full email through when replying 2014-06-12 02:54:07 +00:00
c480103c17 Outbox emails stay marked as unread until sent 2014-06-12 02:21:53 +00:00
f8a69849a8 Handle new vanity prefix parameter in I2P-Bote 2014-06-11 23:04:58 +00:00
4e7d57d8e3 Implement vanity destinations 2014-06-11 21:11:11 +00:00
5832416b91 Only allow moving emails from trash (for now), never allow move to outbox 2014-06-11 08:18:53 +00:00
1f8b87c4ef Moved from and sent lines to bottom 2014-06-11 08:11:08 +00:00
2e98d95213 Show contact picture in notification for single new email 2014-06-11 06:52:32 +00:00
54990b2a45 Moved sent date to second line (to not truncate subject line) 2014-06-11 06:14:30 +00:00
0ad03ce6d4 Fix for new Jetty/Tomcat 2014-06-11 06:13:13 +00:00
c84f1db03e Updated TODO 2014-06-11 05:53:40 +00:00
3b64f589f4 Connect to network immediately for remote router, or if I2P Android is running 2014-06-11 05:29:15 +00:00
5ca77f2d50 Change in i2p.i2p-bote 2014-06-10 12:18:13 +00:00
2082fd0856 Emails are not kept in-memory inside EmailFolder, so store \Recent in metadata 2014-06-10 12:17:35 +00:00
6fd09bccb9 Use NewEmailListener instead of FolderListener 2014-06-09 04:52:58 +00:00
98c56de3a5 Added specific listener for newly-received emails 2014-06-09 04:51:20 +00:00
1801e308b2 Fixed bug in setFlags() 2014-06-08 06:46:56 +00:00
0f15534e73 Fixed use of \Recent flag 2014-06-08 05:01:29 +00:00
f318587209 Helper method 2014-06-08 04:59:13 +00:00
850675dce5 Use \Recent flag to only notify user about newly received emails 2014-06-08 04:33:56 +00:00
0763c8cff2 Updated for new FolderListener API 2014-06-08 04:33:05 +00:00
38d5833551 Improved FolderListener, emails added to a folder outside IMAP now get \Recent 2014-06-08 04:12:30 +00:00
6bbd015d38 Store other IMAP flags in-memory (like \Recent) 2014-06-08 02:59:39 +00:00
5e94144578 String clarification 2014-06-06 08:53:23 +00:00
9885860e91 Notify on new emails
FolderListener is not ideal, it fires on any change. We only want to listen for
new emails arriving (not user marking emails unread), and each email should
only appear in a notification once.
2014-06-06 08:39:47 +00:00
ae731edfd9 Missing file 2014-06-05 04:31:09 +00:00
f1fb48860b Bugfix: redeliver Intent to BoteService if killed and then restarted 2014-06-05 01:26:18 +00:00
185ec7ff9e Updated TODO 2014-06-04 23:48:57 +00:00
4f6cbbaf3a Reply action 2014-06-04 23:48:26 +00:00
13583b4bef Fall back to internal router if I2P Android is pre-0.9.13 2014-06-04 21:20:33 +00:00
f77f273e30 Fixed variable name to match convention 2014-06-04 21:06:06 +00:00
7843f06142 Truncate subject and from lines to not overlap RHS fields 2014-06-04 20:15:16 +00:00
5a088ae969 Call correct startActivityForResult() 2014-06-04 19:57:26 +00:00
c2a9b5202a Updated TODO 2014-06-04 12:08:45 +00:00
eced9e6239 I2P Android integration 2014-06-04 10:28:29 +00:00
b8e4c719ab Method changes in i2p.i2p-bote 2014-06-03 07:56:23 +00:00
f20ae940a8 Add missing file 2014-06-03 06:01:55 +00:00
f7f185a68c Handle index packets that are bigger than a datagram (fixes "After merging, IndexPacket is too big..." errors) 2014-06-02 21:01:26 +00:00
a47100d14a Fix NullPointerException on fresh installs 2014-05-29 22:21:09 +00:00
32bd6e45a8 Fixes for Tomcat 7 which is more anal about the JSP spec than Jetty or Tomcat 6 2014-05-29 22:14:28 +00:00
69b55a8e2d Refresh options menu when starting or stopping Bote 2014-05-29 10:03:40 +00:00
7d70d7c0df Bundle internal router, settings to choose internal or remote 2014-05-29 09:56:02 +00:00
f5732d699c Fixed parameter substitution in strings 2014-05-25 13:55:24 +00:00
ca19b0929e NPE fix for corrupt folder 2014-05-24 00:00:34 +00:00
eb54ff8dcf Show delivery percentage if zero 2014-05-23 03:18:12 +00:00
f228f64bd7 Show sending status in ViewEmailFragment 2014-05-23 01:58:39 +00:00
b3131d2462 Show sending status only in outbox; don't allow outbox messages to be moved 2014-05-23 01:44:48 +00:00
b2bd4158f4 Use the new flexible email status system 2014-05-23 01:19:01 +00:00
3dede23a24 Reworked email status storage for UI flexibility 2014-05-23 01:18:18 +00:00
9a3f7ba541 Updated TODO 2014-05-22 13:32:20 +00:00
5a38238ec8 Fixed NPE in settings when rotating device 2014-05-22 13:32:00 +00:00
37c754d570 Option to delete identities 2014-05-22 12:54:03 +00:00
c08b15fc8f Option to delete contacts 2014-05-22 12:10:58 +00:00
95d3bfe01b Missing resources, removed placeholder text 2014-05-22 10:01:47 +00:00
e540c5733c Show entire email if external 2014-05-21 06:07:18 +00:00
26f96ed7f5 TODO bugfix 2014-05-21 01:56:30 +00:00
ccf1f92be7 Highlight address book and network info links in nav drawer when touched 2014-05-20 13:06:30 +00:00
374e1d71cf Require recipients 2014-05-20 12:58:39 +00:00
f4d845bb95 Fixed NPE (that should never occur, but there is an input validation bug) 2014-05-20 11:58:02 +00:00
36b46f6587 Show any connection error on network info page 2014-05-17 15:00:01 +00:00
15d0d0f950 Updated TODO 2014-05-17 13:12:24 +00:00
341db41207 Show email status image/text using a single TextView, stop content overlapping 2014-05-17 13:10:42 +00:00
2e556727c0 Improved sent mail detection, show email_status_text when set 2014-05-17 11:56:47 +00:00
d21d7d3fcb Show attachments and delivery status in email list 2014-05-17 06:54:58 +00:00
717c697fcc Don't open network info page if Bote has not started 2014-05-17 05:32:04 +00:00
9d6b4ce48f Update network status whenever drawer state changes, not only on swipes 2014-05-17 04:50:08 +00:00
e190587330 Reverted bad log tweak 2014-05-17 04:37:23 +00:00
0d92ddb6e1 Set connectTask to null so getNetworkStatus() returns valid state 2014-05-17 04:36:42 +00:00
52f6405c74 Removed unused import 2014-05-17 04:33:28 +00:00
df9c841606 Updated TODO 2014-05-17 04:33:07 +00:00
99fb8bf264 NPE fixes, log tweak 2014-05-17 04:27:59 +00:00
fa22d47252 Whitespace, don't create SSL key store on Android 2014-05-17 04:21:06 +00:00
f7bba81669 Updated TODO 2014-05-17 03:46:35 +00:00
766b93fa85 Simple network info page 2014-05-17 03:46:27 +00:00
9b4ecf30df Service for starting and stopping Bote 2014-05-17 02:56:29 +00:00
7b39399128 Network status in nav drawer 2014-05-17 01:25:23 +00:00
791a16f36c More TODO items 2014-05-16 23:10:58 +00:00
3510264c56 Set relevant titles on pages 2014-05-16 23:09:09 +00:00
d8cc7278c0 More TODO items 2014-05-16 13:57:57 +00:00
22795c4224 Updated TODO 2014-05-16 13:52:11 +00:00
16ba7f0acd Show when an email is selected 2014-05-16 13:51:31 +00:00
cfd4107bd5 Click on email contact pictures to select them 2014-05-16 13:39:28 +00:00
d3b611d9a6 Mark viewed emails as read ("not new") 2014-05-16 13:03:33 +00:00
8278e51f4d Fixed NPE 2014-05-16 13:01:58 +00:00
14658aa9ea Picture fixes 2014-05-16 10:43:14 +00:00
18542be5f9 Backend for setting identity and contact pictures
Pictures are not saved correctly, something is corrupting them.
2014-05-16 09:33:28 +00:00
3628c50b24 Moved address book link into nav drawer 2014-05-15 10:56:30 +00:00
139557df55 Default image for contacts and identities with no picture 2014-05-15 10:50:12 +00:00
e58b3bd43a Correctly handle empty contact pictures 2014-05-15 05:52:21 +00:00
7b2c78cb09 Fixed edit contact button size 2014-05-15 05:45:52 +00:00
1ce3c6d6eb Simplified name 2014-05-15 05:37:01 +00:00
342f11ac9f Fixed string 2014-05-15 04:33:36 +00:00
770b1a85fa Integrated address book into new email recipients field 2014-05-15 04:29:53 +00:00
3d4d8e598b Added TokenAutoComplete library
Source:
https://github.com/splitwise/TokenAutoComplete

License:
Apache License, Version 2.0
2014-05-15 03:30:09 +00:00
34981a9980 Contact edit page 2014-05-15 02:54:32 +00:00
ac3032e707 Fixed drawable name 2014-05-15 02:53:18 +00:00
0cc2b5fe99 Helper for encoding picture 2014-05-15 02:53:01 +00:00
dd89b919ea Address book list 2014-05-15 00:36:00 +00:00
2dfa656b06 JSSE requires keystore and key passwords to be identical 2014-05-13 05:07:05 +00:00
56aa3f3c1c Generate the SSL cert on-the-fly to prevent MITM 2014-05-13 02:23:09 +00:00
c2e75290fb Reverted mistaken checkin 2014-05-13 02:20:19 +00:00
3387cdd90c Enable STARTTLS for IMAP and SMTP using selfsigned certificate
Certificate domain is 127.0.0.1, clients will throw a warning if using localhost
(alongside the warning for selfsigned cert).
2014-05-12 13:49:13 +00:00
c3088c5931 Mark important comments 2014-05-12 13:23:28 +00:00
a149a0bdc0 Fixed NPE (ticket #1272) 2014-05-12 12:37:47 +00:00
ee16f4416f i2p.bote -> i2p.bote.android 2014-04-19 00:18:33 +00:00
3ab1f1aa19 Implemented send button action with sender as dummy recipient 2014-04-17 23:38:25 +00:00
7867198fe9 Override updateMessageID() on Android to prevent NetworkOnMainThreadException 2014-04-17 23:27:40 +00:00
1f30eef5dd Updated TODO 2014-04-17 05:44:42 +00:00
61ead34a9a Mark emails read/unread, move to folder 2014-04-16 13:09:11 +00:00
a22d1c1f52 Changed LogWriter tag 2014-04-16 13:07:59 +00:00
38293ebcf9 Added elementUpdated() to FolderListener, made setNew() throws consistent 2014-04-16 13:07:17 +00:00
f1191b2f68 Email deletion 2014-04-15 02:51:43 +00:00
6f4a0615ff Update FolderListeners when Email is moved 2014-04-15 00:58:02 +00:00
4aa2bd8b74 Update folder list text on folder changes 2014-04-14 21:36:54 +00:00
5ed7cf5553 Include precompiled native scrypt library for ARM5 - big usability speedup
Source:
ed8025e140/src/android/resources/lib/arm5/libscrypt.so
2014-04-14 12:53:58 +00:00
6e00e398f6 Upgraded to scrypt 1.4.0 2014-04-14 12:21:42 +00:00
22fa6105ac Updated FAQ and User's Guide 2014-04-14 02:30:00 +00:00
946fa8d6ec Insert addressbook/identity names, @bote, {UNK} into IMAP email headers 2014-04-14 02:29:37 +00:00
5a8d9c6f4f Remove @bote before validating sender and recipient addresses in SMTP 2014-04-13 05:05:41 +00:00
5c53c2071b Updated about.jsp 2014-04-13 02:45:57 +00:00
c84770036d Create BoteMailboxes once, and remove their FolderListeners when IMAP stops 2014-04-13 02:27:31 +00:00
d9756407b0 Refactor to handle recipient headers containing multiple recipients 2014-04-13 01:46:13 +00:00
fe9c6d9d47 Pack new mailapi.jar in update for 0.2.10 2014-04-11 22:42:07 +00:00
ee96addef6 Removed signature from scrypt jar (pack200 interferes) 2014-04-10 21:51:49 +00:00
1f9aa61d2f Upgraded to scrypt 1.4.0 2014-04-10 20:59:44 +00:00
zzz
5e559846db one more typo 2014-04-07 13:26:10 +00:00
bbd24eaaff fix another missing '/' 2014-04-07 12:20:12 +00:00
4db680f067 add missing / to paths 2014-04-07 12:14:44 +00:00
79595913f2 Previous fix wasn't quite right 2014-04-04 20:01:29 +00:00
b387b77bfb Fixed concurrency problem 2014-03-30 03:10:04 +00:00
dce43d11ae Fixed IMAP UID implementation to conform to RFC 3501 2014-03-30 02:14:56 +00:00
8fec6b28e8 Remove @bote properly from SMTP messages 2014-03-29 03:19:53 +00:00
17282cda4d Missing Bean method 2014-03-28 20:58:26 +00:00
1664253417 Fixed password "corruption" (ticket #1239) 2014-03-28 20:57:56 +00:00
654b136cb0 Somehow, file duplication has stopped 2014-03-18 20:04:50 +00:00
2b5de7958e Doc tweak 2014-03-18 10:56:28 +00:00
10b9b2e593 Updated identity file spec 2014-03-18 10:22:47 +00:00
c8e10cd3b0 Added TODO 2014-03-13 12:29:40 +00:00
13cc068011 Tweaked layout 2014-03-13 04:12:24 +00:00
71808a71d3 Added view of identity 2014-03-13 03:49:19 +00:00
1c6a654b21 Disable edit fields when saving 2014-03-12 03:33:00 +00:00
703cd94f5a New identity creation 2014-03-09 23:06:00 +00:00
3571adf2ec Another StatusListener 2014-03-09 23:05:22 +00:00
f9d54e10e0 Fixed NPE 2014-03-09 09:34:40 +00:00
49332e7875 Implement required API 19 method override 2014-03-09 09:25:52 +00:00
44027e80ca Save and load default key properly 2014-03-09 09:11:00 +00:00
d6984910ef Set default identity for new email correctly 2014-03-08 23:57:33 +00:00
9d821f66b9 Update default identity when changed (for writing to file) 2014-03-08 23:07:54 +00:00
f967ead55f Set default identity option 2014-03-08 22:49:16 +00:00
47067564d5 Improved edit identity UI 2014-03-08 22:37:56 +00:00
1fcc80edb5 Improved set password UI 2014-03-08 20:54:04 +00:00
dc98c05479 Fixed <intent> bug (targetPackage is package of application, not class) 2014-03-08 20:53:05 +00:00
ec3fe21378 Added missing menu 2014-03-08 20:05:28 +00:00
02c93ed154 Added missing icons 2014-03-08 20:04:51 +00:00
5411043405 Fixed settings headers after file moves 2014-03-08 19:50:56 +00:00
7bafb9546e Start of new email activity 2014-03-08 12:52:41 +00:00
3154a6927a Moved config and identities Activities into subpackage 2014-03-08 03:05:02 +00:00
4993d172ee Added edit page for identities 2014-03-08 01:18:06 +00:00
0f0fedef65 Show contact picture in email list and view (if available) 2014-03-06 23:51:16 +00:00
d5e0bd8429 Added list of EmailIdentities to settings 2014-03-05 20:38:13 +00:00
5c45ac2a14 Use preference headers in settings (prep for identity support) 2014-03-04 10:52:50 +00:00
bef847d270 Moved Servlet-dependent helper methods back to JSPHelper 2014-03-04 01:46:56 +00:00
967a523130 Missing javadoc @param 2014-03-04 01:36:11 +00:00
7e48a8e438 Remove router.jar dependency, stub out Seedless use 2014-03-04 01:26:40 +00:00
e4efc71d99 Recover current status of password setting on config change 2014-03-03 04:44:44 +00:00
6be946aeb8 Recover enabled state of submit button after config change 2014-03-03 04:38:12 +00:00
e3ad5f9564 Fixed crash if navigated back from SetPasswordActivity 2014-03-03 04:19:14 +00:00
e0af648df5 Make password-setting AsyncTask survive configuration changes (screen rotation)
Robust code derived from:
http://stackoverflow.com/a/12303649
2014-03-03 00:35:27 +00:00
ccdfdcf914 Display password set/change progress 2014-03-02 10:33:05 +00:00
c35e9c9139 Enable listening to status of internal operations (for Android UI) 2014-03-02 10:32:23 +00:00
44cbabaa8b Dismiss keyboard when done 2014-03-02 08:17:51 +00:00
cd5d912240 Use AsyncTask for password checking so UI doesn't hang 2014-03-02 03:58:46 +00:00
55b16d952c Support for changing passwords 2014-03-02 03:33:48 +00:00
c2d6b8d131 Added password handling and input 2014-03-01 21:20:16 +00:00
2fccdf3d34 Remove unneeded GSE catching 2014-03-01 02:25:09 +00:00
c0b62e25c1 Remove throw (handled in rev 78f6a5bcf158019aceb75a48aebd8d9c20b203ce) 2014-03-01 01:55:15 +00:00
a22e27c0de Renamed classes 2014-03-01 01:46:30 +00:00
c6f68c5f3e Removed debug line 2014-03-01 01:11:05 +00:00
03270d8625 Stubbed out IMAP and SMTP 2014-03-01 00:15:31 +00:00
936e1e7d50 Updated techdoc.txt for mailapi.jar 2014-02-28 22:08:34 +00:00
592793eaac Add a setting to enable/disable SMTP 2014-02-28 22:02:49 +00:00
045c8393c7 Added I2CP host/port settings 2014-02-28 21:46:24 +00:00
e1513de85c Added settings 2014-02-28 11:05:03 +00:00
bd9ab1fc36 Updated ignore 2014-02-28 09:44:25 +00:00
14445b4948 Ellipsize content in email list 2014-02-28 07:53:22 +00:00
1685b2713c Enable ActionBar app icon to behave as action to go back 2014-02-28 07:49:27 +00:00
e07c73fa03 Fully display emails 2014-02-28 07:29:55 +00:00
2dc77be456 Populate ViewPager with Loader 2014-02-28 05:59:00 +00:00
b2470d544c Factored out common Loader methods 2014-02-28 01:33:15 +00:00
ee0e420423 Email viewing structure 2014-02-28 01:04:06 +00:00
b88dc979ea Sort emails newest-first 2014-02-28 01:03:35 +00:00
e07a5fa574 Moved BoteHelper to i2p.bote.util 2014-02-27 20:44:48 +00:00
21ed7bc1a4 Added missing file 2014-02-27 20:36:43 +00:00
d658a57e1a Show sent date if set, show new emails with bold subject and from 2014-02-27 10:14:21 +00:00
eb1aecd58e JavaMail currently doesn't work on Android, use javamail-android instead
https://code.google.com/p/javamail-android/
2014-02-27 09:32:49 +00:00
167f4752ca FolderAdapter -> FolderListAdapter, highlight selected folder 2014-02-27 08:44:12 +00:00
f1c9ac9788 Updated to mailapi 1.5.1 2014-02-27 05:19:54 +00:00
4b7e270422 Increase min API to 9 2014-02-27 04:44:42 +00:00
522915f577 Fixed duplicate fragment on configuration change 2014-02-27 04:42:21 +00:00
bdd24d7e9d Apparently Jasper can't follow inheritance 2014-02-27 04:40:15 +00:00
256884e2a3 Pass exceptions through from BoteHelper (like in GeneralHelper)
Todo: handle them.
2014-02-27 04:18:06 +00:00
7f0c026d0e Speed fix: only show new count when there is data to show 2014-02-27 04:11:28 +00:00
09f7681ff6 Fixed dependency issues, only compile Bote once 2014-02-27 03:59:10 +00:00
de36d91909 Delete i2pbote.jar in clean 2014-02-27 03:28:18 +00:00
1a6ae22b99 Allow using result of folder.getName() in getMailFolder()
This is used for saving instance state in Android.
2014-02-27 03:00:03 +00:00
ab4b1df657 Fixes 2014-02-27 02:33:05 +00:00
dff9b06017 Start of email item design in list, basic styling 2014-02-26 23:30:46 +00:00
89e143123c Bugfix (crashed on folder not existing) 2014-02-26 23:12:55 +00:00
560780723d Extend i2p.bote.util.GeneralHelper 2014-02-26 23:11:06 +00:00
adde9736be Split JSPHelper so common utilities can be used by other UIs (Android) 2014-02-26 23:08:34 +00:00
35ec3c283a Bugfix, empty folder text, folder name as title, show new message counts 2014-02-26 11:19:25 +00:00
94837b91ce Set files directory 2014-02-26 07:38:06 +00:00
8ec6da0082 Fixed initial folder selection 2014-02-26 07:10:58 +00:00
7e6b5292bf Loader for email list 2014-02-26 07:08:41 +00:00
173d08d798 Allow FolderListeners to be removed 2014-02-26 07:07:42 +00:00
acb1ace33a Don't add source files to i2pbote.jar (it duplicates .txt files) 2014-02-26 07:07:11 +00:00
172463c96d Basic launcher icon for Bote
by str4d
License: Creative Commons BY-SA 4.0
2014-02-26 04:55:16 +00:00
9ef3747222 Started main interface, nav drawer with list of folders 2014-02-26 04:29:39 +00:00
0aae5a94c8 Clarify why classes are deleted from i2p.jar 2014-02-26 04:26:32 +00:00
b530cbe7b9 Fixed mistake 2014-02-26 04:17:04 +00:00
dacc53970e Copied net.i2p.util.* placeholders from i2p.android.base 2014-02-26 03:58:04 +00:00
77b945e36d Initial commit of Android project structure 2014-02-26 03:57:24 +00:00
ffa7450e77 Reordered list of folders 2014-02-26 03:28:18 +00:00
d9d9c47124 Add README.md for GitHub 2014-02-16 18:18:45 +00:00
d960308461 Update techdoc.txt 2014-02-16 13:41:27 +00:00
616df8de9f Update translations from Transifex 2014-02-16 13:40:34 +00:00
427e293bac 0.2.9 2014-02-16 13:39:32 +00:00
163f8d58c0 Make send button easier to find 2014-01-08 01:44:02 +00:00
43e34eb221 Added code to mapper for fetching recent messages
This functionally changes nothing, because BoteMessage.isRecent()
always returns false.
2014-01-01 21:30:02 +00:00
c2d1334c04 Unset flags correctly 2014-01-01 05:57:41 +00:00
a05890eaec Implemented deleting via IMAP
The \Deleted flag is stored in EmailMetadata, but is ignored by Bote and only
used for IMAP deletion.
2013-12-31 20:20:17 +00:00
2a744fb1cd RFC 2822: set From: header 2013-12-31 16:43:55 +00:00
ed60728edd Don't delete libs with "ant clean" 2013-12-31 07:29:26 +00:00
48e69d3cd5 Don't break folder list if an email can't be decrypted 2013-12-27 02:33:26 +00:00
df09332ead Added libs to .jar manifest (so IMAP setting works) 2013-12-26 09:54:01 +00:00
585413fa2b Fixed potential NPE 2013-12-25 23:30:02 +00:00
68c48cb332 Update to jMock 2.6.0 (v2.5.1 is not available from their website anymore) 2013-05-01 18:12:55 +00:00
ae87a801ea Fix classpath and typos 2013-05-01 17:52:44 +00:00
901b051546 Implement SMTP 2013-05-01 17:50:42 +00:00
6590e3122a Shorten known email destinations in the folder view 2013-04-29 19:18:02 +00:00
e601b5f46e Add a setting to enable/disable IMAP 2013-04-29 18:53:41 +00:00
641085ab24 don't try to zip up non-existent images directory (see rev. 1ae37d314f064cfb6c870727775f5b5ccc5ea69e) 2013-04-29 18:34:15 +00:00
5d371f983a IMAP interface, works with Thunderbird but not Evolution 2013-04-29 00:21:37 +00:00
8fc111fc86 Fix Content-Transfer-Encoding (8bit doesn't allow zeros, CR, or LF) 2013-04-26 23:15:13 +00:00
2dc809c025 Close streams when done reading 2013-04-26 23:06:15 +00:00
7b0149dc39 by request, adding my i2pbote node's public destination 2013-04-11 16:00:55 +00:00
24509df2b5 Delete commented out line 2013-04-02 21:47:28 +00:00
263b68550e Fix a compiler warning 2013-04-02 21:42:34 +00:00
ae3ac8b5be Attach file on selection when JS is enabled 2013-04-02 21:33:36 +00:00
7c22899f32 try supressing/fixing a few warnings 2013-04-01 21:01:18 +00:00
3970d6d015 build fix (don't try to zip up non-existent images directory) 2013-04-01 20:40:12 +00:00
4464301119 Remove unused images 2013-04-01 18:08:37 +00:00
f0622d202b Move style attributes to CSS 2013-04-01 17:57:59 +00:00
63e260f932 Show anonymous sender in italics 2013-03-31 22:36:24 +00:00
87ad8db92a Display of signatures:
* Combine signature column and replied column
 * Don't show signature warning for anonymous emails
2013-03-31 22:23:00 +00:00
cc7e0209ec Simplification 2013-03-30 23:01:32 +00:00
1a4c25a32f Add a null check 2013-03-30 22:58:14 +00:00
79339a0208 Fix a comment 2013-03-30 22:57:26 +00:00
249389a4d3 Replace ReentrantLock with synchronized 2013-03-30 08:55:16 +00:00
92a5a998d6 Show a wait page when the password is changed 2013-03-29 17:09:10 +00:00
e1a49cf440 Add a JSP that allows users to check encrypted files 2013-03-24 12:32:31 +00:00
e30cb79c5f 0.2.8 2013-03-08 21:51:26 +00:00
49268b21ea Increase #words to 8 and #zero bits to 10 2013-03-07 21:38:57 +00:00
74384e0517 Fix: unneccessary lookup when confirm=true 2013-03-07 21:25:44 +00:00
9bfde0ac33 Fix formatting on editIdentity.jsp 2013-03-07 21:22:36 +00:00
55d5ea6084 Fix NullPointerException when no word list is available for the current language 2013-03-05 23:01:49 +00:00
0e36c3dcc3 Fix: Escape characters handled incorrectly when saving and loading 2013-03-05 22:40:08 +00:00
0155e5f57f Implement mark() and reset() 2013-03-05 22:14:02 +00:00
a2b3833602 Store fingerprint with email identity and show it on editIdentity.jsp 2013-03-05 19:29:43 +00:00
200d9ceb3d Switch address book to a properties-style format; show imported picture and text on editContact.jsp 2013-03-03 12:48:36 +00:00
69fd2431c8 Switch identities file to a properties-style format; show published picture and text on editIdentity.jsp 2013-02-26 21:48:06 +00:00
66020b2fc7 Add 0.2.7 release notes 2013-02-14 15:35:32 +00:00
34e76ce627 Add a missing test 2013-02-12 13:30:15 +00:00
79393f0ea8 Fix ContactTest when run from Ant 2013-02-12 13:29:41 +00:00
c4cdbb98cc trim() name before publishing or doing a lookup 2013-02-11 21:24:28 +00:00
eda69d1e7e Fix @bote.i2p suffix 2013-02-11 21:23:00 +00:00
1f2c528b9b Sign Contact packets:
* sign on publish
 * verify on import
 * ignore invalid Contact packets in lookups
2013-02-11 21:18:01 +00:00
6a99b034b7 Implement fingerprints for directory entries 2013-02-11 12:03:15 +00:00
17db81eee1 Use native scrypt if available 2013-02-04 22:13:33 +00:00
d50c843145 Change DEFAULT_PASSWORD_CACHE_DURATION back to 10 (I accidentally changed it to 1 in rev f957174cc94b1ed2a836e94cb71850ed612f2622) 2013-02-04 21:51:02 +00:00
7cbf364407 Rename DirectoryEntryTest to ContactTest 2013-02-04 21:47:57 +00:00
bd8674950e Add null check 2013-02-02 12:18:11 +00:00
0a0418f83f Make sure test files and directories are cleaned up 2013-02-02 12:17:11 +00:00
a5832cd1e4 Add comment 2013-02-02 12:15:29 +00:00
ccbc2c3e01 Check for existing name before publishing 2013-02-02 12:09:26 +00:00
d5c6c2d046 Ignore requests to replace existing Contacts 2013-02-02 00:04:12 +00:00
d8a606946e Don't skip sleep() on exceptions; lower log level 2013-01-27 22:30:58 +00:00
dbaa00d5b1 Print number of running threads 2013-01-27 22:30:17 +00:00
844bfcb842 Implement basic address directory 2013-01-27 22:29:01 +00:00
882a6d207a merge of '8d66ebeab68f6165dbd977cb2b5a5d52d09eab03'
and 'd27fa448d59dd46241e032896b0788e9057da5e1'
2013-01-27 21:59:38 +00:00
730aff2a50 Updated Norwegian Bokmål translation updates from transifex.
(100% translation)
2013-01-26 19:17:27 +00:00
de6d2d60a9 Lower max packet size to 10 kBytes 2013-01-24 20:57:57 +00:00
a724a521cd Add an option to change the ping interval 2013-01-24 20:39:49 +00:00
eaaf3c90e4 Remove Util.get(Collection, int) and use toArray() instead 2013-01-24 20:37:39 +00:00
a0febacdfd Fix: addresses and identities still available after password cache expires 2013-01-23 21:42:24 +00:00
2f0cb066ab Fix forward URL when context path is something other than /i2pbote 2013-01-23 21:08:46 +00:00
fc89896246 Don't attempt to show number of new emails when a password needs to be entered first (metadata is password protected as of recently) 2013-01-22 22:05:27 +00:00
e9c262c8b7 Add package.html files 2013-01-22 20:49:32 +00:00
093e439e04 Fix JavaDoc warnings 2013-01-22 20:31:17 +00:00
bf07f9c664 merge of '90e5bcb2bddcd45813c9eb3b354026f37152c66e'
and 'a6a21a22bbe0d4a61750abff96d5aaaa23b8df54'
2013-01-21 22:27:35 +00:00
bba9ac405a Add a command line program for testing datagram reliability 2013-01-21 22:26:19 +00:00
a0457273b7 switch javadocs from bob.i2p to i2p-javadocs.i2p
(sponge no longer hosts them)
2013-01-21 01:56:14 +00:00
e6a330944d update oracle package-list URLs 2013-01-21 01:44:10 +00:00
20940f209b Lower log level 2013-01-20 21:23:06 +00:00
bbc71ad13c Some refactoring 2013-01-20 21:21:53 +00:00
64917757a4 Make delivery checking turn-offable 2013-01-20 21:21:36 +00:00
b966ebbcfd address.tag: replace hover with click-to-expand; share code with editIdentity.jsp 2013-01-20 19:49:23 +00:00
ca7f4124c4 Update en and de strings 2013-01-20 16:13:42 +00:00
b24fe46661 Fix ambiguous i18n source string "Sent" 2013-01-20 16:11:32 +00:00
1db3aee096 merge of '05f071f4da46716c5566db81c177ec3e3ec04852'
and 'de9594e3f85d2d2401af64407c0c54c4e6a61d33'
2013-01-20 03:02:17 +00:00
db5a566e15 Language nb is more precise than no. 2013-01-20 02:53:01 +00:00
f485c68472 DeliveryChecker.java:
* Don't save metadata unless it changed
 * Logging
 * Code formatting
2013-01-20 02:28:41 +00:00
78013d6725 Wait until connected to DHT 2013-01-20 02:20:51 +00:00
261d05a1d6 Prevent "${param.path}" from showing up as a translation string 2013-01-20 02:15:17 +00:00
4a79a28d28 Handle tags that have an attribute but the message key is in the body 2013-01-20 02:02:51 +00:00
844a7a24fe Add help text to editIdentities.jsp 2013-01-20 01:28:23 +00:00
9937ec73f9 Remove more mockup elements 2013-01-19 23:32:30 +00:00
54629d8d05 Appearance changes to sendEmail.jsp 2013-01-19 23:28:19 +00:00
15dcd78936 Show whether an email has been replied to 2013-01-19 23:21:00 +00:00
087a4a68be Remove an extra '<' 2013-01-17 23:10:03 +00:00
7829c44d0d Add a delete button after replying to an email 2013-01-17 22:57:15 +00:00
476d06a588 German, French, Italian, Polish, Portuguese, and Russian translation updates from transifex 2013-01-17 12:43:25 +00:00
53223be28d Display an error if no public name entered 2013-01-16 22:27:27 +00:00
691b8ade1c Bump version to 0.2.7; migrate metadata files 2013-01-16 22:14:08 +00:00
7ac255ec94 Encrypt metadata files (as of rev 24af3e..., they contain email destinations and packet keys) 2013-01-16 22:05:35 +00:00
cf84a6aeee Don't receive packets as streams 2013-01-15 21:18:50 +00:00
15c45ad820 Remove unused import 2013-01-15 20:53:49 +00:00
10100b8177 Synchronize a method I missed in rev 7db2932b3db6bbf32d4ae937c93eb37e06e9f099 2013-01-15 20:51:51 +00:00
6adef679ac Synchronize CryptoFactory (Ticket #610) 2013-01-14 20:16:04 +00:00
00411877ad Change log level 2013-01-14 20:10:33 +00:00
7dc9f6caa0 Change log level to DEBUG (same as the other background threads) 2013-01-14 20:09:57 +00:00
dcb2b0f945 Translate page titles 2013-01-14 20:00:06 +00:00
f396729698 Disable "default" checkbox when there are less than two identities 2013-01-13 21:15:07 +00:00
0aff058acd Add a note about automatic email checking when a password is set 2013-01-13 21:00:12 +00:00
55f363aa73 Add @Overrides 2013-01-13 20:48:27 +00:00
6fec35ce85 Remove unused methods 2013-01-13 20:45:13 +00:00
42001079b5 Don't show private key link when creating a new identity 2013-01-13 20:42:35 +00:00
ab3d23a3af Fix variable name 2013-01-13 20:38:29 +00:00
3b0c9774e5 Remove mockup elements 2013-01-13 20:35:39 +00:00
8b44166b90 Simplify stringification 2013-01-13 20:12:55 +00:00
46ca0add8a Shorten thread name so it doesn't get truncated in the logs 2013-01-13 20:09:58 +00:00
6e967b2365 Fix: Version number not correctly written to file 2013-01-13 20:05:15 +00:00
bc72ebb865 Implement delivery status feature 2013-01-13 17:25:01 +00:00
e204d01fa2 Remove a System.out.println() 2013-01-12 02:51:39 +00:00
4a05868f11 Catch a more specific exception 2013-01-12 02:44:11 +00:00
6400913229 Move remaining third-party code (flexiprovider, lzma, and scrypt) into .jars 2013-01-12 02:42:00 +00:00
d7d3f2f01c Update to NTRU 1.2; move NTRU into a .jar 2013-01-12 02:08:08 +00:00
f9014d7b7b Fix unit test 2013-01-07 15:02:32 +00:00
a64ece90c2 Sort relay peers 2013-01-07 15:02:16 +00:00
e8f16cc83c Remove an unused method 2013-01-07 15:01:36 +00:00
d7b7355dd1 Fix warnings 2013-01-07 15:01:15 +00:00
816cf49b8c Get rid of the I2PBoteThread mechanism and handle thread interruptions correctly 2013-01-05 22:07:38 +00:00
bf2e2224c9 More detailed log message 2013-01-03 13:45:06 +00:00
2824c9a773 Minor comment change 2013-01-02 23:35:04 +00:00
1dbbcd8b78 Fix: themes not found because directory names were localized 2013-01-02 23:34:35 +00:00
d7d6257a36 Add an import for JavaDoc 2013-01-02 21:28:57 +00:00
020ef33b95 Log exceptions 2013-01-02 21:28:21 +00:00
8cfe4c5660 Only send up to 20 peers in a PeerList 2013-01-02 21:27:27 +00:00
e04deb28a1 Add info on how to set up Eclipse 2013-01-02 21:23:56 +00:00
fc0837ede4 Fix identities file name; change some wording 2013-01-02 21:20:22 +00:00
zzz
3ade199175 Specify source=1.6 target=1.6 so we can build with Java 7.
Suppress ant warning.
2013-01-02 12:53:28 +00:00
zzz
7bffe19e9e merge of '9030eb5d24f9d9f0c546f54c8a078749941e28df'
and 'ec0ec8bca6aa7c8d6b314688662891ed0c80aa34'


Merge broken rev from asaf.
Mtn 1.0 can't handle dates with trailing time zone indicators,
such as 'Z'. asaf's checkin was done with guitone which
adds the 'Z'. Hopefully this will make mtn 1.0 happy again.
2013-01-02 12:38:22 +00:00
3ffcf9d8d3 Update German translation from Transifex 2012-12-30 15:52:52 +00:00
111b3d8dbb Add a heading and a brief explanation 2012-12-02 14:27:31 +00:00
e177cd4567 Comments 2012-11-27 17:43:19 +00:00
a0ce9017a9 Include file size in log 2012-11-27 17:39:08 +00:00
bc1848ddcf Add a few more classes from the upstream bouncycastle jar due to recent changes in I2P.
Fixes the error found at http://pastethis.i2p/show/fnVXfo6eWaq4uaQNUjhO/
2012-09-25 23:02:14 +00:00
1f595fc079 Change "New" button text to "Compose" 2012-08-14 18:27:29 +00:00
22c0b789c4 Display last mail check time 2012-08-14 18:23:47 +00:00
967286aed0 New Arabic translation from Transifex
100% complete
2012-08-09 14:00:31 +00:00
ef357a5f2a Updated German and Spanish translations from tx. Pushing en source files to tx. 2012-07-29 10:03:47 +00:00
9fb05ba42e Remove a wrong comment 2012-07-20 12:00:29 +00:00
f13743d7b9 Make the "error" status text a link that shows a stack trace 2012-07-20 11:58:32 +00:00
549c39681d Lower log level 2012-07-20 10:56:09 +00:00
a3e8f657dd Show connect button if connecting failed 2012-07-20 10:54:35 +00:00
9cb3409a1a Add KYTV, change HH's role to Main Dev 2012-07-20 10:53:08 +00:00
5e87d6fa2d Lower log level 2012-07-19 15:16:35 +00:00
902db81fa7 Disable streaming 2012-07-19 15:15:17 +00:00
3cd0f09a41 More detailed log message 2012-07-19 15:13:27 +00:00
d0204effa2 Scroll down to the plugin section 2012-07-19 15:12:48 +00:00
ec619dd830 Fix: some threads not terminating on interrupt() 2012-07-19 15:08:52 +00:00
10e3df428c Spanish translation update from tx 2012-06-06 10:11:49 +00:00
e11f41d7f2 merge of '6f90a7d3635c3fece552972c32e378fc5177dd0b'
and '754beaa17f082deb32703c671277a2c0a5ef9ae5'
2012-06-03 21:37:14 +00:00
f7aae2de4e Add Hungarian translation from TX, thanks AdminLMH! 2012-06-03 13:57:06 +00:00
d1e4d1a715 Apply layout fixes from rev. e3deccd7238bb85c8b21b564b52979e3b3441142 to network.jsp 2012-06-03 09:38:15 +00:00
9435b23ab2 Internationalize a string 2012-06-03 09:23:05 +00:00
bdffd2b8c4 Apply layout fixes from rev. e3deccd7238bb85c8b21b564b52979e3b3441142 to the vanilla theme 2012-06-03 09:19:50 +00:00
590b9a9e05 Handle individual files 2012-06-03 07:20:36 +00:00
86ec61db22 Fix layout issues in recent versions of Firefox and Chromium 2012-05-24 18:33:04 +00:00
45f60e9242 Add translators 2012-04-20 00:11:08 +00:00
56be74a167 Dutch updates from Transifex 2012-04-19 23:31:06 +00:00
14376afddb Translation updates from TX (cs, de, es, pl, & sv) 2012-02-23 12:17:55 +00:00
d2adcd64a7 Fixed bug with "Check mail" button.
Better presentation for "Compose" button.
Some advantages in russian translation
2012-01-01 00:00:00 +00:00
13c19addff copy theme folder 2011-12-17 00:37:29 +00:00
dad6a579c1 Escape & entities 2011-12-17 00:37:15 +00:00
83d585f651 s/wordwrap/word-wrap/ 2011-12-17 00:36:56 +00:00
90ceef4583 Add the ability for users to add their own UI themes 2011-12-15 06:51:16 +00:00
0bf899e291 Update translation 2011-12-14 02:48:25 +00:00
bbdfc5745b * Add theming support
* Add 'vanilla' theme
* Remove unused icon files
2011-12-14 02:44:22 +00:00
c3e6a437c9 HTML/CSS:
* Move CSS out of JSPs
 * Replace layout tables with CSS
 * Make pages variable width
2011-12-10 05:08:12 +00:00
dbe4400409 Internationalize strings 2011-11-27 06:26:59 +00:00
bb5d075a54 Replace no-argument JSP functions with bean properties 2011-11-27 06:01:53 +00:00
5d87d5ec00 Put close() in a finally{ } 2011-11-26 05:06:39 +00:00
d24eea365c Add comment 2011-11-26 04:57:35 +00:00
abd4a55956 Move the two toByteArray methods into the superclass 2011-11-26 04:55:00 +00:00
732b14115b Fix error message 2011-11-26 04:39:13 +00:00
4455fbb172 * Move symmetric encryption to AbstractCryptoImplementation
* Don't use deprecated methods AESEngine.safeEncrypt() and AESEngine.safeDecrypt()
2011-11-26 04:31:35 +00:00
673d43cd86 Use the global context, don't create a new one 2011-11-24 05:23:02 +00:00
b45a162826 Updated (ant poupdate) and pushed to tx 2011-09-23 19:23:44 +00:00
b06d07b5d1 Add Vietnamese language from Transifex 2011-09-23 19:23:16 +00:00
52ac133ffa Add Italian and Ukrainian from transifex 2011-09-23 19:17:23 +00:00
4a240fbb5b Ran ant poupdate for ar and fr. These have been pushed to transifex. 2011-09-23 19:16:52 +00:00
252a7f0dad merge of '6e3866d554a0bce90773691091c5e037aa69cadc'
and '95a0846bd260b60d641a584dceb6ced27aa2e3ce'
2011-09-23 19:01:36 +00:00
1cd51c811d display tooltips in routerconsole 2011-07-20 04:15:26 +00:00
dda1f5b585 corrected version in manual to current release 2011-07-20 03:46:22 +00:00
fb5e1fc4c2 upd i2p-bote messages_fr.po 2011-07-08 14:39:57 +00:00
0e6a32a0a0 Update Arabic po from source and finished translation 100% 2011-06-11 09:23:59 +00:00
d9bc25e1aa translate French strings into Arabic 2011-06-09 13:57:26 +00:00
b124a1046d update .po's 2011-06-02 18:47:29 +00:00
cd21435b60 correction to history.txt 2011-05-02 16:29:02 +00:00
44130323ad typo es 2011-05-02 16:28:38 +00:00
ef339c29d0 Fix: Existing derivparams file not found 2011-04-30 22:28:56 +00:00
8ea8e86d99 Correction to translators 2011-04-30 18:45:46 +00:00
0edf2cfbe3 Replace b32 url with i2pbote.i2p 2011-04-30 18:25:15 +00:00
a4b284a71c 0.2.6 2011-04-25 23:36:22 +00:00
e1b9eb5bbc When replying to an email sent by a local identity, send to the recipient of the original email by default 2011-04-25 00:22:33 +00:00
f45a185502 Update techdoc.txt; remove To Do / Ideas section 2011-04-24 15:06:40 +00:00
be545c0836 Don't run migration if already migrated to the latest version 2011-04-24 15:06:39 +00:00
6c10b8ac64 Fix: signature flag (Email.SIGNATURE_VALID_HEADER) not set on emails in the "sent" folder 2011-04-23 23:35:23 +00:00
92ff6b58f0 Add FileInfo tool 2011-04-23 22:16:26 +00:00
1f54ed9ba8 Change order of parameters in the derivparams file so it matches encrypted files 2011-04-23 21:36:47 +00:00
19452867b4 Fix error messages in log: "Request contains a parameter named recipient0, but no parameter named recipientType0." 2011-04-23 08:35:22 +00:00
1fe6c4fc36 Move illegal <description> tags out of i2pbote.tld 2011-04-23 07:15:44 +00:00
57ead77d39 Capitalize static variable 2011-04-23 06:29:46 +00:00
dd3ed14174 Remove empty directories (see rev. 65aabb15c2a5df2959bdd8987b6ef8959fe3a537) 2011-04-23 06:23:43 +00:00
efc2363ade Translate page title 2011-04-23 06:01:24 +00:00
19a586b170 Integrate FAQ and user guide 2011-04-23 05:59:54 +00:00
3851faff3f Fix a few things in the German translation, and make it more German 2011-04-22 21:29:08 +00:00
a583a1fcc3 Add/fix comments 2011-04-22 20:58:41 +00:00
0464c76884 Wrap (fold) long signatures 2011-04-22 08:28:14 +00:00
2bc05d4d46 Use identities from TestUtil 2011-04-22 07:18:25 +00:00
19970b1d53 Fix: "Check Mail" button brings up an error page if a password is required 2011-04-22 05:54:48 +00:00
035cd346a4 Fix: "constant string too long" when compiling tests with Ant (javac) 2011-04-21 04:56:04 +00:00
cb1d5bbf67 Set encoding to ISO-8859-1 when compiling flexiprovider sources 2011-04-21 04:50:54 +00:00
058d09d262 Pare down flexiprovider some more 2011-04-21 04:48:05 +00:00
14c728c588 merge of '110cd0892a45b8d875bce780895a137da24510d8'
and 'df7b1ac4e3f00852ae445bab6259fb76bd56c5b6'
2011-04-20 03:08:49 +00:00
7823b38d24 Update russian translation 2011-04-19 17:44:33 +00:00
cb76489c0f Use SHA-512 for ECDSA-521 2011-04-18 05:57:28 +00:00
fb0352cd7e Don't create zero-length packet files 2011-04-17 19:22:46 +00:00
b7fcbb5829 Fix plugin target 2011-04-16 22:03:16 +00:00
80a04a70cb Fix IdentitiesTest 2011-04-16 04:58:40 +00:00
b48534b5f7 * Fix: endless JSP forwarding loop when two emails in the same folder have been encrypted with different passwords
* Make PasswordException a checked exception
* Remove unused method AddressBook.iterator()
2011-04-16 04:57:53 +00:00
a1085909e1 ego inflation 2011-04-07 21:50:23 +00:00
dc7777be7f fix about identity creation 2011-04-07 08:20:48 +00:00
0ed58252d9 fix about identity creation 2011-04-07 07:31:13 +00:00
660b77c112 fix about identity creation 2011-04-07 07:15:44 +00:00
1c2fe9a481 typos 2011-04-06 19:37:47 +00:00
dc2d8397ab ug_fr.html saved as manual_fr.odt 2011-04-06 13:20:04 +00:00
926a06e53c end (0.2.6) 2011-04-06 13:03:08 +00:00
7de621dc82 end? 2011-04-06 08:55:16 +00:00
35bf2b930a still wip 2011-04-06 07:24:31 +00:00
307e4e8863 fix Guide 2011-04-05 23:33:57 +00:00
04569977d5 still wip 2011-04-05 18:40:39 +00:00
b11bd7b53b small fixes 2011-04-05 18:16:59 +00:00
4f42050cf2 wip 2011-04-05 17:13:49 +00:00
4b95f34430 wip 2011-04-05 12:22:12 +00:00
e10933cc0e typos 2011-04-05 11:03:54 +00:00
db4c3f23cd wip 2011-04-05 11:03:10 +00:00
3ff443fcc8 upd 2011-04-05 10:28:29 +00:00
3c302441ea upd 2011-04-05 08:32:19 +00:00
250f12a69f upd 2011-04-05 07:50:20 +00:00
cc15bff91b add 2011-04-05 07:24:31 +00:00
9b8828109c testers 2011-04-03 21:43:58 +00:00
72e033e2ef merge of '8bc35fe3851864fad1642e80e73229d6c14d1ba7'
and 'e2e8fcdb307d607cd9d22db33030468d94ec1883'
2011-04-03 20:17:22 +00:00
08f96661fa endend 2011-04-03 19:10:25 +00:00
7bfffe413c Update to BouncyCastle 1.46 which fixes a bug in ECDSA 2011-04-03 06:51:59 +00:00
1425a641ae merge of '4fc6f4c8c7963990ab7f079b86d96b7b8aeb761d'
and 'fd7b2809cdc6fa75aacb97c2ed09a2df4101f41f'
2011-04-03 04:41:16 +00:00
8ed88d1331 ECDH_ECDSA.java:
* Remove exceptions that are declared but never thrown
 * Remove safeEncrypt() TODO, not worth breaking compatibility
2011-04-03 04:40:05 +00:00
a99c08d0e7 typos 2011-04-03 02:17:32 +00:00
c1191d803c translation tweaks es, pt 2011-04-03 00:30:14 +00:00
844f1d3741 translation tweaks es & pt 2011-04-03 00:20:57 +00:00
612753b39e Fix: message not showing when an identity is deleted 2011-04-03 00:11:41 +00:00
cecccddc2e translation tweak es 2011-04-03 00:08:56 +00:00
17073ed8cd translation tweak es 2011-04-03 00:01:19 +00:00
efe14faae4 Move "delete identity" link to editIdentity.jsp and replace the trash can icon with a "delete" button to make it harder to accidentally delete an identity 2011-04-02 23:14:22 +00:00
08aa68d257 Suppress echoing of .bat commands during JRE installation, show a wait message instead 2011-04-02 23:03:12 +00:00
2b9b19030f Undo rev. fcd151e70c681bd38f2677cf4e9be334c7ca4cbe which broke win32installer 2011-04-02 22:27:39 +00:00
87b70ff7bc merge of 'e1744d66f44b6c2f5d1b7d6c2d337c5f2db49687'
and 'fcd151e70c681bd38f2677cf4e9be334c7ca4cbe'
2011-04-02 17:16:21 +00:00
6776b87040 update Swedish translation; thx to hottuna and jaywalk 2011-04-02 12:39:44 +00:00
210ad7f767 Dutch translation updated 2011-04-02 11:34:48 +00:00
3960e41534 Transifex config 2011-04-02 11:26:06 +00:00
32fc56754a Add support for Transifex, fixing po errors. 2011-04-02 11:24:30 +00:00
082f553be4 Create i2pbote.jar in ant_build/lib instead of WebContent/WEB-INF/lib 2011-04-02 02:44:20 +00:00
bcb31dca0a merge of 'a887eef6899db3400a21fb2319e61208d91cbb29'
and 'b156a7cf0018eebcf3ca07c4273a7918ef260ce7'
2011-04-01 19:55:53 +00:00
a508fa1a0f update translations from web-based translations for sv and zh 2011-04-01 19:55:22 +00:00
dbf769a8cb end2 2011-04-01 14:30:28 +00:00
f3825f3890 end 2011-04-01 12:48:58 +00:00
4125ada488 typo 2011-03-31 19:28:24 +00:00
6a425d1880 merge of 'a11d73c48aa593549c58e0caf94c3effd46ad971'
and 'a7d2d9270b5d91a90992345375b12bd392697635'
2011-03-31 01:28:44 +00:00
173c89306f go on and go2bed 2011-03-31 01:23:06 +00:00
8c6e3bedfb go on 2011-03-31 00:47:01 +00:00
db9a13d58d go on. Please user have a look...I PM my 2 questions 2011-03-30 23:38:51 +00:00
fa37238e00 update translation es and pt 2011-03-30 21:07:40 +00:00
2f10c2bb29 update FAQ and Manual 2011-03-30 20:16:16 +00:00
437686b1e6 redo 2011-03-30 19:41:45 +00:00
53508009b9 upd 2011-03-30 17:01:08 +00:00
49ff872b88 go on FAQ_fr 2011-03-30 11:24:38 +00:00
115688de03 begin 2011-03-30 09:17:51 +00:00
346d0cd822 Fix remaining file deletion issues on Windows (see rev. 10d16759d75f1ee7ddcbe3bbf16a826d21345619) 2011-03-28 07:56:28 +00:00
e51c29565e Don't report an error if there is no metadata file 2011-03-28 07:43:49 +00:00
15f209844d Internationalize string 2011-03-28 07:38:11 +00:00
86a50c7986 Update win32installer to work with latest router install.xml, add flexi-gmss-1.7p1.jar 2011-03-27 06:50:46 +00:00
5d44198fef Update German translation 2011-03-27 06:49:38 +00:00
3ab31b8b16 Don't log stack trace when plugin.config not found 2011-03-27 06:48:53 +00:00
4bbab19d14 If no good relay peers are known, query for peers more often 2011-03-25 00:34:24 +00:00
d532c6117d Replace NtruSign with GMSS which uses smaller public keys and inspires more confidence security-wise 2011-03-24 04:38:21 +00:00
63dc3ea208 Fix: Identities and address book still available after the Clear Password icon is clicked 2011-03-16 22:10:00 +00:00
82cb40e9bf Check for null values from I2PSession.receiveMessage() 2011-03-16 19:11:27 +00:00
ec0b072fca Refactor / cleanup:
* Reduce visibility of classes and methods
* Remove unused methods and commented-out code
*
2011-03-16 19:09:57 +00:00
4718424d11 Fix: "Password cleared" message not showing 2011-03-16 17:05:26 +00:00
4300ce67e8 Fix NPE in clearPasswordCache.jsp when password is not in cache 2011-03-16 15:33:11 +00:00
c58a8bb562 Add null checks 2011-03-16 04:30:56 +00:00
55859d669b Remove unnecessary instance variables 2011-03-16 03:51:11 +00:00
e11e80832a Update German translation 2011-03-16 01:31:47 +00:00
e5f7e1e78f Show an icon when the password is in the cache, and uncache the password when the icon is clicked on 2011-03-16 01:25:02 +00:00
6893f5b487 Remove unused constructor UniqueId(InputStream) 2011-03-14 06:20:10 +00:00
d56ccaf436 If Seedless not available, continue trying 2011-03-14 06:18:26 +00:00
1110bbe386 add credits 2011-03-12 03:36:16 +00:00
65290098e6 Add an option to show the full email identity (public + private keys) 2011-03-08 05:33:53 +00:00
5fd314753b 2011-03-08 05:20:56 +00:00
8dd98f2733 Update russian translation 2011-03-07 19:34:05 +00:00
d33882848f Include encrypt.bat and decrypt.bat in the plugin install (I missed this in rev. 01eb620588f85f0d86184be1fb349a43a83fbcfb) 2011-03-06 22:56:56 +00:00
0e174f870c 2011-03-06 22:08:02 +00:00
6954e5c7a8 Change /passive option to /s 2011-03-06 22:04:39 +00:00
8de6a0ab07 Update JRE download link to 6u24 2011-03-06 22:04:00 +00:00
d7cf897072 Add launch scripts for the Encrypt and Decrypt tools 2011-03-06 21:05:09 +00:00
596d19d212 * Add command line programs Encrypt.java and Decrypt.java
* EncryptedInputStream: throw a PasswordException if AESEngine.safeDecrypt() returns null
2011-03-05 23:21:22 +00:00
4e44fc204e merge of '0c43ec8566a97f8ad57f17575db898b259932435'
and '662195feb3df61d6b5a84dea177727d94cedb8b6'
2011-03-03 21:23:00 +00:00
277a535be4 l8tion 2011-03-03 18:13:39 +00:00
1dbd6ebc4c Add Override annotations 2011-03-03 03:46:25 +00:00
bcf869586f Update comments 2011-03-03 03:41:33 +00:00
6b5bc59775 Update techdoc.txt 2011-03-03 03:38:55 +00:00
17feb49b0a Fix: Cached scrypt params and salt not correctly reused 2011-03-02 01:51:21 +00:00
9625566913 update zh translation
adapt gettext script to work under windows:
set $LG2 to update/build ONLY with the language you want.
2011-02-28 06:13:51 +00:00
b2a3308c2e Ignore blank lines 2011-02-28 05:30:55 +00:00
89e5a195f8 Remove extraneous character from destination key 2011-02-28 05:30:41 +00:00
e4c130f51f merge of 'c2a5904fdb10cc3d4cbfa195c557c06cf2f65b5e'
and 'e32e7d661f1c0e4ba49afe94f9b2d89acd40e0d5'
2011-02-28 04:59:29 +00:00
899c0f6796 add credits 2011-02-28 02:11:18 +00:00
fc1287a016 add 安全,分布式电子邮件 and بريد الكتروني الآمن و الغير متمركز 2011-02-28 01:49:40 +00:00
fd3ca21a0c 2011-02-27 23:58:52 +00:00
789f8ddc3f If the forwardUrl parameter is not present in an ib:requirePassword tag, go back to the originating page after password entry 2011-02-27 19:51:50 +00:00
bda40109ed Don't show the "known" and "signature" columns in the sent folder 2011-02-27 17:09:42 +00:00
8c44d3890d Replace PBKDF2 with scrypt 2011-02-27 06:15:53 +00:00
0e2b89dff1 Fix: Duplicate identity created if password was not entered beforehand 2011-02-27 06:07:46 +00:00
7c46127738 fix and update .po's, update de,es,pt,fr 2011-02-27 04:40:42 +00:00
d33b8da111 Fix: PasswordException never thrown when saving/deleting an identity 2011-02-19 20:56:02 +00:00
5d6c30f0bb merge of '4e3d88847518753370c65b8240f9f8a623bb3834'
and '9a74d7db99343b21d83e1fb4d89977feb4b541d2'
2011-02-19 19:37:05 +00:00
7008c26545 Show a wait page if an email identity will take long to generate 2011-02-19 19:36:25 +00:00
b8597014dd Fix anonymous sending of emails 2011-02-17 03:55:16 +00:00
0457bd307f Refactor:
* Rename package i2p.bote.io to i2p.bote.fileencryption
 * Move DHT packets to the DHT package
 * Move relay packets to their own package
 * Move ExpirationThread to i2p.bote.service
 * Rename RelayPacketTest to RelayRequestTest
2011-02-17 02:23:10 +00:00
eb446f8703 Add Arabic translation of the plugin 2011-02-16 19:25:19 +00:00
9df307365c Show attachment sizes 2011-02-14 03:12:05 +00:00
fcd4fb63ed New CryptoImplementation: NTRUEncrypt1087_NTRUSign349 2011-02-14 00:11:37 +00:00
27713fb7b6 Remove unused method 2011-02-13 19:23:58 +00:00
0db3ce5026 Make the default status translatable 2011-02-13 19:14:54 +00:00
0f04c2c51a Display an error if the attach button is clicked and no file is selected 2011-02-13 16:35:11 +00:00
206be17615 Show the first few characters of the email destination in the "From:" drop-down 2011-02-13 06:08:26 +00:00
2511c5b851 2011-02-13 04:34:23 +00:00
4db98b2c2b Fix RelayPacketTest 2011-02-13 04:31:05 +00:00
8d2d360180 Fix email deletion on Windows 2011-02-13 03:50:19 +00:00
7d7b509eb9 initial translation for chinese 2011-02-10 16:02:26 +00:00
124ca1ba62 Inform user when an update is available 2011-02-05 23:24:52 +00:00
e9cd4a9f6c * Send big packets as a stream
* Don't rely on InputStream.available() in Util.readBytes()
2011-02-05 16:10:41 +00:00
c627299a35 Fix: All input after first flush() is dropped 2011-02-05 15:53:56 +00:00
e0f183efa8 Comments 2011-02-02 19:30:09 +00:00
89395885ba Fix a NPE 2011-02-01 05:41:53 +00:00
a77309ab36 Change constant scope to private 2011-01-31 07:33:03 +00:00
efd04f322c Remove unused method getMaxFragmentSize 2011-01-31 07:09:42 +00:00
a12f90d432 merge of 'a0f3953b8929001edc2b9d59c3bf314040fc4c7b'
and 'fd9b359f0a1eb07fcc4c0c8ec5abe95a0bc073dd'
2011-01-11 19:29:29 +00:00
5919da4930 merge of '62dcd55b7726aedd5624b076d838cbd11787695a'
and 'cb7ee59eaf0eb93c0dcb91a8226591ca01e51342'
2011-01-11 19:22:47 +00:00
2d291f1ce2 added credits 2011-01-02 05:45:53 +00:00
e299d2e83d Quick fixes to Swedish translation. 2011-01-01 21:54:22 +00:00
76551e3d3f Added Swedish translation. 2011-01-01 21:47:34 +00:00
2f134bfbe4 add translation to Norwegian 2010-12-31 07:01:23 +00:00
c03c481b94 merge of '9a8a529946e94b430d154a127ca08010f15d66d5'
and 'cc0e869985b06631a79b60bf1060d86cc0e88afd'
2010-12-31 01:09:35 +00:00
7d1fed76da mods to pt 2010-12-29 15:51:57 +00:00
7148172e9c New unit test: PasswordCacheTest 2010-12-22 06:01:17 +00:00
5c102da3a0 * Reset expiration when a password is set
* Clear derived key when password is cleared
2010-12-22 05:49:18 +00:00
f3a1c618e5 merge of '243e68f714882dc1fe2f79958b31ac862053ba9f'
and 'f9ab92a43553dde223bb24940d32f19a60022b34'
2010-12-20 05:38:39 +00:00
5c221f31d9 Add missing copyright notices 2010-12-20 00:42:37 +00:00
7ac42c0e4e translation updates 2010-12-19 23:32:17 +00:00
4da3ee6aed New Ant task: win32installer 2010-12-19 23:00:50 +00:00
3aecc56933 Restore plugin.config after mismerge 2010-12-19 22:27:47 +00:00
5ea124b7d2 Jetty fix 2010-12-19 22:22:17 +00:00
7fb33a0d35 Fix return to folder.jsp after password entry 2010-12-19 22:21:04 +00:00
6d2878db5c * Add jMock .jars to JUnit classpath
* Offer to download JUnit and jMock if they're not found
2010-12-18 03:46:00 +00:00
9bee5057f1 merge of '0b216d7818b91cf69fdfb88048dfcbfbfce02833'
and 'de95731855e9e7f8a81b42642fd566ac6ee76c08'
2010-12-17 19:54:42 +00:00
bdce2ecad5 merge of '561ea16dc8eb82dc348dca962f30e579c8ed3d63'
and 'b1b87fc5a28b9aad267b6c3aeddc1c23377c5ab6'
2010-12-17 19:54:42 +00:00
d3ee66ff60 * Remove unused method toBase32
* Replace instanceof with getClass() in equals()
2010-12-17 03:43:44 +00:00
647efead3b Fix unit tests; jMock now required to run them 2010-12-17 03:41:57 +00:00
09f5d107c9 translation update de 2010-12-17 02:01:47 +00:00
d33bc11a31 Contributors update 2010-12-16 01:27:09 +00:00
2d6066ad58 Remove serialVersionUID from non-serializable class 2010-12-16 00:40:21 +00:00
0bdd216ef0 Remove an obsolete comment 2010-12-16 00:39:08 +00:00
75336e899d Fix an import 2010-12-16 00:28:22 +00:00
3870ca5335 plugin.config nl link translation 2010-12-13 18:46:26 +00:00
5aaae853af plugin.config translation 2010-12-13 16:39:01 +00:00
a64cdca34e merge of '0039d7fd449038234648a57ff05906383d0ce75d'
and '92c193a4f6709ce5205a43617bcd8a144ae598ca'
2010-12-13 11:52:48 +00:00
99f64adc05 merge of 'b85935006af43967ba763bb53ab75c6f0278c539'
and 'eff65f71bc633578ad72cbcb2d5301f1d1766c30'
2010-12-13 11:52:47 +00:00
05c24ab545 explicit merge of '93973b70de2d4a41180a7d2f201b684e5ea6d445'
and '451bb8a70baa411c0d401c329b5b60b7c14d09b3'
2010-12-13 11:44:04 +00:00
897d43ff51 Fix a typo 2010-12-13 11:42:56 +00:00
46b9e3a039 explicit merge of '7ca94be8ef990b10920f144f441a51d239d33c48'
and 'ef38ab9835eb8fec5dbd981f2951d12d5d0d5d0d'
2010-12-13 08:06:21 +00:00
018209bed5 Dutch translation by KwukDuck 2010-12-13 08:03:04 +00:00
fe14f97dfd explicit merge of '5c7fd9edde41ff7c26cd2fb28e1a46b96c1f8538'
and '9245efd7b168684236148a7102535f0a009e2c4f'
2010-12-13 01:39:10 +00:00
f2b1d3cc1c Portuguese translation 2010-12-13 01:13:25 +00:00
0600b6e595 merge of '08eff6bfa6a2b340f314f1de096c771337eb3a3b'
and '1dbb023c5b2674f49708ad83060aed35cdb3a7a1'
2010-12-12 20:10:11 +00:00
aab0b139d3 * Don't encrypt meta files
* Don't attempt to convert a non-existing directory
2010-12-12 20:06:18 +00:00
872c386929 Focus the "old password" field 2010-12-12 20:03:19 +00:00
23f57da28e * Cache file encryption key, reuse salt for performance
* EncryptedOutputStream: Cache key, not password; keep a copy of the key until close() is called so the key is not lost when the original is erased by PasswordCache
* Prevent password expiration during password change (which can take some time)
2010-12-12 20:01:09 +00:00
7fbd29b75d File encryption 2010-12-10 20:10:49 +00:00
b8fd5b5c1d Add TO-DO note for HH to do an important fix. 2010-12-07 00:36:20 +00:00
c81f2cb46c merge of '43b11afe7287401107616e5cff0c1d4773c0e1d3'
and '92344efb7a96786e8ef1e256f754ea8e9666c765'
2010-12-07 00:23:23 +00:00
6c859388c5 changes to Spanish translation 2010-12-04 10:56:38 +00:00
087b8ddc42 changes to the manual 2010-12-04 10:52:54 +00:00
0eba059a8c merge of '28760fe7e19dd265a2ede35a51f73e5c07b1a2aa'
and 'db84face38fdd977157aaf02c444b894c7049d84'
2010-11-28 06:45:44 +00:00
7136ab215c Refactor out Deprecated destFromName calls, fixup nb files 2010-11-28 06:45:22 +00:00
3f867d8041 plugin.config: add russian translation 2010-11-20 19:29:23 +00:00
a8bb3652fe added string to plugin.config and a minor correction 2010-11-16 22:34:07 +00:00
1672d95ae6 merge of '2e376f13320c297b93e734b3f39e113752891578'
and 'f2d47e61537a5a2f2077eae200af07ef12114b2e'
2010-11-11 21:01:03 +00:00
c3c832649c update manual 2010-11-11 21:00:10 +00:00
c115278016 merge of '6b7cd0a43c55b9a2ae53778a25e6940cfe4fba1f'
and '9245efd7b168684236148a7102535f0a009e2c4f'
2010-11-11 17:32:29 +00:00
a65653b30b Spanish translation for Bote; fixes sent in by Redzara to French translation 2010-11-11 17:31:18 +00:00
d92f66720a merge of '37af2db09b85c46e860280cdb70d92bf07810401'
and 'df5454597876a5df6f61e5adb97d30fb57b63d48'
2010-11-04 06:57:33 +00:00
0954398245 minor corrections 2010-11-03 18:10:46 +00:00
50da5ccc80 Fix a NPE 2010-11-03 06:09:00 +00:00
2b71438c82 French translation by albat and mixxy 2010-10-29 20:19:23 +00:00
1b5356e845 Fix a comment 2010-10-26 20:24:52 +00:00
9c951dc2f1 Fix a NPE 2010-10-26 18:27:11 +00:00
117c35cc5a * Sign email before saving to sent folder
* Don't hide signature column in folder.jsp. It was a kludge, signing before saving is the right way to do it.
2010-10-14 19:02:11 +00:00
dbd46e2b4f Comments 2010-10-03 22:06:20 +00:00
a2c55a78fc 0.2.5 2010-10-03 22:05:54 +00:00
c423b7331e Implement KBucketTest 2010-10-03 21:59:48 +00:00
a031f4da28 Decrease text field width on the "new email" page 2010-10-03 19:19:15 +00:00
7573eed929 Minor things 2010-10-03 19:13:53 +00:00
1e78638692 Refresh folder list when new mail arrives so the inbox message count is updated 2010-10-03 08:07:48 +00:00
19ea512d79 Fix: Two new recipient fields appear when the "plus" button is clicked after entering text in the existing recipient field 2010-10-03 07:25:07 +00:00
b9155254d0 Use base64 for RelayPacketFolder filenames, which is what all other folders do 2010-10-03 00:19:14 +00:00
0ebe7e68c7 Update techdoc.txt 2010-10-02 22:50:02 +00:00
48340cb790 Update manual 2010-10-02 20:14:24 +00:00
02b803fda7 Fix duplicate "Unknown" in the date/time column in folder.jsp 2010-10-02 19:49:41 +00:00
4d306d5f85 Translation update 2010-10-02 19:47:05 +00:00
388843a439 Implement reading from ByteBuffer and writing to OutputStream 2010-10-02 18:34:56 +00:00
e03521821a Make the outbox check interval a config setting 2010-10-02 06:23:40 +00:00
ac77853485 Fix display of date/time in showEmail.jsp 2010-10-02 04:57:30 +00:00
ce664ebf8b * Use a non-deprecated variant of ElGamalAESEngine.encrypt()
* Do ElGamal/AES in the new methods Util.encrypt() and Util.decrypt()
* Use a min padding size of zero when calling ElGamalAESEngine.encrypt()
2010-10-02 03:51:59 +00:00
93041cb1d4 Simplifications:
* Get rid of RelayDataPackets, put everything in RelayRequests
 * Put a packet ID in all Communication Packets (most of them had it already)
 * Combine MalformedCommunicationPacketException and MalformedDataPacketException into MalformedPacketException
2010-10-02 03:00:53 +00:00
d68fd5429e Log deletion of relay packets 2010-09-30 05:50:03 +00:00
a7b4b9acc6 Pad relay packets 2010-09-30 05:11:42 +00:00
20841b7a4a Remove the unused and confusing Email Address field from the Edit Identity page 2010-09-27 05:54:08 +00:00
e7f186b7f3 * Write to metadata the time an email is created
* Add a "create time" column to the outbox
* Make "create time" the default sort column in the outbox
2010-09-27 05:37:27 +00:00
2a2adc6645 Change a few more logging statements I missed in rev. 6c3cec9cf9ac70dadd403a2f2bc10a5be18f1fbd to print peers in base32 2010-09-26 19:52:11 +00:00
9989940693 * Fix NegativeArraySizeException when a two-byte length field in a packet contains a value greater than Short.MAX_VALUE
* Keep packet size below I2PBotePacket.MAX_DATAGRAM_SIZE, take into account relay packet overhead
2010-09-26 18:51:49 +00:00
f71f0e1470 Show a message after saving settings 2010-09-25 07:32:00 +00:00
3c7bd9acf6 Simplify folderLink.tag by using folder.jsp for the outbox; see rev. 7790618d3eba2f460983f87db7ddc1fe1b94053c 2010-09-25 07:11:58 +00:00
1439358221 Take the user back to outbox.jsp after an outbox email is deleted 2010-09-25 06:48:31 +00:00
0895c3c976 Sort by name and destination, not just name, so different destinations/identities can have the same name 2010-09-25 06:45:53 +00:00
8a6c6c2627 Fix non-ASCII characters in the New Identity / Edit Identity form by changing it to POST (should be POST anyway) 2010-09-24 06:50:08 +00:00
c3883bb069 Make the "unknown" column a "known" column, show a check mark when an email dest is known 2010-09-23 05:52:06 +00:00
4e5104ff52 Show status for outbox emails 2010-09-23 05:29:22 +00:00
ecf093119a Reduce default delay to 5..40 minutes per hop 2010-09-23 05:04:26 +00:00
cbd7a8dffb Don't show signature column in "sent" folder 2010-09-23 04:57:37 +00:00
08cd5406f4 Sort address book and identities by name 2010-09-23 04:06:10 +00:00
af2f63565f Update technical documentation 2010-09-22 17:38:34 +00:00
1b481dcf24 Add user manual by mixxy, delete userdoc.txt 2010-09-22 17:38:15 +00:00
6ee5005944 Align Sent Time 2010-09-22 07:18:27 +00:00
b4b94ad38a Log peer destinations in base32 2010-09-22 06:21:57 +00:00
aa4551fc14 Show local destination and peer destinations in base32 2010-09-20 05:18:46 +00:00
9d77f57123 Add a note about attachment sizes 2010-09-20 03:31:11 +00:00
3115faedb2 Fix comments, remove unnecessary braces 2010-09-20 02:37:46 +00:00
4e7b0c115e Keep trying to send an email if there is an error 2010-09-20 02:27:33 +00:00
c28c99060b Replication:
* Replicate onto the closest nodes to the packet's DHT key instead of the local destination hash
 * When a packet has been re-stored since the last replication, assume it has been stored on all closest nodes and don't replicate it to any peer
 * When a delete request is received for a replicated packet, stop replicating that packet and replicate the delete request instead
2010-09-19 08:31:36 +00:00
d3a072c54b * Don't replicate packets that were recently replicated
* Log number of replicated packets
2010-09-16 05:06:44 +00:00
174a94f492 Fix: ReplicateThread.packetStored() not called when storing in a DeletionAwareDhtFolder 2010-09-16 05:02:12 +00:00
9f6806a738 Verify hashes of received email packets against the DHT key 2010-09-15 06:50:52 +00:00
809dc90bc8 Fix non-ascii characters (switching from Commons FileUpload to JavaMail-based multipart parsing broke them) 2010-09-09 06:04:40 +00:00
c14faf5fc4 Lower the LZMA dictionary size, 2^24 causes heap space errors when running as a .war in the router 2010-09-07 07:40:03 +00:00
d9034fee31 Change a few log levels 2010-09-06 22:56:27 +00:00
c30180fcbf Remove newlines from the end of files (just a personal preference) 2010-09-06 22:46:58 +00:00
110a4bd484 Fix LZMA dictionary size 2010-09-06 19:46:47 +00:00
06106c0b57 Use isProtocolVersionOk() for version checking, see rev. c848aa079ff8d62fac18c1c7782d48befc13b98d 2010-09-06 19:44:32 +00:00
a006e3b688 Use different texts for the two test emails 2010-09-06 07:53:18 +00:00
d37911e5b1 Add a new field containing a Return Chain to the Relay Request format. Only empty return chains are supported for now. 2010-09-06 07:10:35 +00:00
b128693296 * Talk to peers that use protocol version 4 or higher
* Do the version check in one place (I2PBotePacket.isProtocolVersionOk()), not two
2010-09-06 06:32:37 +00:00
85d958a0cc Add Russian translation by suhr 2010-09-05 19:59:11 +00:00
ca1d5e6d8c Update RelayPacketTest to use the new file format introduced in revision bfde50fb7c22410d7859a6914990798c68127bc5 2010-08-25 02:28:48 +00:00
245e50aaa9 Count emails with no metadata as new to match the behavior of the EmailMetaData class 2010-08-22 20:49:33 +00:00
4d0d5d245c Minor things 2010-08-18 20:58:51 +00:00
f1d8554ab3 Put a space between name and angle bracket 2010-08-18 20:56:42 +00:00
c76a33fa69 Fix: Extra blank char after the name when clicking "add to address book" in an email 2010-08-18 20:48:21 +00:00
5a3e49c4dd Replace Thread.sleep() with awaitShutdownRequest() 2010-08-18 04:33:57 +00:00
2ad8d2ac8e Fix: Folder view only shows the recipient if it is a local identity 2010-08-18 04:32:38 +00:00
d0c4fd7ca8 * Only use the last 20 peer list requests / responses to calculate reachability
* Increase RelayPeerManager.UPDATE_INTERVAL to 60 minutes
2010-08-18 03:05:28 +00:00
6b38f0d520 Comments and other minor things 2010-08-17 17:52:33 +00:00
0105bb360a Cleanup 2010-08-17 17:47:54 +00:00
35d22a9638 * Fix: EmailPacketFolder.storeAndCreateDeleteRequest() does not correctly handle multiple deletion records in the same DEL_ file
* Fix: Delete verification hash contains the same data as the delete auth key. Apply SHA256 so the delete verification hash actually is the hash of the auth key.
2010-08-17 17:41:25 +00:00
2cce8c9a7a Remove option to set the UI language, always use the router console setting. The language option was changing the router console language, so it was kind of pointless. 2010-08-16 19:44:13 +00:00
a525c3d920 * Rewrite of MultipartFilter: Use javax.mail.internet.MimeMultipart for multipart parsing, get rid of commons-io and commons-fileupload.
* Move attachment adding code in SendEmailTag.java to Email.java
* Encode attachments in 8bit rather than base64 because base64 negates compression
2010-08-16 08:41:37 +00:00
8f090fd3e3 Clean up test folder when finished 2010-08-12 06:10:49 +00:00
c36b24bad6 * Comments, Formatting
* Move ProxyRequest.java into the seedless package because it is only used there
2010-08-12 06:08:31 +00:00
9b8fd8e6fc * Store metadata files along with email files
* Move the new/old flag to metadata (no more N_ and O_ filename prefixes)
* Write the received time to metadata when an email is received
* Use the received time as the sort criteria when an email has no sent time
2010-08-12 05:46:56 +00:00
078f0952ef Fix an NPE when trying to connect before Seedless is initialized 2010-08-01 06:21:11 +00:00
834331903e * Don't set the Date header if it is already there
* Slight code simplification
2010-08-01 06:18:55 +00:00
b112ad89f3 * Remove KademliaDHTTest (would have been to complicated to implement)
* Add KBucketTest (does nothing yet)
2010-08-01 01:26:12 +00:00
73f8295c9c A few little things 2010-08-01 01:23:25 +00:00
c2da679543 * Make getFilenames() protected
* Remove individualPackets() override in DeletionAwareDhtFolder which does nothing
2010-08-01 01:21:27 +00:00
ac46459a0d Send a specific delay time in relay packets, not a min/max range 2010-08-01 01:16:37 +00:00
c663415ebc Add includeSendTime parameter so Email.updateHeaders() doesn't have to call I2PBote.getInstance(). It also has the small benefit of the "include send time" setting being determined at the time the user clicks Send rather than the time the email is sent from the outbox (which doesn't always happen instantly, for example when there is no network connection yet or several emails are queued in the outbox). 2010-08-01 01:08:38 +00:00
68f9360f40 Folder.iterator(): Don't return null if an invalid/unreadable file is encountered, skip it instead 2010-08-01 00:43:45 +00:00
e0436d1e43 * Do not exit background threads on unchecked exceptions
* I2PBote.java: put all background threads in a Collection like sponge suggested
* Do automatic and user-initiated email checking in one class (EmailChecker)
* New interface NetworkStatusSource, used by OutboxProcessor and EmailChecker
* Several other changes
2010-08-01 00:20:52 +00:00
a488294bb3 Convert tabs to spaces 2010-07-29 00:29:50 +00:00
175345dfe4 Shut down EmailPacketTask threads when the tasks are finished 2010-07-25 05:09:12 +00:00
78bb4f7366 Fix member field name in comment 2010-07-24 07:50:08 +00:00
29f485f4e2 Fix: Reachability of relay peers changes to 100% after startup 2010-07-24 07:47:58 +00:00
b794aaf0e0 Remove unused methods 2010-07-24 02:17:22 +00:00
465d509bcc * Autorefresh "inbox" and "sent" folders
* Autorefresh outbox if it is not empty
2010-07-19 05:39:51 +00:00
a4b25f0ae8 Stop refreshing when the CONNECTED or ERROR status is reached 2010-07-19 01:26:26 +00:00
7feba2f15e * Don't disable the "new email" button
* Refresh the frame until the Check Mail button becomes ungreyed
2010-07-19 01:22:20 +00:00
2e72fb7f36 Don't send email if no recipient, show error message instead 2010-07-18 05:48:17 +00:00
a225fa20b1 Fix NPE when sending before connected 2010-07-18 05:47:22 +00:00
f87fe26811 javadoc Ant target, javadoc formatting, documentation 2010-07-17 22:52:24 +00:00
d2b38a3f33 merge of '34a49b587bbb0c309e60a1df7a9a792654e48fd2'
and 'bf83f8762c1d6cfaa71871d32bdc9a3e089058c1'
2010-07-16 15:41:45 +00:00
ef8a143eb0 Ant build file:
* Use I2P libs from $I2P/lib instead of the mtn working directory
 * More help text
 * Check for presence of the I2P env variable
 * Add "src" to the "all" target
2010-07-16 07:07:48 +00:00
e33d186ed0 When writing a file or creating a directory, set permissions to readable / writable only by owner 2010-07-15 05:20:57 +00:00
0527c1f246 Fix counting of #lines read 2010-07-14 18:46:20 +00:00
7ee570e375 * Translate the string for the outbox status "Queued"
* Remove an unused method
2010-07-14 07:09:14 +00:00
c7182a27cf Remember the selected sender identity when leaving the "new email" page (via the address book button) and returning. 2010-07-14 07:05:57 +00:00
f972c9e2e6 Fix: MultipartFilter drops request parameters that are added via jsp:param in a jsp:forward 2010-07-13 07:52:44 +00:00
29df785ab6 merge of '9c9772d22e302d48a4c849c18c8ff3bcb3ff8404'
and 'e09e454db05d06c660ba0e8e2ff5021aa52ec787'
2010-07-12 06:14:06 +00:00
e3448c9b17 Added needed jar files to project.xml for NB 6.5 2010-07-12 04:29:16 +00:00
a3a8c77963 merge of '3e369acec144cfce4278be9456f6b81de0455791'
and 'dc2bcd3d2bdc8b5c7462377da7462d3f5108b3ee'
2010-07-12 03:48:39 +00:00
67dab8aaf7 merge of '3e369acec144cfce4278be9456f6b81de0455791'
and 'dc2bcd3d2bdc8b5c7462377da7462d3f5108b3ee'
2010-07-12 02:14:52 +00:00
7c2fc47933 Refactor of Seedless:
* Move the doSeedless* methods out of I2PBote.java into the Seedless* classes
 * Move the other Seedless stuff into SeedlessParameters.java
 * Don't inject peers into DHT, call a getter on SeedlessScrapePeers instead
 * Many little things
2010-07-11 23:17:33 +00:00
0a3a2bedf8 Add commons-fileupload-1.2.1.jar and commons-io-1.4.jar to build file 2010-07-11 22:56:17 +00:00
38ab0630af Change announce to 60 minutes to deal with new inter-server transfer specs.
Announces should be done once within 1/5th of it's TTL 72 minutes for bote.
2010-07-11 11:23:41 +00:00
d6be15ca4d * Fix for a bug in the multipart code that broke sending of emails on Jetty
* Ditto for non-ASCII chars not being encoded properly
2010-07-11 00:49:43 +00:00
77ec925d45 Post-merge cleanup. This revision diffs cleanly against 14ada4dcae2f555f4b5539305e32f96bafec675e (the last rev before Seedless). 2010-07-07 05:14:08 +00:00
2bd6eac5af merge of '15679f7110fe5f55407d2d04c63bc18e1d460113'
and '289456e0b294a38c3d0e75d69873d614170f9e58'
2010-07-04 07:42:56 +00:00
50673b6358 Comments 2010-07-04 07:28:05 +00:00
ee578e7b06 Use a SHA256 hash rather than random data for the base32 part of the filename to avoid creating a new copy if a packet is received more than once. 2010-07-04 07:26:14 +00:00
4025734680 * When a relay packet is received, acknowledge receipt to the sender
* Resend relay packets until acknowledgement is received
2010-07-04 07:22:14 +00:00
2bbba38007 * Attachment support
* Lower the priority of some background tasks
2010-07-03 21:59:14 +00:00
479b28f35f Fix: Corrupt padding error with ECDH when the input size is a multiple of 16 2010-06-30 18:36:06 +00:00
07a3340ffa Depend on Seedless _first_, then fallback to built-in, as per HH. 2010-06-28 21:10:51 +00:00
69f289b0d9 Fix annoying NPE 2010-06-25 14:57:59 +00:00
308330c8a0 Success!
We now turn off Seedless peer searches and scrapes when network is connected.
Announce to random ordered Seedless servers.
Thanks to ALL our testers, especially superuser and darrob. 
Thier feedback was very valuable!
2010-06-25 14:50:50 +00:00
aac785dda8 Specify PROTO_DATAGRAM, PORT_UNSPECIFIED, and PORT_UNSPECIFIED when sending.
The default uses PROTO_UNSPECIFIED, which isn't what we wanted.
2010-06-25 13:45:31 +00:00
6bd4cab290 Muxed depends on gzip == true? 2010-06-25 06:49:04 +00:00
816eb59c94 Compress emails before encrypting them 2010-06-25 02:36:15 +00:00
889a28063d * Add sponge as a contributor
* Replace HungryHobo email address with email destination
2010-06-25 02:18:43 +00:00
a783a0ddb8 Salt relay peers like bote does 2010-06-24 22:41:17 +00:00
617122dfe1 Fix NPE. 2010-06-24 20:19:31 +00:00
e471ddd57a Added injection to relay peers as well. This possibly isn't the correct intent. 2010-06-24 20:06:28 +00:00
1999eb2432 merge of '7561cf99368e4a244f4b67c350c0c92f7a1afc08'
and 'a74daea914c9a98d3953dbe4e2507b4ad032ae66'
2010-06-24 17:56:14 +00:00
a22eea0f66 - 2010-06-24 17:55:44 +00:00
62a1a85a8f zap comment 2010-06-24 09:41:21 +00:00
dev
00fbabded2 merge of '5ee8d2f16550b6e4181d9123c069dd8e800059de'
and 'f9c92102d1893e1d6e2597ce50dd617256a4aee3'
2010-06-24 07:46:04 +00:00
dev
53a052382f . 2010-06-24 07:26:58 +00:00
66b970d22b disapproval of revision '0c7dc27eead74ddbe0d89793014d5c1cf2e35b2f' 2010-06-24 06:58:57 +00:00
2b5ce8cf27 And now we hope it is set correctly.
disapproval of revision 'f9c92102d1893e1d6e2597ce50dd617256a4aee3'
2010-06-24 06:26:03 +00:00
408f63bdf9 Hopeful fix to the commits that mtn seems to have goofed up on.
There is nothing in this patch.
2010-06-24 06:24:25 +00:00
1ce75177e9 Change for log debugging. 2010-06-24 06:18:55 +00:00
0df770bc68 Temp fix. See comment KademliaDHT.java ~line 364 2010-06-24 06:12:06 +00:00
120de4af9e Fix stupid bug *embarrassment* 2010-06-24 03:03:50 +00:00
b557973e79 Should work with passworded routerconsole,
Don't know if the injector works right. Needs testing, etc.
That's the part HH needs to finish if it is incorrect.
2010-06-24 03:00:52 +00:00
6b833622cf Seedless:
Can scrape servers
  Can scrape peers
  Can announce
  Can't work with a passworded router console yet.
2010-06-22 18:13:51 +00:00
99baa56af3 Apply patch by zzz to abort the build if <exec> returns non-zero. 2010-06-20 05:20:21 +00:00
95ac52b7ea Increase expiration times in testCheckExpiration() to fix test failures reported by one user. 2010-06-20 05:16:58 +00:00
e4696b2bad Add Netbeans 6.5 Project file 2010-06-18 13:17:50 +00:00
00226dfe4d Remove some unnecessary files from the src target 2010-06-14 08:09:20 +00:00
c0ad846a3d 2010-06-14 07:07:13 +00:00
9a0226674f 0.2.4 2010-06-14 07:06:28 +00:00
5d5a4bc50f Fix "Gateway disabled" error with non-gateway emails 2010-06-14 07:06:06 +00:00
2976f10d92 Don't unnecessarily send a IndexPacketDeleteRequest if there is nothing to delete 2010-06-14 07:00:36 +00:00
9ac0fd128d * Set the X-I2PBote-Sig-Valid header when an email is received
* Use the X-I2PBote-Sig-Valid header flag to determine if an email has a valid signature; only verify the signature if no X-I2PBote-Sig-Valid header
2010-06-14 06:58:45 +00:00
4062cfa97c Restore contents of built-in-peers.txt which I accidentally changed when I moved it to a different package 2010-06-13 07:35:09 +00:00
ff0d6e2ff2 * Change console link name
* Use a b32 update URL
2010-06-13 07:27:34 +00:00
9461bc1edb * Don't use protocol v2 peers as relay peers, put them on the ban list
* Fix: BucketManager doesn't ban unknown peers if they use the wrong protocol
2010-06-13 07:25:15 +00:00
f06e936e10 Update translations and fix a few strings 2010-06-13 07:19:30 +00:00
f90f6059c3 Minor things 2010-06-12 07:40:21 +00:00
f529cd3db0 Gateway (in/outproxy) support 2010-06-12 07:39:37 +00:00
a9a0a5202e Fix sending and displaying of anonymous emails 2010-06-11 20:11:19 +00:00
67a871814e Start peers out at 0 reachability 2010-06-11 20:01:57 +00:00
24d2f2fd9a Suppress two warnings in the ant "compile" target 2010-06-11 07:15:41 +00:00
c3a3a19751 * Include bcprov-ecc-jdk16-145.jar
* Fix path to built-in-peers.txt which has moved
* Don't add jstl.jar and standard jar to the war
2010-06-11 07:12:59 +00:00
37643b15b7 Use 'outbox', not 'outbox/local' for the outbox dir 2010-06-11 02:42:53 +00:00
defbb68445 Make packet type codes compatible to the older packet version so no exceptions are thrown when v0.2.3 peers and v0.2.4 peers try to talk to each other 2010-06-11 02:28:07 +00:00
744c1c1b61 Fix: Existing IndexPackets get lost when adding a new one to the IndexPacketFolder 2010-06-11 02:18:03 +00:00
88e0f5c081 More tests 2010-06-10 18:30:24 +00:00
01eb01dc24 Minor 2010-06-10 08:32:40 +00:00
03dac6db84 Shut down replicateThread when Kademlia shuts down 2010-06-10 08:25:45 +00:00
535dd76c8e * Start / stop the RelayPeerManager service on app startup / shutdown
* Move RelayPeerManager to i2p.bote.service
* Keep reachability statistics on relay peers
2010-06-10 08:24:53 +00:00
e211b1040c Cleanup 2010-06-09 23:22:08 +00:00
8533790aa3 Logging tweak 2010-06-09 20:37:32 +00:00
09a271ba12 Fix NPE when sending email before DHT is done bootstrapping 2010-06-09 20:21:24 +00:00
09337fa259 Minor things 2010-06-09 20:19:40 +00:00
97a93db976 * Fix: Relaying does not work with more than one relay peer
* Add remaining relay-related files
2010-06-09 20:13:49 +00:00
9c9b4b5404 Fix: sentSignal and sentTime were not initialized in one of the CommunicationPacket constructors 2010-06-09 03:11:17 +00:00
9212075b08 Call removePacketListener() even if an exception occurs 2010-06-09 03:08:38 +00:00
0c95890d77 Minor things 2010-06-09 02:48:02 +00:00
135190058a Implement sending of emails through relays 2010-06-09 02:12:13 +00:00
ff87fa0a78 Remove an unused class 2010-06-09 01:34:42 +00:00
ed1b2bcdd2 Start OutboxProcessor and ReplicateThread when Kademlia is finished bootstrapping 2010-06-05 04:51:44 +00:00
6b796c1715 Remove unused constant STALE_THRESHOLD 2010-06-04 06:29:16 +00:00
cdfbd98612 Cleanup 2010-06-04 06:25:58 +00:00
8e471d84a1 Add a paragraph about encryption algorithms to noIdentities.jsp 2010-06-04 06:25:19 +00:00
db72661f57 Implement ECC-256 2010-06-04 06:24:10 +00:00
5524b4b563 Periodically replicate email packets and index packet entries onto other nodes 2010-06-04 06:15:17 +00:00
6488aeac56 Preliminary work for packet replication: Keep a record of (locally) deleted email packets, like it is already the case for index packet entries. Use one common format (the new packet type DeletionInfoPacket) for both packet types so Delete Authorization Keys don't unnecessarily take up space in Index Packets. 2010-05-29 10:05:11 +00:00
91e6daa303 Some minor stuff 2010-05-28 07:12:20 +00:00
f56c3ca1bf Read the Delete Authorization Key from an UnencryptedEmailPacket, not from a received Index Packet (because it's always zeroes) 2010-05-28 07:10:29 +00:00
8c5b402edf * Zero out Delete Authorizations before sending a response to an Index Packet retrieve request
* When an Index Packet Delete Request is received, compare the hash of the Delete Authorization against the local Delete Verification Hash, not the local Delete Authorization (which is always zeros)
2010-05-28 07:04:35 +00:00
50eb754d19 Add license.txt 2010-05-27 03:04:11 +00:00
ae41a90478 Use the AES implementation that comes with I2P instead of a JCE provider because the Sun JRE makes it a PITA to enable "unlimited strength encryption" in an end user friendly manner. Fortunately, only AES is affected by the crypto restrictions.
Another problem with the Sun JRE is that it makes it near impossible to ship only a subset of the 1.7 meg BouncyCastle .jar. The .jar has to be signed with a key certified by Sun, which is a lengthy process that involves giving Sun a DNA sample and sacrificing an animal in front of their headquarters. So the ECC code now uses the BouncyCastle API instead of JCE.
2010-05-27 02:11:58 +00:00
892a795c68 Remove an unused method 2010-05-25 20:50:07 +00:00
d2634bd130 Fix a potential lockup 2010-05-25 20:49:35 +00:00
6a482911b3 Minor stuff 2010-05-25 20:46:18 +00:00
35f1320ef1 Delete email packets and index packet entries that are more than 100 days old 2010-05-25 20:42:48 +00:00
6a9729f898 * Add BouncyCastle ECC library (a subset of bcprov-jdk16-145.jar that only contains the ECDH/ECDSA provider)
* Add BouncyCastle license
2010-05-25 20:14:30 +00:00
a064857ff5 Minor things 2010-05-22 01:48:13 +00:00
3fab712da7 Add null checks 2010-05-22 01:45:47 +00:00
d4542b8449 Add some missing license headers 2010-05-22 01:32:05 +00:00
366fa10ce8 Several changes to the packet format:
* Add time stamps in preparation for packet expiration
 * Change a few packet type codes
 * Support addt'l crypto algorithms, implement elliptic curve encryption
 * Fix two design flaws in the packet format:
    1) anybody can delete index packet entries of others
    2) malicious storage nodes can delete all copies of an email packet before
       the recipient receives the packet
2010-05-22 00:35:46 +00:00
050a6dbbbc 0.2.3 2010-05-10 07:12:57 +00:00
8bb3313f38 Make the INFO log level more useful 2010-05-09 23:24:03 +00:00
e7575018bc Synchronize on packetListeners when iterating 2010-05-09 23:22:21 +00:00
863b58913a Name the "sent" folder something different than the "sent" time column because they translate to different German words. 2010-05-09 09:57:46 +00:00
6d581f37a9 editIdentity.jsp:
* Remove garbage characters at the top of the page
  * Fix the cancel button
2010-05-09 07:58:39 +00:00
8c152f27d8 Delete index packet entries (email packet keys) from the DHT even if all requests for the email packet come back negative. This deals with the problem of index packet entries not going away when the email packet does not exist. 2010-05-09 07:25:44 +00:00
07fdac2312 Some comments 2010-05-09 05:54:25 +00:00
e467e6232a Fix constant s-bucket refreshes when there are very few peers. 2010-05-09 05:46:05 +00:00
6e2a14af39 Override toString() for logging 2010-05-07 07:28:19 +00:00
f23db925ad Move the logStatus() call so it doesn't report alpha-1 parallel requests when there are alpha. 2010-05-07 07:24:52 +00:00
93bf69d0aa Increase alpha to 10 2010-05-07 06:54:34 +00:00
cd74c6aac2 Fix a NPE 2010-05-07 06:35:53 +00:00
240a8c12eb Fix a bug that hides some text if the "ellipsis" CSS class is used.
The fix is to get rid of ellipsis, which was a hack anyway, but it
means no more fade effect when text is cut off.
2010-05-07 06:34:57 +00:00
4888f0dd54 Javascript fix 2010-05-06 18:06:15 +00:00
7c5aa40d26 Increase Kademlia lookup timeout to 5 minutes 2010-05-06 08:16:15 +00:00
d9fb751b7b 2010-05-06 08:14:02 +00:00
a23983f686 Eliminate duplicate message IDs, they cause msgmerge to fail 2010-05-06 08:11:29 +00:00
561b4244d6 Rename activeSince to firstSeen 2010-05-06 05:04:07 +00:00
331f72ef67 2010-05-05 22:49:54 +00:00
bea47a0c5d Remove some dead code 2010-05-05 13:28:12 +00:00
232e6f72e7 Remove the localDestination parameter from the KademliaDHT constructor, read localDestination from the I2PSendQueue instead. 2010-05-05 13:26:55 +00:00
1557694beb * Handle requests to the local destination directly, not through the network
* Fix/add some comments
* Rename CheckEmailTask.findEmailPacketKeys() to CheckEmailTask.getEmailPacketKeys(), rename the parameter to dhtPackets
* Add some debug output
2010-05-05 12:56:12 +00:00
1824e0ddcc 2010-05-05 12:42:55 +00:00
d6d0756a6b Use a bean property for displaying the local destination hash, no need for a tag. 2010-05-05 12:20:38 +00:00
c08eff08db Remove an unused method 2010-05-04 22:31:22 +00:00
7664dcbd5b 2010-05-04 17:30:00 +00:00
1e666ca7de Refresh folder list every 20 seconds when sending, otherwise every 2 minutes 2010-05-04 02:10:48 +00:00
56baffba77 BootstrapTask: unlock bootstrap node before adding to bucket 2010-05-03 21:35:49 +00:00
529b39efe9 Cache message IDs of emails that have been received, and ignore email packets whose message ID is in the cache. 2010-05-03 05:39:54 +00:00
a826750254 When new email arrives, don't do a page refresh, but go to the inbox URL 2010-05-02 03:19:49 +00:00
cff2752b8f Insert a random number into the request string so others can't see contacts or identities using the CSS history hack 2010-05-01 23:11:33 +00:00
3daa866b10 Strip local address book names before sending an email 2010-05-01 21:46:53 +00:00
e1e1a7a8e4 Don't throw a NPE in the call to log.error() 2010-05-01 21:23:03 +00:00
b76288e944 One more trash folder related commit in addition to rev. ec310a306f43f2b5e51f77640f415bca08e5971d 2010-05-01 21:19:20 +00:00
905f9aa974 * Implement the trash folder
* Move emails to trash instead of deleting them; do delete emails from the trash folder
2010-04-30 06:18:21 +00:00
57a2d2b891 * Implement the "Sent" folder
* After an email has been sent, move it to the "Sent" folder
2010-04-29 10:04:05 +00:00
7d060c495a Make the folders box an iframe, auto-refresh if outbox contains emails. 2010-04-29 09:58:24 +00:00
e2c83d58da Add a file I missed in rev. 82ebf22a7ebcec38ddaff619afe27a8e19dc9fcc 2010-04-28 06:33:25 +00:00
f5a73349fb Instead of a random DHT key, use a SHA-256 hash so receivers can verify the integrity of email packets. 2010-04-28 03:26:32 +00:00
f02148e159 Show any recipient, not a local recipient, in the "To" column in the outbox (usually there are no local recipients in an outgoing email). 2010-04-27 05:22:35 +00:00
e03a6640fb Make the "To:" column sortable 2010-04-27 05:04:26 +00:00
a4c37841ec When sorting a folder by address, sort by displayed address, not by header value. 2010-04-27 03:45:42 +00:00
b44f67011f Fix duplicate "identity created" message when creating a new identity 2010-04-26 05:39:06 +00:00
a5765a5920 Remove the "new" parameter which does nothing 2010-04-26 05:34:33 +00:00
ae7c0947cf Fix page title 2010-04-26 05:33:32 +00:00
af9e46c983 Focus the destination field if it is empty; if it is not, focus the name field 2010-04-26 05:01:01 +00:00
eb39dfdb90 Always put a blank recipient field at the end 2010-04-26 04:44:28 +00:00
6b5ad92ea6 Rename Field to EmailAttribute 2010-04-26 04:23:02 +00:00
d60f26561a Escape HTML in the textarea 2010-04-25 23:47:28 +00:00
f36d0f2298 Implement the "remove recipient" button 2010-04-25 23:03:06 +00:00
c796311c55 Replace some JSP code in newEmail.jsp with Java code (it was getting too complex for JSP) 2010-04-25 21:40:03 +00:00
4ee42e9a1f Show the "add to address book" button only for the last address 2010-04-25 05:44:27 +00:00
33ce63db4e * newEmail.jsp: Implement the "add recipient" button
* Rename the JSP function getRecipients to getSortedRecipientParams
* newEmail.jsp: Remove redundant filtering of recipient* parameters which is already done in getSortedRecipientParams()
* sendEmail.jsp: Keep ordering of recipients the same as in the UI
2010-04-25 04:36:47 +00:00
340d6cd83b Handle single quotes and double quotes in text fields 2010-04-24 07:30:56 +00:00
560f6e4946 2010-04-23 06:00:26 +00:00
6445dd5d4c Handle whitespace in email destinations 2010-04-23 04:23:22 +00:00
86fe700d9f When displaying an email, show full Email Destinations only on mouse over 2010-04-22 07:21:59 +00:00
d2d4031f2e Update outbox status after every sendToOne call 2010-04-21 06:29:20 +00:00
e598b81600 Handle CC and BCC addresses 2010-04-21 06:27:38 +00:00
7f9f55fbac Fix title of the outbox page; remove reference to non-existant param.path parameter 2010-04-21 06:23:00 +00:00
a707f2830b After deleting an email, go to the containing folder instead of the inbox. 2010-04-20 20:49:27 +00:00
0f95825828 Save outgoing emails to the outbox and send them in the background 2010-04-20 20:48:29 +00:00
c764008170 More detailed debug output 2010-04-19 02:11:31 +00:00
dc43492537 * If an email destination in an email is known, always show the name from the address book or the identity
* Don't show an add button if sender is a local identity
* Several minor things
2010-04-19 01:48:33 +00:00
e87a4efe2f Default to descending sort order for dates, ascending for all other fields 2010-04-15 05:49:17 +00:00
95cfab5e71 Remove an unused constant 2010-04-15 05:45:27 +00:00
ab1ad92278 * New Email page: Add a button which adds a recipient to the address book
* Address book: In select mode, show a "return" button instead of the "add" button if the address book is empty.
* Rename the HTTP request parameter "search" to "select"
* Fix some JSP compile warnings caused by invalid XML in i2pbote.tld
2010-04-15 03:21:48 +00:00
f2afa866f9 Some comments 2010-04-15 02:25:19 +00:00
de53a1fbc9 Add missing license headers 2010-04-15 02:13:19 +00:00
97f078721b Say whether the unit tests passed or failed 2010-04-11 08:27:52 +00:00
52f295396c Show mail folders sorted; clickable column headers for sorting 2010-04-11 08:23:57 +00:00
ade9804ae6 Remove unnecessary call to getProperty() 2010-04-04 08:04:57 +00:00
7fb9cb8290 * JspStrings.java is the new jsp2po.sh
* Extract strings from <ib:message> tag bodies
* Add a few more missing translations
* Don't translate "value" parameters in <button> tags
* Finish the text in noIdentities.jsp
2010-04-04 08:03:33 +00:00
3d262db251 Fix high CPU usage bug introduced in rev. 54d0f214f9894b433925bc2d0979c5ea6eb924b6,
reported at http://forum.i2p/viewtopic.php?p=23465#23465 (Thanks to the reporter!)
2010-04-03 08:20:43 +00:00
d093f81e46 * Folder.jsp: Add "Unknown Sender" and "Signature" columns
* showEmail.jsp: Show whether the signature is valid
2010-04-01 06:49:25 +00:00
1c2af589fd Oops, forgot to add Locales.java in rev. 9b93a31813555d2157e6a815e907bb3f6b5c31fe 2010-03-31 07:57:26 +00:00
daa289bed1 Add EmailTest.java 2010-03-31 07:54:34 +00:00
40ad4cd488 Fix a NPE 2010-03-31 07:51:43 +00:00
d9931f519d * Sign outgoing emails, verify signatures of incoming emails
* Don't make a copy of the email for removing BCC headers, work on the original
* Remove unused methods from i2p.bote.Util
2010-03-31 07:47:31 +00:00
78a5d59ef3 * Fix: i2pbote-update.xpi2p too big when building the plugin target twice in a row
* Add missing files to the src target
2010-03-29 23:58:23 +00:00
c4be1543ce A few minor things 2010-03-29 05:31:05 +00:00
2652d75346 Don't query locked peers 2010-03-29 05:30:18 +00:00
72621a20c9 * Add a UI option to include / not include the "sent" time when sending an email
* Format the "active since" column on network.jsp according to the locale
2010-03-29 05:28:20 +00:00
960f480d3a Remove extra line in the <textarea> 2010-03-28 06:18:08 +00:00
d8f7d8bfb2 * Show "sent" date/time in showEmail.jsp
* Format date/time according to locale
2010-03-28 04:10:45 +00:00
4b0fdf6932 * Alternate colors in folder list
* Replace tabs with spaces in folder.jsp
2010-03-27 21:13:55 +00:00
739e517c3f Do not translate the directory name in a folder link, only the display name 2010-03-27 20:52:45 +00:00
0af131b043 * Make language user-configurable
* Optionally leave text that is auto-added to an email, such as "Re:", untranslated
2010-03-27 04:21:54 +00:00
b780f79e30 * Move setLastLookupTime() calls from KademliaDHT to BucketManager
* More logging changes
2010-03-25 03:47:31 +00:00
6fcaf3761c Synchronize on pendingRequests in packetReceived 2010-03-25 03:17:55 +00:00
9bf0f45ad0 2010-03-23 19:41:03 +00:00
d23ac867a4 Add/change log output, add some comments 2010-03-23 19:34:50 +00:00
3195fd1cb3 2010-03-23 19:00:56 +00:00
76fd98c346 0.2.2 2010-03-22 07:17:20 +00:00
f3bdebead6 Spelling of the plugin display name 2010-03-22 07:16:32 +00:00
d735db129a Increase max. lock time for unresponsive peers 2010-03-22 04:16:31 +00:00
903c58d7ce Use a solid background for displaying emails 2010-03-21 17:38:18 +00:00
96d51bbc4d Fix: Bootstrap nodes are shown as active since 1970 2010-03-21 17:12:03 +00:00
35050ae5c9 Don't create a N_ file if an O_ file exists for the same message ID. 2010-03-21 16:21:31 +00:00
2fa58dc2c2 * Rename I2PBoteThread.awaitShutdown() to awaitShutdownRequest(), keep DHT.awaitShutdown()
* Write peers to file on shutdown
2010-03-21 08:00:21 +00:00
f307561705 Abort lookup on shutdown request 2010-03-21 07:41:09 +00:00
d313cd89e6 Use BufferedWriter.newline() instead of System.getProperty(line.separator) 2010-03-21 07:36:22 +00:00
13174f7164 Cleanup 2010-03-21 01:27:06 +00:00
668c7c4896 Implement IndexPacketTest 2010-03-21 01:26:17 +00:00
8724648d53 Don't exit until send queue is empty (or the thread is killed) 2010-03-19 23:07:49 +00:00
707aaf0bd0 Shut down ExecutorService when done so threads exit 2010-03-19 20:43:05 +00:00
bb055ce55c Adjust text field widths, now that the CSS doesn't make them all 100% wide 2010-03-19 20:04:54 +00:00
5bf930102c * Make auto mail checking configurable
* Configuration.java:
  - Wrap Properties instead of extending it
  - Replace tabs with spaces
2010-03-19 19:55:18 +00:00
554b0e251d Add some missing translations 2010-03-15 07:07:55 +00:00
9a84bb2ae1 Put a space after Re: 2010-03-15 06:38:37 +00:00
ce6bcecff9 Increase stack size to 256 kB for mail check threads 2010-03-15 06:30:41 +00:00
d6fd4e971e PacketQueue:
* Now based on an ArrayList instead of LinkedList
 * Any synchronization issues should be gone
2010-03-15 05:28:36 +00:00
f399f8d691 Rename REFRESH_TIMEOUT to BUCKET_REFRESH_INTERVAL 2010-03-14 21:04:44 +00:00
5acebd19fe Fix a ConcurrentModificationException 2010-03-14 20:57:26 +00:00
80db993ca7 Make saveToFile() parameterless and rename to save() 2010-03-14 20:56:10 +00:00
9d59cd074c Remove some unused stuff 2010-03-14 18:42:15 +00:00
e86ef43058 German translation 2010-03-14 18:40:31 +00:00
b928c41d22 * Multi-language support
* Delete PrintAppVersionTag, replace with a JSPHelper property
2010-03-14 17:41:35 +00:00
1cc96558d5 Second bootstrap node 2010-03-05 16:28:11 +00:00
7371f835de Fix a NPE 2010-03-05 16:18:46 +00:00
e9010555a1 Fix: refilling the s-bucket broke BucketManagerTest 2010-03-04 07:05:16 +00:00
ee2f9f7471 Fix a potential IndexOutOfBoundsException 2010-03-04 07:02:11 +00:00
8c1e166007 * Use locked + unlocked peers for refilling the s-bucket
* Fix: refillSiblings() generates duplicate peers
2010-03-04 07:00:27 +00:00
030ec907f4 * add missing "}"
* replace tabs with spaces
2010-03-04 06:22:41 +00:00
e9bd3ed60e 2010-03-03 18:06:04 +00:00
e156962b24 0.2.1 2010-03-03 18:05:03 +00:00
aa6b5489f7 2010-03-03 18:00:15 +00:00
1adec9c83b Shutdown:
* Fix: NPE when stopping plugin before the application has started connecting
  * Shut down all threads so the app exits without having to kill the JVM
2010-03-03 17:59:34 +00:00
e21db2b740 store(): Include local node if not enough peers found, or if local node is closer than one of the peers found 2010-03-01 08:05:01 +00:00
5ff0f41988 * Always query the closest unqueried peer, instead of picking one randomly
* Have a list of all (not just S) peers ready at the beginning of the lookup. This fixes the "No storages found" bug (which I think would have gone away in the next release due to the higher S value).
2010-03-01 08:02:56 +00:00
302db49850 2010-03-01 06:26:52 +00:00
b11459eeba Fix: An IndexPacketDeleteRequest for an already deleted entry causes a NPE 2010-03-01 06:04:37 +00:00
1ae332a85d Partial undo of rev. 30c74967ae81dff73f62ecca085cd72a85424ffe: Don't move unresponsive siblings to a k-bucket. I don't know what I was thinking, there is no reason to do that. 2010-03-01 03:21:32 +00:00
653a162643 Layout fix 2010-03-01 02:27:06 +00:00
cf3d58920a Re-bootstrap when there are no more active peers 2010-02-28 22:44:07 +00:00
1431272618 Run pack200 on plugin jars 2010-02-28 20:52:07 +00:00
3c0fb794d9 Code simplification 2010-02-28 06:34:35 +00:00
5135d07fa4 Ant plugin target: get the app version in a platform-independent way
Ant src target: get the app version from the source like the plugin target does
2010-02-28 02:42:12 +00:00
c1aa31bdfb 2010-02-28 02:35:24 +00:00
79b2197dc0 Option to compile I2P-Bote as an I2P plugin 2010-02-26 23:32:43 +00:00
ec7e057b6d * Fix: ClosestNodesLookupTask returned too early in some cases
* Periodical s-bucket refresh
* Exponential backoff for unresponsive peers
* Lots of minor stuff
2010-02-25 02:02:00 +00:00
d6a69e237e * when a sibling times out, move it to a k-bucket and refill the s-bucket
* don't use unresponsive peers for lookups, but keep them unless there is a replacement peer
* remove some unused or redundant code
2010-02-19 05:43:44 +00:00
cc48d56682 * Bucket refresh (periodically refresh a bucket if there hasn't been a DHT lookup for a key within the bucket's range)
* New method KBucket.toString() for logging purposes
* Fix: bucket prefix was calculated wrong
2010-02-17 04:10:07 +00:00
831d263449 2010-02-16 04:47:25 +00:00
7ae9575ed2 Use fn:escapeXml for displaying email data 2010-02-16 04:46:17 +00:00
215a9ab160 Fix junit Ant task 2010-02-16 04:02:29 +00:00
5fac243e70 Ant can use web.xml directly, no need for web-template.xml 2010-02-16 03:51:54 +00:00
6b3c11322e Some cleaning up and other minor stuff 2010-02-15 06:39:58 +00:00
422eacd53d 2010-02-15 06:38:18 +00:00
947ad0014c Set character encoding in requests to UTF-8 unless the browser sets an encoding 2010-02-15 06:37:46 +00:00
b91f08fa01 Implement the Address Book feature 2010-02-15 06:31:38 +00:00
2227955eee Implement the "default identity" feature. 2010-02-12 05:30:17 +00:00
fddefc9275 Fix: Trying to edit an existing identity creates a new one 2010-02-11 05:06:23 +00:00
352df69d51 Fix NPE when navigating to network.jsp right after the 3-minute initial wait 2010-02-11 03:03:41 +00:00
82917aaaab When replying, don't add a Re: if there is one already 2010-02-10 23:13:16 +00:00
75fbcbaecf 2010-02-10 23:11:51 +00:00
45c0fff636 Display Kademlia-specific information on network.jsp while still keeping the DHT interface implementation-agnostic 2010-02-10 21:06:59 +00:00
cc846c3abe * Rename KademliaException to DhtException; in DHT, throw DhtException instead of Exception
* Drop Comparable from I2PSendQueue.ScheduledPacket
* Remove an unneccessary "throws"
* Fix some minor issues found by FindBugs:
  - Don't ignore return values of File.mkdir, File.renameTo, etc.
  - Close open files even if an exception happens
  - Use Integer.valueOf instead of the Integer constructor
  - Make some inner classes static
  - Remove an unused method from UniqueId
  - Make two fields volatile
2010-02-10 04:59:44 +00:00
a131fa6600 90k chars can't fit into 3 email packets anymore due to email encoding, so lower it to 80k chars. 2010-02-09 19:36:17 +00:00
40bd121a77 * Fix: test failed because peers[199] was the same destination as localDestination
* Comments
2010-02-09 19:13:39 +00:00
e4d317c76d Change K and S to real world values. B is still 1 for now. 2010-02-09 19:10:04 +00:00
22a0ef14b6 Return K peers in a Kademlia lookup, not S 2010-02-09 19:05:18 +00:00
f26c8116e8 Remove an incorrect log message 2010-02-09 19:01:07 +00:00
0a03d90696 New unit test BucketManagerTest 2010-02-09 04:39:36 +00:00
3d7788632e * Several Kademlia bugs fixed (bucket splitting was severely broken)
* Use all positive numbers for representing a Hash as a BigInteger
* Some refactoring of the Kademlia code, mainly in order to keep buckets sorted by last reception time without having to explicitly store a "last seen" time for each peer.
2010-02-09 04:38:55 +00:00
6df3e8b845 remove unused variable 2010-02-09 04:29:17 +00:00
39fed9ec83 remove unused import 2010-02-09 04:28:09 +00:00
e2cc37c3e3 Catch all exceptions in messageAvailable() and log them before they get caught in I2PSessionImpl which doesn\'t print a stack trace 2010-02-09 04:21:12 +00:00
b17bcbaa07 More detailed log output 2010-02-09 04:17:35 +00:00
8bdb64c39b * Remove Email.getUniqueID() and Email.setUniqueID(), only use getMessageID()/setMessageID()
* call super method in Email.updateHeaders()
* Explicitly encode subject line from UTF-8
2010-02-06 06:38:31 +00:00
ff880b25ef * Use UTF-8 in HTTP requests
* Instead of an HTTP param, use an EL variable for passing the page title to header.jsp so non-US characters don't get messed up
* Fix title on network.jsp
2010-02-06 06:27:22 +00:00
c341d47b27 2010-02-05 06:21:28 +00:00
8212edca98 * Use JavaMail
* Get rid of the FolderElement interface
2010-02-05 06:20:04 +00:00
bdabe0e19a Add some missing unit tests to AllTests.java 2010-02-04 06:18:10 +00:00
37cbc5cfd9 * Get rid of MessageId, use UniqueId instead
* Don't use the "Message-Id" header at all. The message Id is already in the
  file name (for .mail files) or in the MSID field (for packetized emails).
2010-02-04 06:16:29 +00:00
015af37a59 * Pass the whole Ant classpath to Ant tasks because JSPC seems to need a java compiler for tag files
* Partial fix for the junit task (tests still fail, but they compile now)
* Print SHA256 sum for the war file
* Delete src.zip in the "clean" task
2010-02-03 07:36:53 +00:00
38ad423353 Rename test/.../kademlia package to test/.../dht for consistency 2010-02-02 03:48:37 +00:00
b64614ea45 Version 0.2 2010-02-01 16:09:38 +00:00
c7277b944a Remove the cancel button, disable the save button 2010-02-01 16:05:04 +00:00
27e8723753 Put a tag back in that got accidentally deleted in rev. d6d2a84b2ce55918029c8362a8526ed21e254fe6 2010-02-01 07:01:56 +00:00
bdce1fc8a3 2010-02-01 06:17:36 +00:00
cf6a89a731 Prevent network.jsp from throwing an error when the app has not started connecting yet 2010-02-01 03:44:26 +00:00
e929c8fb18 Show "to" addresses in the inbox and when displaying an email 2010-02-01 03:03:07 +00:00
4e051a8111 Remove unused file 2010-02-01 02:56:51 +00:00
e5385f4017 Reverse sort order, was oldest to newest 2010-02-01 02:56:12 +00:00
146bb6f876 Auto-check for new mail periodically 2010-02-01 01:31:21 +00:00
3698843c0f Fix the delete button 2010-02-01 01:23:07 +00:00
24fb21ea2c * N_ or O_ prefix in the filename indicates if an email is new (read) or old (unread)
* Show new emails in bold, read ones in normal font
* Set an email to "old" when the user displays it
2010-01-31 21:37:41 +00:00
44d09a1b37 2010-01-31 21:37:01 +00:00
64fe831e0c * Move some code out of getElements() into the new method getFilenames()
* Add method getNumElements()
2010-01-31 01:59:12 +00:00
0bc9825a46 2010-01-31 01:45:57 +00:00
4c7f3bd029 remove redundant interface declaration 2010-01-31 01:36:54 +00:00
be58442eed Fix: wrong field name in the javascript that focuses the "public name" field on page load 2010-01-31 01:14:01 +00:00
aafcce644e IncomingPacketHandler:
* Ignore response packets whose id doesn't match the request
 * Rename addPeers to updatePeers
2010-01-30 19:44:07 +00:00
24c76dd897 * Handle invalid packets better
* Ban peers that use a protocol version other than 2
2010-01-30 10:48:29 +00:00
38171179df Remove some unnecessary code 2010-01-30 10:38:33 +00:00
808b895951 * Print local dest hash on the network status page
* List active peers and banned peers separately on the network status page
2010-01-30 10:24:49 +00:00
540b0990eb 2010-01-30 09:49:24 +00:00
f5e3b128f3 Remove unused class 2010-01-30 09:39:23 +00:00
0d12ee1240 2010-01-29 04:55:20 +00:00
d26032595e * AbstractBucket/KBucket: Replace ConcurrentSkipListSet with ConcurrentHashSet for the peer list and the replacement cache
* drop a peer after 3 consecutive non-responses
* several minor changes
2010-01-29 04:54:47 +00:00
80c66d7c23 Oops, two more files should have gone into revision 6b0ba3c712564df1061979948a109779470024f8 2010-01-27 20:13:15 +00:00
5785c5836b . 2010-01-27 07:14:00 +00:00
1c52df1713 Fix the stale counter 2010-01-27 07:13:33 +00:00
591c3bff98 . 2010-01-27 06:32:00 +00:00
28f09ac7d4 Implement reply button 2010-01-27 04:20:59 +00:00
6a059521f0 Remove debug message 2010-01-27 04:16:20 +00:00
7164b7f428 Fix: Public Name and Description headings were in the wrong order 2010-01-25 01:09:38 +00:00
471e8f4b25 Fix: left angle bracket disappears when inserting line break in the sender field 2010-01-25 01:08:09 +00:00
84eb1ded73 Make textarea one row bigger because some email destinations have more line breaks than others 2010-01-25 01:04:44 +00:00
db3429d0e0 * Include sender's Public Name in the "sender" header field
* Use a proportional font for the "new email" text box
2010-01-25 00:17:39 +00:00
ed4439ef4d Don't try to read peers.txt if it does not exist. 2010-01-24 18:24:06 +00:00
63b8bf29e8 Preserve line breaks in emails when sending and convert them to HTML for displaying emails. 2010-01-24 06:36:34 +00:00
7a09d652d1 Fix incorrect logger names 2010-01-24 02:09:04 +00:00
2ebb389900 Use POST for the "New Email" form 2010-01-23 06:07:35 +00:00
2e50916897 Add license notices 2010-01-23 05:11:16 +00:00
b40e327508 Fix unnecessary timeouts when doing DHT retrievals 2010-01-23 04:46:41 +00:00
b9534b2a2a PacketQueue: Insert packets with an earliestSendTime>0 in the right place 2010-01-23 01:05:25 +00:00
c022088190 Always send a response to retrieve requests, even if nothing found. 2010-01-23 00:52:00 +00:00
f5db9e18b5 Don't report a peer timeout if the packet hasn't been sent to the peer yet 2010-01-22 19:04:24 +00:00
87cc21dcf1 * Show peer info on the network page
* Remove some dead code
* Major refactoring of the Kademlia code:
 - Interface DhtPeer replaces the PeerInfo class
 - KademliaPeer extends Destination
 - Get rid of BanList, store ban info in KademliaPeer instead
 - Use separate classes for k-buckets and s-buckets, both extend AbstractBucket
2010-01-22 01:59:25 +00:00
d0af45125e Show peer info on network.jsp 2010-01-19 01:59:21 +00:00
39b66e85ce Put peers on the ban list if they use a wrong protocol version (has no effect yet) 2010-01-18 06:54:40 +00:00
b4072d4813 * Implement deletion of email packets from the DHT and deletion of index packet entries
* Version 2 of the network protocol
2010-01-18 06:51:27 +00:00
525cd894f5 Log dest hash, not dest key in packetReceived 2010-01-08 21:00:01 +00:00
b8dfe7f96d * Allow the X-Priority header field
* Set the "From" field when setting the "Sender" field
2010-01-08 20:43:47 +00:00
289c78108d . 2010-01-08 20:41:50 +00:00
fb9fb7844e Throw an exception if no peers were found for storing a packet 2009-12-20 04:28:19 +00:00
10e32012f9 . 2009-12-20 04:25:48 +00:00
1218c1545c Ver. 0.1.5 2009-12-20 00:08:27 +00:00
a3be21648b 0.1.5 2009-12-20 00:00:03 +00:00
7694ea3e58 Fix: redirect to noIdentities.jsp was broken 2009-12-19 23:56:10 +00:00
c6a12fc336 support deletion of emails 2009-12-19 23:24:32 +00:00
f50b0a9da1 Fix: DHT packets only got stored on one node because the StoreRequests all had the same packet Id. 2009-12-19 23:23:56 +00:00
e9554c7924 Doing a findOne for index packets didn't seem such a good idea after all because
it might return an incomplete version of the index packet when there is a more
complete version stored on another peer. So I'm reverting back to findAll.
2009-12-19 23:22:20 +00:00
424e0dfb35 support deletion of emails 2009-12-19 23:19:24 +00:00
fec9052413 new 2009-12-19 23:15:57 +00:00
cc85ef3d0b . 2009-12-19 20:41:08 +00:00
35fbaa377c Add disabled button style 2009-12-19 19:27:02 +00:00
ee941ea7a1 Move some app status related code to getStatus.jsp so it can be shared with other JSPs 2009-12-19 19:26:24 +00:00
2005a2db4d Disable the Network Status link until the I2P session is ready 2009-12-19 19:21:51 +00:00
31cd0b9bd1 Disable two buttons and one link until I2P session is ready 2009-12-19 19:20:36 +00:00
e8fc716b21 new file 2009-12-19 17:26:36 +00:00
bb33c8dcd9 remove all unimplemented HTML links 2009-12-19 08:26:51 +00:00
ff88ec13ba don't say "connected" until done bootstrapping 2009-12-19 08:21:55 +00:00
62f1850144 Remove some dead code;
in the find method, return the local result immediately if only one result was asked for
2009-12-19 07:34:38 +00:00
0e4a732c76 . 2009-12-19 07:30:39 +00:00
7d4b9d051f Just use the first index packet received, don't wait for all peers to respond 2009-12-19 07:30:21 +00:00
b3d883f7fa fix: distance can't be negative; add JavaDoc 2009-12-19 06:07:13 +00:00
7c51fd6848 Set max bucket size to k for k-buckets, and s for the s-bucket 2009-12-19 04:24:30 +00:00
02e28eb59f . 2009-12-19 03:50:39 +00:00
737237c836 synchronize the list of responses received; more log output; log dest hash, not the full dest key; use the correct key type when removing a mapping from pendingRequests 2009-12-19 03:49:56 +00:00
3d343e7949 Fix: toByteArray wrote all 387 bytes of an I2P destination instead of just the two public keys 2009-12-19 03:30:13 +00:00
278f612aa4 rename FindCloseNodesPacketTest to FindClosePeersPacketTest; add PeerListTest 2009-12-19 03:27:12 +00:00
b858cccb59 . 2009-12-19 03:24:42 +00:00
6d535e6699 New file PeerListTest.java 2009-12-19 03:24:06 +00:00
af193ec09b rename FindCloseNodesPacketTest to FindClosePeersPacketTest 2009-12-19 03:23:19 +00:00
e5d84be0db Implement equals and hashCode 2009-12-18 18:06:55 +00:00
e330c4bf22 log destination hash, not the complete dest key 2009-12-18 18:04:57 +00:00
b5191bc663 add the src target which makes a src.zip file 2009-12-17 18:38:54 +00:00
9dc72d2c70 . 2009-12-17 18:13:58 +00:00
19758eab8e add createRandomHash() 2009-12-17 18:13:20 +00:00
fb2726b0b0 Move all network and threading code out of BucketManager into KademliaDHT;
don't add self to buckets;
use a peer for bootstrapping if it initiated communication;
fix the refresh(KBucket) method which didn't do a closest nodes lookup;
other, minor changes
2009-12-17 18:12:33 +00:00
11438e3649 Move all network and threading code out of BucketManager into KademliaDHT; don't add self to buckets; make BucketManager iterable; other, minor changes 2009-12-17 17:56:50 +00:00
b03534c339 Only log the dest hash, not the multi-line output of Destination.toString(); get rid of an unnecessary if 2009-12-17 17:44:44 +00:00
f6ac9b910a Check for expired packets once a day, not every time when checking for new packets 2009-12-17 17:33:27 +00:00
ed765834fe 0.1.4 2009-12-13 07:41:58 +00:00
459075ca7f Release 0.1.4; Do the 3-minute wait in the background and support skipping the wait time. 2009-12-13 07:41:28 +00:00
47da9495fe New tag "connect"; new JSP function getNetworkStatus 2009-12-13 07:28:06 +00:00
b51733ff2f New JSP function getNetworkStatus() 2009-12-13 07:26:50 +00:00
df463fd6d0 New method isConnected() 2009-12-13 07:25:51 +00:00
d50d9a5318 Modify statusbox class for revised statusFrame.jsp 2009-12-13 07:24:11 +00:00
3b7b748e71 Fix jstllib path; don't add JSPs to war (thanks zzz) 2009-12-13 07:20:05 +00:00
d77b61e939 Link to network.jsp 2009-12-13 07:17:35 +00:00
fbba5d461b Add JSTL for displaying local I2P destination, commented out for now. 2009-12-13 07:17:03 +00:00
0fc434b6d7 Remove unused welcome-file entries 2009-12-13 07:14:46 +00:00
57be687166 New file 2009-12-13 07:12:07 +00:00
835 changed files with 137327 additions and 8313 deletions

66
.github/workflows/sync.yaml vendored Normal file
View File

@ -0,0 +1,66 @@
# GitHub Actions workflow file to sync an external repository to this GitHub mirror.
# This file was automatically generated by go-github-sync.
#
# The workflow does the following:
# - Runs on a scheduled basis (and can also be triggered manually)
# - Clones the GitHub mirror repository
# - Fetches changes from the primary external repository
# - Applies those changes to the mirror repository
# - Pushes the updated content back to the GitHub mirror
#
# Authentication is handled by the GITHUB_TOKEN secret provided by GitHub Actions.
jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Validate Github Actions Environment
run: if [ "$GITHUB_ACTIONS" != "true" ]; then echo 'This script must be run in a GitHub Actions environment.'; exit 1; fi
- name: Checkout GitHub Mirror
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Configure Git
run: |-
git config user.name 'GitHub Actions'
git config user.email 'actions@github.com'
- env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
name: Sync Primary Repository
run: |-
# Add the primary repository as a remote
git remote add primary https://i2pgit.org/I2P_Developers/i2p.i2p-bote.git
# Fetch the latest changes from the primary repository
git fetch primary
# Check if the primary branch exists in the primary repository
if git ls-remote --heads primary master | grep -q master; then
echo "Primary branch master found in primary repository"
else
echo "Error: Primary branch master not found in primary repository"
exit 1
fi
# Check if we're already on the mirror branch
if git rev-parse --verify --quiet master; then
git checkout master
else
# Create the mirror branch if it doesn't exist
git checkout -b master
fi
# Force-apply all changes from primary, overriding any conflicts
echo "Performing force sync from primary/master to master"
git reset --hard primary/master
# Push changes back to the mirror repository
git push origin master
name: Sync Primary Repository to GitHub Mirror
"on":
push: {}
schedule:
- cron: 0 * * * *
workflow_dispatch: {}

21
.gitignore vendored Normal file
View File

@ -0,0 +1,21 @@
# Config files
local.properties
signing.properties
# Gradle
.gradle/
build/
android/build
core/build
webapp/build
# I2P-specific ignores
android/src/main/res/raw/certificates_zip
# James Server dependencies
webapp/cache
webapp/libs
# Standalone WAR user files
webapp/i2pbote
webapp/logs

94
.mtn-ignore Normal file
View File

@ -0,0 +1,94 @@
# General ignores
^.settings
# Android-specific ignores
lint.xml
local.properties
signing.properties
#IntelliJ IDEA
^.idea
.*.iml
.*.ipr
.*.iws
#Gradle
^.gradle
build
# I2P-specific ignores
^android/src/main/res/raw/certificates_zip
# Just to try and prevent some noob disasters.
# Use mtn add --no-respect-ignore foo.jar to ignore this ignore list
# Temporary/build files
i2pbote.jar
i2pbote.xpi2p
i2pbote-update.xpi2p
~$
# Temporary/build dirs
^ant_build
^plugin/plugin.tmp
^core/build/
^webapp/build/
# Build property overrides
override.properties
# Downloaded JavaMail libraries
mailapi-.*.jar
# Downloaded BouncyCastle Provider libraries
bcprov-jdk15on-.*.jar
# Downloaded IMAP libraries
apache-james-imap-api-0.4-.*.jar
apache-james-imap-message-0.4-.*.jar
apache-james-imap-processor-0.4-.*.jar
apache-james-mailbox-api-0.6-.*.jar
apache-james-mailbox-store-0.6-.*.jar
apache-mime4j-core-.*.jar
apache-mime4j-dom-.*.jar
commons-codec-.*.jar
commons-collections-.*.jar
commons-configuration-.*.jar
commons-io-.*.jar
commons-lang-.*.jar
commons-logging-.*.jar
csrfguard-.*.jar
james-server-filesystem-api-3.0.0-beta5-SNAPSHOT.jar
james-server-lifecycle-api-3.0.0-beta5-SNAPSHOT.jar
james-server-protocols-imap4-3.0.0-beta5-SNAPSHOT.jar
james-server-protocols-library-3.0.0-beta5-SNAPSHOT.jar
james-server-util-3.0.0-beta5-SNAPSHOT.jar
jutf7-1.0.0.jar
log4j-1.2.17.jar
netty-3.3.1.Final.jar
protocols-api-1.6.4-.*.jar
protocols-imap-1.6.4-.*.jar
protocols-netty-1.6.4-.*.jar
slf4j-api-.*.jar
slf4j-log4j12-.*.jar
# Downloaded SMTP libraries
subethasmtp-3.1.7.jar
# Downloaded JUnit libraries
junit-4.8.1.jar
# Downloaded Mockito libraries
mockito-core-2.0.31-beta.jar
# Downloaded jMock libraries
cglib-nodep-2.2.3.jar
hamcrest-core-1.3.jar
hamcrest-library-1.3.jar
hamcrest-unit-test-1.3.jar
jmock-2.6.0.jar
jmock-junit3-2.6.0.jar
jmock-junit4-2.6.0.jar
jmock-legacy-2.6.0.jar
jmock-script-2.6.0.jar
objenesis-1.0.jar

54
.travis.yml Normal file
View File

@ -0,0 +1,54 @@
language: java
jdk:
- oraclejdk11
- oraclejdk9
- oraclejdk8
- openjdk12
- openjdk11
- openjdk10
- openjdk9
- openjdk8
matrix:
include:
- jdk: openjdk7
sudo: required
before_install: # Work around missing crypto in openjdk7
- wget http://security.ubuntu.com/ubuntu/pool/main/g/gcc-5/gcc-5-base_5.4.0-6ubuntu1~16.04.10_amd64.deb
- wget http://security.ubuntu.com/ubuntu/pool/main/g/gcc-5/libgomp1_5.4.0-6ubuntu1~16.04.10_amd64.deb
- wget http://archive.ubuntu.com/ubuntu/pool/main/n/ncurses/libtinfo5_6.0+20160213-1ubuntu1_amd64.deb
- wget http://archive.ubuntu.com/ubuntu/pool/main/g/gettext/gettext_0.19.7-2ubuntu3.1_amd64.deb
- sudo dpkg -i gcc-5-base_5.4.0-6ubuntu1~16.04.10_amd64.deb
- sudo dpkg -i libgomp1_5.4.0-6ubuntu1~16.04.10_amd64.deb
- sudo dpkg -i libtinfo5_6.0+20160213-1ubuntu1_amd64.deb
- sudo dpkg -i gettext_0.19.7-2ubuntu3.1_amd64.deb
- export JAVA7_HOME=$(jdk_switcher home openjdk7)
- sudo wget "https://bouncycastle.org/download/bcprov-ext-jdk15on-158.jar" -O "${JAVA_HOME}/jre/lib/ext/bcprov-ext-jdk15on-158.jar"
- sudo perl -pi.bak -e 's/^(security\.provider\.)([0-9]+)/$1.($2+1)/ge' /etc/java-7-openjdk/security/java.security
- echo "security.provider.1=org.bouncycastle.jce.provider.BouncyCastleProvider" | sudo tee -a /etc/java-7-openjdk/security/java.security
install:
- export TARGET_JAVA_HOME=$JAVA_HOME
- jdk_switcher use oraclejdk8
- ./gradlew assemble
allow_failures:
- jdk: openjdk12
before_install:
- wget http://security.ubuntu.com/ubuntu/pool/main/g/gcc-5/gcc-5-base_5.4.0-6ubuntu1~16.04.10_amd64.deb
- wget http://security.ubuntu.com/ubuntu/pool/main/g/gcc-5/libgomp1_5.4.0-6ubuntu1~16.04.10_amd64.deb
- wget http://archive.ubuntu.com/ubuntu/pool/main/n/ncurses/libtinfo5_6.0+20160213-1ubuntu1_amd64.deb
- wget http://archive.ubuntu.com/ubuntu/pool/main/g/gettext/gettext_0.19.7-2ubuntu3.1_amd64.deb
- sudo dpkg -i gcc-5-base_5.4.0-6ubuntu1~16.04.10_amd64.deb
- sudo dpkg -i libgomp1_5.4.0-6ubuntu1~16.04.10_amd64.deb
- sudo dpkg -i libtinfo5_6.0+20160213-1ubuntu1_amd64.deb
- sudo dpkg -i gettext_0.19.7-2ubuntu3.1_amd64.deb
- export JAVA7_HOME=$(jdk_switcher home openjdk7)
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
cache:
directories:
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/

42
.tx/config Normal file
View File

@ -0,0 +1,42 @@
[main]
host = https://www.transifex.com
lang_map = he: iw, id: in, pt_BR: pt-rBR, ru_RU: ru, sv_SE: sv, tr_TR: tr, uk_UA: uk, yi: ji, zh_CN: zh
[I2P.plugin_i2pbote]
file_filter = webapp/src/main/locale/messages_<lang>.po
source_file = webapp/src/main/locale/messages_en.po
source_lang = en
minimum_perc = 40
[I2P.plugin_i2pbote_userguide]
file_filter = webapp/src/main/webapp/html/userGuide_<lang>.html
source_file = webapp/src/main/webapp/html/userGuide.html
source_lang = en
minimum_perc = 50
[I2P.plugin_i2pbote_faq]
file_filter = webapp/src/main/webapp/html/FAQ_<lang>.html
source_file = webapp/src/main/webapp/html/FAQ.html
source_lang = en
minimum_perc = 50
[I2P.android_bote]
file_filter = android/src/main/res/values-<lang>/strings.xml
source_file = android/src/main/res/values/strings.xml
source_lang = en
type = ANDROID
minimum_perc = 50
[I2P.android_bote_help_start]
file_filter = android/src/main/res/raw-<lang>/help_start.html
source_file = android/src/main/res/raw/help_start.html
source_lang = en
type = HTML
minimum_perc = 50
[I2P.android_bote_help_identities]
file_filter = android/src/main/res/raw-<lang>/help_identities.html
source_file = android/src/main/res/raw/help_identities.html
source_lang = en
type = HTML
minimum_perc = 50

140
README.md Normal file
View File

@ -0,0 +1,140 @@
# I2P-Bote
[![Build Status](https://travis-ci.org/i2p/i2p.i2p-bote.svg?branch=master)](https://travis-ci.org/i2p/i2p.i2p-bote)
I2P-Bote is a plugin for I2P that allows users to send and receive emails while preserving privacy. It does not need a mail server because emails are stored in a distributed hash table. They are automatically encrypted and digitally signed, which ensures no one but the intended recipient can read the email, and third parties cannot forge them.
## Features
- Themeable webmail interface
- User interface translated in many languages
- One-click creation of email accounts (called email identities)
- Emails can be sent under a sender identity, or anonymously
- ElGamal, Elliptic Curve, and NTRU Encryption
- Encryption and signing is transparent, without the need to know about PGP
- Delivery confirmation
- Basic support for short recipient names
- IMAP / SMTP
### Planned Features
- Custom folders
- Sending and receiving via relays, similar to Mixmaster
- Lots of small improvements
## Build process
### Dependencies:
- Java SDK (preferably Oracle/Sun or OpenJDK) 1.7.0 or higher
- Apache Ant 1.8.0 or higher
- Gradle 2.14.1
### Gradle
The build system is based on Gradle. There are several methods for setting Gradle up:
* It can be downloaded from [the Gradle website](http://www.gradle.org/downloads).
* Most distributions will have Gradle packages. Be careful to check the provided version; Debian and Ubuntu have old versions in their main repositories. There is a [PPA](https://launchpad.net/~cwchien/+archive/gradle) for Ubuntu with the latest version of Gradle.
* A Gradle wrapper is provided in the codebase. It takes all the same commands as the regular `gradle` command. The first time that any command is run, it will automatically download, cache and use the correct version of Gradle. This is the simplest way to get started with the codebase. To use it, replace `gradle` with `./gradlew` (or `./gradlew.bat` on Windows) in the commands below.
Gradle will pull dependencies over the clearnet by default. To use Tor, create a `gradle.properties` file in `i2p.android.base` containing:
```
systemProp.socksProxyHost=localhost
systemProp.socksProxyPort=9150
```
### Building the I2P plugin
```
gradle :webapp:plugin
```
The plugin will be placed in `i2p.i2p-bote/webapp/build/plugin`.
### Building the standalone WAR
```
gradle :webapp:war
```
The WAR will be placed in `i2p.i2p-bote/webapp/build/libs`.
### Running the standalone WAR
Ensure you have an I2P router running locally with an I2CP server port open (on port 7654). Then run:
```
gradle :webapp:tomcatRunWar
```
This will build and run the WAR. (Jetty currently does not work.)
The data directory will be placed in `i2p.i2p-bote/webapp/i2pbote`; logs will be in `i2p.i2p-bote/webapp/logs`.
## Android build process
### Additional dependencies:
- [I2P source](https://github.com/i2p/i2p.i2p)
- Android SDK 25
- Android Build Tools 25.0.2
- Android Support Repository
### Preparation
1. Download the Android SDK. The simplest method is to download Android Studio.
2. Create a `local.properties` file in `i2p.i2p-bote/android` containing:
```
i2psrc=/path/to/i2p.i2p
```
3. If you want to use a local copy of the I2P Android client library, install it in your local Maven repository with:
```
cd path/to/i2p.android.base
./gradlew client:installArchives
```
### Building from the command line
1. Create a `local.properties` file in `i2p.i2p-bote` containing:
```
sdk.dir=/path/to/android-studio/sdk
```
2. `gradle :android:assembleDebug`
3. The APK will be placed in `i2p.i2p-bote/android/build/apk`.
### Building with Android Studio
1. Import `i2p.i2p-bote` into Android Studio. (This creates the `local.properties` file automatically).
2. Build and run the app (`Shift+F10`).
### Signing release builds
1. Create a `signing.properties` file in `i2p.i2p-bote` containing:
```
STORE_FILE=/path/to/android.keystore
STORE_PASSWORD=store.password
KEY_ALIAS=key.alias
KEY_PASSWORD=key.password
```
2. `gradle assembleRelease`
## More information
The links below only work within I2P, i.e., make sure you are running I2P and your browser is using the proxy at localhost:4444.
- http://i2pbote.i2p I2P-Bote homepage
- http://forum.i2p/viewforum.php?f=35 I2P-Bote forum

28
TODO Normal file
View File

@ -0,0 +1,28 @@
User guide
- Howto for IMAP/SMTP
Email composition
- "Include sent time" as an option, pre-filled with settings default
Identities
- Generate dests in background rather than needing a dedicated tab open
IMAP/SMTP
- "Force SSL" option
- When disabled, users can connect with "insecure" or "STARTTLS"
- When enabled, users can connect with "SSL"
- Separate password cache (IMAP auth should not unlock webui)
Not-yet-implemented
- Drafts folder
- User-defined folders
- Automatically disable timestamps when using mail routes
- Random offset for fetch interval (#1360)
- Per-identity settings (#1359)
- Fetch interval
- Mail route per-hop settings
- Delay (#1361)
- Fixed forward time
- Traffic correlation countermeasures at relays
- Mixing anonymity (minimum threshold on relay packets to send)
- Constant traffic (test and dummy messages)
- Balanced incoming/outgoing ratio
- Packet padding
- Retrieving email via relays

View File

@ -1,2 +0,0 @@
<%@ attribute name="index" required="true" description="0-based index of the email identity" %>
test

View File

@ -1,17 +0,0 @@
<%@ attribute name="text" required="true" description="The text to display" %>
<%@ attribute name="width" required="false" description="The maximum number of characters in each line" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<c:if test="${empty width}">
<c:set var="width" value="64"/>
</c:if>
<c:set var="length" value="${fn:length(text)}"/>
<c:forEach var="i" begin="0" end="${length}" step="${width}">
<c:if test="${i > 0}">
<br/>
</c:if>
${fn:substring(text, i, i+64)}
</c:forEach>

View File

@ -1,127 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd">
<tlib-version>1.0</tlib-version>
<short-name>i2pbote</short-name>
<uri>I2pBoteTags</uri>
<tag>
<name>sendEmail</name>
<tag-class>i2p.bote.web.SendEmailTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>sender</name>
<rtexprvalue>true</rtexprvalue>
<required>true</required>
</attribute>
<attribute>
<name>recipient</name>
<rtexprvalue>true</rtexprvalue>
<required>true</required>
</attribute>
<attribute>
<name>subject</name>
<rtexprvalue>true</rtexprvalue>
<required>true</required>
</attribute>
<attribute>
<name>message</name>
<rtexprvalue>true</rtexprvalue>
<required>true</required>
</attribute>
</tag>
<tag>
<name>checkForMail</name>
<tag-class>i2p.bote.web.CheckMailTag</tag-class>
<body-content>empty</body-content>
</tag>
<tag>
<name>numDhtPeers</name>
<tag-class>i2p.bote.web.PrintNumDhtPeersTag</tag-class>
<body-content>empty</body-content>
</tag>
<tag>
<name>numRelayPeers</name>
<tag-class>i2p.bote.web.PrintNumRelayPeersTag</tag-class>
<body-content>empty</body-content>
</tag>
<tag>
<name>printVersion</name>
<tag-class>i2p.bote.web.PrintAppVersionTag</tag-class>
<body-content>empty</body-content>
</tag>
<function>
<name>getIdentities</name>
<function-class>i2p.bote.web.JSPHelper</function-class>
<function-signature>
i2p.bote.email.Identities getIdentities()
</function-signature>
</function>
<function>
<name>getLocalDestination</name>
<function-class>i2p.bote.web.JSPHelper</function-class>
<function-signature>
java.lang.String getLocalDestination()
</function-signature>
</function>
<function>
<name>saveIdentity</name>
<function-class>i2p.bote.web.JSPHelper</function-class>
<function-signature>
boolean saveIdentity(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
</function-signature>
</function>
<function>
<name>deleteIdentity</name>
<function-class>i2p.bote.web.JSPHelper</function-class>
<function-signature>
boolean deleteIdentity(java.lang.String)
</function-signature>
</function>
<function>
<name>isCheckingForMail</name>
<function-class>i2p.bote.web.JSPHelper</function-class>
<function-signature>
boolean isCheckingForMail()
</function-signature>
</function>
<function>
<name>newMailReceived</name>
<function-class>i2p.bote.web.JSPHelper</function-class>
<function-signature>
boolean newMailReceived()
</function-signature>
</function>
<function>
<name>getMailFolder</name>
<function-class>i2p.bote.web.JSPHelper</function-class>
<function-signature>
i2p.bote.EmailFolder getMailFolder(java.lang.String)
</function-signature>
</function>
<function>
<name>getEmail</name>
<function-class>i2p.bote.web.JSPHelper</function-class>
<function-signature>
i2p.bote.email.Email getEmail(java.lang.String, java.lang.String)
</function-signature>
</function>
<tag-file>
<name>printWithLineBreaks</name>
<path>/WEB-INF/tags/printWithLineBreaks.tag</path>
</tag-file>
</taglib>

View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>i2pbote</display-name>
<!-- precompiled servlets -->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<listener>
<listener-class>i2p.bote.web.ServiceInitializer</listener-class>
</listener>
</web-app>

View File

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>i2pbote</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<listener>
<listener-class>i2p.bote.web.ServiceInitializer</listener-class>
</listener>
</web-app>

View File

@ -1,79 +0,0 @@
<%--
Copyright (C) 2009 HungryHobo@mail.i2p
The GPG fingerprint for HungryHobo@mail.i2p is:
6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
This file is part of I2P-Bote.
I2P-Bote 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.
I2P-Bote 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 I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
--%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="ib" uri="I2pBoteTags" %>
<c:if test="${param.checkMail == 1}">
<ib:checkForMail/>
</c:if>
<c:if test="${ib:isCheckingForMail()}">
<c:set var="checkingForMail" value="true"/>
</c:if>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" href="i2pbote.css" />
<c:if test="${checkingForMail}">
<meta http-equiv="refresh" content="20;url=buttonFrame.jsp" />
</c:if>
</head>
<body style="background-color: transparent; margin: 0px;">
<table><tr>
<td>
<c:if test="${checkingForMail}">
<div class="checkmail">
<img src="images/wait.gif"/>Checking for mail...
</div>
</c:if>
<c:if test="${!checkingForMail}">
<div class="checkmail">
<form action="buttonFrame.jsp" method="GET">
<input type="hidden" name="checkMail" value="1"/>
<button type="submit" value="Check Mail">Check Mail</button>
</form>
</div>
<c:if test="${ib:newMailReceived()}">
<script language="Javascript">
// If inbox is being displayed, reload so the new email(s) show
if (parent.document.getElementById('inboxFlag'))
parent.location.reload();
</script>
</c:if>
</c:if>
</td>
<td>
<form action="newEmail.jsp" target="_top" method="GET">
<button type="submit" value="New">New</button>
</form>
</td>
</tr></table>
</body>
</html>

View File

@ -1,99 +0,0 @@
<%--
Copyright (C) 2009 HungryHobo@mail.i2p
The GPG fingerprint for HungryHobo@mail.i2p is:
6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
This file is part of I2P-Bote.
I2P-Bote 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.
I2P-Bote 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 I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
--%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="ib" uri="I2pBoteTags" %>
<c:choose>
<c:when test="${param.new}">
<c:set var="title" value="New Email Identity"/>
<c:set var="commitAction" value="Create"/>
</c:when>
<c:otherwise>
<c:set var="title" value="Edit Email Identity"/>
<c:set var="commitAction" value="Save"/>
</c:otherwise>
</c:choose>
<jsp:include page="header.jsp">
<jsp:param name="title" value="${title}"/>
</jsp:include>
<div class="errorMessage">
${param.errorMessage}
</div>
<div class="main">
<form name="form" action="saveIdentity.jsp">
<table>
<tr>
<td>
<div style="font-weight: bold;">Public Name:</div>
<div style="font-size: 0.8em;">(required field, shown to recipients)</div>
</td>
<td>
<input type="text" size="25" name="publicName" value="${param.publicName}"/>
</td>
</tr>
<tr>
<td>
<div style="font-weight: bold;">Description:</div>
<div style="font-size: 0.8em;">(optional, kept private)</div>
</td>
<td>
<input type="text" size="25" name="description" value="${param.description}"/>
</td>
</tr>
<tr>
<td>
<div style="font-weight: bold;">Email Address:</div>
<div style="font-size: 0.8em;">(optional)</div>
</td>
<td>
<input type="text" size="50" name="emailAddress" value="${param.emailAddress}"/>
</td>
</tr>
<c:if test="${!empty param.key}">
<tr>
<td style="font-weight: bold; vertical-align: top;">
Email Destination:
</td>
<td>
<textarea cols="64" rows="8" readonly="yes" wrap="soft" style="background-color: transparent; border: none;">${param.key}</textarea>
</td>
</tr>
</c:if>
</table>
<input type="submit" name="action" value="${commitAction}"/>
<input type="submit" name="action" value="Cancel"/>
</form>
<script type="text/javascript" language="JavaScript">
document.forms['form'].elements['name'].focus();
</script>
</div>
<jsp:include page="footer.jsp"/>

View File

@ -1,81 +0,0 @@
<%--
Copyright (C) 2009 HungryHobo@mail.i2p
The GPG fingerprint for HungryHobo@mail.i2p is:
6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
This file is part of I2P-Bote.
I2P-Bote 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.
I2P-Bote 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 I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
--%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="ib" uri="I2pBoteTags" %>
<jsp:include page="header.jsp">
<jsp:param name="title" value="${param.path}"/>
</jsp:include>
<c:set var="folderName" value="Inbox"/>
<c:if test="${folderName == 'Inbox'}">
<div id="inboxFlag"></div>
</c:if>
[DEBUG MSG: This is the <b>${param.path}</b> folder.]
<div class="main">
<div class="folder">
<table>
<tr>
<th style="width: 100px;">Sender</th>
<th style="width: 150px;">Subject</th>
<th style="width: 100px;">Date</th>
<th style="width: 20px;"></th>
</tr>
<c:forEach items="${ib:getMailFolder(folderName).elements}" var="email">
<tr>
<c:set var="sender" value="${email.sender}"/>
<c:if test="${empty sender}">
<c:set var="sender" value="Anonymous"/>
</c:if>
<c:set var="date" value="${email.date}"/>
<c:if test="${empty date}">
<c:set var="date" value="${email.dateString}"/>
</c:if>
<c:if test="${empty date}">
<c:set var="date" value="Unknown"/>
</c:if>
<c:set var="subject" value="${email.subject}"/>
<c:if test="${empty subject}">
<c:set var="subject" value="(No subject)"/>
</c:if>
<c:set var="mailUrl" value="showEmail.jsp?folder=${folderName}&messageID=${email.messageID}"/>
<td><div class="ellipsis"><a href="${mailUrl}">${sender}</a></div></td>
<td><div class="ellipsis"><a href="${mailUrl}">${subject}</a></div></td>
<td><a href="${mailUrl}">${date}</a></td>
<td><a href="deleteEmail.jsp?folder=${folderName}&messageID=${email.messageID}"><img src="images/delete.png" alt="Delete" title="Delete this email"/></a></td>
</tr>
</c:forEach>
</table>
</div>
</div>
<jsp:include page="footer.jsp"/>

View File

@ -1,85 +0,0 @@
<%--
Copyright (C) 2009 HungryHobo@mail.i2p
The GPG fingerprint for HungryHobo@mail.i2p is:
6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
This file is part of I2P-Bote.
I2P-Bote 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.
I2P-Bote 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 I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
--%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="ib" uri="I2pBoteTags" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" href="i2pbote.css" />
<link rel="icon" type="image/png" href="images/favicon.png" />
<title>${param.title} - I2P-Bote</title>
</head>
<body>
<div class="titlebar" style="cursor:pointer" onclick="document.location='.'">
<div class="title">I2P-Bote</div>
<br/>
<div class="subtitle">Secure Distributed Email</div>
</div>
<div class="menubox">
<iframe src="buttonFrame.jsp" width="100%" height="40px" scrolling="no" frameborder="0" allowtransparency="true"></iframe>
</div>
<div class="menubox">
<h2>Folders</h2>
<a href="folder.jsp?path=Inbox"><img src="images/folder.png"/>Inbox</a><br/>
<a href="folder.jsp?path=Outbox"><img src="images/folder.png"/>Outbox</a><br/>
<a href="folder.jsp?path=Sent"><img src="images/folder.png"/>Sent</a><br/>
<a href="folder.jsp?path=Drafts"><img src="images/folder.png"/>Drafts</a><br/>
<a href="folder.jsp?path=Trash"><img src="images/folder.png"/>Trash</a><br/>
<hr/>
<a href="folder.jsp?path=User_created_Folder_1"><img src="images/folder.png"/>User_created_Folder_1</a><br/>
<a href="folder.jsp?path=User_created_Folder_2"><img src="images/folder.png"/>User_created_Folder_2</a><br/>
<a href="folder.jsp?path=User_created_Folder_3"><img src="images/folder.png"/>User_created_Folder_3</a><br/>
<hr/>
<a href=.>Manage Folders</a>
</div>
<div class="menubox">
<h2>Addresses</h2>
<a href="identities.jsp">Identities</a><br/>
<a href=.>Private Address Book</a><br/>
<a href=.>Public Address Directory</a><br/>
</div>
<div class="menubox">
<h2>Configuration</h2>
<a href=.>Settings</a><br/>
</div>
<div class="menubox">
<h2>Network Status</h2>
<iframe src="statusFrame.jsp" width="100%" height="40px" scrolling="no" frameborder="0" allowtransparency="true"></iframe>
</div>
<div class="menubox">
<h2>Help</h2>
<a href=.>User Guide</a><br/>
<a href="about.jsp">About</a><br/>
</div>

View File

@ -1,336 +0,0 @@
/* Based on the router console css */
body {
margin: 15px 0 0 10px;
padding: 0em;
text-align: center;
background: #eef;
color: #000;
font: 9pt/130% "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
}
.titlebar {
width: 800px;
margin: 0px 20px 20px 240px;
background-color: #fff;
color: #310;
padding: 0 15px 15px 25px;
-moz-border-radius: 4px;
-khtml-border-radius: 4px;
border-radius: 4px;
border: 1px solid #001;
text-align: center;
-moz-box-shadow: inset 0px 0px 1px 0px #002;
white-space: normal;
background: #ddf url('images/lightbluetile.png');
opacity: 1.0;
}
.title {
font: normal bold 20pt/120% 'Lucida Sans Unicode', 'Bitstream Vera Sans', Verdana, Tahoma, Helvetica, sans-serif;
font-weight: bold;
letter-spacing: 0.15em;
text-shadow: 0px 0px 1px rgba(0, 0, 148, 0.9);
}
.subtitle {
font-size: 8pt;
color: #cf0606;
letter-spacing: 5pt;
text-shadow: 0px 0px 1px rgba(148, 0, 0, 0.9);
}
.checkmail {
font: bold 8pt "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
color: #555;
}
.menubox {
float: left;
width: 175px;
margin: 0px 0px 10px 10px;
padding: 10px;
text-align: left;
border: 1px solid #000033;
background: #ddf url('images/lightbluetile.png');
color: #000;
font-size: 8pt;
clear: left;
-moz-border-radius: 4px;
-khtml-border-radius: 4px;
border-radius: 4px;
-moz-box-shadow: inset 0px 0px 1px 0px #002;
}
.menubox a:link, .menubox a:visited {
text-shadow: 0px 0px 1px rgba(0, 0, 32, 0.5);
}
.menubox a:hover {
text-shadow: 0px 0px 0.5px rgba(255, 255, 255, 0.7);
color: #f60;
}
.menubox h2 {
font-size: 12pt;
color: #001;
letter-spacing: 0.05em;
text-shadow: 0px 0px 1px rgba(0, 0, 64, 0.5);
wordwrap: none;
}
.statusbox table {
width: 100%;
}
.statusbox td {
text-align: left;
color: #000;
font-size: 8pt;
vertical-align: top;
padding: 0px 30px 0px 0px;
width: 100%;
}
.main {
width: 800px;
margin: 0px 20px 20px 240px;
padding: 0 15px 15px 25px;
background: #eef;
font: 9pt/130% "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
text-align: left;
color: #001;
border: 1px solid #000033;
background: #ddf url('images/lightbluetile.png');
-moz-border-radius: 4px;
-khtml-border-radius: 4px;
border-radius: 4px;
-moz-box-shadow: inset 0px 0px 1px 0px #002;
}
.infoMessage {
margin: 0px 20px 5px 240px;
text-align: left;
color: green;
}
.errorMessage {
margin: 0px 20px 5px 240px;
text-align: left;
color: red;
}
img {
border: none;
}
a:active {
color: #900;
}
a:link {
color: #007;
text-decoration: none;
font-weight: bold;
word-wrap: break-word;
}
a:visited {
color: #606;
text-decoration: none;
font-weight: bold;
}
a:hover {
color: #f60;
text-decoration: underline;
font-weight: bold;
}
a:active {
color: #f93;
text-decoration: underline;
font-weight: bold;
}
button, button:visited {
font: bold 8pt "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
border: 1px outset #999;
padding: 1px 3px;
background: #ddf !important;
text-decoration: none;
border-radius: 4px;
-moz-border-radius: 4px;
-khtml-border-radius: 4px;
margin: 0 1px;
text-align: center;
min-width: 80px;
-moz-box-shadow: inset 0px 2px 8px 0px #fff;
color: #006;
}
button:hover {
border: 1px solid #f60;
background: #f60 !important;
color: #fff;
-moz-box-shadow: inset 0px 0px 0px 1px #fff;
}
button:active {
border: 1px solid #f60;
background: #001 !important;
color: #f60;
-moz-box-shadow: inset 0px 0px 0px 1px #f60;
}
.underline {
border-bottom: 1px solid #000022;
padding: 5px 0px 5px 0px;
margin: 0px 0px 10px 0px;
}
input {
background: #eef;
color: #001;
margin: 5px 10px 5px 10px;
padding: 4px 2px;
font: bold 8pt "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
border: 1px solid #001;
text-decoration: none;
min-width: 110px;
}
input, input:visited {
border: 1px outset #999;
background: #ddf;
color: #001;
margin: 5px;
font: bold 8pt "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
padding: 1px 2px;
text-decoration: none;
min-width: 110px;
border-radius: 4px;
-moz-border-radius: 4px;
-khtml-border-radius: 4px;
-moz-box-shadow: inset 0px 2px 8px 0px #fff;
color: #006;
opacity: 0.9;
}
input:hover {
background: #f60;
color: #fff;
border: 1px solid #f60;
opacity: 1.0;
-moz-box-shadow: inset 0px 0px 0px 1px #fff;
}
input:active {
background: #002;
color: #f60;
border: 1px solid #f60;
opacity: 1.0;
-moz-box-shadow: inset 0px 0px 0px 1px #f60;
}
input[type=text] {
width: 100%;
background: #eef;
color: #001;
margin: 5px;
padding: 5px;
font: bold 8pt "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
border: 1px solid #001;
text-decoration: none;
}
input checkbox {
border: 0 !important;
}
textarea {
width: 100%;
padding: 5px;
margin: 5px;
background: #eef;
color: #003;
border-radius: 4px;
-moz-border-radius: 4px;
-khtml-border-radius: 4px;
font: 8pt "Lucida Console", "DejaVu Sans Mono", Courier, mono;
min-height: 100px;
border: 1px solid #001;
}
submit {
background: #f00;
color: #eef;
margin: 10px 2px 10px 0;
padding: 2px;
font-family: "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
font-weight: bold;
border: 1px solid #001;
text-decoration: none;
}
select {
background: #eef;
color: #003;
padding: 2px;
margin: 5px;
border: 1px solid #001;
font: 9pt "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
border-radius: 4px;
-moz-border-radius: 4px;
-khtml-border-radius: 4px;
text-align: left !important;
}
.folder table {
table-layout: fixed;
width: 800px;
}
.identities table {
table-layout: fixed;
width: 800px;
}
.identities td, .identities th {
vertical-align: top;
padding: 0px 30px 0px 0px;
white-space: nowrap;
overflow-x: hidden;
}
.ellipsis
{
line-height: 1.2em;
height: 1.2em;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
-o-text-overflow: ellipsis;
-moz-binding: url(moz_fix2.xml#ellipsis);
}
.moz-ellipsis > DIV:first-child
{
float: left;
margin-right: -26px;
}
.moz-ellipsis > DIV + DIV
{
float: right;
margin-top: -1.2em;
background: url('images/lightbluefade.png');
padding-left: 26px;
}
.moz-ellipsis > DIV + DIV::after
{
background: url('images/lightbluetile.png');
content: '...';
}

View File

@ -1,93 +0,0 @@
<%--
Copyright (C) 2009 HungryHobo@mail.i2p
The GPG fingerprint for HungryHobo@mail.i2p is:
6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
This file is part of I2P-Bote.
I2P-Bote 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.
I2P-Bote 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 I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
--%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="ib" uri="I2pBoteTags" %>
<jsp:include page="header.jsp">
<jsp:param name="title" value="Identities"/>
</jsp:include>
<div class="infoMessage">
${param.infoMessage}
</div>
<div class="main">
<h2>
Email Identities
</h2>
<c:if test="${empty ib:getIdentities().all}">
No email identities are defined.
</c:if>
<div class="identities">
<table>
<tr>
<th>Description</th>
<th>Public Name</th>
<th>Email Address</th>
<th>Key</th>
<th style="width: 20px; padding: 0px"></th>
</tr>
<c:forEach items="${ib:getIdentities().all}" var="identity">
<tr>
<td style="width: 100px;">
<div class="ellipsis">
<a href="editIdentity.jsp?new=false&key=${identity.key}&publicName=${identity.publicName}&description=${identity.description}&emailAddress=${identity.emailAddress}">
${identity.publicName}
</a>
</div>
</td>
<td style="width: 150px;">
<div class="ellipsis">
${identity.description}
</div>
</td>
<td style="width: 150px;">
<div class="ellipsis">
${identity.emailAddress}
</div>
</td>
<td style="width: 100px;">
<div class="ellipsis">
${identity.key}
</div>
</td>
<td>
<a href="deleteIdentity.jsp?key=${identity.key}"><img src="images/delete.png" alt="Delete" title="Delete this identity"/></a>
</td>
</tr>
</c:forEach>
</table>
</div>
<p/>
<form action="editIdentity.jsp?new=true" method="POST">
<button type="submit" value="New">New Identity</button>
</form>
</div>
<jsp:include page="footer.jsp"/>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 298 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 852 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 780 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 234 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 794 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 227 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 803 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,22 +0,0 @@
<?xml version="1.0"?>
<bindings xmlns="http://www.mozilla.org/xbl" xmlns:html="http://www.w3.org/1999/xhtml">
<binding id="ellipsis" applyauthorstyles="false">
<implementation>
<constructor><![CDATA[
(function(block){
setTimeout(function(){
block.style.mozBinding = '';
var t = document.createElement('DIV');
block.appendChild(t);
block.appendChild(document.createElement('DIV'));
while (block.firstChild != t)
t.appendChild(block.firstChild);
block.className = block.className + ' moz-ellipsis';
}, 0);
})(this);
]]></constructor>
</implementation>
</binding>
</bindings>

View File

@ -1,84 +0,0 @@
<%--
Copyright (C) 2009 HungryHobo@mail.i2p
The GPG fingerprint for HungryHobo@mail.i2p is:
6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
This file is part of I2P-Bote.
I2P-Bote 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.
I2P-Bote 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 I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
--%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="ib" uri="I2pBoteTags" %>
<jsp:include page="header.jsp">
<jsp:param name="title" value="New Email"/>
</jsp:include>
<div class="main">
<form action="sendEmail.jsp">
<table>
<tr>
<td>
From:
</td>
<td>
<select name="sender">
<option value="anonymous">Anonymous</option>
<c:forEach items="${ib:getIdentities().all}" var="identity">
<option value="${identity.key}">
${identity.publicName}
<c:if test="${!empty identity.description}"> - ${identity.description}</c:if>
</option>
</c:forEach>
</select>
</td>
</tr>
<tr>
<td>
<select name="recipientType0">
<option value="to">To:</option>
<option value="cc">CC:</option>
<option value="bcc">BCC:</option>
<option value="reply_to">Reply To:</option>
</select>
</td>
<td>
<input type="text" size="80" name="recipient0"/>
</td>
</tr>
<tr>
<td valign="top"><br/>Subject:</td>
<td><input type="text" size="80" name="subject"/></td>
</tr>
<tr>
<td valign="top"><br/>Message:</td>
<td><textarea rows="30" cols="80" name="message"></textarea></td>
</tr>
<tr>
<td colspan=2 align="center">
<input type="submit" value="Send"/>
<input type="submit" value="Cancel"/>
<input type="submit" value="Save"/>
</td>
</tr>
</table>
</form>
</div>
<jsp:include page="footer.jsp"/>

View File

@ -1,64 +0,0 @@
<%--
Copyright (C) 2009 HungryHobo@mail.i2p
The GPG fingerprint for HungryHobo@mail.i2p is:
6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
This file is part of I2P-Bote.
I2P-Bote 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.
I2P-Bote 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 I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
--%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<jsp:include page="header.jsp">
<jsp:param name="title" value="No Identity"/>
</jsp:include>
<div class="main">
<h2>No Email Identity Defined</h2>
<p>
In order to receive email from other people, you need to create an email identity
first.
</p><p>
This is similar to traditional email where you first have to set up an email account
with an email provider. The difference is that in I2P-Bote, there is no provider that
can read all your email because I2P-Bote stores all emails encrypted on the network.
</p><p>
I2P-Bote automatically decrypts emails sent to you, using the email identity you
created.
</p><p>
An email identity has a secret key, and a public key. Anybody who has the secret key
can download and decrypt your emails, so you should never give it to anybody. The
public key, on the other hand, allows people to send email to you. Think of it as
the equivalent of a traditional email address. Give it to everybody you want to be
able to contact you, or make it public.
</p><p>
[TODO paragraph about spam, HashCash].
</p><p>
You will find that email identities are long combinations of numbers and letters,
and that they are impossible to memorize. I2P-Bote lets you assign an email address
to an email identity to make it easier for you and your contacts to remember.
</p><p>
[TODO name attribute, address attribute].
The public addressbook should not be 100% trusted. Only destination keys are secure.
</p><p>
[TODO private address book, address directory].
<form action="editIdentity.jsp?new=true" method="POST">
<button type="submit" value="New">Create a New Email Identity</button>
</form>
</div>
<jsp:include page="footer.jsp"/>

View File

@ -1,60 +0,0 @@
<%--
Copyright (C) 2009 HungryHobo@mail.i2p
The GPG fingerprint for HungryHobo@mail.i2p is:
6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
This file is part of I2P-Bote.
I2P-Bote 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.
I2P-Bote 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 I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
--%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="ib" uri="I2pBoteTags" %>
<c:set var="email" value="${ib:getEmail(param.folder, param.messageID)}"/>
<jsp:include page="header.jsp">
<jsp:param name="title" value="${email.subject}"/>
</jsp:include>
<div class="main">
<table>
<tr>
<td valign="top"><strong>From:</strong></td>
<td>
<c:forEach var="i" begin="0" end="${fn:length(email.sender)}" step="64">
<c:if test="${i > 0}">
<br/>
</c:if>
${fn:substring(email.sender, i, i+64)}
</c:forEach>
</td>
</tr>
<tr>
<td valign="top"><strong>Subject:</strong></td>
<td>${email.subject}</td>
</tr>
<tr>
<td valign="top"><strong>Message:</strong></td>
<td>${email.bodyText}</td>
</tr>
</table>
</div>
<jsp:include page="footer.jsp"/>

56
android/CHANGELOG Normal file
View File

@ -0,0 +1,56 @@
0.7
* Address book import/export
0.6 / 2015-06-21 / 77d67b4f2d465a5c528fb31af5785d176276b94b
* Identity switcher
* Language can be configured
* Block screenshots by default
* Fixed internal router
* Reorganized settings
* UI improvements, bug fixes, translation updates
0.5 / 2015-01-13 / f5eaab42b6faa25450b2772315c7849f1eb4a03a
* Attachments!
* Email content can be selected/copied (API 11+)
* Cc: and Bcc: fields
* Improved performance of email and contact lists
* Use ripple effect on API 21+
* Much better support for legacy devices
* Help and About screens
* UI improvements, bug fixes, translation updates
0.4 / 2014-12-19 / fdc9c619eff53745d49c987b353ce5b04278dfd6
* New email notifications!
* Fixed crash when clicking "Create new contact" button
* Fixed crash when sending email
* Fixed occasional crashes when Bote connects to / disconnects from the network
* Added "Copy to clipboard" button to identities and contacts
* Added labels to the address book actions menu
* UI improvements, bug fixes, translation updates
0.3 / 2014-12-01 / ebba8aac78d50eda9d25f936e8bd348553966649
* Migrated to new Material design from Android Lollipop
* Overhauled password usability
* Identicons for contacts without pictures
* Use new-style swipe for checking emails
* Users can now check emails from menu, or swipe on empty inbox
* "Forward" and "Reply all" actions for emails
* QR codes for sharing identities
* Separated viewing and editing contacts
* Improved network information page
* Bug fixes, translation updates
0.3-rc3 / 2014-08-23 / 6e7655836552d13d6fa6b7512e7dcf77cb3d413b
* Test release to Norway on Google Play
0.2 / 2014-07-10 / 6c9c580bf6272db575091a1b48f66315535380e0
* Fixed locale hiding in replies
* New notification icons
* New translation
* UI improvements and bug fixes
0.1.1 / 2014-06-20 / a9c83d573dc3c5fbeeb288b347f996f440b33621
* Fixed bugs in identity creation and password setting
0.1 / 2014-06-19 / 8457f58f367a2f362bdacd470c792b4bcd6d42f6
* Initial release

70
android/TODO Normal file
View File

@ -0,0 +1,70 @@
Fixes:
- Auto-comma the To: field when it loses focus
- Fix tick over selected emails
- Delete/read/unread/move actions on view email page that don't break everything
- Prevent router option being changed while Bote is running
- Remove internal router
Tasks:
- Show logged-in status in persistent notification
- Intro and setup
- More layouts (tune for each screen size)
- Subclass EmailListFragment for each default mailbox
- First-run content of inbox should tell users about pull-to-refresh.
- Cache Identicons for speed
- Refactor code
- Reorganize for clarity
- Optimize use of Android lifecycles
Silent Store approval checks to confirm/implement:
- Known Vulnerabilities
- Apps will be tested to ensure that they are not susceptible to known
publicly disclosed vulnerabilities. For example:
- Heartbleed
- Poodle
- MasterKey
- Common Path Traversal attacks
- Common SQL Injection attacks
- Network Security Protocols
- All Apps that require transmission of data from the App to a system that
does not exist on the device must use, at a minimum, TLS1.1 standards.
However, Blackphone would prefer the usage of TLS1.2.
- Apps must not use algorithms for cryptographic purposes that are considered
obsolete or outdated i.e. MD5, SHA1, RC4, DES, or any encryption algorithm
that is weaker than AES128.
- Transport Layer Protection
- All network communication should be encrypted
- Not vulnerable to SSl Strip
- Data Leakage
- No storage of sensitive data outside of application sandbox
- Files should not be created with MODE_WORLD_READABLE or MODE_WORLD_WRITABLE
- Copy & Paste will be evaluated on a case by case basis
- App logs should not contain sensitive information
- Authentication and Authorization
- Validate that authentication credentials are not stored on the device
- Must use an approved password-based key derivation function ie. PBKDF2, scrypt
- Data-at-rest Encryption
- Must use at a minimum AES128 with modes CCM or GCM
- Should not store the encryption key on the file system
- Permission Checks
- The App must function with all permissions disabled
- Apps must not hard crash if a permission is disabled
- Apps should ask users to enable permissions that are disabled if needed to
function properly and explain why the permission is necessary
- Privacy Policy
- Apps must have a privacy policy that details how customer data is used,
stored, shared, etc...
- Apps must be configured with the customer opted out by default
- App logs should not contain PII
- Error Handling
- Apps should follow best-practices for error handling and logging
Features:
- Search
- Fingerprints that users can compare to validate
- Public address book lookup
- "Write email" link in address book
- "Empty trash" option in Trash folder
- Overlay to explain
- Option to run only when connected to an outlet
- Option to run only when connected to wifi

View File

@ -0,0 +1,382 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="1024"
height="500"
id="svg3027"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="bote-feature.svg">
<defs
id="defs3029">
<linearGradient
gradientTransform="matrix(1.4585784,0,0,1.4585784,-198.10585,-178.10619)"
y2="481.5072"
x2="523.5882"
y1="481.5072"
x1="340.4118"
gradientUnits="userSpaceOnUse"
id="linearGradient6049"
xlink:href="#linearGradient3903"
inkscape:collect="always" />
<linearGradient
id="linearGradient3903-0">
<stop
id="stop3905-0"
offset="0"
style="stop-color:#000000;stop-opacity:0.17647059;" />
<stop
style="stop-color:#000000;stop-opacity:0.09803922;"
offset="0.25018752"
id="stop3915-03" />
<stop
id="stop3919-2"
offset="0.50031251"
style="stop-color:#000000;stop-opacity:0.05882353;" />
<stop
id="stop3917-29"
offset="0.75"
style="stop-color:#000000;stop-opacity:0.09803922;" />
<stop
id="stop3907-6"
offset="1"
style="stop-color:#000000;stop-opacity:0.17647059;" />
</linearGradient>
<linearGradient
gradientUnits="userSpaceOnUse"
y2="481.5072"
x2="523.5882"
y1="481.5072"
x1="340.4118"
id="linearGradient5962-5"
xlink:href="#linearGradient3903-0"
inkscape:collect="always" />
<linearGradient
id="linearGradient3903-3-2">
<stop
id="stop3905-2-3"
offset="0"
style="stop-color:#000000;stop-opacity:0.15686275;" />
<stop
style="stop-color:#000000;stop-opacity:0.05882353;"
offset="0.2"
id="stop3915-0-3" />
<stop
id="stop3919-4-8"
offset="0.50031251"
style="stop-color:#000000;stop-opacity:0.01960784;" />
<stop
id="stop3917-2-6"
offset="0.80000001"
style="stop-color:#000000;stop-opacity:0.05882353;" />
<stop
id="stop3907-9-6"
offset="1"
style="stop-color:#000000;stop-opacity:0.15686275;" />
</linearGradient>
<linearGradient
gradientTransform="matrix(1.4585784,0,0,1.4585784,-198.10585,-178.10619)"
gradientUnits="userSpaceOnUse"
y2="342.44925"
x2="500.96887"
y1="342.44925"
x1="363.03113"
id="linearGradient5942"
xlink:href="#linearGradient3903-3"
inkscape:collect="always" />
<linearGradient
id="linearGradient3903-3">
<stop
id="stop3905-2"
offset="0"
style="stop-color:#000000;stop-opacity:0.17647059;" />
<stop
style="stop-color:#000000;stop-opacity:0.09803922;"
offset="0.25018752"
id="stop3915-0" />
<stop
id="stop3919-4"
offset="0.50031251"
style="stop-color:#000000;stop-opacity:0.05882353;" />
<stop
id="stop3917-2"
offset="0.75"
style="stop-color:#000000;stop-opacity:0.09803922;" />
<stop
id="stop3907-9"
offset="1"
style="stop-color:#000000;stop-opacity:0.17647059;" />
</linearGradient>
<linearGradient
gradientTransform="matrix(0.99875033,0,0,0.99826344,0.53986459,0.7502204)"
gradientUnits="userSpaceOnUse"
y2="432.00003"
x2="832"
y1="432.00003"
x1="32.000004"
id="linearGradient3911-5"
xlink:href="#linearGradient3903-3"
inkscape:collect="always" />
<inkscape:perspective
id="perspective3012"
inkscape:persp3d-origin="432 : 288 : 1"
inkscape:vp_z="1566.8571 : 394.85714 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="-291.42857 : 94.857143 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
id="linearGradient3903">
<stop
id="stop3905"
offset="0"
style="stop-color:#000000;stop-opacity:0.17647059;" />
<stop
style="stop-color:#000000;stop-opacity:0.09803922;"
offset="0.25018752"
id="stop3915" />
<stop
id="stop3919"
offset="0.50031251"
style="stop-color:#000000;stop-opacity:0.05882353;" />
<stop
id="stop3917"
offset="0.75"
style="stop-color:#000000;stop-opacity:0.09803922;" />
<stop
id="stop3907"
offset="1"
style="stop-color:#000000;stop-opacity:0.17647059;" />
</linearGradient>
<linearGradient
id="linearGradient5944">
<stop
style="stop-color:#000000;stop-opacity:0.09803922;"
offset="0"
id="stop5952" />
<stop
style="stop-color:#000000;stop-opacity:0.09803922;"
offset="1"
id="stop5954" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3903-3-2"
id="linearGradient3248"
gradientUnits="userSpaceOnUse"
x1="23"
y1="431"
x2="841"
y2="431" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3903-3-2"
id="linearGradient3250"
gradientUnits="userSpaceOnUse"
x1="267"
y1="522"
x2="597"
y2="522" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3903-0"
id="radialGradient3252"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,0.72727297,0,73.499943)"
cx="430.75"
cy="269.50003"
fx="430.75"
fy="269.50003"
r="123.75" />
<filter
id="filter3854"
y="-0.15"
height="1.3"
x="-0.15"
width="1.3"
inkscape:menu-tooltip="Aquarelle paper effect which can be used for pictures as for objects"
inkscape:menu="Textures"
inkscape:label="Rough paper"
color-interpolation-filters="sRGB">
<feTurbulence
id="feTurbulence3856"
type="fractalNoise"
baseFrequency="0.04"
numOctaves="5"
seed="0"
result="result4" />
<feDisplacementMap
id="feDisplacementMap3858"
in2="result4"
in="SourceGraphic"
yChannelSelector="G"
xChannelSelector="R"
scale="10"
result="result3" />
<feDiffuseLighting
id="feDiffuseLighting3860"
lighting-color="rgb(233,230,215)"
diffuseConstant="1"
surfaceScale="2"
result="result1"
in="result4">
<feDistantLight
id="feDistantLight3862"
azimuth="235"
elevation="40" />
</feDiffuseLighting>
<feComposite
id="feComposite3864"
in2="result1"
operator="in"
in="result3"
result="result2" />
<feComposite
id="feComposite3866"
in2="result1"
result="result5"
operator="arithmetic"
k1="1.7" />
<feBlend
id="feBlend3868"
in2="result3"
in="result5"
mode="normal" />
</filter>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.5"
inkscape:cx="351.48063"
inkscape:cy="250"
inkscape:document-units="px"
inkscape:current-layer="layer2"
showgrid="false"
inkscape:window-width="1366"
inkscape:window-height="719"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1" />
<metadata
id="metadata3032">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Background"
style="opacity:0.3">
<rect
style="fill:#ffffde;fill-opacity:0.87058824000000001;fill-rule:nonzero;stroke:none;filter:url(#filter3854)"
id="rect3032"
width="1124"
height="600"
x="-50"
y="-50" />
</g>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-552.36218)">
<g
id="g3224"
transform="matrix(0.40342298,0,0,0.40342298,91.309473,628.48688)">
<g
style="display:inline"
inkscape:label="Bg"
id="layer3">
<rect
y="145.99701"
x="32"
height="574.00299"
width="798.00293"
id="rect2994-9"
style="fill:#fafae6;fill-opacity:1;stroke:none;display:inline" />
</g>
<g
style="display:inline"
inkscape:label="Envelope"
id="layer1-6">
<rect
y="142"
x="32"
height="578"
width="800"
id="rect2994"
style="fill:url(#linearGradient3248);fill-opacity:1;stroke:#000000;stroke-width:18;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<path
sodipodi:nodetypes="ccccccc"
inkscape:connector-curvature="0"
d="M 32.99995,144.99995 431.99998,479.00212 831.00005,144.99995 M 375.41474,431.99882 33.000566,719.00005 m 797.998874,0 -342.41418,-287.00123"
style="fill:none;stroke:#000000;stroke-width:17.99989891;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path3044-9" />
</g>
<g
style="display:inline"
id="g6041"
inkscape:label="Lock bg">
<path
inkscape:connector-curvature="0"
id="path6043"
d="m 267,359.5 0,325 330,0 0,-325 -330,0 z M 432,418 c 29.82337,0 54,24.17661 54,54 0,19.98456 -10.86345,37.41209 -27,46.75 l 0,107.25 -54,0 0,-107.25 c -16.13656,-9.33791 -27,-26.76544 -27,-46.75 0,-29.82339 24.17662,-54 54,-54 z"
style="fill:#ffda00;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:8.89999962;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<path
style="fill:#faffff;fill-opacity:1;stroke:#000000;stroke-width:9;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 369.75735,359.49999 0,-57.36595 c 0.21395,-6.69058 1.48284,-13.34882 3.92587,-19.62604 8.34674,-22.92119 31.23869,-39.42587 55.47413,-39.71118 0.75084,-0.0301 1.52631,-0.0581 2.27586,-0.0572 23.23569,0.0265 45.61943,14.77505 54.96208,36.26801 3.26038,7.2537 5.06733,15.16666 5.34827,23.12659 l 0,57.36584 M 554.5,359.5 554.49909,302.13415 C 553.31086,234.20574 498.38227,179.5 430.74976,179.5 363.11725,179.5 308.18822,234.20574 307,302.13405 L 307,359.5"
id="path6045"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccscc" />
</g>
<g
inkscape:label="Lock"
id="g4234"
style="display:inline">
<path
inkscape:connector-curvature="0"
style="fill:url(#linearGradient3250);fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 267,359.5 0,325 330,0 0,-325 -330,0 z M 432,418 c 29.82337,0 54,24.17661 54,54 0,19.98456 -10.86345,37.41209 -27,46.75 l 0,107.25 -54,0 0,-107.25 c -16.13656,-9.33791 -27,-26.76544 -27,-46.75 0,-29.82339 24.17662,-54 54,-54 z"
id="path4236" />
<path
sodipodi:nodetypes="ccccccccccscc"
inkscape:connector-curvature="0"
id="path4238"
d="m 369.75735,359.49999 0,-57.36595 c 0.21395,-6.69058 1.48284,-13.34882 3.92587,-19.62604 8.34674,-22.92119 31.23869,-39.42587 55.47413,-39.71118 0.75084,-0.0301 1.52631,-0.0581 2.27586,-0.0572 23.23569,0.0265 45.61943,14.77505 54.96208,36.26801 3.26038,7.2537 5.06733,15.16666 5.34827,23.12659 l 0,57.36584 M 554.5,359.5 554.49909,302.13415 C 553.31086,234.20574 498.38227,179.5 430.74976,179.5 363.11725,179.5 308.18822,234.20574 307,302.13405 L 307,359.5"
style="fill:url(#radialGradient3252);fill-opacity:1;stroke:none" />
</g>
</g>
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Webdings;-inkscape-font-specification:Webdings"
x="497.5329"
y="863.97107"
id="text3273"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
x="497.5329"
y="863.97107"
style="font-size:175px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial Bold"
id="tspan3277">Bote.</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,296 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="864"
height="864"
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="ic_launcher_master.svg">`
<defs
id="defs4">
<linearGradient
id="linearGradient5944">
<stop
id="stop5952"
offset="0"
style="stop-color:#000000;stop-opacity:0.09803922;" />
<stop
id="stop5954"
offset="1"
style="stop-color:#000000;stop-opacity:0.09803922;" />
</linearGradient>
<linearGradient
id="linearGradient3903">
<stop
style="stop-color:#000000;stop-opacity:0.17647059;"
offset="0"
id="stop3905" />
<stop
id="stop3915"
offset="0.25018752"
style="stop-color:#000000;stop-opacity:0.09803922;" />
<stop
style="stop-color:#000000;stop-opacity:0.05882353;"
offset="0.50031251"
id="stop3919" />
<stop
style="stop-color:#000000;stop-opacity:0.09803922;"
offset="0.75"
id="stop3917" />
<stop
style="stop-color:#000000;stop-opacity:0.17647059;"
offset="1"
id="stop3907" />
</linearGradient>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="-291.42857 : 94.857143 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="1566.8571 : 394.85714 : 1"
inkscape:persp3d-origin="432 : 288 : 1"
id="perspective3012" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3903-3"
id="linearGradient3911-5"
x1="32.000004"
y1="432.00003"
x2="832"
y2="432.00003"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.99875033,0,0,0.99826344,0.53986459,0.7502204)" />
<linearGradient
id="linearGradient3903-3">
<stop
style="stop-color:#000000;stop-opacity:0.17647059;"
offset="0"
id="stop3905-2" />
<stop
id="stop3915-0"
offset="0.25018752"
style="stop-color:#000000;stop-opacity:0.09803922;" />
<stop
style="stop-color:#000000;stop-opacity:0.05882353;"
offset="0.50031251"
id="stop3919-4" />
<stop
style="stop-color:#000000;stop-opacity:0.09803922;"
offset="0.75"
id="stop3917-2" />
<stop
style="stop-color:#000000;stop-opacity:0.17647059;"
offset="1"
id="stop3907-9" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3903-3"
id="linearGradient5942"
x1="363.03113"
y1="342.44925"
x2="500.96887"
y2="342.44925"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.4585784,0,0,1.4585784,-198.10585,-178.10619)" />
<linearGradient
id="linearGradient3903-3-2">
<stop
style="stop-color:#000000;stop-opacity:0.15686275;"
offset="0"
id="stop3905-2-3" />
<stop
id="stop3915-0-3"
offset="0.2"
style="stop-color:#000000;stop-opacity:0.05882353;" />
<stop
style="stop-color:#000000;stop-opacity:0.01960784;"
offset="0.50031251"
id="stop3919-4-8" />
<stop
style="stop-color:#000000;stop-opacity:0.05882353;"
offset="0.80000001"
id="stop3917-2-6" />
<stop
style="stop-color:#000000;stop-opacity:0.15686275;"
offset="1"
id="stop3907-9-6" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3903-0"
id="linearGradient5962-5"
x1="340.4118"
y1="481.5072"
x2="523.5882"
y2="481.5072"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3903-0">
<stop
style="stop-color:#000000;stop-opacity:0.17647059;"
offset="0"
id="stop3905-0" />
<stop
id="stop3915-03"
offset="0.25018752"
style="stop-color:#000000;stop-opacity:0.09803922;" />
<stop
style="stop-color:#000000;stop-opacity:0.05882353;"
offset="0.50031251"
id="stop3919-2" />
<stop
style="stop-color:#000000;stop-opacity:0.09803922;"
offset="0.75"
id="stop3917-29" />
<stop
style="stop-color:#000000;stop-opacity:0.17647059;"
offset="1"
id="stop3907-6" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3903"
id="linearGradient6049"
gradientUnits="userSpaceOnUse"
x1="340.4118"
y1="481.5072"
x2="523.5882"
y2="481.5072"
gradientTransform="matrix(1.4585784,0,0,1.4585784,-198.10585,-178.10619)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3903-3-2"
id="linearGradient4246"
x1="23"
y1="431"
x2="841"
y2="431"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3903-3-2"
id="linearGradient4254"
x1="267"
y1="522"
x2="597"
y2="522"
gradientUnits="userSpaceOnUse" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3903-0"
id="radialGradient4266"
cx="430.75"
cy="269.50003"
fx="430.75"
fy="269.50003"
r="123.75"
gradientTransform="matrix(1,0,0,0.72727297,0,73.499943)"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.70710678"
inkscape:cx="355.79427"
inkscape:cy="460.20102"
inkscape:document-units="px"
inkscape:current-layer="g6041"
showgrid="false"
inkscape:window-width="1366"
inkscape:window-height="719"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="Bg"
style="display:inline">
<rect
style="fill:#fafae6;fill-opacity:1;stroke:none;display:inline"
id="rect2994-9"
width="798.00293"
height="574.00299"
x="32"
y="145.99701" />
</g>
<g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="Envelope"
style="display:inline;opacity:1">
<rect
style="fill:url(#linearGradient4246);fill-opacity:1.0;stroke:#000000;stroke-width:18;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect2994"
width="800"
height="578"
x="32"
y="142" />
<path
id="path3044-9"
style="fill:none;stroke:#000000;stroke-width:17.99989890999999886;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="M 32.99995,144.99995 431.99998,479.00212 831.00005,144.99995 M 375.41474,431.99882 33.000566,719.00005 m 797.998874,0 -342.41418,-287.00123"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" />
</g>
<g
inkscape:label="Lock bg"
id="g6041"
inkscape:groupmode="layer"
style="display:inline">
<path
style="fill:#ffda00;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-opacity:1;stroke-width:8.9;stroke-miterlimit:4;stroke-dasharray:none"
d="m 267,359.5 0,325 330,0 0,-325 -330,0 z M 432,418 c 29.82337,0 54,24.17661 54,54 0,19.98456 -10.86345,37.41209 -27,46.75 l 0,107.25 -54,0 0,-107.25 c -16.13656,-9.33791 -27,-26.76544 -27,-46.75 0,-29.82339 24.17662,-54 54,-54 z"
id="path6043"
inkscape:connector-curvature="0" />
<path
sodipodi:nodetypes="ccccccccccscc"
inkscape:connector-curvature="0"
id="path6045"
d="m 369.75735,359.49999 0,-57.36595 c 0.21395,-6.69058 1.48284,-13.34882 3.92587,-19.62604 8.34674,-22.92119 31.23869,-39.42587 55.47413,-39.71118 0.75084,-0.0301 1.52631,-0.0581 2.27586,-0.0572 23.23569,0.0265 45.61943,14.77505 54.96208,36.26801 3.26038,7.2537 5.06733,15.16666 5.34827,23.12659 l 0,57.36584 M 554.5,359.5 554.49909,302.13415 C 553.31086,234.20574 498.38227,179.5 430.74976,179.5 363.11725,179.5 308.18822,234.20574 307,302.13405 L 307,359.5"
style="fill:#faffff;fill-opacity:1;stroke:#000000;stroke-width:9;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
style="display:inline"
inkscape:groupmode="layer"
id="g4234"
inkscape:label="Lock">
<path
id="path4236"
d="m 267,359.5 0,325 330,0 0,-325 -330,0 z M 432,418 c 29.82337,0 54,24.17661 54,54 0,19.98456 -10.86345,37.41209 -27,46.75 l 0,107.25 -54,0 0,-107.25 c -16.13656,-9.33791 -27,-26.76544 -27,-46.75 0,-29.82339 24.17662,-54 54,-54 z"
style="fill:url(#linearGradient4254);fill-opacity:1;fill-rule:nonzero;stroke:none"
inkscape:connector-curvature="0" />
<path
style="fill:url(#radialGradient4266);fill-opacity:1;stroke:none"
d="m 369.75735,359.49999 0,-57.36595 c 0.21395,-6.69058 1.48284,-13.34882 3.92587,-19.62604 8.34674,-22.92119 31.23869,-39.42587 55.47413,-39.71118 0.75084,-0.0301 1.52631,-0.0581 2.27586,-0.0572 23.23569,0.0265 45.61943,14.77505 54.96208,36.26801 3.26038,7.2537 5.06733,15.16666 5.34827,23.12659 l 0,57.36584 M 554.5,359.5 554.49909,302.13415 C 553.31086,234.20574 498.38227,179.5 430.74976,179.5 363.11725,179.5 308.18822,234.20574 307,302.13405 L 307,359.5"
id="path4238"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccscc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -0,0 +1,239 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="864"
height="864"
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="ic_notif_master.svg">
<defs
id="defs4">
<linearGradient
id="linearGradient5944">
<stop
id="stop5952"
offset="0"
style="stop-color:#000000;stop-opacity:0.09803922;" />
<stop
id="stop5954"
offset="1"
style="stop-color:#000000;stop-opacity:0.09803922;" />
</linearGradient>
<linearGradient
id="linearGradient3903">
<stop
style="stop-color:#000000;stop-opacity:0.17647059;"
offset="0"
id="stop3905" />
<stop
id="stop3915"
offset="0.25018752"
style="stop-color:#000000;stop-opacity:0.09803922;" />
<stop
style="stop-color:#000000;stop-opacity:0.05882353;"
offset="0.50031251"
id="stop3919" />
<stop
style="stop-color:#000000;stop-opacity:0.09803922;"
offset="0.75"
id="stop3917" />
<stop
style="stop-color:#000000;stop-opacity:0.17647059;"
offset="1"
id="stop3907" />
</linearGradient>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="-291.42857 : 94.857143 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="1566.8571 : 394.85714 : 1"
inkscape:persp3d-origin="432 : 288 : 1"
id="perspective3012" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3903-3"
id="linearGradient3911-5"
x1="32.000004"
y1="432.00003"
x2="832"
y2="432.00003"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.99875033,0,0,0.99826344,0.53986459,0.7502204)" />
<linearGradient
id="linearGradient3903-3">
<stop
style="stop-color:#000000;stop-opacity:0.17647059;"
offset="0"
id="stop3905-2" />
<stop
id="stop3915-0"
offset="0.25018752"
style="stop-color:#000000;stop-opacity:0.09803922;" />
<stop
style="stop-color:#000000;stop-opacity:0.05882353;"
offset="0.50031251"
id="stop3919-4" />
<stop
style="stop-color:#000000;stop-opacity:0.09803922;"
offset="0.75"
id="stop3917-2" />
<stop
style="stop-color:#000000;stop-opacity:0.17647059;"
offset="1"
id="stop3907-9" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3903-3"
id="linearGradient5942"
x1="363.03113"
y1="342.44925"
x2="500.96887"
y2="342.44925"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.4585784,0,0,1.4585784,-198.10585,-178.10619)" />
<linearGradient
id="linearGradient3903-3-2">
<stop
style="stop-color:#000000;stop-opacity:0.17647059;"
offset="0"
id="stop3905-2-3" />
<stop
id="stop3915-0-3"
offset="0.25018752"
style="stop-color:#000000;stop-opacity:0.09803922;" />
<stop
style="stop-color:#000000;stop-opacity:0.05882353;"
offset="0.50031251"
id="stop3919-4-8" />
<stop
style="stop-color:#000000;stop-opacity:0.09803922;"
offset="0.75"
id="stop3917-2-6" />
<stop
style="stop-color:#000000;stop-opacity:0.17647059;"
offset="1"
id="stop3907-9-6" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3903-0"
id="linearGradient5962-5"
x1="340.4118"
y1="481.5072"
x2="523.5882"
y2="481.5072"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3903-0">
<stop
style="stop-color:#000000;stop-opacity:0.17647059;"
offset="0"
id="stop3905-0" />
<stop
id="stop3915-03"
offset="0.25018752"
style="stop-color:#000000;stop-opacity:0.09803922;" />
<stop
style="stop-color:#000000;stop-opacity:0.05882353;"
offset="0.50031251"
id="stop3919-2" />
<stop
style="stop-color:#000000;stop-opacity:0.09803922;"
offset="0.75"
id="stop3917-29" />
<stop
style="stop-color:#000000;stop-opacity:0.17647059;"
offset="1"
id="stop3907-6" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3903"
id="linearGradient6049"
gradientUnits="userSpaceOnUse"
x1="340.4118"
y1="481.5072"
x2="523.5882"
y2="481.5072"
gradientTransform="matrix(1.4585784,0,0,1.4585784,-198.10585,-178.10619)" />
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath4056">
<path
style="fill:#000000;fill-opacity:1;stroke:none;display:inline"
d="M 57.46875,142 307,350.875 l 0,-56.25 C 308.18822,226.69669 363.11749,172 430.75,172 c 67.63251,0 122.56177,54.69659 123.75,122.625 l 0,57.375 1.15625,0 250.875,-210 -749.0625,0 z M 32,167.65625 32,696.375 267,499.40625 267,364.375 32,167.65625 z m 800,0 L 597,364.375 597,499.40625 832,696.375 832,167.65625 z M 431.4375,235.25 c -0.74955,-0.001 -1.53041,0.001 -2.28125,0.0312 -24.23544,0.28531 -47.12201,16.79756 -55.46875,39.71875 -2.44303,6.27722 -3.72355,12.93442 -3.9375,19.625 l 0,57.375 122,0 0,-57.375 c -0.28094,-7.95993 -2.08337,-15.8713 -5.34375,-23.125 -9.34265,-21.49296 -31.73306,-36.2235 -54.96875,-36.25 z M 432,404.5 c -34.79394,0 -63,28.20605 -63,63 0,21.40237 10.6868,40.3019 27,51.6875 l 0,120.3125 72,0 0,-120.3125 c 16.31319,-11.3856 27,-30.28513 27,-51.6875 0,-34.79395 -28.20607,-63 -63,-63 z M 267,546.34375 59.8125,720 804.1875,720 597,546.34375 597,692 l -330,0 0,-145.65625 z"
id="path4058"
inkscape:connector-curvature="0" />
</clipPath>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.5"
inkscape:cx="659.75749"
inkscape:cy="504.31379"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1366"
inkscape:window-height="719"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="Bg"
style="display:none" />
<g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="Envelope"
style="display:inline">
<rect
style="fill:#ffffff;fill-opacity:1;stroke:none"
id="rect2994"
width="800"
height="578"
x="32"
y="142"
clip-path="url(#clipPath4056)" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Chain"
style="display:none" />
<g
inkscape:label="Lock bg"
id="g6041"
inkscape:groupmode="layer"
style="display:inline" />
</svg>

After

Width:  |  Height:  |  Size: 7.8 KiB

View File

@ -0,0 +1,260 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="864"
height="864"
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="ic_notif_premaster.svg">
<defs
id="defs4">
<linearGradient
id="linearGradient5944">
<stop
id="stop5952"
offset="0"
style="stop-color:#000000;stop-opacity:0.09803922;" />
<stop
id="stop5954"
offset="1"
style="stop-color:#000000;stop-opacity:0.09803922;" />
</linearGradient>
<linearGradient
id="linearGradient3903">
<stop
style="stop-color:#000000;stop-opacity:0.17647059;"
offset="0"
id="stop3905" />
<stop
id="stop3915"
offset="0.25018752"
style="stop-color:#000000;stop-opacity:0.09803922;" />
<stop
style="stop-color:#000000;stop-opacity:0.05882353;"
offset="0.50031251"
id="stop3919" />
<stop
style="stop-color:#000000;stop-opacity:0.09803922;"
offset="0.75"
id="stop3917" />
<stop
style="stop-color:#000000;stop-opacity:0.17647059;"
offset="1"
id="stop3907" />
</linearGradient>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="-291.42857 : 94.857143 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="1566.8571 : 394.85714 : 1"
inkscape:persp3d-origin="432 : 288 : 1"
id="perspective3012" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3903-3"
id="linearGradient3911-5"
x1="32.000004"
y1="432.00003"
x2="832"
y2="432.00003"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.99875033,0,0,0.99826344,0.53986459,0.7502204)" />
<linearGradient
id="linearGradient3903-3">
<stop
style="stop-color:#000000;stop-opacity:0.17647059;"
offset="0"
id="stop3905-2" />
<stop
id="stop3915-0"
offset="0.25018752"
style="stop-color:#000000;stop-opacity:0.09803922;" />
<stop
style="stop-color:#000000;stop-opacity:0.05882353;"
offset="0.50031251"
id="stop3919-4" />
<stop
style="stop-color:#000000;stop-opacity:0.09803922;"
offset="0.75"
id="stop3917-2" />
<stop
style="stop-color:#000000;stop-opacity:0.17647059;"
offset="1"
id="stop3907-9" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3903-3"
id="linearGradient5942"
x1="363.03113"
y1="342.44925"
x2="500.96887"
y2="342.44925"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.4585784,0,0,1.4585784,-198.10585,-178.10619)" />
<linearGradient
id="linearGradient3903-3-2">
<stop
style="stop-color:#000000;stop-opacity:0.17647059;"
offset="0"
id="stop3905-2-3" />
<stop
id="stop3915-0-3"
offset="0.25018752"
style="stop-color:#000000;stop-opacity:0.09803922;" />
<stop
style="stop-color:#000000;stop-opacity:0.05882353;"
offset="0.50031251"
id="stop3919-4-8" />
<stop
style="stop-color:#000000;stop-opacity:0.09803922;"
offset="0.75"
id="stop3917-2-6" />
<stop
style="stop-color:#000000;stop-opacity:0.17647059;"
offset="1"
id="stop3907-9-6" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3903-0"
id="linearGradient5962-5"
x1="340.4118"
y1="481.5072"
x2="523.5882"
y2="481.5072"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3903-0">
<stop
style="stop-color:#000000;stop-opacity:0.17647059;"
offset="0"
id="stop3905-0" />
<stop
id="stop3915-03"
offset="0.25018752"
style="stop-color:#000000;stop-opacity:0.09803922;" />
<stop
style="stop-color:#000000;stop-opacity:0.05882353;"
offset="0.50031251"
id="stop3919-2" />
<stop
style="stop-color:#000000;stop-opacity:0.09803922;"
offset="0.75"
id="stop3917-29" />
<stop
style="stop-color:#000000;stop-opacity:0.17647059;"
offset="1"
id="stop3907-6" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3903"
id="linearGradient6049"
gradientUnits="userSpaceOnUse"
x1="340.4118"
y1="481.5072"
x2="523.5882"
y2="481.5072"
gradientTransform="matrix(1.4585784,0,0,1.4585784,-198.10585,-178.10619)" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.5"
inkscape:cx="312.75749"
inkscape:cy="504.31379"
inkscape:document-units="px"
inkscape:current-layer="g6041"
showgrid="false"
inkscape:window-width="1366"
inkscape:window-height="719"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="Bg"
style="display:inline">
<rect
style="fill:#fafae6;fill-opacity:1;stroke:none;display:inline"
id="rect2994-9"
width="798.00293"
height="574.00299"
x="32"
y="145.99701" />
</g>
<g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="Envelope"
style="display:inline">
<rect
style="fill:#b3b3b3;fill-opacity:1;stroke:none"
id="rect2994"
width="800"
height="578"
x="32"
y="142" />
<path
id="path3044-9"
style="fill:none;stroke:#000000;stroke-width:36;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="M 32.99995,144.99995 431.99998,479.00212 831.00005,144.99995 M 375.41474,431.99882 33.000566,719.00005 m 797.998874,0 -342.41418,-287.00123"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Chain"
style="display:none" />
<g
inkscape:label="Lock bg"
id="g6041"
inkscape:groupmode="layer"
style="display:inline">
<path
id="path6043"
d="m 267,352.00003 0,340 330,0 0,-340 -330,0 z m 165,80.54046 c 20.333,0 36.82476,17.28664 36.82476,38.59975 0,17.02222 -10.50755,31.44372 -25.10002,36.57132 l 0,103.74803 -23.44948,0 0,-103.74803 c -14.57211,-5.13964 -25.10002,-19.5643 -25.10002,-36.57131 0,-21.31312 16.49176,-38.59976 36.82476,-38.59976 z"
style="fill:#ffda00;fill-opacity:1;fill-rule:nonzero;stroke:none"
inkscape:connector-curvature="0" />
<path
sodipodi:nodetypes="ccccccccccscc"
inkscape:connector-curvature="0"
id="path6045"
d="m 369.75735,351.99996 0,-57.36595 c 0.21395,-6.69058 1.48284,-13.34882 3.92587,-19.62604 8.34674,-22.92119 31.23869,-39.42587 55.47413,-39.71118 0.75084,-0.0301 1.52631,-0.0581 2.27586,-0.0572 23.23569,0.0265 45.61943,14.77505 54.96208,36.26801 3.26038,7.2537 5.06733,15.16666 5.34827,23.12659 l 0,57.36584 m 62.75644,-6e-5 -9.1e-4,-57.36585 C 553.31086,226.70571 498.38227,171.99997 430.74976,171.99997 363.11725,171.99997 308.18822,226.70571 307,294.63402 l 0,57.36595"
style="fill:#faffff;fill-opacity:1;stroke:none" />
<path
id="rect3796"
style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 396,511.50001 72,0 0,128 -72,0 z m 99,-43.99996 c 0,34.79393 -28.20606,62.99999 -62.99999,62.99999 -34.79394,0 -63.00001,-28.20606 -63.00001,-62.99999 0,-34.79395 28.20607,-63.00001 63.00001,-63.00001 34.79393,0 62.99999,28.20606 62.99999,63.00001 z"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="24"
height="24"
viewBox="0 0 24 24"
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="ic_scan_qr_code_24px.svg">
<metadata
id="metadata14">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs12" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1366"
inkscape:window-height="719"
id="namedview10"
showgrid="false"
inkscape:zoom="22.583333"
inkscape:cx="6.0442804"
inkscape:cy="12"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
inkscape:current-layer="layer2" />
<path
d="M0 0h24v24h-24z"
fill="none"
id="path8" />
<g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="Camera"
style="display:inline">
<path
d="M 8 6.09375 C 6.9396133 6.09375 6.09375 6.9396133 6.09375 8 C 6.09375 9.0603867 6.9396133 9.90625 8 9.90625 L 8 8 L 9.90625 8 C 9.90625 6.9396133 9.0603867 6.09375 8 6.09375 z "
id="circle4-5"
style="fill:#000000" />
<path
d="M 6.1875 2 L 5.09375 3.1875 L 3.1875 3.1875 C 2.5275 3.1875 2 3.74625 2 4.40625 L 2 11.59375 C 2 12.25375 2.5275 12.8125 3.1875 12.8125 L 8 12.8125 L 8 11 C 6.344 11 5 9.656 5 8 C 5 6.344 6.344 5 8 5 C 9.656 5 11 6.344 11 8 L 14 8 L 14 4.40625 C 14 3.74625 13.4725 3.1875 12.8125 3.1875 L 10.90625 3.1875 L 9.8125 2 L 6.1875 2 z "
id="path6-5"
style="fill:#000000" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="QR Code"
style="display:inline">
<path
style="fill:#000000;stroke:none"
d="m 8.625,8.625 0,4.46875 4.46875,0 0,-4.46875 -4.46875,0 z m 6.34375,0 0,0.65625 0.65625,0 0,-0.65625 -0.65625,0 z m 1.28125,0 0,1.28125 0.625,0 0,-1.28125 -0.625,0 z m 1.28125,0 0,4.46875 4.4375,0 0,-4.46875 -4.4375,0 z m -8.25,0.65625 3.15625,0 0,3.15625 -3.15625,0 0,-3.15625 z m 8.875,0 3.1875,0 0,3.15625 -3.1875,0 0,-3.15625 z"
id="path6"
inkscape:connector-curvature="0" />
<path
style="fill:#000000;stroke:none"
d="M 9.90625 9.90625 L 9.90625 11.8125 L 11.8125 11.8125 L 11.8125 9.90625 L 9.90625 9.90625 z M 13.71875 9.90625 L 13.71875 10.53125 L 14.34375 10.53125 L 14.34375 9.90625 L 13.71875 9.90625 z M 14.96875 9.90625 L 14.96875 10.53125 L 15.625 10.53125 L 15.625 9.90625 L 14.96875 9.90625 z M 18.78125 9.90625 L 18.78125 11.8125 L 20.6875 11.8125 L 20.6875 9.90625 L 18.78125 9.90625 z M 16.25 10.53125 L 16.25 11.1875 L 15.625 11.1875 L 15.625 11.8125 L 14.96875 11.8125 L 14.96875 11.1875 L 14.34375 11.1875 L 14.34375 12.4375 L 14.96875 12.4375 L 14.96875 14.34375 L 15.625 14.34375 L 15.625 12.4375 L 16.25 12.4375 L 16.25 11.8125 L 16.875 11.8125 L 16.875 10.53125 L 16.25 10.53125 z M 16.25 12.4375 L 16.25 13.09375 L 16.875 13.09375 L 16.875 12.4375 L 16.25 12.4375 z M 15.625 14.34375 L 15.625 15.625 L 14.96875 15.625 L 14.96875 16.25 L 15.625 16.25 L 15.625 16.875 L 13.71875 16.875 L 13.71875 19.4375 L 14.34375 19.4375 L 14.34375 20.0625 L 13.71875 20.0625 L 13.71875 21.96875 L 14.34375 21.96875 L 14.34375 21.34375 L 14.96875 21.34375 L 14.96875 20.0625 L 15.625 20.0625 L 15.625 21.96875 L 16.875 21.96875 L 16.875 21.34375 L 16.25 21.34375 L 16.25 20.6875 L 17.53125 20.6875 L 17.53125 20.0625 L 16.875 20.0625 L 16.875 18.78125 L 15.625 18.78125 L 15.625 19.4375 L 14.96875 19.4375 L 14.96875 18.78125 L 14.34375 18.78125 L 14.34375 18.15625 L 15.625 18.15625 L 15.625 17.53125 L 16.25 17.53125 L 16.25 16.25 L 16.875 16.25 L 16.875 15.625 L 16.25 15.625 L 16.25 14.34375 L 15.625 14.34375 z M 16.25 14.34375 L 16.875 14.34375 L 16.875 14.96875 L 17.53125 14.96875 L 17.53125 14.34375 L 18.15625 14.34375 L 18.15625 13.71875 L 16.25 13.71875 L 16.25 14.34375 z M 18.15625 14.34375 L 18.15625 14.96875 L 18.78125 14.96875 L 18.78125 15.625 L 18.15625 15.625 L 18.15625 16.25 L 18.78125 16.25 L 18.78125 16.875 L 19.4375 16.875 L 19.4375 15.625 L 20.0625 15.625 L 20.0625 14.34375 L 18.15625 14.34375 z M 20.0625 14.34375 L 20.6875 14.34375 L 20.6875 13.71875 L 20.0625 13.71875 L 20.0625 14.34375 z M 20.6875 14.34375 L 20.6875 15.625 L 21.34375 15.625 L 21.34375 16.875 L 21.96875 16.875 L 21.96875 14.34375 L 20.6875 14.34375 z M 19.4375 16.875 L 19.4375 17.53125 L 18.15625 17.53125 L 18.15625 18.15625 L 20.6875 18.15625 L 20.6875 19.4375 L 20.0625 19.4375 L 20.0625 20.0625 L 21.34375 20.0625 L 21.34375 18.15625 L 21.96875 18.15625 L 21.96875 17.53125 L 20.0625 17.53125 L 20.0625 16.875 L 19.4375 16.875 z M 21.34375 20.0625 L 21.34375 20.6875 L 21.96875 20.6875 L 21.96875 20.0625 L 21.34375 20.0625 z M 21.34375 20.6875 L 20.6875 20.6875 L 20.6875 21.96875 L 21.96875 21.96875 L 21.96875 21.34375 L 21.34375 21.34375 L 21.34375 20.6875 z M 18.15625 16.25 L 17.53125 16.25 L 17.53125 16.875 L 18.15625 16.875 L 18.15625 16.25 z M 17.53125 16.875 L 16.875 16.875 L 16.875 17.53125 L 16.25 17.53125 L 16.25 18.15625 L 17.53125 18.15625 L 17.53125 16.875 z M 17.53125 20.0625 L 18.15625 20.0625 L 18.15625 19.4375 L 17.53125 19.4375 L 17.53125 20.0625 z M 18.15625 20.0625 L 18.15625 21.34375 L 18.78125 21.34375 L 18.78125 20.6875 L 19.4375 20.6875 L 19.4375 20.0625 L 18.15625 20.0625 z M 19.4375 20.6875 L 19.4375 21.34375 L 20.0625 21.34375 L 20.0625 20.6875 L 19.4375 20.6875 z M 19.4375 21.34375 L 18.78125 21.34375 L 18.78125 21.96875 L 19.4375 21.96875 L 19.4375 21.34375 z M 13.71875 16.875 L 13.71875 16.25 L 12.4375 16.25 L 12.4375 16.875 L 13.71875 16.875 z M 12.4375 16.25 L 12.4375 15.625 L 13.09375 15.625 L 13.09375 14.96875 L 12.4375 14.96875 L 12.4375 14.34375 L 14.34375 14.34375 L 14.34375 12.4375 L 13.71875 12.4375 L 13.71875 13.71875 L 11.1875 13.71875 L 11.1875 14.34375 L 11.8125 14.34375 L 11.8125 16.25 L 12.4375 16.25 z M 11.1875 14.34375 L 10.53125 14.34375 L 10.53125 14.96875 L 11.1875 14.96875 L 11.1875 14.34375 z M 10.53125 14.96875 L 9.90625 14.96875 L 9.90625 15.625 L 10.53125 15.625 L 10.53125 14.96875 z M 9.90625 15.625 L 8.625 15.625 L 8.625 16.875 L 9.90625 16.875 L 9.90625 15.625 z M 9.90625 14.96875 L 9.90625 14.34375 L 10.53125 14.34375 L 10.53125 13.71875 L 8.625 13.71875 L 8.625 14.34375 L 9.28125 14.34375 L 9.28125 14.96875 L 9.90625 14.96875 z "
id="path10" />
<path
style="fill:#000000;stroke:none"
d="m 13.71875,14.96875 0,1.28125 0.625,0 0,-1.28125 -0.625,0 z m 3.8125,0 0,0.65625 0.625,0 0,-0.65625 -0.625,0 z m -7,1.28125 0,0.625 0.65625,0 0,-0.625 -0.65625,0 z m -1.90625,1.28125 0,4.4375 4.46875,0 0,-4.4375 -4.46875,0 z m 0.65625,0.625 3.15625,0 0,3.1875 -3.15625,0 0,-3.1875 z"
id="path14"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:#000000;stroke:none"
d="m 9.9043479,18.791304 0,1.904348 1.9043481,0 0,-1.904348 -1.9043481,0 m 8.8869561,0 0,0.634783 0.634783,0 0,-0.634783 -0.634783,0 z"
id="path18" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.6 KiB

3421
android/art/intro_1.svg Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 215 KiB

232
android/art/intro_3.svg Normal file
View File

@ -0,0 +1,232 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="550"
height="330"
id="svg3593"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="intro_3.svg">
<defs
id="defs3595">
<inkscape:path-effect
effect="spiro"
id="path-effect10505"
is_visible="true" />
<inkscape:path-effect
effect="spiro"
id="path-effect10509"
is_visible="true" />
<inkscape:path-effect
effect="spiro"
id="path-effect10513"
is_visible="true" />
<inkscape:path-effect
effect="spiro"
id="path-effect10517"
is_visible="true" />
<inkscape:path-effect
effect="spiro"
id="path-effect10527"
is_visible="true" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.8066667"
inkscape:cx="273.05915"
inkscape:cy="216.42064"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1366"
inkscape:window-height="719"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata3598">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-72.047244,-414.12837)">
<g
id="g3656"
transform="matrix(1.8527508,0,0,1.8527508,-534.03482,-567.62578)">
<g
inkscape:export-ydpi="166.74196"
inkscape:export-xdpi="166.74196"
inkscape:export-filename="/home/rlafuente/Projects/GPG/ilustras-site/weboftrust.png"
style="stroke:#5f8dd3;stroke-opacity:0.26600988"
id="g10533"
transform="translate(310.49464,529.52136)">
<path
style="fill:none;stroke:#5f8dd3;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter"
d="m 53.577152,36.539389 c 3.446669,6.821656 9.246643,12.426248 16.182357,15.637227 6.935714,3.210979 14.961735,4.00731 22.393192,2.221824 7.907299,-1.899812 14.832479,-6.515235 21.932259,-10.481067 3.54988,-1.982916 7.19909,-3.826658 11.06232,-5.095157 3.86324,-1.268499 7.9621,-1.950918 12.01023,-1.568465 7.55605,0.713868 14.29924,5.037997 20.0543,9.986012 5.75505,4.948014 10.88297,10.652598 17.09252,15.016659 12.23228,8.596828 27.64271,11.323994 40.71864,18.573413 6.04873,3.353473 11.52302,7.634008 17.23318,11.53619 5.71015,3.902183 11.78069,7.481432 18.48492,9.180305 5.1565,1.30667 10.61142,1.45602 15.8165,0.35874 5.20509,-1.09728 10.15089,-3.446136 14.24242,-6.845596 4.09153,-3.39946 7.31575,-7.848629 9.2019,-12.822496 1.88615,-4.973867 2.42149,-10.462917 1.45781,-15.694384 -1.62813,-8.838564 -7.38236,-16.470616 -14.28877,-22.221494 -6.90641,-5.750877 -14.97858,-9.888348 -22.85805,-14.210969 -6.67516,-3.661949 -13.34554,-7.524675 -20.68086,-9.564462 -3.66766,-1.019893 -7.48659,-1.570017 -11.28788,-1.364723 -3.80128,0.205295 -7.58759,1.182729 -10.89298,3.071203 -4.13421,2.362008 -7.4027,6.094007 -9.57536,10.330791 -2.17267,4.236784 -3.28771,8.963787 -3.66217,13.710427 -0.74893,9.493278 1.38814,18.940272 2.93547,28.336495 1.54732,9.396223 2.47833,19.246711 -0.57059,28.268198 -1.52447,4.51075 -4.06131,8.72983 -7.62149,11.89147 -3.56017,3.16164 -8.17786,5.2126 -12.93779,5.33022 -4.16366,0.10288 -8.27321,-1.25405 -11.87295,-3.34893 -3.59974,-2.09488 -6.73235,-4.90518 -9.64832,-7.87903 -5.83193,-5.94769 -11.09784,-12.779115 -18.483,-16.632163 -4.39315,-2.292031 -9.39515,-3.403741 -14.34559,-3.188348 -4.95043,0.215392 -9.83697,1.757353 -14.01444,4.422291 -4.17747,2.664938 -7.63548,6.446226 -9.91745,10.84461 -2.281978,4.39838 -3.38224,9.40291 -3.155521,14.35284"
id="path10503"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#5f8dd3;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter"
d="m 62.863858,97.260161 c 5.097542,1.2417 10.535263,1.054311 15.535232,-0.535361 4.99997,-1.589672 9.547503,-4.576956 12.992021,-8.534487 3.444517,-3.957531 5.775903,-8.873685 6.660624,-14.045147 C 98.936456,68.973704 98.371914,63.562122 96.438873,58.684612 93.295457,50.753045 86.579149,44.460097 78.70377,41.178446 70.828391,37.896796 61.897847,37.510872 53.577152,39.396837 45.07097,41.324844 37.068097,45.628355 31.011904,51.904889 c -6.056192,6.276533 -10.10073,14.535604 -11.009767,23.210049 -0.659048,6.28893 0.316775,12.723156 2.661565,18.595715 2.34479,5.872559 6.042984,11.181417 10.628449,15.535547 9.17093,8.70828 21.746528,13.41706 34.373791,14.1186 12.627264,0.70153 25.285435,-2.41925 36.744388,-7.77016 11.45896,-5.35091 21.79045,-12.87585 31.31845,-21.191927 17.74857,-15.491021 32.97169,-33.867347 51.88219,-47.916416 9.45525,-7.024534 19.85036,-12.939244 31.11597,-16.378996 11.26561,-3.439753 23.45429,-4.322673 34.87158,-1.425894 16.92854,4.295087 31.28951,16.988129 38.57555,32.860653 6.64539,14.476878 7.55739,31.493879 2.49737,46.59809 -5.06003,15.10422 -16.03914,28.1376 -30.06439,35.68966 -14.02525,7.55206 -30.94989,9.54377 -46.34516,5.45394 -15.39528,-4.08982 -29.10011,-14.21837 -37.5283,-27.73528"
id="path10507"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#5f8dd3;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter"
d="m 210.7368,15.108529 c -9.52495,3.060099 -18.19707,8.730176 -24.82036,16.228234 -6.62329,7.498058 -11.1797,16.803687 -13.04083,26.633487 -3.11209,16.436863 1.4141,33.760561 10.32072,47.92134 8.90661,14.16078 21.56713,26.2605 36.11281,34.23029 13.44766,7.36817 29.50755,13.57769 44.641,11.10619 11.28635,-1.84322 21.62967,-9.64003 28.92782,-18.44436 9.26901,-11.18194 14.01979,-26.08798 16.20543,-40.446713 1.96182,-12.888262 2.02067,-26.992939 -3.08496,-38.988294 -3.92476,-9.220957 -10.69253,-18.662768 -19.95598,-22.486142 -10.45699,-4.315992 -23.22057,-1.704106 -33.87266,2.105018 -27.98558,10.007472 -49.35913,33.035776 -72.31941,52.153432 -11.48014,9.558828 -23.61256,18.515379 -37.12743,24.880379 -13.51486,6.36499 -28.55064,10.05696 -43.426629,8.68963 C 83.922493,117.27793 68.836585,110.10932 59.292048,97.974523 51.598811,88.193446 47.783569,75.44405 48.83938,63.044831 49.895191,50.645612 55.811132,38.72484 65.047363,30.385331 74.283594,22.045822 86.744566,17.373849 99.186856,17.585462 c 12.442294,0.211614 24.737184,5.304626 33.684484,13.953393"
id="path10511"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csssaaaaaaasssssssc" />
<path
style="fill:none;stroke:#5f8dd3;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter"
d="m 172.87561,57.255888 c 8.01552,7.276306 13.79209,16.986143 16.36168,27.502338 2.56959,10.516195 1.92171,21.795824 -1.83508,31.948644 -3.75678,10.15282 -10.60712,19.13738 -19.40305,25.44807 -8.79593,6.31069 -19.50154,9.92173 -30.32279,10.22799 -10.82124,0.30626 -21.71395,-2.69351 -30.85273,-8.49661 -9.13877,-5.8031 -16.486304,-14.38584 -20.8113,-24.30993 C 81.687344,109.6523 80.402541,98.427374 82.373237,87.78268 84.343934,77.137987 89.562085,67.116978 97.153235,59.398974 108.15303,48.215368 123.29475,42.333858 137.87187,36.539389 c 28.70028,-11.408483 58.29155,-23.895549 89.1345,-22.291834 15.42148,0.801857 30.87843,5.329118 43.31677,14.480748 12.43835,9.151631 21.59546,23.178461 23.27965,38.528653 1.04157,9.493175 -0.76057,19.216885 -4.6936,27.919559 -3.93303,8.702675 -9.9518,16.396405 -17.13383,22.691145 -14.36406,12.58946 -32.98622,19.40885 -51.75186,22.96858 -18.05976,3.42583 -37.42238,3.91954 -54.12871,-3.7479 -8.35317,-3.83373 -15.85684,-9.70893 -21.03065,-17.30527 -5.17382,-7.59635 -7.91434,-16.95018 -6.99227,-26.094719 0.88562,-8.783118 5.05751,-17.008373 10.68216,-23.812107 5.62466,-6.803734 12.6598,-12.302867 19.94401,-17.289627 14.56842,-9.973521 30.87199,-18.599268 40.81003,-33.191916"
id="path10515"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cssssssssssssssssssssc" />
<path
style="fill:none;stroke:#5f8dd3;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter"
d="m 216.95343,76.218535 c -0.63775,-10.663931 2.74188,-21.52057 9.31969,-29.938324 6.5778,-8.417754 16.29534,-14.321824 26.79712,-16.281092 10.12874,-1.889671 21.15578,0.01023 29.3712,6.228668 4.10771,3.10922 7.45652,7.247127 9.48852,11.981208 2.03199,4.734081 2.723,10.059732 1.80327,15.128719 -0.82355,4.538908 -2.9037,8.791225 -5.62962,12.512686 -2.72593,3.721461 -6.08741,6.938042 -9.64973,9.868873 -7.12465,5.861663 -15.13974,10.67971 -21.59516,17.271167 -6.10626,6.23494 -10.59583,13.82967 -15.41085,21.10816 -4.81503,7.27849 -10.1631,14.45867 -17.42261,19.30226 -4.70252,3.13755 -10.14495,5.20747 -15.77111,5.759 -5.62616,0.55153 -11.42693,-0.44266 -16.44787,-3.04036 -5.02094,-2.5977 -9.2248,-6.81889 -11.60028,-11.9487 -2.37548,-5.12981 -2.86461,-11.1488 -1.13733,-16.53159 1.51394,-4.71795 4.62952,-8.8005 8.31707,-12.11003 3.68756,-3.30954 7.94829,-5.909015 12.23973,-8.385821 4.29144,-2.476805 8.65526,-4.860296 12.64033,-7.804834 3.98507,-2.944537 7.61316,-6.500118 9.9915,-10.846904 2.4769,-4.526912 3.50341,-9.800373 3.11774,-14.94617 -0.38567,-5.145796 -2.15822,-10.155315 -4.91446,-14.517782 -5.51247,-8.724936 -14.77814,-14.642151 -24.66663,-17.596744 -9.88849,-2.954594 -20.40488,-3.170152 -30.69277,-2.350934 -10.28789,0.819217 -20.46984,2.645355 -30.73654,3.697344 -8.44667,0.865498 -17.15082,1.255551 -24.9183,4.685068 -3.88374,1.714758 -7.47926,4.203998 -10.114932,7.532218 -2.635675,3.328219 -4.260174,7.533457 -4.11436,11.7764 0.166509,4.845137 2.611199,9.412323 5.975762,12.902712 3.36456,3.490389 7.60074,6.014831 11.9365,8.183757 4.33577,2.168926 8.83001,4.028261 13.04511,6.423272 4.21509,2.395011 8.19738,5.386788 10.96844,9.364766 3.08088,4.422741 4.5049,9.937482 4.13305,15.314672 -0.37185,5.37719 -2.50704,10.5913 -5.87043,14.8032 -3.36338,4.2119 -7.92575,7.42411 -12.96108,9.34712 -5.03532,1.92302 -10.52909,2.57136 -15.895,2.06197 C 95.817577,134.1537 85.842178,128.53798 78.297211,120.83841 70.752243,113.13884 65.474495,103.46263 61.625888,93.392963 55.045111,76.174732 52.432838,57.453444 54.048935,39.091463"
id="path10525"
inkscape:connector-curvature="0" />
</g>
<path
inkscape:export-ydpi="166.74196"
inkscape:export-xdpi="166.74196"
inkscape:export-filename="/home/rlafuente/Projects/GPG/ilustras-site/weboftrust.png"
style="color:#000000;fill:#5f8dd3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.76498926;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 528.73107,597.28713 9.13163,5.2711 0,10.54963 -7.93635,4.58066 0,18.03313 c 0,0.53513 -0.43001,0.96513 -0.96513,0.96513 l -0.46772,0 c -0.53513,0 -0.96513,-0.43 -0.96513,-0.96513 l 0,-0.48999 -0.69044,0 -3.50418,0 0,-1.04681 1.31408,0 c 0.13383,0 0.24499,-0.28144 0.24499,-0.40831 0,-0.12684 -0.11116,-0.4306 -0.24499,-0.4306 l -0.59393,0 0,-0.57907 c 0,0 0.24499,-0.10375 0.24499,-0.23758 l 0,-0.0668 c 0,-0.13382 -0.11116,-0.24498 -0.24499,-0.24498 l -0.72015,0 0,-0.7053 0.95029,0 c 0.13385,0 0.24501,-0.11115 0.24501,-0.24498 l 0,-0.77211 c 0,-0.13385 -0.11116,-0.24498 -0.24501,-0.24498 l -0.95029,0 0,-0.68301 3.50418,0 0.69044,0 0,-11.886 -7.92892,-4.57324 0,-10.54963 9.13162,-5.2711 z"
id="rect7319-3"
inkscape:connector-curvature="0" />
<path
inkscape:export-ydpi="166.74196"
inkscape:export-xdpi="166.74196"
inkscape:export-filename="/home/rlafuente/Projects/GPG/ilustras-site/weboftrust.png"
inkscape:connector-curvature="0"
id="path10477"
d="m 448.27081,614.25901 9.13163,5.2711 0,10.54964 -7.93635,4.58064 0,18.03311 c 0,0.53513 -0.43001,0.96513 -0.96513,0.96513 l -0.46772,0 c -0.53513,0 -0.96513,-0.43 -0.96513,-0.96513 l 0,-0.48999 -0.69044,0 -3.50418,0 0,-1.04681 1.31408,0 c 0.13383,0 0.24499,-0.28144 0.24499,-0.40831 0,-0.12684 -0.11116,-0.4306 -0.24499,-0.4306 l -0.59393,0 0,-0.57907 c 0,0 0.24499,-0.10375 0.24499,-0.23758 l 0,-0.0668 c 0,-0.13382 -0.11116,-0.24498 -0.24499,-0.24498 l -0.72015,0 0,-0.7053 0.95029,0 c 0.13385,0 0.24501,-0.11115 0.24501,-0.24498 l 0,-0.77211 c 0,-0.13385 -0.11116,-0.24498 -0.24501,-0.24498 l -0.95029,0 0,-0.68301 3.50418,0 0.69044,0 0,-11.88598 -7.92892,-4.57322 0,-10.54964 9.13162,-5.2711 z"
style="color:#000000;fill:#5f8dd3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.76498926;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
inkscape:export-ydpi="166.74196"
inkscape:export-xdpi="166.74196"
inkscape:export-filename="/home/rlafuente/Projects/GPG/ilustras-site/weboftrust.png"
style="color:#000000;fill:#5f8dd3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.76498926;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 484.18906,576.53886 9.13163,5.2711 0,10.54963 -7.93635,4.58066 0,18.03311 c 0,0.53513 -0.43001,0.96513 -0.96513,0.96513 l -0.46772,0 c -0.53513,0 -0.96513,-0.43 -0.96513,-0.96513 l 0,-0.48999 -0.69044,0 -3.50418,0 0,-1.04681 1.31408,0 c 0.13383,0 0.24499,-0.28144 0.24499,-0.40831 0,-0.12684 -0.11116,-0.4306 -0.24499,-0.4306 l -0.59393,0 0,-0.57907 c 0,0 0.24499,-0.10375 0.24499,-0.23758 l 0,-0.0668 c 0,-0.13382 -0.11116,-0.24498 -0.24499,-0.24498 l -0.72015,0 0,-0.7053 0.95029,0 c 0.13385,0 0.24501,-0.11115 0.24501,-0.24498 l 0,-0.77211 c 0,-0.13385 -0.11116,-0.24498 -0.24501,-0.24498 l -0.95029,0 0,-0.68301 3.50418,0 0.69044,0 0,-11.88598 -7.92892,-4.57324 0,-10.54963 9.13162,-5.2711 z"
id="path10479"
inkscape:connector-curvature="0" />
<path
inkscape:export-ydpi="166.74196"
inkscape:export-xdpi="166.74196"
inkscape:export-filename="/home/rlafuente/Projects/GPG/ilustras-site/weboftrust.png"
inkscape:connector-curvature="0"
id="path10481"
d="m 444.47926,552.78386 9.13163,5.2711 0,10.54963 -7.93635,4.58066 0,18.03311 c 0,0.53513 -0.43001,0.96513 -0.96513,0.96513 l -0.46772,0 c -0.53513,0 -0.96513,-0.43 -0.96513,-0.96513 l 0,-0.48999 -0.69044,0 -3.50418,0 0,-1.04681 1.31408,0 c 0.13383,0 0.24499,-0.28144 0.24499,-0.40831 0,-0.12684 -0.11116,-0.4306 -0.24499,-0.4306 l -0.59393,0 0,-0.57907 c 0,0 0.24499,-0.10375 0.24499,-0.23758 l 0,-0.0668 c 0,-0.13382 -0.11116,-0.24498 -0.24499,-0.24498 l -0.72015,0 0,-0.7053 0.95029,0 c 0.13385,0 0.24501,-0.11115 0.24501,-0.24498 l 0,-0.77211 c 0,-0.13385 -0.11116,-0.24498 -0.24501,-0.24498 l -0.95029,0 0,-0.68301 3.50418,0 0.69044,0 0,-11.88598 -7.92892,-4.57324 0,-10.54963 9.13162,-5.2711 z"
style="color:#000000;fill:#5f8dd3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.76498926;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
inkscape:export-ydpi="166.74196"
inkscape:export-xdpi="166.74196"
inkscape:export-filename="/home/rlafuente/Projects/GPG/ilustras-site/weboftrust.png"
style="color:#000000;fill:#5f8dd3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.76498926;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 408.06273,577.35447 9.13162,5.2711 0,10.54963 -7.93634,4.58066 0,18.03311 c 0,0.53513 -0.43001,0.96513 -0.96513,0.96513 l -0.46772,0 c -0.53513,0 -0.96513,-0.43 -0.96513,-0.96513 l 0,-0.48999 -0.69044,0 -3.50418,0 0,-1.04681 1.31408,0 c 0.13383,0 0.24499,-0.28144 0.24499,-0.40831 0,-0.12684 -0.11116,-0.4306 -0.24499,-0.4306 l -0.59393,0 0,-0.57907 c 0,0 0.24499,-0.10375 0.24499,-0.23758 l 0,-0.0668 c 0,-0.13382 -0.11116,-0.24498 -0.24499,-0.24498 l -0.72015,0 0,-0.7053 0.95029,0 c 0.13385,0 0.24501,-0.11115 0.24501,-0.24498 l 0,-0.77211 c 0,-0.13385 -0.11116,-0.24498 -0.24501,-0.24498 l -0.95029,0 0,-0.68301 3.50418,0 0.69044,0 0,-11.88598 -7.92892,-4.57324 0,-10.54963 9.13162,-5.2711 z"
id="path10483"
inkscape:connector-curvature="0" />
<path
inkscape:export-ydpi="166.74196"
inkscape:export-xdpi="166.74196"
inkscape:export-filename="/home/rlafuente/Projects/GPG/ilustras-site/weboftrust.png"
inkscape:connector-curvature="0"
id="path10485"
d="m 409.49657,636.84092 9.13162,5.2711 0,10.54963 -7.93634,4.58066 0,18.03311 c 0,0.53513 -0.43001,0.96513 -0.96513,0.96513 l -0.46772,0 c -0.53513,0 -0.96513,-0.43 -0.96513,-0.96513 l 0,-0.48999 -0.69044,0 -3.50418,0 0,-1.04681 1.31408,0 c 0.13383,0 0.24499,-0.28144 0.24499,-0.40831 0,-0.12684 -0.11116,-0.4306 -0.24499,-0.4306 l -0.59393,0 0,-0.57907 c 0,0 0.24499,-0.10375 0.24499,-0.23758 l 0,-0.0668 c 0,-0.13382 -0.11116,-0.24498 -0.24499,-0.24498 l -0.72015,0 0,-0.7053 0.95029,0 c 0.13385,0 0.24501,-0.11115 0.24501,-0.24498 l 0,-0.77211 c 0,-0.13385 -0.11116,-0.24498 -0.24501,-0.24498 l -0.95029,0 0,-0.68301 3.50418,0 0.69044,0 0,-11.88598 -7.92892,-4.57324 0,-10.54963 9.13162,-5.2711 z"
style="color:#000000;fill:#5f8dd3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.76498926;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
inkscape:export-ydpi="166.74196"
inkscape:export-xdpi="166.74196"
inkscape:export-filename="/home/rlafuente/Projects/GPG/ilustras-site/weboftrust.png"
style="color:#000000;fill:#5f8dd3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.76498926;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 373.31363,615.62797 9.13163,5.27111 0,10.54963 -7.93635,4.58066 0,18.03311 c 0,0.53513 -0.43001,0.96513 -0.96513,0.96513 l -0.46772,0 c -0.53513,0 -0.96513,-0.43 -0.96513,-0.96513 l 0,-0.48999 -0.69044,0 -3.50418,0 0,-1.04681 1.31408,0 c 0.13383,0 0.24499,-0.28144 0.24499,-0.40831 0,-0.12684 -0.11116,-0.4306 -0.24499,-0.4306 l -0.59393,0 0,-0.57907 c 0,0 0.24499,-0.10375 0.24499,-0.23758 l 0,-0.0668 c 0,-0.13382 -0.11116,-0.24498 -0.24499,-0.24498 l -0.72015,0 0,-0.7053 0.95029,0 c 0.13385,0 0.24501,-0.11115 0.24501,-0.24498 l 0,-0.77211 c 0,-0.13385 -0.11116,-0.24498 -0.24501,-0.24498 l -0.95029,0 0,-0.68301 3.50418,0 0.69044,0 0,-11.88598 -7.92892,-4.57324 0,-10.54963 9.13162,-5.27111 z"
id="path10487"
inkscape:connector-curvature="0" />
<path
inkscape:export-ydpi="166.74196"
inkscape:export-xdpi="166.74196"
inkscape:export-filename="/home/rlafuente/Projects/GPG/ilustras-site/weboftrust.png"
inkscape:connector-curvature="0"
id="path10489"
d="m 364.72877,558.55827 9.13163,5.27111 0,10.54963 -7.93635,4.58066 0,18.03311 c 0,0.53513 -0.43001,0.96513 -0.96513,0.96513 l -0.46772,0 c -0.53513,0 -0.96513,-0.43 -0.96513,-0.96513 l 0,-0.48999 -0.69044,0 -3.50418,0 0,-1.04681 1.31408,0 c 0.13383,0 0.24499,-0.28144 0.24499,-0.40831 0,-0.12684 -0.11116,-0.4306 -0.24499,-0.4306 l -0.59393,0 0,-0.57907 c 0,0 0.24499,-0.10375 0.24499,-0.23758 l 0,-0.0668 c 0,-0.13382 -0.11116,-0.24498 -0.24499,-0.24498 l -0.72015,0 0,-0.7053 0.95029,0 c 0.13385,0 0.24501,-0.11115 0.24501,-0.24498 l 0,-0.77211 c 0,-0.13385 -0.11116,-0.24498 -0.24501,-0.24498 l -0.95029,0 0,-0.68301 3.50418,0 0.69044,0 0,-11.88598 -7.92892,-4.57324 0,-10.54963 9.13162,-5.27111 z"
style="color:#000000;fill:#5f8dd3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.76498926;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
inkscape:export-ydpi="166.74196"
inkscape:export-xdpi="166.74196"
inkscape:export-filename="/home/rlafuente/Projects/GPG/ilustras-site/weboftrust.png"
style="color:#000000;fill:#5f8dd3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.76498926;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 519.15159,537.4864 9.13162,5.27111 0,10.54963 -7.93634,4.58066 0,18.03311 c 0,0.53513 -0.43001,0.96513 -0.96513,0.96513 l -0.46772,0 c -0.53513,0 -0.96513,-0.43 -0.96513,-0.96513 l 0,-0.48999 -0.69044,0 -3.50418,0 0,-1.04681 1.31408,0 c 0.13383,0 0.24499,-0.28144 0.24499,-0.40831 0,-0.12684 -0.11116,-0.4306 -0.24499,-0.4306 l -0.59393,0 0,-0.57907 c 0,0 0.24499,-0.10375 0.24499,-0.23758 l 0,-0.0668 c 0,-0.13382 -0.11116,-0.24498 -0.24499,-0.24498 l -0.72015,0 0,-0.7053 0.95029,0 c 0.13385,0 0.24501,-0.11115 0.24501,-0.24498 l 0,-0.77211 c 0,-0.13385 -0.11116,-0.24498 -0.24501,-0.24498 l -0.95029,0 0,-0.68301 3.50418,0 0.69044,0 0,-11.88598 -7.92892,-4.57324 0,-10.54963 9.13162,-5.27111 z"
id="path10491"
inkscape:connector-curvature="0" />
<path
inkscape:export-ydpi="166.74196"
inkscape:export-xdpi="166.74196"
inkscape:export-filename="/home/rlafuente/Projects/GPG/ilustras-site/weboftrust.png"
inkscape:connector-curvature="0"
id="path10493"
d="m 490.91156,638.98513 9.13162,5.27111 0,10.54963 -7.93634,4.58066 0,18.03311 c 0,0.53513 -0.43001,0.96513 -0.96513,0.96513 l -0.46772,0 c -0.53513,0 -0.96513,-0.43 -0.96513,-0.96513 l 0,-0.48999 -0.69044,0 -3.50418,0 0,-1.04681 1.31408,0 c 0.13383,0 0.24499,-0.28144 0.24499,-0.40831 0,-0.12684 -0.11116,-0.4306 -0.24499,-0.4306 l -0.59393,0 0,-0.57907 c 0,0 0.24499,-0.10375 0.24499,-0.23758 l 0,-0.0668 c 0,-0.13382 -0.11116,-0.24498 -0.24499,-0.24498 l -0.72015,0 0,-0.7053 0.95029,0 c 0.13385,0 0.24501,-0.11115 0.24501,-0.24498 l 0,-0.77211 c 0,-0.13385 -0.11116,-0.24498 -0.24501,-0.24498 l -0.95029,0 0,-0.68301 3.50418,0 0.69044,0 0,-11.88598 -7.92892,-4.57324 0,-10.54963 9.13162,-5.27111 z"
style="color:#000000;fill:#5f8dd3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.76498926;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
inkscape:export-ydpi="166.74196"
inkscape:export-xdpi="166.74196"
inkscape:export-filename="/home/rlafuente/Projects/GPG/ilustras-site/weboftrust.png"
style="color:#000000;fill:#5f8dd3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.76498926;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 532.20067,661.00753 9.13162,5.27111 0,10.54963 -7.93634,4.58066 0,18.03311 c 0,0.53513 -0.43001,0.96513 -0.96513,0.96513 l -0.46772,0 c -0.53513,0 -0.96513,-0.43 -0.96513,-0.96513 l 0,-0.48999 -0.69044,0 -3.50418,0 0,-1.04681 1.31408,0 c 0.13383,0 0.24499,-0.28144 0.24499,-0.40831 0,-0.12684 -0.11116,-0.4306 -0.24499,-0.4306 l -0.59393,0 0,-0.57907 c 0,0 0.24499,-0.10375 0.24499,-0.23758 l 0,-0.0668 c 0,-0.13382 -0.11116,-0.24498 -0.24499,-0.24498 l -0.72015,0 0,-0.7053 0.95029,0 c 0.13385,0 0.24501,-0.11115 0.24501,-0.24498 l 0,-0.77211 c 0,-0.13385 -0.11116,-0.24498 -0.24501,-0.24498 l -0.95029,0 0,-0.68301 3.50418,0 0.69044,0 0,-11.88598 -7.92892,-4.57324 0,-10.54963 9.13162,-5.27111 z"
id="path10495"
inkscape:connector-curvature="0" />
<path
inkscape:export-ydpi="166.74196"
inkscape:export-xdpi="166.74196"
inkscape:export-filename="/home/rlafuente/Projects/GPG/ilustras-site/weboftrust.png"
inkscape:connector-curvature="0"
id="path10497"
d="m 567.87483,620.45653 9.13162,5.27111 0,10.54964 -7.93634,4.58066 0,18.0331 c 0,0.53513 -0.43001,0.96513 -0.96513,0.96513 l -0.46772,0 c -0.53513,0 -0.96513,-0.43 -0.96513,-0.96513 l 0,-0.48999 -0.69044,0 -3.50418,0 0,-1.04681 1.31408,0 c 0.13383,0 0.24499,-0.28144 0.24499,-0.40831 0,-0.12684 -0.11116,-0.4306 -0.24499,-0.4306 l -0.59393,0 0,-0.57907 c 0,0 0.24499,-0.10375 0.24499,-0.23757 l 0,-0.0668 c 0,-0.13382 -0.11116,-0.24498 -0.24499,-0.24498 l -0.72015,0 0,-0.7053 0.95029,0 c 0.13385,0 0.24501,-0.11115 0.24501,-0.24498 l 0,-0.77211 c 0,-0.13385 -0.11116,-0.24498 -0.24501,-0.24498 l -0.95029,0 0,-0.68301 3.50418,0 0.69044,0 0,-11.88598 -7.92892,-4.57324 0,-10.54964 9.13162,-5.27111 z"
style="color:#000000;fill:#5f8dd3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.76498926;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
inkscape:export-ydpi="166.74196"
inkscape:export-xdpi="166.74196"
inkscape:export-filename="/home/rlafuente/Projects/GPG/ilustras-site/weboftrust.png"
style="color:#000000;fill:#5f8dd3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.76498926;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 563.92217,551.69123 9.13162,5.2711 0,10.54963 -7.93634,4.58066 0,18.03311 c 0,0.53513 -0.43001,0.96513 -0.96513,0.96513 l -0.46772,0 c -0.53513,0 -0.96513,-0.43 -0.96513,-0.96513 l 0,-0.48999 -0.69044,0 -3.50418,0 0,-1.04681 1.31408,0 c 0.13383,0 0.24499,-0.28144 0.24499,-0.40831 0,-0.12684 -0.11116,-0.4306 -0.24499,-0.4306 l -0.59393,0 0,-0.57907 c 0,0 0.24499,-0.10375 0.24499,-0.23758 l 0,-0.0668 c 0,-0.13382 -0.11116,-0.24498 -0.24499,-0.24498 l -0.72015,0 0,-0.7053 0.95029,0 c 0.13385,0 0.24501,-0.11115 0.24501,-0.24498 l 0,-0.77211 c 0,-0.13385 -0.11116,-0.24498 -0.24501,-0.24498 l -0.95029,0 0,-0.68301 3.50418,0 0.69044,0 0,-11.88598 -7.92892,-4.57324 0,-10.54963 9.13162,-5.2711 z"
id="path10499"
inkscape:connector-curvature="0" />
<path
inkscape:export-ydpi="166.74196"
inkscape:export-xdpi="166.74196"
inkscape:export-filename="/home/rlafuente/Projects/GPG/ilustras-site/weboftrust.png"
style="color:#000000;fill:#5f8dd3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.76498926;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 602.43919,583.17759 9.13162,5.27111 0,10.54963 -7.93634,4.58066 0,18.0331 c 0,0.53513 -0.43001,0.96513 -0.96513,0.96513 l -0.46772,0 c -0.53513,0 -0.96513,-0.43 -0.96513,-0.96513 l 0,-0.48999 -0.69044,0 -3.50418,0 0,-1.04681 1.31408,0 c 0.13383,0 0.24499,-0.28144 0.24499,-0.40831 0,-0.12684 -0.11116,-0.4306 -0.24499,-0.4306 l -0.59393,0 0,-0.57907 c 0,0 0.24499,-0.10375 0.24499,-0.23757 l 0,-0.0668 c 0,-0.13382 -0.11116,-0.24498 -0.24499,-0.24498 l -0.72015,0 0,-0.7053 0.95029,0 c 0.13385,0 0.24501,-0.11115 0.24501,-0.24498 l 0,-0.77211 c 0,-0.13385 -0.11116,-0.24498 -0.24501,-0.24498 l -0.95029,0 0,-0.68301 3.50418,0 0.69044,0 0,-11.88598 -7.92892,-4.57324 0,-10.54963 9.13162,-5.27111 z"
id="path10501"
inkscape:connector-curvature="0" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 27 KiB

5092
android/art/intro_4.svg Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 326 KiB

139
android/build.gradle Normal file
View File

@ -0,0 +1,139 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
versionCode 15
versionName '0.6.1'
minSdkVersion 14
targetSdkVersion 26
multiDexEnabled true
// For Espresso
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
applicationIdSuffix '.debug'
versionNameSuffix '-DEBUG'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
lintOptions {
abortOnError false
}
packagingOptions {
exclude 'LICENSE.txt'
}
dexOptions {
javaMaxHeapSize '2g'
jumboMode = true
}
}
def supportVersion = '25.4.0'
configurations.all {
resolutionStrategy {
force "com.android.support:design:$supportVersion"
force "com.android.support:support-annotations:$supportVersion"
}
}
dependencies {
// Local dependencies
implementation(project(':core')) {
// Replaced with Android-specific JavaMail
exclude group: 'com.sun.mail'
exclude group: 'com.sun.activation'
// BouncyCastle is replaced with SpongyCastle
exclude group: 'org.bouncycastle'
}
implementation project(':crypto')
implementation fileTree(dir: 'libs', include: '*.jar')
// Android Support Repository dependencies
//noinspection GradleCompatible
implementation 'com.android.support:multidex:1.0.3'
implementation "com.android.support:support-annotations:$supportVersion"
implementation "com.android.support:support-v4:$supportVersion"
implementation "com.android.support:appcompat-v7:$supportVersion"
implementation "com.android.support:preference-v7:$supportVersion"
implementation "com.android.support:preference-v14:$supportVersion"
implementation "com.android.support:recyclerview-v7:$supportVersion"
// Remote dependencies
implementation 'com.sun.mail:android-mail:1.6.3'
implementation 'com.sun.mail:android-activation:1.6.3'
implementation "net.i2p:router:$rootProject.i2pVersion"
implementation "net.i2p.android:client:$rootProject.i2pVersion@aar"
implementation 'net.i2p.android:helper:0.9.5@aar'
implementation 'net.i2p.android.ext:floatingactionbutton:1.10.1'
implementation 'com.madgag.spongycastle:core:1.58.0.0'
implementation 'com.madgag.spongycastle:prov:1.58.0.0'
implementation 'com.lambdaworks:scrypt:1.4.0'
implementation 'com.google.zxing:core:3.3.0'
implementation 'com.google.zxing:android-integration:3.3.0'
implementation 'com.androidplot:androidplot-core:1.5.6'
implementation 'com.inkapplications.viewpageindicator:library:2.4.4'
implementation 'com.pnikosis:materialish-progress:1.7'
implementation 'com.mikepenz:iconics-core:2.7.2@aar'
implementation 'com.mikepenz:google-material-typeface:2.2.0.3.original@aar'
implementation('com.mikepenz:materialdrawer:5.4.0@aar') {
transitive = true
}
implementation 'org.sufficientlysecure:html-textview:3.1'
// Testing-only dependencies
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test:rules:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-intents:3.0.2'
}
project.ext.i2pbase = '../i2p.i2p'
def Properties props = new Properties()
def propFile = new File(project(':android').projectDir, 'local.properties')
if (propFile.canRead()) {
props.load(new FileInputStream(propFile))
if (props != null &&
props.containsKey('i2psrc')) {
i2pbase = props['i2psrc']
} else {
println 'local.properties found but some entries are missing'
}
} else {
println 'local.properties not found'
}
task certificatesZip(type: Zip) {
archiveName = 'certificates_zip'
into('reseed') {
from files('' + i2pbase + '/installer/resources/certificates/reseed')
}
into('ssl') {
from files('' + i2pbase + '/installer/resources/certificates/ssl')
}
}
task copyI2PResources(type: Copy) {
// Force this to always run: Copy only detects source changes, not if missing in destination
outputs.upToDateWhen { false }
into 'src/main/res/raw'
from certificatesZip
}
task cleanI2PResources(type: Delete) {
delete file('src/main/res/raw/certificates_zip')
}
preBuild.dependsOn copyI2PResources
clean.dependsOn cleanI2PResources
apply from: "${project.rootDir}/gradle/signing.gradle"

Binary file not shown.

Binary file not shown.

22
android/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,22 @@
-dontobfuscate
-dontoptimize
-dontpreverify
-dontshrink
-dontwarn com.sun.mail.handlers.handler_base
-dontwarn java.awt.**
-dontwarn java.beans.Beans
-dontwarn javax.naming.**
-dontwarn javax.security.sasl.**
-dontwarn javax.security.auth.callback.NameCallback
-dontwarn android.test.**
-dontwarn org.junit.**
-dontwarn net.sf.ntru.**
-keepclassmembers class i2p.bote.crypto.ECUtils {
public static java.security.spec.ECParameterSpec getParameters(java.lang.String);
public static byte[] encodePoint(java.security.spec.ECParameterSpec, java.security.spec.ECPoint, boolean);
public static java.security.spec.ECPoint decodePoint(java.security.spec.EllipticCurve, byte[]);
}

View File

@ -0,0 +1,81 @@
package i2p.bote.android;
import android.support.test.espresso.NoMatchingViewException;
import android.support.test.espresso.intent.rule.IntentsTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.support.v4.widget.DrawerLayout;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import i2p.bote.android.config.SettingsActivity;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.swipeDown;
import static android.support.test.espresso.action.ViewActions.swipeRight;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.intent.Intents.intended;
import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent;
import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withClassName;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
@RunWith(AndroidJUnit4.class)
public class EmailListActivityTest {
@Rule
public IntentsTestRule<EmailListActivity> mActivityRule = new IntentsTestRule<>(EmailListActivity.class);
@Before
public void closeIntro() {
try {
// Close intro on first open
onView(withId(R.id.skip_intro)).perform(click());
// Close nav drawer on first open
// FIXME: 6/20/15 doesn't work
onView(withClassName(equalTo(DrawerLayout.class.getName()))).perform(swipeRight());
} catch (NoMatchingViewException e) {
}
}
@Test
public void checkEmailFromActionMenuWhenNotConnected() {
openActionBarOverflowOrOptionsMenu(mActivityRule.getActivity());
onView(withText(R.string.check_email)).perform(click());
onView(withText(R.string.bote_needs_to_be_connected)).inRoot(withDecorView(not(mActivityRule.getActivity().getWindow().getDecorView()))) .check(matches(isDisplayed()));
}
@Test
public void checkEmailByPullWhenNotConnected() {
onView(withId(R.id.swipe_refresh)).perform(swipeDown());
onView(withText(R.string.bote_needs_to_be_connected)).inRoot(withDecorView(not(mActivityRule.getActivity().getWindow().getDecorView()))) .check(matches(isDisplayed()));
}
@Test
public void newEmail() {
onView(withId(R.id.promoted_action)).perform(click());
intended(hasComponent(NewEmailActivity.class.getName()));
}
@Test
public void openSettings() {
openActionBarOverflowOrOptionsMenu(mActivityRule.getActivity());
onView(withText(R.string.action_settings)).perform(click());
intended(hasComponent(SettingsActivity.class.getName()));
}
@Test
public void openHelp() {
openActionBarOverflowOrOptionsMenu(mActivityRule.getActivity());
onView(withText(R.string.help)).perform(click());
intended(hasComponent(HelpActivity.class.getName()));
}
}

View File

@ -0,0 +1,35 @@
package i2p.bote.android.intro;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import i2p.bote.android.R;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.swipeLeft;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
@RunWith(AndroidJUnit4.class)
public class IntroActivityTest {
@Rule
public ActivityTestRule<IntroActivity> mRule = new ActivityTestRule<>(IntroActivity.class);
@Test
public void enterSetup() {
onView(withId(R.id.pager)).perform(swipeLeft(), swipeLeft(), swipeLeft(), swipeLeft(), swipeLeft());
onView(withId(R.id.start_setup_wizard)).perform(click());
// TODO check result
}
@Test
public void closeIntro() {
onView(withId(R.id.skip_intro)).perform(click());
// TODO check result
}
}

View File

@ -0,0 +1,84 @@
package i2p.bote.android.intro;
import android.app.Activity;
import android.app.Instrumentation;
import android.support.test.espresso.intent.rule.IntentsTestRule;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import i2p.bote.android.R;
import i2p.bote.android.config.SetPasswordActivity;
import i2p.bote.android.identities.EditIdentityActivity;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.intent.Intents.intended;
import static android.support.test.espresso.intent.Intents.intending;
import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
@RunWith(AndroidJUnit4.class)
public class SetupActivityTest {
@Rule
public IntentsTestRule<SetupActivity> mRule = new IntentsTestRule<>(SetupActivity.class);
@Test
public void setPassword() {
// Check we are on "Set password" page
onView(withId(R.id.textView)).check(matches(withText(R.string.set_password)));
onView(withId(R.id.button_set_password)).perform(click());
intended(hasComponent(SetPasswordActivity.class.getName()));
}
@Test
public void nextPageAfterSetPassword() {
// Check we are on "Set password" page
onView(withId(R.id.textView)).check(matches(withText(R.string.set_password)));
intending(hasComponent(SetPasswordActivity.class.getName())).respondWith(new Instrumentation.ActivityResult(Activity.RESULT_OK, null));
onView(withId(R.id.button_set_password)).perform(click());
onView(withId(R.id.textView)).check(matches(withText(R.string.create_identity)));
}
@Test
public void createIdentity() {
onView(withId(R.id.textView)).check(matches(withText(R.string.set_password)));
onView(withId(R.id.button_skip)).perform(click());
onView(withId(R.id.textView)).check(matches(withText(R.string.create_identity)));
onView(withId(R.id.button_set_password)).perform(click());
intended(hasComponent(EditIdentityActivity.class.getName()));
}
@Test
public void nextPageAfterCreateIdentity() {
// Check we are on "Create identity" page
onView(withId(R.id.button_skip)).perform(click());
onView(withId(R.id.textView)).check(matches(withText(R.string.create_identity)));
intending(hasComponent(EditIdentityActivity.class.getName())).respondWith(new Instrumentation.ActivityResult(Activity.RESULT_OK, null));
onView(withId(R.id.button_set_password)).perform(click());
onView(withId(R.id.textView)).check(matches(withText(R.string.setup_finished)));
}
@Test
public void setupFinished() {
onView(withId(R.id.textView)).check(matches(withText(R.string.set_password)));
onView(withId(R.id.button_skip)).perform(click());
onView(withId(R.id.textView)).check(matches(withText(R.string.create_identity)));
onView(withId(R.id.button_skip)).perform(click());
onView(withId(R.id.textView)).check(matches(withText(R.string.setup_finished)));
onView(withId(R.id.button_finish)).perform(click());
// TODO check result
}
}

View File

@ -0,0 +1,213 @@
package i2p.bote.android.provider;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.provider.OpenableColumns;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.ProviderTestCase2;
import android.test.suitebuilder.annotation.LargeTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.Part;
import javax.mail.internet.MimeBodyPart;
import i2p.bote.I2PBote;
import i2p.bote.android.InitActivities;
import i2p.bote.android.R;
import i2p.bote.android.util.BoteHelper;
import i2p.bote.android.util.ContentAttachment;
import i2p.bote.email.Attachment;
import i2p.bote.email.Email;
import i2p.bote.folder.EmailFolder;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
@RunWith(AndroidJUnit4.class)
@LargeTest
public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvider> {
private static final String URI_PREFIX = "content://" + AttachmentProvider.AUTHORITY;
private static final String INVALID_URI = URI_PREFIX + "/invalid";
private static final String NO_MATCH_URI = URI_PREFIX + "/foo/bar/1/RAW";
public AttachmentProviderTests() {
super(AttachmentProvider.class, AttachmentProvider.AUTHORITY);
setContext(InstrumentationRegistry.getTargetContext());
}
@Before
public void setUp() throws Exception {
super.setUp();
testAndroidTestCaseSetupProperly();
new InitActivities(getMockContext().getDir("botetest", Context.MODE_PRIVATE).getAbsolutePath()).initialize();
}
@Test(expected = IllegalArgumentException.class)
public void queryWithInvalidUriThrows() {
Uri invalidUri = Uri.parse(INVALID_URI);
getMockContentResolver().query(invalidUri, null, null, null, null);
}
@Test
public void getTypeWithInvalidUriReturnsNull() {
Uri invalidUri = Uri.parse(INVALID_URI);
String type = getMockContentResolver().getType(invalidUri);
assertThat("Type was not null", type, is(nullValue()));
}
@Test(expected = FileNotFoundException.class)
public void openFileWithInvalidUriThrows() throws FileNotFoundException {
Uri invalidUri = Uri.parse(INVALID_URI);
getMockContentResolver().openFileDescriptor(invalidUri, "r");
}
@Test
public void queryWithNoMatchReturnsNull() {
Uri noMatchUri = Uri.parse(NO_MATCH_URI);
Cursor c = getMockContentResolver().query(noMatchUri, null, null, null, null);
assertThat(c, is(nullValue()));
}
@Test
public void queryWithValidTextUri() throws Exception {
ContentAttachment attachment = createTextAttachment();
Uri uri = createEmailWithAttachment(attachment);
Cursor c = getMockContentResolver().query(uri, null, null, null, null);
assertThat(c.getCount(), is(1));
int nameIndex = c.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int sizeIndex = c.getColumnIndex(OpenableColumns.SIZE);
c.moveToFirst();
assertThat(c.getString(nameIndex), is(equalTo(attachment.getFileName())));
assertThat(c.getLong(sizeIndex), is(equalTo(attachment.getSize())));
}
@Test
public void getTypeWithValidTextUri() throws Exception {
ContentAttachment attachment = createTextAttachment();
Uri uri = createEmailWithAttachment(attachment);
String type = getMockContentResolver().getType(uri);
assertThat("Type was not correct", type, is("text/plain"));
}
@Test
public void getTypeWithValidImageUri() throws Exception {
ContentAttachment attachment = createImageAttachment();
Uri uri = createEmailWithAttachment(attachment);
String type = getMockContentResolver().getType(uri);
assertThat("Type was not correct", type, is("image/png"));
}
@Test
public void openFileWithValidTextUri() throws Exception {
ContentAttachment attachment = createTextAttachment();
Uri uri = createEmailWithAttachment(attachment);
openFileWithValidUri(attachment, uri);
}
@Test
public void openFileWithValidImageUri() throws Exception {
ContentAttachment attachment = createImageAttachment();
Uri uri = createEmailWithAttachment(attachment);
openFileWithValidUri(attachment, uri);
assertThat("Image could not be decoded",
BitmapFactory.decodeStream(getMockContentResolver().openInputStream(uri)),
is(notNullValue()));
}
private void openFileWithValidUri(ContentAttachment attachment, Uri uri) throws Exception {
InputStream inProv = getMockContentResolver().openInputStream(uri);
InputStream inOrig = attachment.getDataHandler().getInputStream();
ByteArrayOutputStream outProv = new ByteArrayOutputStream();
ByteArrayOutputStream outOrig = new ByteArrayOutputStream();
BoteHelper.copyStream(inProv, outProv);
BoteHelper.copyStream(inOrig, outOrig);
assertThat("Provider content does not match original content",
outProv.toByteArray(), is(equalTo(outOrig.toByteArray())));
}
private ContentAttachment createTextAttachment() throws Exception {
return createAttachment("test.txt", "text/plain", "Test file content".getBytes());
}
private ContentAttachment createImageAttachment() throws Exception {
Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.intro_1);
ByteArrayOutputStream out = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
return createAttachment("intro_1.png", "image/png", out.toByteArray());
}
private ContentAttachment createAttachment(final String fileName, final String mimeType, final byte[] content) throws Exception {
Part part = new MimeBodyPart();
part.setDataHandler(new DataHandler(new DataSource() {
@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(content);
}
@Override
public OutputStream getOutputStream() throws IOException {
throw new IOException("Cannot write to attachments");
}
@Override
public String getContentType() {
return mimeType;
}
@Override
public String getName() {
return fileName;
}
}));
part.setFileName(fileName);
return new ContentAttachment(getMockContext(), part);
}
private Uri createEmailWithAttachment(Attachment attachment) throws Exception {
List<Attachment> attachments = new ArrayList<Attachment>();
attachments.add(attachment);
Email email = new Email(false);
email.setContent("", attachments);
I2PBote.getInstance().getInbox().add(email);
return AttachmentProvider.getUriForAttachment(
I2PBote.getInstance().getInbox().getName(),
email.getMessageID(),
1
);
}
@After
public void tearDown() throws Exception {
super.tearDown();
EmailFolder inbox = I2PBote.getInstance().getInbox();
for (Email email : BoteHelper.getEmails(inbox, null, true)) {
inbox.delete(email.getMessageID());
}
System.setProperty("i2pbote.initialized", "false");
}
}

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name" translatable="false">Bote DEBUG</string>
</resources>

View File

@ -0,0 +1,180 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
package="i2p.bote.android"
xmlns:android="http://schemas.android.com/apk/res/android"
android:sharedUserId="net.i2p">
<uses-sdk xmlns:tools="http://schemas.android.com/tools"
tools:overrideLibrary="android.support.v14.preference" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.NFC"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:name="android.support.multidex.MultiDexApplication"
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.Bote">
<service android:name=".service.BoteService"/>
<activity
android:name=".EmailListActivity"
android:label="@string/app_name"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".intro.IntroActivity"
android:parentActivityName=".EmailListActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="i2p.bote.android.EmailListActivity"/>
</activity>
<activity
android:name=".intro.SetupActivity"
android:label="@string/title_activity_setup"
android:parentActivityName=".EmailListActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="i2p.bote.android.EmailListActivity"/>
</activity>
<activity
android:name=".ViewEmailActivity"
android:parentActivityName=".EmailListActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="i2p.bote.android.EmailListActivity"/>
</activity>
<activity
android:name=".NewEmailActivity"
android:label="@string/compose"
android:parentActivityName=".EmailListActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="i2p.bote.android.EmailListActivity"/>
</activity>
<activity
android:name=".addressbook.AddressBookActivity"
android:label="@string/address_book"
android:parentActivityName=".EmailListActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="i2p.bote.android.EmailListActivity"/>
</activity>
<activity
android:name=".addressbook.ViewContactActivity"
android:parentActivityName=".addressbook.AddressBookActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="i2p.bote.android.addressbook.AddressBookActivity"/>
</activity>
<activity
android:name=".addressbook.EditContactActivity"
android:label="@string/action_new_contact"
android:parentActivityName=".addressbook.ViewContactActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="i2p.bote.android.addressbook.ViewContactActivity"/>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
<data
android:host="ext"
android:pathPrefix="/i2p.bote:contact"
android:scheme="vnd.android.nfc"/>
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TAG_DISCOVERED"/>
</intent-filter>
</activity>
<activity
android:name=".addressbook.AddressBookShipActivity"
android:parentActivityName=".addressbook.AddressBookActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="i2p.bote.android.addressbook.AddressBookActivity"/>
</activity>
<activity
android:name=".NetworkInfoActivity"
android:label="@string/network_status"
android:parentActivityName=".EmailListActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="i2p.bote.android.EmailListActivity"/>
</activity>
<activity
android:name=".config.SettingsActivity"
android:label="@string/action_settings"
android:parentActivityName=".EmailListActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="i2p.bote.android.EmailListActivity"/>
</activity>
<activity
android:name=".config.SetPasswordActivity"
android:label="@string/pref_title_change_password"
android:parentActivityName=".config.SettingsActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="i2p.bote.android.config.SettingsActivity"/>
</activity>
<activity
android:name=".identities.IdentityListActivity"
android:label="@string/pref_title_identities"
android:parentActivityName=".config.SettingsActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="i2p.bote.android.config.SettingsActivity"/>
</activity>
<activity
android:name=".identities.ViewIdentityActivity"
android:parentActivityName=".identities.IdentityListActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="i2p.bote.android.identities.IdentityListActivity"/>
</activity>
<activity
android:name=".identities.EditIdentityActivity"
android:label="@string/title_new_identity"
android:parentActivityName=".identities.ViewIdentityActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".identities.ViewIdentityActivity"/>
</activity>
<activity
android:name=".identities.IdentityShipActivity"
android:parentActivityName=".identities.IdentityListActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="i2p.bote.android.identities.IdentityListActivity"/>
</activity>
<activity
android:name=".HelpActivity"
android:label="@string/help"
android:parentActivityName=".EmailListActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="i2p.bote.android.EmailListActivity"/>
</activity>
<provider
android:name=".provider.AttachmentProvider"
android:authorities="${applicationId}.attachmentprovider"
android:enabled="true"
android:exported="false"
android:grantUriPermissions="true">
</provider>
</application>
</manifest>

View File

@ -0,0 +1,44 @@
/**
* Copyright (C) 2017 str4d@mail.i2p
* <p>
* This file is part of I2P-Bote.
* I2P-Bote 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.
* <p>
* I2P-Bote 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.
* <p>
* You should have received a copy of the GNU General Public License
* along with I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
*/
package i2p.bote.android;
import i2p.bote.network.DhtPeerStats;
import i2p.bote.network.DhtPeerStatsRenderer;
/**
* Renders UI strings for DHT peer stats.
* @see DhtPeerStatsRenderer
*/
class AndroidPeerStatsRenderer implements DhtPeerStatsRenderer {
AndroidPeerStatsRenderer() {
}
@Override
public String translateHeading(DhtPeerStats.Columns column) {
// No-op, headings currently unused
return "";
}
@Override
public String translateContent(DhtPeerStats.Content content) {
// No-op, content currently unused
return "";
}
}

View File

@ -0,0 +1,44 @@
package i2p.bote.android;
import android.annotation.SuppressLint;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.view.WindowManager;
import i2p.bote.android.util.LocaleManager;
@SuppressLint("Registered")
public class BoteActivityBase extends AppCompatActivity {
private final LocaleManager localeManager = new LocaleManager();
@Override
protected void onCreate(Bundle savedInstanceState) {
localeManager.onCreate(this);
super.onCreate(savedInstanceState);
// Initialize I2P settings
InitActivities init = new InitActivities(this);
init.initialize();
// Initialize screen security
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH &&
prefs.getBoolean("pref_screen_security", true))
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
else
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
}
@Override
public void onResume() {
super.onResume();
localeManager.onResume(this);
}
public void notifyLocaleChanged() {
localeManager.onResume(this);
}
}

View File

@ -0,0 +1,16 @@
package i2p.bote.android;
public class Constants {
public static final String ANDROID_LOG_TAG = "I2P-Bote";
public static final String SHARED_PREFS = "i2p.bote";
public static final String PREF_SELECTED_IDENTITY = "selectedIdentity";
public static final String EMAILDEST_SCHEME = "bote";
public static final String NDEF_DOMAIN = "i2p.bote";
public static final String NDEF_TYPE_CONTACT = "contact";
public static final String NDEF_TYPE_CONTACT_DESTINATION = "contactDestination";
public static final String NDEF_LEGACY_TYPE_CONTACT = NDEF_DOMAIN + ":" + NDEF_TYPE_CONTACT;
public static final String NDEF_LEGACY_TYPE_CONTACT_DESTINATION = NDEF_DOMAIN + ":" + NDEF_TYPE_CONTACT_DESTINATION;
}

View File

@ -0,0 +1,734 @@
package i2p.bote.android;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.Toolbar;
import android.util.DisplayMetrics;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import com.mikepenz.google_material_typeface_library.GoogleMaterial;
import com.mikepenz.iconics.IconicsDrawable;
import com.mikepenz.iconics.typeface.IIcon;
import com.mikepenz.materialdrawer.AccountHeader;
import com.mikepenz.materialdrawer.AccountHeaderBuilder;
import com.mikepenz.materialdrawer.Drawer;
import com.mikepenz.materialdrawer.DrawerBuilder;
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem;
import com.mikepenz.materialdrawer.model.ProfileDrawerItem;
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem;
import com.mikepenz.materialdrawer.model.interfaces.IProfile;
import net.i2p.android.ui.I2PAndroidHelper;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.mail.MessagingException;
import i2p.bote.I2PBote;
import i2p.bote.android.addressbook.AddressBookActivity;
import i2p.bote.android.config.SettingsActivity;
import i2p.bote.android.intro.IntroActivity;
import i2p.bote.android.intro.SetupActivity;
import i2p.bote.android.service.BoteService;
import i2p.bote.android.service.Init;
import i2p.bote.android.service.Init.RouterChoice;
import i2p.bote.android.util.BetterAsyncTaskLoader;
import i2p.bote.android.util.BoteHelper;
import i2p.bote.android.util.MoveToDialogFragment;
import i2p.bote.email.EmailIdentity;
import i2p.bote.email.IdentitiesListener;
import i2p.bote.fileencryption.PasswordCacheListener;
import i2p.bote.fileencryption.PasswordException;
import i2p.bote.folder.EmailFolder;
import i2p.bote.folder.FolderListener;
import i2p.bote.network.NetworkStatusListener;
public class EmailListActivity extends BoteActivityBase implements
EmailListFragment.OnEmailSelectedListener,
MoveToDialogFragment.MoveToDialogListener,
PasswordCacheListener,
NetworkStatusListener {
private I2PAndroidHelper mHelper;
private RouterChoice mRouterChoice;
private SharedPreferences mSharedPrefs;
/**
* Navigation drawer variables
*/
private AccountHeader mAccountHeader;
private Drawer mDrawer;
private long mSelected;
private static final String PREF_FIRST_START = "firstStart";
private static final int SHOW_INTRODUCTION = 1;
private static final int RUN_SETUP = 2;
private static final int ID_ADDRESS_BOOK = 1;
private static final int ID_NET_STATUS = 2;
private static final int ID_ALL_MAIL = 3;
private static final int ID_LOCKED = 4;
private static final int LOADER_IDENTITIES = 0;
private static final int LOADER_DRAWER_FOLDERS = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Set the action bar
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
setSupportActionBar(toolbar);
// Initialize variables
mHelper = new I2PAndroidHelper(this);
mSharedPrefs = getSharedPreferences(Constants.SHARED_PREFS, 0);
mAccountHeader = new AccountHeaderBuilder()
.withActivity(this)
.withHeaderBackground(R.drawable.drawer_header_background)
.withSelectionListEnabledForSingleProfile(false)
.addProfiles(getLockedProfile())
.withOnAccountHeaderListener(new AccountHeader.OnAccountHeaderListener() {
@Override
public boolean onProfileChanged(View view, IProfile profile, boolean currentProfile) {
if (profile.getIdentifier() == ID_LOCKED)
findViewById(R.id.action_log_in).performClick();
else if (!currentProfile)
identitySelected(profile);
return false;
}
})
.withSavedInstance(savedInstanceState)
.build();
IDrawerItem addressBook = new PrimaryDrawerItem()
.withIdentifier(ID_ADDRESS_BOOK)
.withName(R.string.address_book)
.withIcon(new IconicsDrawable(this, GoogleMaterial.Icon.gmd_contacts).colorRes(R.color.md_grey_600).sizeDp(24))
.withIconTintingEnabled(true)
.withSelectedIconColorRes(R.color.primary);
IDrawerItem networkStatus = getNetStatusItem(
R.string.network_status, GoogleMaterial.Icon.gmd_cloud_off, R.color.md_grey_600, 0);
// Set the drawer width per Material design spec
// http://www.google.com/design/spec/layout/structure.html#structure-side-nav-1
// Mobile: side nav width = min(screen width - app bar height, 320dp)
// Desktop: side nav width = min(screen width - app bar height, 400dp)
int maxWidth = getResources().getDimensionPixelSize(R.dimen.nav_max_width);
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
int drawerWidth = Math.min(dm.widthPixels - toolbar.getLayoutParams().height, maxWidth);
mDrawer = new DrawerBuilder()
.withActivity(this)
.withToolbar(toolbar)
.withDrawerWidthPx(drawerWidth)
.withShowDrawerOnFirstLaunch(true)
.withAccountHeader(mAccountHeader)
.addStickyDrawerItems(addressBook, networkStatus)
.withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() {
@Override
public boolean onItemClick(View view, int i, IDrawerItem iDrawerItem) {
long id = iDrawerItem.getIdentifier();
if (id == ID_ADDRESS_BOOK) {
mDrawer.setSelection(mSelected, false);
mDrawer.closeDrawer();
Intent ai = new Intent(EmailListActivity.this, AddressBookActivity.class);
startActivity(ai);
return true;
} else if (id == ID_NET_STATUS) {
mDrawer.setSelection(mSelected, false);
netStatusSelected();
return true;
} else {
drawerFolderSelected((EmailFolder) iDrawerItem.getTag(), mSelected == i);
mSelected = mDrawer.getCurrentSelection();
return false;
}
}
})
.withSavedInstance(savedInstanceState)
.build();
mSelected = mDrawer.getCurrentSelection();
// Enable ActionBar app icon to behave as action to toggle nav drawer
//noinspection ConstantConditions
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
mDrawer.getActionBarDrawerToggle().setDrawerIndicatorEnabled(true);
if (savedInstanceState == null) {
EmailListFragment f = EmailListFragment.newInstance("inbox");
getSupportFragmentManager().beginTransaction()
.add(R.id.list_fragment, f).commit();
}
// If first start, go to introduction and setup wizard
if (mSharedPrefs.getBoolean(PREF_FIRST_START, true)) {
mSharedPrefs.edit().putBoolean(PREF_FIRST_START, false).apply();
Intent i = new Intent(EmailListActivity.this, IntroActivity.class);
startActivityForResult(i, SHOW_INTRODUCTION);
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mAccountHeader.saveInstanceState(outState);
mDrawer.saveInstanceState(outState);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu
getMenuInflater().inflate(R.menu.main, menu);
if (isBoteServiceRunning())
menu.findItem(R.id.action_start_bote).setVisible(false);
else
menu.findItem(R.id.action_stop_bote).setVisible(false);
return super.onCreateOptionsMenu(menu);
}
@Override
protected void onStart() {
super.onStart();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
if (prefs.getBoolean("i2pbote.router.auto", true) ||
prefs.getString("i2pbote.router.use", "internal").equals("android")) {
// Try to bind to I2P Android
mHelper.bind();
}
I2PBote.getInstance().addPasswordCacheListener(this);
I2PBote.getInstance().addNetworkStatusListener(this);
// Fetch current network status
networkStatusChanged();
}
@Override
public void onResume() {
super.onResume();
if (I2PBote.getInstance().isPasswordRequired()) {
// Ensure any existing data is destroyed.
getSupportLoaderManager().destroyLoader(LOADER_IDENTITIES);
} else {
// Password is cached, or not set.
getSupportLoaderManager().initLoader(LOADER_IDENTITIES, null, new IdentityLoaderCallbacks());
}
getSupportLoaderManager().initLoader(LOADER_DRAWER_FOLDERS, null, new DrawerFolderLoaderCallbacks());
}
@Override
protected void onStop() {
super.onStop();
mHelper.unbind();
I2PBote.getInstance().removePasswordCacheListener(this);
I2PBote.getInstance().removeNetworkStatusListener(this);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_start_bote:
// Init from settings
Init init = new Init(this);
mRouterChoice = init.initialize(mHelper);
if (mRouterChoice == RouterChoice.ANDROID) {
if (!mHelper.isI2PAndroidInstalled()) {
// I2P Android not installed
mHelper.promptToInstall(this);
} else if (!mHelper.isI2PAndroidRunning()) {
// Ask user to start I2P Android
mHelper.requestI2PAndroidStart(this);
} else
startBote();
} else
startBote();
return true;
case R.id.action_stop_bote:
Intent stop = new Intent(this, BoteService.class);
stopService(stop);
supportInvalidateOptionsMenu();
return true;
case R.id.action_settings:
Intent si = new Intent(this, SettingsActivity.class);
startActivity(si);
return true;
case R.id.action_help:
Intent hi = new Intent(this, HelpActivity.class);
startActivity(hi);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == SHOW_INTRODUCTION) {
if (resultCode == RESULT_OK) {
Intent i = new Intent(EmailListActivity.this, SetupActivity.class);
startActivityForResult(i, RUN_SETUP);
}
} else if (requestCode == RUN_SETUP) {
if (resultCode == RESULT_OK) {
// TODO implement a UI tutorial?
}
} else if (requestCode == I2PAndroidHelper.REQUEST_START_I2P) {
if (resultCode == RESULT_OK) {
startBote();
}
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
@SuppressWarnings("ConstantConditions")
@Override
public void setTitle(CharSequence title) {
getSupportActionBar().setTitle(title);
}
//
// Helpers
//
private IProfile getLockedProfile() {
return new ProfileDrawerItem()
.withIdentifier(ID_LOCKED)
.withEmail(getString(R.string.touch_lock_to_log_in))
.withIcon(new IconicsDrawable(this, GoogleMaterial.Icon.gmd_lock).color(Color.WHITE).sizeRes(com.mikepenz.materialdrawer.R.dimen.material_drawer_account_header_selected));
}
private IDrawerItem getNetStatusItem(int nameRes, IIcon icon, int iconColorRes, int padding) {
return new PrimaryDrawerItem()
.withIdentifier(ID_NET_STATUS)
.withName(nameRes)
.withIcon(new IconicsDrawable(this, icon).colorRes(iconColorRes).sizeDp(24).paddingDp(padding))
.withIconTintingEnabled(true)
.withSelectedIconColorRes(R.color.primary);
}
private void identitySelected(IProfile profile) {
EmailIdentity identity = (EmailIdentity) ((ProfileDrawerItem) profile).getTag();
mSharedPrefs.edit()
.putString(Constants.PREF_SELECTED_IDENTITY,
identity == null ? null : identity.getKey())
.apply();
// Trigger the drawer folder loader to update the drawer badges
getSupportLoaderManager().restartLoader(LOADER_DRAWER_FOLDERS, null, new DrawerFolderLoaderCallbacks());
EmailListFragment f = (EmailListFragment) getSupportFragmentManager()
.findFragmentById(R.id.list_fragment);
f.onIdentitySelected();
}
private void drawerFolderSelected(EmailFolder folder, boolean alreadySelected) {
if (!alreadySelected) {
// Create the new fragment
EmailListFragment f = EmailListFragment.newInstance(folder.getName());
// Insert the fragment
getSupportFragmentManager().beginTransaction()
.replace(R.id.list_fragment, f).commit();
}
// Close the drawer
mDrawer.closeDrawer();
}
private void netStatusSelected() {
int boteNotStartedMessage = R.string.network_info_unavailable;
switch (I2PBote.getInstance().getNetworkStatus()) {
case DELAY:
boteNotStartedMessage = R.string.network_info_unavailable_delay;
case NOT_STARTED:
DialogFragment df = NetStatusDialogFragment.newInstance(boteNotStartedMessage);
df.show(getSupportFragmentManager(), "noinfo");
break;
default:
mDrawer.closeDrawer();
Intent nii = new Intent(EmailListActivity.this, NetworkInfoActivity.class);
startActivity(nii);
}
}
private boolean isBoteServiceRunning() {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (BoteService.class.getName().equals(service.service.getClassName()))
return true;
}
return false;
}
private void startBote() {
Intent start = new Intent(this, BoteService.class);
start.putExtra(BoteService.ROUTER_CHOICE, mRouterChoice);
startService(start);
supportInvalidateOptionsMenu();
}
public static class NetStatusDialogFragment extends DialogFragment {
public static DialogFragment newInstance(int message) {
DialogFragment f = new NetStatusDialogFragment();
Bundle args = new Bundle();
args.putInt("message", message);
f.setArguments(args);
return f;
}
@Override
@NonNull
public Dialog onCreateDialog(Bundle savedInstanceState) {
int message = getArguments().getInt("message");
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage(message)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
return builder.create();
}
}
//
// Loaders
//
private class IdentityLoaderCallbacks implements LoaderManager.LoaderCallbacks<ArrayList<IProfile>> {
@Override
public Loader<ArrayList<IProfile>> onCreateLoader(int id, Bundle args) {
return new DrawerIdentityLoader(EmailListActivity.this);
}
@Override
public void onLoadFinished(Loader<ArrayList<IProfile>> loader, ArrayList<IProfile> data) {
mAccountHeader.setProfiles(data);
String selectedIdentity = mSharedPrefs.getString(Constants.PREF_SELECTED_IDENTITY, null);
for (IProfile profile : data) {
EmailIdentity identity = (EmailIdentity) ((ProfileDrawerItem) profile).getTag();
if ((identity == null && selectedIdentity == null) ||
(identity != null && identity.getKey().equals(selectedIdentity))) {
mAccountHeader.setActiveProfile(profile, true);
break;
}
}
}
@Override
public void onLoaderReset(Loader<ArrayList<IProfile>> loader) {
mAccountHeader.clear();
mAccountHeader.addProfiles(getLockedProfile());
}
}
private static class DrawerIdentityLoader extends BetterAsyncTaskLoader<ArrayList<IProfile>> implements IdentitiesListener {
private int identiconSize;
public DrawerIdentityLoader(Context context) {
super(context);
// Must be a multiple of nine
identiconSize = context.getResources().getDimensionPixelSize(R.dimen.identicon);
}
@Override
public ArrayList<IProfile> loadInBackground() {
ArrayList<IProfile> profiles = new ArrayList<>();
try {
// Fetch the identities first, so we trigger any exceptions
Collection<EmailIdentity> identities = I2PBote.getInstance().getIdentities().getAll();
profiles.add(new ProfileDrawerItem()
.withIdentifier(ID_ALL_MAIL)
.withTag(null)
.withEmail(getContext().getString(R.string.all_mail))
.withIcon(getContext().getResources().getDrawable(R.drawable.ic_contact_picture))
);
for (EmailIdentity identity : identities) {
profiles.add(getIdentityDrawerItem(identity));
}
} catch (PasswordException e) {
// TODO handle, but should not get here
e.printStackTrace();
} catch (GeneralSecurityException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return profiles;
}
private IProfile getIdentityDrawerItem(EmailIdentity identity) {
return new ProfileDrawerItem()
.withIdentifier(identity.hashCode())
.withTag(identity)
.withName(identity.getDescription())
.withEmail(identity.getPublicName() + " <" + identity.getKey().substring(0, 4) + ">")
.withIcon(BoteHelper.getIdentityPicture(identity, identiconSize, identiconSize));
}
@Override
protected void onStartMonitoring() {
I2PBote.getInstance().getIdentities().addIdentitiesListener(this);
}
@Override
protected void onStopMonitoring() {
I2PBote.getInstance().getIdentities().removeIdentitiesListener(this);
}
@Override
protected void releaseResources(ArrayList<IProfile> data) {
}
// IdentitiesListener
@Override
public void identityAdded(String s) {
onContentChanged();
}
@Override
public void identityUpdated(String s) {
onContentChanged();
}
@Override
public void identityRemoved(String s) {
onContentChanged();
}
}
private class DrawerFolderLoaderCallbacks implements LoaderManager.LoaderCallbacks<ArrayList<IDrawerItem>> {
@Override
public Loader<ArrayList<IDrawerItem>> onCreateLoader(int id, Bundle args) {
return new DrawerFolderLoader(EmailListActivity.this, I2PBote.getInstance().getEmailFolders());
}
@Override
public void onLoadFinished(Loader<ArrayList<IDrawerItem>> loader, ArrayList<IDrawerItem> data) {
if (mDrawer.getDrawerItems() == null || mDrawer.getDrawerItems().size() == 0)
mDrawer.setItems(data);
else {
// Assumes that no folders have been added or removed
// TODO change this if necessary when user folders are implemented
for (IDrawerItem item : data) {
mDrawer.updateItem(item);
}
}
}
@Override
public void onLoaderReset(Loader<ArrayList<IDrawerItem>> loader) {
mDrawer.removeAllItems();
}
}
private static class DrawerFolderLoader extends BetterAsyncTaskLoader<ArrayList<IDrawerItem>> implements FolderListener {
private List<EmailFolder> mFolders;
public DrawerFolderLoader(Context context, List<EmailFolder> folders) {
super(context);
mFolders = folders;
}
@Override
public ArrayList<IDrawerItem> loadInBackground() {
ArrayList<IDrawerItem> drawerItems = new ArrayList<>();
for (EmailFolder folder : mFolders) {
drawerItems.add(getFolderDrawerItem(folder));
}
return drawerItems;
}
private IDrawerItem getFolderDrawerItem(EmailFolder folder) {
PrimaryDrawerItem item = new PrimaryDrawerItem()
.withIdentifier(folder.hashCode())
.withTag(folder)
.withIconTintingEnabled(true)
.withSelectedIconColorRes(R.color.primary)
.withIcon(BoteHelper.getFolderIcon(getContext(), folder))
.withName(BoteHelper.getFolderDisplayName(getContext(), folder));
try {
int numNew = BoteHelper.getNumNewEmails(getContext(), folder);
if (numNew > 0)
item.withBadge("" + numNew);
} catch (PasswordException e) {
// Password fetching is handled in EmailListFragment
} catch (MessagingException | GeneralSecurityException | IOException e) {
e.printStackTrace();
}
return item;
}
@Override
protected void onStartMonitoring() {
if (mFolders != null) {
for (EmailFolder folder : mFolders) {
folder.addFolderListener(this);
}
}
}
@Override
protected void onStopMonitoring() {
if (mFolders != null) {
for (EmailFolder folder : mFolders) {
folder.removeFolderListener(this);
}
}
}
@Override
protected void releaseResources(ArrayList<IDrawerItem> data) {
}
// FolderListener
@Override
public void elementAdded(String s) {
onContentChanged();
}
@Override
public void elementUpdated() {
onContentChanged();
}
@Override
public void elementRemoved(String s) {
onContentChanged();
}
}
//
// Interfaces
//
// FolderFragment.OnEmailSelectedListener
@Override
public void onEmailSelected(String folderName, String messageId) {
// In single-pane mode, simply start the detail activity
// for the selected message ID.
Intent detailIntent = new Intent(this, ViewEmailActivity.class);
detailIntent.putExtra(ViewEmailActivity.FOLDER_NAME, folderName);
detailIntent.putExtra(ViewEmailActivity.MESSAGE_ID, messageId);
startActivity(detailIntent);
}
// MoveToDialogFragment.MoveToDialogListener
@Override
public void onFolderSelected(EmailFolder newFolder) {
EmailListFragment f = (EmailListFragment) getSupportFragmentManager().findFragmentById(R.id.list_fragment);
f.onFolderSelected(newFolder);
}
// PasswordCacheListener
@Override
public void passwordProvided() {
// Password is cached, or not set.
getSupportLoaderManager().restartLoader(LOADER_IDENTITIES, null, new IdentityLoaderCallbacks());
// Trigger the drawer folder loader to show the drawer badges
getSupportLoaderManager().restartLoader(LOADER_DRAWER_FOLDERS, null, new DrawerFolderLoaderCallbacks());
}
@Override
public void passwordCleared() {
// Ensure any existing data is destroyed.
getSupportLoaderManager().destroyLoader(LOADER_IDENTITIES);
// Trigger the drawer folder loader to hide the drawer badges
getSupportLoaderManager().restartLoader(LOADER_DRAWER_FOLDERS, null, new DrawerFolderLoaderCallbacks());
// Hide account selection list
if (mAccountHeader.isSelectionListShown())
mAccountHeader.toggleSelectionList(this);
}
// NetworkStatusListener
@Override
public void networkStatusChanged() {
// Update network status
final int statusText;
final IIcon statusIcon;
final int colorRes;
final int padding;
switch (I2PBote.getInstance().getNetworkStatus()) {
case DELAY:
statusText = R.string.connect_delay;
statusIcon = GoogleMaterial.Icon.gmd_av_timer;
colorRes = R.color.md_grey_600;
padding = 3;
break;
case CONNECTING:
statusText = R.string.connecting;
statusIcon = GoogleMaterial.Icon.gmd_cloud_queue;
colorRes = R.color.md_grey_600;
padding = 0;
break;
case CONNECTED:
statusText = R.string.connected;
statusIcon = GoogleMaterial.Icon.gmd_cloud_done;
colorRes = R.color.md_grey_600;
padding = 0;
break;
case ERROR:
statusText = R.string.error;
statusIcon = GoogleMaterial.Icon.gmd_error;
colorRes = R.color.red;
padding = 2;
break;
case NOT_STARTED:
default:
statusText = R.string.not_started;
statusIcon = GoogleMaterial.Icon.gmd_cloud_off;
colorRes = R.color.md_grey_600;
padding = 0;
}
runOnUiThread(new Runnable() {
@Override
public void run() {
mDrawer.updateStickyFooterItem(getNetStatusItem(statusText, statusIcon, colorRes, padding));
}
});
}
}

View File

@ -0,0 +1,319 @@
package i2p.bote.android;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Typeface;
import android.os.Build;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import javax.mail.Part;
import i2p.bote.android.util.BoteHelper;
import i2p.bote.android.util.MultiSelectionUtil;
import i2p.bote.email.Email;
public class EmailListAdapter extends MultiSelectionUtil.SelectableAdapter<RecyclerView.ViewHolder> {
private static final DateFormat DATE_BEFORE_THIS_YEAR = DateFormat.getDateInstance(DateFormat.MEDIUM);
private static final DateFormat DATE_THIS_YEAR = new SimpleDateFormat(
((SimpleDateFormat) SimpleDateFormat.getDateInstance(DateFormat.MEDIUM))
.toPattern().replaceAll(",?\\W?[Yy]+\\W?", "")
);
private static final DateFormat DATE_TODAY = DateFormat.getTimeInstance();
private Calendar BOUNDARY_DAY;
private Calendar BOUNDARY_YEAR;
private Context mCtx;
private String mFolderName;
private EmailListFragment.OnEmailSelectedListener mListener;
private boolean mIsOutbox;
private List<Email> mEmails;
private int mIncompleteEmails;
public static class SimpleViewHolder extends RecyclerView.ViewHolder {
public SimpleViewHolder(View itemView) {
super(itemView);
}
}
public static class EmailViewHolder extends RecyclerView.ViewHolder {
public ImageView picture;
//public ImageView emailSelected;
public TextView subject;
public TextView address;
public TextView content;
public TextView sent;
public ImageView emailAttachment;
public TextView emailStatus;
public ImageView emailDelivered;
public EmailViewHolder(View itemView) {
super(itemView);
picture = (ImageView) itemView.findViewById(R.id.contact_picture);
//emailSelected = view.findViewById(R.id.email_selected);
subject = (TextView) itemView.findViewById(R.id.email_subject);
address = (TextView) itemView.findViewById(R.id.email_address);
content = (TextView) itemView.findViewById(R.id.email_content);
sent = (TextView) itemView.findViewById(R.id.email_sent);
emailAttachment = (ImageView) itemView.findViewById(R.id.email_attachment);
emailStatus = (TextView) itemView.findViewById(R.id.email_status);
emailDelivered = (ImageView) itemView.findViewById(R.id.email_delivered);
}
}
public EmailListAdapter(Context context, String folderName,
EmailListFragment.OnEmailSelectedListener listener) {
super();
mCtx = context;
mFolderName = folderName;
mListener = listener;
mIsOutbox = BoteHelper.isOutbox(folderName);
mIncompleteEmails = 0;
setHasStableIds(true);
setDateBoundaries();
}
/**
* Set up the boundaries for date display formats.
* <p/>
* TODO: call this method at midnight to refresh the UI
*/
public void setDateBoundaries() {
BOUNDARY_DAY = Calendar.getInstance();
BOUNDARY_DAY.set(Calendar.HOUR, 0);
BOUNDARY_DAY.set(Calendar.MINUTE, 0);
BOUNDARY_DAY.set(Calendar.SECOND, 0);
BOUNDARY_YEAR = Calendar.getInstance();
BOUNDARY_YEAR.set(Calendar.MONTH, Calendar.JANUARY);
BOUNDARY_YEAR.set(Calendar.DAY_OF_MONTH, 1);
BOUNDARY_YEAR.set(Calendar.HOUR, 0);
BOUNDARY_YEAR.set(Calendar.MINUTE, 0);
BOUNDARY_YEAR.set(Calendar.SECOND, 0);
if (mEmails != null)
notifyDataSetChanged();
}
public void setEmails(List<Email> emails) {
mEmails = emails;
notifyDataSetChanged();
}
public Email getEmail(int position) {
if (mIncompleteEmails > 0)
position--;
if (position < 0)
return null;
return mEmails.get(position);
}
public void setIncompleteEmails(int incompleteEmails) {
if (incompleteEmails > 0) {
if (mIncompleteEmails == 0) {
mIncompleteEmails = incompleteEmails;
notifyItemInserted(0);
} else {
mIncompleteEmails = incompleteEmails;
notifyItemChanged(0);
}
} else if (mIncompleteEmails > 0) {
mIncompleteEmails = 0;
notifyItemRemoved(0);
}
}
@Override
public int getItemViewType(int position) {
if (mEmails == null || mEmails.isEmpty())
return R.layout.listitem_empty;
if (mIncompleteEmails > 0)
position--;
return position < 0 ? R.layout.listitem_incomplete : R.layout.listitem_email;
}
// Create new views (invoked by the layout manager)
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(viewType, parent, false);
switch (viewType) {
case R.layout.listitem_email:
return new EmailViewHolder(v);
default:
return new SimpleViewHolder(v);
}
}
// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (holder.getItemViewType()) {
case R.layout.listitem_empty:
((TextView) holder.itemView).setText(
mCtx.getResources().getString(R.string.folder_empty));
break;
case R.layout.listitem_incomplete:
((TextView) holder.itemView).setText(
mCtx.getResources().getQuantityString(R.plurals.incomplete_emails,
mIncompleteEmails, mIncompleteEmails));
break;
case R.layout.listitem_email:
final EmailViewHolder evh = (EmailViewHolder) holder;
final Email email = getEmail(position);
evh.picture.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
selectEmail(evh.getAdapterPosition(), evh.getItemId(), true);
}
});
evh.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
selectEmail(evh.getAdapterPosition(), evh.getItemId(), false);
}
});
evh.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
selectEmail(evh.getAdapterPosition(), evh.getItemId(), true);
return true;
}
});
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB)
evh.itemView.setSelected(isSelected(position));
else
evh.itemView.setActivated(isSelected(position));
// TODO fix
//holder.emailSelected.setVisibility(isSelected(position) ? View.VISIBLE : View.GONE);
try {
boolean isSentEmail = BoteHelper.isSentEmail(email);
String otherAddress;
if (isSentEmail)
otherAddress = email.getOneRecipient();
else
otherAddress = email.getOneFromAddress();
Bitmap pic = BoteHelper.getPictureForAddress(otherAddress);
if (pic != null)
evh.picture.setImageBitmap(pic);
else if (isSentEmail || !email.isAnonymous()) {
ViewGroup.LayoutParams lp = evh.picture.getLayoutParams();
evh.picture.setImageBitmap(BoteHelper.getIdenticonForAddress(otherAddress, lp.width, lp.height));
} else
evh.picture.setImageDrawable(
mCtx.getResources().getDrawable(R.drawable.ic_contact_picture));
evh.subject.setText(email.getSubject());
evh.address.setText(BoteHelper.getNameAndShortDestination(otherAddress));
Date date = email.getSentDate();
if (date == null)
date = email.getReceivedDate();
if (date != null) {
DateFormat df;
if (date.before(BOUNDARY_DAY.getTime())) {
if (date.before(BOUNDARY_YEAR.getTime())) // Sent before this year
df = DATE_BEFORE_THIS_YEAR;
else // Sent this year before today
df = DATE_THIS_YEAR;
} else // Sent today
df = DATE_TODAY;
evh.sent.setText(df.format(date));
evh.sent.setVisibility(View.VISIBLE);
} else
evh.sent.setVisibility(View.GONE);
evh.emailAttachment.setVisibility(View.GONE);
for (Part part : email.getParts()) {
if (Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition())) {
evh.emailAttachment.setVisibility(View.VISIBLE);
break;
}
}
evh.subject.setTypeface(email.isUnread() ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT);
evh.address.setTypeface(email.isUnread() ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT);
if (email.isAnonymous() && !isSentEmail) {
if (email.isUnread())
evh.address.setTypeface(Typeface.DEFAULT, Typeface.BOLD_ITALIC);
else
evh.address.setTypeface(Typeface.DEFAULT, Typeface.ITALIC);
}
// Set email sending status if this is the outbox,
// or set email delivery status if we sent it.
if (mIsOutbox) {
evh.emailStatus.setText(BoteHelper.getEmailStatusText(
mCtx, email, false));
evh.emailStatus.setVisibility(View.VISIBLE);
} else if (isSentEmail) {
if (email.isDelivered()) {
evh.emailStatus.setVisibility(View.GONE);
} else {
evh.emailStatus.setText(email.getDeliveryPercentage() + "%");
evh.emailStatus.setVisibility(View.VISIBLE);
}
}
evh.emailDelivered.setVisibility(
!mIsOutbox && isSentEmail && email.isDelivered() ?
View.VISIBLE : View.GONE);
} catch (Exception e) {
evh.subject.setText("ERROR: " + e.getMessage());
}
evh.content.setText(email.getText());
break;
default:
break;
}
}
private void selectEmail(int position, long id, boolean selectorOnly) {
if (selectorOnly || getSelector().inActionMode()) {
getSelector().selectItem(position, id);
} else {
final Email email = getEmail(position);
mListener.onEmailSelected(mFolderName, email.getMessageID());
}
}
// Return the size of the dataset (invoked by the layout manager)
@Override
public int getItemCount() {
if (mEmails == null || mEmails.isEmpty())
return 1;
return mIncompleteEmails > 0 ? mEmails.size() + 1 : mEmails.size();
}
public long getItemId(int position) {
if (mEmails == null || mEmails.isEmpty())
return 0;
Email email = getEmail(position);
return email == null ? 1 : email.getMessageID().hashCode();
}
}

View File

@ -0,0 +1,577 @@
package i2p.bote.android;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.view.ActionMode;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.Toast;
import com.mikepenz.google_material_typeface_library.GoogleMaterial;
import com.pnikosis.materialishprogress.ProgressWheel;
import net.i2p.I2PAppContext;
import net.i2p.util.Log;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.List;
import javax.mail.Address;
import javax.mail.Flags.Flag;
import javax.mail.MessagingException;
import i2p.bote.I2PBote;
import i2p.bote.android.util.AuthenticatedFragment;
import i2p.bote.android.util.BetterAsyncTaskLoader;
import i2p.bote.android.util.BoteHelper;
import i2p.bote.android.util.MoveToDialogFragment;
import i2p.bote.android.util.MultiSelectionUtil;
import i2p.bote.android.widget.DividerItemDecoration;
import i2p.bote.android.widget.LoadingRecyclerView;
import i2p.bote.android.widget.MultiSwipeRefreshLayout;
import i2p.bote.email.Email;
import i2p.bote.fileencryption.PasswordException;
import i2p.bote.folder.EmailFolder;
import i2p.bote.folder.FolderListener;
public class EmailListFragment extends AuthenticatedFragment implements
LoaderManager.LoaderCallbacks<List<Email>>,
MoveToDialogFragment.MoveToDialogListener,
SwipeRefreshLayout.OnRefreshListener {
public static final String FOLDER_NAME = "folder_name";
private static final int EMAIL_LIST_LOADER = 1;
OnEmailSelectedListener mCallback;
private MultiSwipeRefreshLayout mSwipeRefreshLayout;
private AsyncTask<Void, Void, Void> mCheckingTask;
private LoadingRecyclerView mEmailsList;
private EmailListAdapter mAdapter;
private EmailFolder mFolder;
private ImageButton mNewEmail;
private MenuItem mCheckEmail;
// The Controller which provides CHOICE_MODE_MULTIPLE_MODAL-like functionality
private MultiSelectionUtil.Controller mMultiSelectController;
private ModalChoiceListener mModalChoiceListener;
public static EmailListFragment newInstance(String folderName) {
EmailListFragment f = new EmailListFragment();
Bundle args = new Bundle();
args.putString(FOLDER_NAME, folderName);
f.setArguments(args);
return f;
}
// Container Activity must implement this interface
public interface OnEmailSelectedListener {
public void onEmailSelected(String folderName, String messageId);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (OnEmailSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnEmailSelectedListener");
}
}
@Override
public View onCreateAuthenticatedView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
String folderName = getArguments().getString(FOLDER_NAME);
mFolder = BoteHelper.getMailFolder(folderName);
boolean isInbox = BoteHelper.isInbox(mFolder);
View v = inflater.inflate(
isInbox ? R.layout.fragment_list_emails_with_refresh : R.layout.fragment_list_emails,
container, false);
mEmailsList = (LoadingRecyclerView) v.findViewById(R.id.emails_list);
View empty = v.findViewById(R.id.empty);
ProgressWheel loading = (ProgressWheel) v.findViewById(R.id.loading);
mEmailsList.setLoadingView(empty, loading);
mNewEmail = (ImageButton) v.findViewById(R.id.promoted_action);
mNewEmail.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startNewEmail();
}
});
if (isInbox) {
mSwipeRefreshLayout = (MultiSwipeRefreshLayout) v;
// Set up the MultiSwipeRefreshLayout
mSwipeRefreshLayout.setColorSchemeResources(
R.color.primary, R.color.accent, R.color.primary, R.color.accent);
mSwipeRefreshLayout.setSwipeableChildren(R.id.emails_list);
mSwipeRefreshLayout.setOnRefreshListener(this);
}
return v;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mEmailsList.setHasFixedSize(true);
mEmailsList.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST));
// Use a linear layout manager
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
mEmailsList.setLayoutManager(mLayoutManager);
// Set the adapter for the list view
mAdapter = new EmailListAdapter(getActivity(), mFolder.getName(), mCallback);
mEmailsList.setAdapter(mAdapter);
// Attach a MultiSelectionUtil.Controller to the ListView, giving it an instance of
// ModalChoiceListener (see below)
mModalChoiceListener = new ModalChoiceListener();
mMultiSelectController = MultiSelectionUtil
.attachMultiSelectionController(mEmailsList, (AppCompatActivity) getActivity(),
mModalChoiceListener);
// Allow the Controller to restore itself
mMultiSelectController.restoreInstanceState(savedInstanceState);
if (mFolder == null) {
mFolder = I2PBote.getInstance().getInbox();
Toast.makeText(getActivity(), R.string.folder_does_not_exist, Toast.LENGTH_SHORT).show();
}
getActivity().setTitle(
BoteHelper.getFolderDisplayName(getActivity(), mFolder));
}
@Override
public void onStart() {
super.onStart();
if (mSwipeRefreshLayout != null) {
boolean isChecking = I2PBote.getInstance().isCheckingForMail();
mSwipeRefreshLayout.setRefreshing(isChecking);
if (isChecking)
onRefresh();
}
}
@Override
public void onStop() {
super.onStop();
if (mCheckingTask != null) {
mCheckingTask.cancel(true);
mCheckingTask = null;
mSwipeRefreshLayout.setRefreshing(false);
}
}
/**
* Start loading the list of emails from this folder.
* Only called when we have a password cached, or no
* password is required.
*/
protected void onInitializeFragment() {
if (mFolder == null)
return;
if (BoteHelper.isInbox(mFolder)) {
mAdapter.setIncompleteEmails(I2PBote.getInstance().getNumIncompleteEmails());
}
getLoaderManager().initLoader(EMAIL_LIST_LOADER, null, this);
}
protected void onDestroyFragment() {
mAdapter.setIncompleteEmails(0);
getLoaderManager().destroyLoader(EMAIL_LIST_LOADER);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Allow the Controller to save it's instance state so that any checked items are
// stored
if (mMultiSelectController != null)
mMultiSelectController.saveInstanceState(outState);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.email_list, menu);
mCheckEmail = menu.findItem(R.id.action_check_email);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
boolean passwordRequired = I2PBote.getInstance().isPasswordRequired();
mNewEmail.setVisibility(passwordRequired ? View.GONE : View.VISIBLE);
mCheckEmail.setVisible(mSwipeRefreshLayout != null && !passwordRequired);
if (mSwipeRefreshLayout != null) {
mSwipeRefreshLayout.setEnabled(!passwordRequired);
if (mSwipeRefreshLayout.isRefreshing()) {
mCheckEmail.setTitle(R.string.checking_email);
mCheckEmail.setEnabled(false);
} else {
mCheckEmail.setTitle(R.string.check_email);
mCheckEmail.setEnabled(true);
}
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_check_email:
if (!mSwipeRefreshLayout.isRefreshing()) {
mSwipeRefreshLayout.setRefreshing(true);
onRefresh();
getActivity().supportInvalidateOptionsMenu();
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void startNewEmail() {
Intent nei = new Intent(getActivity(), NewEmailActivity.class);
startActivity(nei);
}
private class ModalChoiceListener implements MultiSelectionUtil.MultiChoiceModeListener {
private boolean areUnread;
@Override
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
int numChecked = mAdapter.getSelectedItemCount();
mode.setTitle(getResources().getString(R.string.items_selected, numChecked));
if (checked && numChecked == 1) { // This is the first checked item
Email email = mAdapter.getEmail(position);
areUnread = email.isUnread();
mode.invalidate();
}
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
// Respond to clicks on the actions in the CAB
switch (item.getItemId()) {
case R.id.action_delete:
List<Integer> toDelete = mAdapter.getSelectedItems();
if (toDelete.size() == 0)
return false;
for (int i = (toDelete.size() - 1); i >= 0; i--) {
Email email = mAdapter.getEmail(toDelete.get(i));
BoteHelper.revokeAttachmentUriPermissions(
getActivity(),
mFolder.getName(),
email);
// The Loader will update mAdapter
I2PBote.getInstance().deleteEmail(mFolder, email.getMessageID());
}
mode.finish();
return true;
case R.id.action_mark_read:
case R.id.action_mark_unread:
List<Integer> selected = mAdapter.getSelectedItems();
for (int i = (selected.size() - 1); i >= 0; i--) {
Email email = mAdapter.getEmail(selected.get(i));
try {
// The Loader will update mAdapter
mFolder.setNew(email, !areUnread);
} catch (PasswordException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (GeneralSecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
areUnread = !areUnread;
mode.invalidate();
return true;
case R.id.action_move_to:
DialogFragment f = MoveToDialogFragment.newInstance(mFolder);
f.show(getFragmentManager(), "moveTo");
return true;
default:
return false;
}
}
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// Inflate the menu for the CAB
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.email_list_context, menu);
MenuItem markRead = menu.findItem(R.id.action_mark_read);
MenuItem markUnread = menu.findItem(R.id.action_mark_unread);
MenuItem moveTo = menu.findItem(R.id.action_move_to);
menu.findItem(R.id.action_delete).setIcon(BoteHelper.getMenuIcon(getActivity(), GoogleMaterial.Icon.gmd_delete));
markRead.setIcon(BoteHelper.getMenuIcon(getActivity(), GoogleMaterial.Icon.gmd_drafts));
markUnread.setIcon(BoteHelper.getMenuIcon(getActivity(), GoogleMaterial.Icon.gmd_markunread));
moveTo.setIcon(BoteHelper.getMenuIcon(getActivity(), GoogleMaterial.Icon.gmd_folder));
if (BoteHelper.isOutbox(mFolder)) {
markRead.setVisible(false);
markUnread.setVisible(false);
}
// Only allow moving from the trash
// TODO change this when user folders are implemented
if (!BoteHelper.isTrash(mFolder))
moveTo.setVisible(false);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// Here you can perform updates to the CAB due to
// an invalidate() request
if (!BoteHelper.isOutbox(mFolder)) {
menu.findItem(R.id.action_mark_read).setVisible(areUnread);
menu.findItem(R.id.action_mark_unread).setVisible(!areUnread);
}
return true;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
}
}
// Called by EmailListActivity.onIdentitySelected()
public void onIdentitySelected() {
getLoaderManager().restartLoader(EMAIL_LIST_LOADER, null, this);
}
// Called by EmailListActivity.onFolderSelected()
public void onFolderSelected(EmailFolder newFolder) {
List<Integer> toMove = mAdapter.getSelectedItems();
for (int i = (toMove.size() - 1); i >= 0; i--) {
Email email = mAdapter.getEmail(toMove.get(i));
mFolder.move(email, newFolder);
}
mMultiSelectController.finish();
}
// LoaderManager.LoaderCallbacks<List<Email>>
public Loader<List<Email>> onCreateLoader(int id, Bundle args) {
return new EmailListLoader(getActivity(), mFolder,
getActivity().getSharedPreferences(Constants.SHARED_PREFS, 0)
.getString(Constants.PREF_SELECTED_IDENTITY, null));
}
private static class EmailListLoader extends BetterAsyncTaskLoader<List<Email>> implements
FolderListener {
private EmailFolder mFolder;
private String mSelectedIdentityKey;
public EmailListLoader(Context context, EmailFolder folder, String selectedIdentityKey) {
super(context);
mFolder = folder;
mSelectedIdentityKey = selectedIdentityKey;
}
@Override
public List<Email> loadInBackground() {
List<Email> emails = null;
try {
List<Email> allEmails = BoteHelper.getEmails(mFolder, null, true);
if (mSelectedIdentityKey != null) {
emails = new ArrayList<>();
for (Email email : allEmails) {
boolean add = false;
if (BoteHelper.isSentEmail(email)) {
String senderDest = BoteHelper.extractEmailDestination(email.getOneFromAddress());
if (mSelectedIdentityKey.equals(senderDest))
add = true;
} else {
for (Address recipient : email.getAllRecipients()) {
String recipientDest = BoteHelper.extractEmailDestination(recipient.toString());
if (mSelectedIdentityKey.equals(recipientDest)) {
add = true;
break;
}
}
}
if (add)
emails.add(email);
}
} else
emails = allEmails;
} catch (PasswordException pe) {
// XXX: Should not get here.
} catch (MessagingException e) {
e.printStackTrace();
} catch (GeneralSecurityException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return emails;
}
protected void onStartMonitoring() {
mFolder.addFolderListener(this);
}
protected void onStopMonitoring() {
mFolder.removeFolderListener(this);
}
protected void releaseResources(List<Email> data) {
}
// FolderListener
@Override
public void elementAdded(String messageId) {
onContentChanged();
}
@Override
public void elementUpdated() {
onContentChanged();
}
@Override
public void elementRemoved(String messageId) {
onContentChanged();
}
}
public void onLoadFinished(Loader<List<Email>> loader,
List<Email> data) {
// Clear recent flags
for (Email email : data)
try {
email.setFlag(Flag.RECENT, false);
} catch (MessagingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mAdapter.setEmails(data);
try {
getActivity().setTitle(
BoteHelper.getFolderDisplayNameWithNew(getActivity(), mFolder));
} catch (PasswordException e) {
// Should not get here.
Log log = I2PAppContext.getGlobalContext().logManager().getLog(EmailListFragment.class);
if (log.shouldLog(Log.WARN))
log.warn("Email list loader finished, but password is no longer cached", e);
} catch (MessagingException | GeneralSecurityException | IOException e) {
e.printStackTrace();
}
}
public void onLoaderReset(Loader<List<Email>> loader) {
mAdapter.setEmails(null);
getActivity().setTitle(
BoteHelper.getFolderDisplayName(getActivity(), mFolder));
}
// SwipeRefreshLayout.OnRefreshListener
public void onRefresh() {
// If we are already checking, do nothing else
if (mCheckingTask != null)
return;
I2PBote bote = I2PBote.getInstance();
if (bote.isConnected()) {
try {
if (!bote.isCheckingForMail())
bote.checkForMail();
mCheckingTask = new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
while (I2PBote.getInstance().isCheckingForMail()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
if (isCancelled()) {
break;
}
}
return null;
}
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
mAdapter.setIncompleteEmails(I2PBote.getInstance().getNumIncompleteEmails());
// Notify PullToRefreshLayout that the refresh has finished
mSwipeRefreshLayout.setRefreshing(false);
getActivity().supportInvalidateOptionsMenu();
}
};
mCheckingTask.execute();
} catch (PasswordException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (GeneralSecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
mSwipeRefreshLayout.setRefreshing(false);
Toast.makeText(getActivity(), R.string.bote_needs_to_be_connected, Toast.LENGTH_SHORT).show();
}
}
}

View File

@ -0,0 +1,59 @@
package i2p.bote.android;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import org.sufficientlysecure.htmltextview.HtmlTextView;
public class HelpAboutFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_help_about, container, false);
TextView versionText = (TextView) view.findViewById(R.id.help_about_version);
versionText.setText(getString(R.string.version) + " " + getVersion());
TextView licenseText = (TextView) view.findViewById(R.id.help_about_license);
licenseText.setText(getString(R.string.license, "GPLv3+"));
HtmlTextView aboutLibsView = (HtmlTextView) view.findViewById(R.id.help_about_libraries);
// load html from raw resource (Parsing handled by HtmlTextView library)
aboutLibsView.setHtml(R.raw.help_about_libraries);
// no flickering when clicking textview for Android < 4
aboutLibsView.setTextColor(getResources().getColor(android.R.color.black));
return view;
}
/**
* Get the current package version.
*
* @return The current version.
*/
private String getVersion() {
String result = "";
try {
PackageManager manager = getActivity().getPackageManager();
PackageInfo info = manager.getPackageInfo(getActivity().getPackageName(), 0);
result = String.format("%s (%s)", info.versionName, info.versionCode);
} catch (NameNotFoundException e) {
Log.w(Constants.ANDROID_LOG_TAG, "Unable to get application version: " + e.getMessage());
result = "Unable to get application version.";
}
return result;
}
}

View File

@ -0,0 +1,98 @@
package i2p.bote.android;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.widget.Toolbar;
import com.viewpagerindicator.TitlePageIndicator;
public class HelpActivity extends BoteActivityBase {
/**
* The {@link android.support.v4.view.PagerAdapter} that will provide
* fragments for each of the sections. We use a
* {@link FragmentPagerAdapter} derivative, which will keep every
* loaded fragment in memory. If this becomes too memory intensive, it
* may be best to switch to a
* {@link android.support.v4.app.FragmentStatePagerAdapter}.
*/
SectionsPagerAdapter mSectionsPagerAdapter;
/**
* The {@link ViewPager} that will host the section contents.
*/
ViewPager mViewPager;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_help);
// Set the action bar
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
setSupportActionBar(toolbar);
// Enable ActionBar app icon to behave as action to go back
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// Create the sections adapter.
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
// Bind the page indicator to the pager.
TitlePageIndicator pageIndicator = (TitlePageIndicator) findViewById(R.id.page_indicator);
pageIndicator.setViewPager(mViewPager);
}
/**
* A {@link android.support.v4.app.FragmentPagerAdapter} that returns a fragment corresponding to
* one of the help sections.
*/
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 1:
return getString(R.string.pref_title_identities);
case 2:
return getString(R.string.changelog);
case 3:
return getString(R.string.about);
case 0:
default:
return getString(R.string.start);
}
}
@Override
public Fragment getItem(int position) {
switch (position) {
case 1:
return HelpHtmlFragment.newInstance(R.raw.help_identities);
case 2:
return HelpHtmlFragment.newInstance(R.raw.help_changelog);
case 3:
return new HelpAboutFragment();
case 0:
default:
return HelpHtmlFragment.newInstance(R.raw.help_start);
}
}
@Override
public int getCount() {
return 4;
}
}
}

View File

@ -0,0 +1,35 @@
package i2p.bote.android;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ScrollView;
import org.sufficientlysecure.htmltextview.HtmlTextView;
public class HelpHtmlFragment extends Fragment {
public static final String ARG_HTML_FILE = "htmlFile";
static HelpHtmlFragment newInstance(int htmlFile) {
HelpHtmlFragment f = new HelpHtmlFragment();
Bundle args = new Bundle();
args.putInt(ARG_HTML_FILE, htmlFile);
f.setArguments(args);
return f;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
ScrollView scroller = new ScrollView(getActivity());
HtmlTextView text = new HtmlTextView(getActivity());
scroller.addView(text);
int padH = getResources().getDimensionPixelOffset(R.dimen.activity_horizontal_margin);
int padV = getResources().getDimensionPixelOffset(R.dimen.activity_vertical_margin);
text.setPadding(padH, padV, padH, padV);
text.setHtml(getArguments().getInt(ARG_HTML_FILE));
text.setTextColor(getResources().getColor(R.color.primary_text_default_material_light));
return scroller;
}
}

View File

@ -0,0 +1,34 @@
package i2p.bote.android;
import android.content.Context;
import java.security.Security;
public class InitActivities {
static {
Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);
}
private final String myDir;
public InitActivities(Context c) {
this(c.getFilesDir().getAbsolutePath());
}
public InitActivities(String i2pBaseDir) {
myDir = i2pBaseDir;
}
public void initialize() {
// Don't initialize twice
if (System.getProperty("i2pbote.initialized", "false").equals("true"))
return;
// Set up the locations so settings can find them
System.setProperty("i2p.dir.base", myDir);
System.setProperty("i2p.dir.config", myDir);
System.setProperty("wrapper.logfile", myDir + "/wrapper.log");
System.setProperty("i2pbote.initialized", "true");
}
}

View File

@ -0,0 +1,25 @@
package i2p.bote.android;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
public class NetworkInfoActivity extends BoteActivityBase {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_toolbar);
// Set the action bar
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
setSupportActionBar(toolbar);
// Enable ActionBar app icon to behave as action to go back
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
if (savedInstanceState == null) {
NetworkInfoFragment f = new NetworkInfoFragment();
getSupportFragmentManager().beginTransaction()
.add(R.id.container, f).commit();
}
}
}

View File

@ -0,0 +1,234 @@
package i2p.bote.android;
import android.content.Context;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import com.androidplot.pie.PieChart;
import com.androidplot.pie.Segment;
import com.androidplot.pie.SegmentFormatter;
import net.i2p.android.ui.I2PAndroidHelper;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collection;
import java.util.Set;
import i2p.bote.I2PBote;
import i2p.bote.network.BannedPeer;
import i2p.bote.network.DhtPeerStats;
import i2p.bote.network.DhtPeerStatsRow;
import i2p.bote.network.RelayPeer;
public class NetworkInfoFragment extends Fragment {
private Exception mConnectError;
private PieChart mKademliaPie;
private TextView mKademliaPeers;
private PieChart mRelayPie;
private TextView mRelayPeers;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mConnectError = I2PBote.getInstance().getConnectError();
if (mConnectError == null)
return inflater.inflate(R.layout.fragment_network_info, container, false);
else
return inflater.inflate(R.layout.fragment_network_error, container, false);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (mConnectError == null) {
mKademliaPie = (PieChart) view.findViewById(R.id.kademlia_peers_pie);
mKademliaPeers = (TextView) view.findViewById(R.id.kademlia_peers);
mRelayPie = (PieChart) view.findViewById(R.id.relay_peers_pie);
mRelayPeers = (TextView) view.findViewById(R.id.relay_peers);
setupKademliaPeers();
setupRelayPeers();
Collection<BannedPeer> bannedPeers = I2PBote.getInstance().getBannedPeers();
((TextView) view.findViewById(R.id.banned_peers)).setText(
"" + bannedPeers.size());
} else {
((TextView) view.findViewById(R.id.error)).setText(mConnectError.toString());
view.findViewById(R.id.copy_error).setOnClickListener(new View.OnClickListener() {
@SuppressWarnings("deprecation")
@Override
public void onClick(View view) {
String fullError = joinStackTrace(mConnectError);
Object clipboardService = getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
android.text.ClipboardManager clipboard = (android.text.ClipboardManager) clipboardService;
clipboard.setText(fullError);
} else {
android.content.ClipboardManager clipboard = (android.content.ClipboardManager) clipboardService;
android.content.ClipData clip = android.content.ClipData.newPlainText(
getString(R.string.bote_connection_error), fullError);
clipboard.setPrimaryClip(clip);
}
Toast.makeText(getActivity(), R.string.full_error_copied_to_clipboard, Toast.LENGTH_SHORT).show();
}
});
if ((new I2PAndroidHelper(getActivity())).isI2PAndroidInstalled())
view.findViewById(R.id.error_page_i2p_content).setVisibility(View.VISIBLE);
}
}
private void setupSegmentFormatter(SegmentFormatter sf) {
sf.getLabelPaint().setTextSize(20);
}
private void setupKademliaPeers() {
DhtPeerStats dhtStats = I2PBote.getInstance().getDhtStats(new AndroidPeerStatsRenderer());
if (dhtStats != null) {
if (dhtStats.getData().size() == 0) {
Segment n = new Segment("", 100);
SegmentFormatter nf = new SegmentFormatter(getResources().getColor(android.R.color.darker_gray));
setupSegmentFormatter(nf);
mKademliaPie.addSeries(n, nf);
} else {
int reachable = 0;
for (DhtPeerStatsRow row : dhtStats.getData()) {
if (row.isReachable())
reachable += 1;
}
int unreachable = dhtStats.getData().size() - reachable;
mKademliaPeers.setText("" + dhtStats.getData().size());
if (reachable > 0) {
Segment r = new Segment(getString(R.string.reachable), reachable);
SegmentFormatter rf = new SegmentFormatter(getResources().getColor(R.color.green));
setupSegmentFormatter(rf);
mKademliaPie.addSeries(r, rf);
}
if (unreachable > 0) {
Segment u = new Segment(getString(R.string.unreachable), dhtStats.getData().size() - reachable);
SegmentFormatter uf = new SegmentFormatter(getResources().getColor(R.color.error_color));
setupSegmentFormatter(uf);
mKademliaPie.addSeries(u, uf);
}
}
}
mKademliaPie.getBorderPaint().setColor(Color.TRANSPARENT);
mKademliaPie.getBackgroundPaint().setColor(Color.TRANSPARENT);
}
private void setupRelayPeers() {
Set<RelayPeer> relayPeers = I2PBote.getInstance().getRelayPeers();
mRelayPeers.setText("" + relayPeers.size());
if (relayPeers.size() == 0) {
Segment n = new Segment("", 100);
SegmentFormatter nf = new SegmentFormatter(getResources().getColor(android.R.color.darker_gray));
setupSegmentFormatter(nf);
mRelayPie.addSeries(n, nf);
} else {
int good = 0;
int untested = 0;
for (RelayPeer relayPeer : relayPeers) {
int reachability = relayPeer.getReachability();
if (reachability == 0)
untested += 1;
else if (reachability > 80)
good += 1;
}
int bad = relayPeers.size() - good - untested;
if (good > 0) {
Segment g = new Segment(getString(R.string.good), good);
SegmentFormatter gf = new SegmentFormatter(getResources().getColor(R.color.green));
setupSegmentFormatter(gf);
mRelayPie.addSeries(g, gf);
}
if (bad > 0) {
Segment b = new Segment(getString(R.string.unreliable), bad);
SegmentFormatter bf = new SegmentFormatter(getResources().getColor(R.color.red));
setupSegmentFormatter(bf);
mRelayPie.addSeries(b, bf);
}
if (untested > 0) {
Segment u = new Segment(getString(R.string.untested), untested);
SegmentFormatter uf = new SegmentFormatter(getResources().getColor(R.color.accent));
setupSegmentFormatter(uf);
mRelayPie.addSeries(u, uf);
}
}
mRelayPie.getBorderPaint().setColor(Color.TRANSPARENT);
mRelayPie.getBackgroundPaint().setColor(Color.TRANSPARENT);
}
private static String joinStackTrace(Throwable e) {
StringWriter writer = null;
try {
writer = new StringWriter();
joinStackTrace(e, writer);
return writer.toString();
}
finally {
if (writer != null)
try {
writer.close();
} catch (IOException e1) {
// ignore
}
}
}
private static void joinStackTrace(Throwable e, StringWriter writer) {
PrintWriter printer = null;
try {
printer = new PrintWriter(writer);
while (e != null) {
printer.println(e);
StackTraceElement[] trace = e.getStackTrace();
for (StackTraceElement aTrace : trace) printer.println("\tat " + aTrace);
e = e.getCause();
if (e != null)
printer.println("Caused by:\r\n");
}
}
finally {
if (printer != null)
printer.close();
}
}
}

View File

@ -0,0 +1,57 @@
package i2p.bote.android;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.widget.Toast;
public class NewEmailActivity extends BoteActivityBase implements
NewEmailFragment.Callbacks {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_toolbar);
// Set the action bar
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
setSupportActionBar(toolbar);
// Enable ActionBar app icon to behave as action to go back
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
if (savedInstanceState == null) {
NewEmailFragment f;
String quoteMsgFolder = null;
String quoteMsgId = null;
NewEmailFragment.QuoteMsgType quoteMsgType = null;
Bundle args = getIntent().getExtras();
if (args != null) {
quoteMsgFolder = args.getString(NewEmailFragment.QUOTE_MSG_FOLDER);
quoteMsgId = args.getString(NewEmailFragment.QUOTE_MSG_ID);
quoteMsgType =
(NewEmailFragment.QuoteMsgType) args.getSerializable(NewEmailFragment.QUOTE_MSG_TYPE);
}
f = NewEmailFragment.newInstance(quoteMsgFolder, quoteMsgId, quoteMsgType);
getSupportFragmentManager().beginTransaction()
.add(R.id.container, f).commit();
}
}
@Override
public void onBackPressed() {
NewEmailFragment f = (NewEmailFragment) getSupportFragmentManager().findFragmentById(R.id.container);
f.onBackPressed();
}
// NewEmailFragment.Callbacks
public void onTaskFinished() {
Toast.makeText(this, R.string.email_queued_for_sending,
Toast.LENGTH_SHORT).show();
finish();
}
@Override
public void onBackPressAllowed() {
super.onBackPressed();
}
}

View File

@ -0,0 +1,712 @@
package i2p.bote.android;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ClipData;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.app.AlertDialog;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import com.mikepenz.google_material_typeface_library.GoogleMaterial;
import com.mikepenz.iconics.IconicsDrawable;
import com.tokenautocomplete.FilteredArrayAdapter;
import net.i2p.data.DataFormatException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import javax.mail.Address;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import i2p.bote.I2PBote;
import i2p.bote.android.util.BoteHelper;
import i2p.bote.android.util.ContentAttachment;
import i2p.bote.android.util.Person;
import i2p.bote.android.widget.ContactsCompletionView;
import i2p.bote.email.Attachment;
import i2p.bote.email.Email;
import i2p.bote.email.EmailIdentity;
import i2p.bote.fileencryption.PasswordException;
import i2p.bote.packet.dht.Contact;
public class NewEmailFragment extends Fragment {
private Callbacks mCallbacks = sDummyCallbacks;
public interface Callbacks {
public void onTaskFinished();
public void onBackPressAllowed();
}
private static Callbacks sDummyCallbacks = new Callbacks() {
public void onTaskFinished() {
}
public void onBackPressAllowed() {
}
};
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (!(activity instanceof Callbacks))
throw new IllegalStateException("Activity must implement fragment's callbacks.");
mCallbacks = (Callbacks) activity;
}
@Override
public void onDetach() {
super.onDetach();
mCallbacks = sDummyCallbacks;
}
public static final String QUOTE_MSG_FOLDER = "sender";
public static final String QUOTE_MSG_ID = "recipient";
public static enum QuoteMsgType {
REPLY,
REPLY_ALL,
FORWARD
}
public static final String QUOTE_MSG_TYPE = "type";
private static final long MAX_RECOMMENDED_ATTACHMENT_SIZE = 1048576;
private static final int REQUEST_FILE = 1;
private String mSenderKey;
Spinner mSpinner;
int mDefaultPos;
ArrayAdapter<Person> mAdapter;
ImageView mMore;
ContactsCompletionView mTo;
ContactsCompletionView mCc;
ContactsCompletionView mBcc;
EditText mSubject;
EditText mContent;
LinearLayout mAttachments;
private long mTotalAttachmentSize;
private View mAttachmentSizeWarning;
boolean mMoreVisible;
boolean mDirty;
public static NewEmailFragment newInstance(String quoteMsgFolder, String quoteMsgId,
QuoteMsgType quoteMsgType) {
NewEmailFragment f = new NewEmailFragment();
Bundle args = new Bundle();
args.putString(QUOTE_MSG_FOLDER, quoteMsgFolder);
args.putString(QUOTE_MSG_ID, quoteMsgId);
args.putSerializable(QUOTE_MSG_TYPE, quoteMsgType);
f.setArguments(args);
return f;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_new_email, container, false);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mSpinner = (Spinner) view.findViewById(R.id.sender_spinner);
mMore = (ImageView) view.findViewById(R.id.more);
mTo = (ContactsCompletionView) view.findViewById(R.id.to);
mCc = (ContactsCompletionView) view.findViewById(R.id.cc);
mBcc = (ContactsCompletionView) view.findViewById(R.id.bcc);
mSubject = (EditText) view.findViewById(R.id.subject);
mContent = (EditText) view.findViewById(R.id.message);
mAttachments = (LinearLayout) view.findViewById(R.id.attachments);
String quoteMsgFolder = getArguments().getString(QUOTE_MSG_FOLDER);
String quoteMsgId = getArguments().getString(QUOTE_MSG_ID);
QuoteMsgType quoteMsgType = (QuoteMsgType) getArguments().getSerializable(QUOTE_MSG_TYPE);
boolean hide = I2PBote.getInstance().getConfiguration().getHideLocale();
List<Person> toRecipients = new ArrayList<Person>();
List<Person> ccRecipients = new ArrayList<Person>();
String origSubject = null;
String origContent = null;
String origFrom = null;
try {
Email origEmail = BoteHelper.getEmail(quoteMsgFolder, quoteMsgId);
if (origEmail != null) {
mSenderKey = BoteHelper.extractEmailDestination(
BoteHelper.getOneLocalRecipient(origEmail).toString());
if (quoteMsgType == QuoteMsgType.REPLY) {
String recipient = BoteHelper.getNameAndDestination(
origEmail.getReplyAddress(I2PBote.getInstance().getIdentities()));
toRecipients.add(extractPerson(recipient));
} else if (quoteMsgType == QuoteMsgType.REPLY_ALL) {
// TODO split between To and Cc
// TODO don't include our address
// What happens if an email is received by multiple local identities?
for (Address address : origEmail.getAllAddresses(true)) {
Person person = extractPerson(address.toString());
if (person != null)
toRecipients.add(person);
}
}
origSubject = origEmail.getSubject();
origContent = origEmail.getText();
origFrom = BoteHelper.getShortSenderName(origEmail.getOneFromAddress(), 50);
}
} catch (PasswordException e) {
// Should not happen, we cannot get to this page without authenticating
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (GeneralSecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (MessagingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// Set up identities spinner
IdentityAdapter identities = new IdentityAdapter(getActivity());
mSpinner.setAdapter(identities);
mSpinner.setSelection(mDefaultPos);
// Set up Cc/Bcc button
mMore.setImageDrawable(new IconicsDrawable(getActivity(), GoogleMaterial.Icon.gmd_unfold_more).colorRes(R.color.md_grey_600).sizeDp(24).paddingDp(3));
mMore.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mCc.setVisibility(mMoreVisible ? View.GONE : View.VISIBLE);
mBcc.setVisibility(mMoreVisible ? View.GONE : View.VISIBLE);
mMore.setImageDrawable(new IconicsDrawable(getActivity(), mMoreVisible ?
GoogleMaterial.Icon.gmd_unfold_more : GoogleMaterial.Icon.gmd_unfold_less)
.colorRes(R.color.md_grey_600)
.sizeDp(24)
.paddingDp(mMoreVisible ? 3 : 4));
mMoreVisible = !mMoreVisible;
}
});
// Set up contacts auto-complete
List<Person> contacts = new ArrayList<Person>();
try {
for (Contact contact : I2PBote.getInstance().getAddressBook().getAll()) {
contacts.add(new Person(contact.getName(), contact.getBase64Dest(),
BoteHelper.decodePicture(contact.getPictureBase64())));
}
} catch (PasswordException e) {
// TODO handle
e.printStackTrace();
}
mAdapter = new FilteredArrayAdapter<Person>(getActivity(), android.R.layout.simple_list_item_1, contacts) {
@Override
protected boolean keepObject(Person obj, String mask) {
mask = mask.toLowerCase(Locale.US);
return obj.getName().toLowerCase(Locale.US).startsWith(mask) || obj.getAddress().toLowerCase(Locale.US).startsWith(mask);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v;
if (convertView == null)
v = ((LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE))
.inflate(R.layout.listitem_contact, parent, false);
else
v = convertView;
setViewContent(v, position);
return v;
}
private void setViewContent(View v, int position) {
Person person = getItem(position);
((TextView) v.findViewById(R.id.contact_name)).setText(person.getName());
ImageView picView = (ImageView) v.findViewById(R.id.contact_picture);
Bitmap picture = person.getPicture();
if (picture == null) {
ViewGroup.LayoutParams lp = picView.getLayoutParams();
picture = BoteHelper.getIdenticonForAddress(person.getAddress(), lp.width, lp.height);
}
picView.setImageBitmap(picture);
}
};
mTo.setAdapter(mAdapter);
mCc.setAdapter(mAdapter);
mBcc.setAdapter(mAdapter);
for (Person recipient : toRecipients) {
mTo.addObject(recipient);
}
for (Person recipient : ccRecipients) {
mCc.addObject(recipient);
}
if (origSubject != null) {
String subjectPrefix;
if (quoteMsgType == QuoteMsgType.FORWARD) {
subjectPrefix = getResources().getString(
hide ? R.string.subject_prefix_fwd_hide
: R.string.subject_prefix_fwd);
} else {
subjectPrefix = getResources().getString(
hide ? R.string.response_prefix_re_hide
: R.string.response_prefix_re);
}
if (!origSubject.startsWith(subjectPrefix))
origSubject = subjectPrefix + " " + origSubject;
mSubject.setText(origSubject);
}
if (origContent != null) {
StringBuilder quotation = new StringBuilder();
quotation.append("\n\n");
quotation.append(getResources().getString(
hide ? R.string.response_quote_wrote_hide
: R.string.response_quote_wrote,
origFrom));
String[] lines = origContent.split("\r?\n|\r");
for (String line : lines)
quotation = quotation.append("\n> ").append(line);
mContent.setText(quotation);
}
if (savedInstanceState == null) {
mTo.setPrefix(getResources().getString(R.string.email_to) + " ");
mCc.setPrefix(getResources().getString(R.string.email_cc) + " ");
mBcc.setPrefix(getResources().getString(R.string.email_bcc) + " ");
}
TextWatcher dirtyWatcher = 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) {
mDirty = true;
}
@Override
public void afterTextChanged(Editable s) {
}
};
mSubject.addTextChangedListener(dirtyWatcher);
mContent.addTextChangedListener(dirtyWatcher);
}
private Person extractPerson(String recipient) {
if (recipient.equals("Anonymous"))
return null;
String recipientName = BoteHelper.extractName(recipient);
try {
recipientName = BoteHelper.getName(recipient);
} catch (PasswordException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (GeneralSecurityException e) {
e.printStackTrace();
}
String recipientAddr = BoteHelper.extractEmailDestination(recipient);
if (recipientAddr == null) { // Assume external address
recipientAddr = recipient;
if (recipientName.isEmpty())
recipientName = recipientAddr;
return new Person(recipientName, recipientAddr, null, true);
} else {
if (recipientName.isEmpty()) // Dest with no name
recipientName = recipientAddr.substring(0, 5);
Bitmap recipientPic = null;
try {
recipientPic = BoteHelper.getPictureForDestination(recipientAddr);
} catch (PasswordException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (GeneralSecurityException e) {
e.printStackTrace();
}
return new Person(recipientName, recipientAddr, recipientPic);
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.new_email, menu);
menu.findItem(R.id.action_attach_file).setIcon(BoteHelper.getMenuIcon(getActivity(), GoogleMaterial.Icon.gmd_attach_file));
menu.findItem(R.id.action_send_email).setIcon(BoteHelper.getMenuIcon(getActivity(), GoogleMaterial.Icon.gmd_send));
}
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case R.id.action_attach_file:
requestFile();
return true;
case R.id.action_send_email:
if (sendEmail())
mCallbacks.onTaskFinished();
return true;
case android.R.id.home:
if (mDirty) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(R.string.stop_composing_email)
.setMessage(R.string.all_changes_will_be_discarded)
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
mCallbacks.onBackPressAllowed();
else
getActivity().onNavigateUp();
}
}).setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.cancel();
}
}).show();
return true;
} else
return super.onOptionsItemSelected(item);
default:
return super.onOptionsItemSelected(item);
}
}
private void requestFile() {
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.setType("*/*");
i.addCategory(Intent.CATEGORY_OPENABLE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)
i.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
startActivityForResult(
Intent.createChooser(i,
getResources().getString(R.string.select_attachment)),
REQUEST_FILE);
}
@SuppressLint("NewApi")
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK) {
if (resultCode == Activity.RESULT_CANCELED) {
System.out.println("Cancelled");
}
return;
}
switch (requestCode) {
case REQUEST_FILE:
addAttachment(data.getData());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 &&
data.getClipData() != null) {
ClipData clipData = data.getClipData();
for (int i = 0; i < clipData.getItemCount(); i++) {
addAttachment(clipData.getItemAt(i).getUri());
}
}
break;
}
}
private void addAttachment(Uri uri) {
// Try to create a ContentAttachment using the provided Uri.
try {
final ContentAttachment attachment = new ContentAttachment(getActivity(), uri);
final View v = getActivity().getLayoutInflater().inflate(R.layout.listitem_attachment, mAttachments, false);
v.setTag(attachment);
((TextView) v.findViewById(R.id.filename)).setText(attachment.getFileName());
((TextView) v.findViewById(R.id.size)).setText(attachment.getHumanReadableSize());
ImageView attachmentAction = (ImageView) v.findViewById(R.id.attachment_action);
attachmentAction.setImageDrawable(new IconicsDrawable(getActivity(), GoogleMaterial.Icon.gmd_clear).colorRes(R.color.md_grey_600).sizeDp(24).paddingDp(5));
attachmentAction.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
updateAttachmentSizeCount(attachment.getSize(), false);
attachment.clean();
mAttachments.removeView(v);
}
});
mAttachments.addView(v);
updateAttachmentSizeCount(attachment.getSize(), true);
} catch (IllegalArgumentException iae) {
Log.e(Constants.ANDROID_LOG_TAG, "Failed to get attachment", iae);
} catch (FileNotFoundException e) {
e.printStackTrace();
Log.e(Constants.ANDROID_LOG_TAG, "File not found: " + uri);
}
}
private void updateAttachmentSizeCount(long size, boolean increase) {
if (increase) {
mTotalAttachmentSize += size;
if (mTotalAttachmentSize > MAX_RECOMMENDED_ATTACHMENT_SIZE &&
mAttachmentSizeWarning == null) {
mAttachmentSizeWarning = getActivity().getLayoutInflater().inflate(
R.layout.listitem_attachment_warning, mAttachments, false);
TextView warning = (TextView) mAttachmentSizeWarning.findViewById(
R.id.attachment_warning_text);
warning.setText(
getString(R.string.attachment_size_warning,
BoteHelper.getHumanReadableSize(
getActivity(), MAX_RECOMMENDED_ATTACHMENT_SIZE))
);
mAttachments.addView(mAttachmentSizeWarning, 0);
}
} else {
mTotalAttachmentSize -= size;
if (mTotalAttachmentSize <= MAX_RECOMMENDED_ATTACHMENT_SIZE &&
mAttachmentSizeWarning != null) {
mAttachments.removeView(mAttachmentSizeWarning);
mAttachmentSizeWarning = null;
}
}
}
private boolean sendEmail() {
Email email = new Email(I2PBote.getInstance().getConfiguration().getIncludeSentTime());
try {
// Set sender
EmailIdentity sender = (EmailIdentity) mSpinner.getSelectedItem();
InternetAddress ia = new InternetAddress(
sender == null ? "Anonymous" :
BoteHelper.getNameAndDestination(sender.getKey()));
email.setFrom(ia);
// We must continue to set "Sender:" even with only one mailbox
// in "From:", which is against RFC 2822 but required for older
// Bote versions to see a sender (and validate the signature).
email.setSender(ia);
for (Object obj : mTo.getObjects()) {
Person person = (Person) obj;
email.addRecipient(Message.RecipientType.TO, new InternetAddress(
person.getAddress(), person.getName()));
}
if (mMoreVisible) {
for (Object obj : mCc.getObjects()) {
Person person = (Person) obj;
email.addRecipient(Message.RecipientType.CC, new InternetAddress(
person.getAddress(), person.getName()));
}
for (Object obj : mBcc.getObjects()) {
Person person = (Person) obj;
email.addRecipient(Message.RecipientType.BCC, new InternetAddress(
person.getAddress(), person.getName()));
}
}
// Check that we have someone to send to
Address[] rcpts = email.getAllRecipients();
if (rcpts == null || rcpts.length == 0) {
// No recipients
mTo.setError(getActivity().getString(R.string.add_one_recipient));
mTo.requestFocus();
return false;
} else {
mTo.setError(null);
}
email.setSubject(mSubject.getText().toString(), "UTF-8");
// Extract the attachments
List<Attachment> attachments = new ArrayList<Attachment>();
for (int i = 0; i < mAttachments.getChildCount(); i++) {
View v = mAttachments.getChildAt(i);
// Warning views don't have tags set
if (v.getTag() != null)
attachments.add((Attachment) v.getTag());
}
// Set the text and add attachments
email.setContent(mContent.getText().toString(), attachments);
// Cache the fact that we sent this email
BoteHelper.setEmailSent(email, true);
// Send the email
I2PBote.getInstance().sendEmail(email);
// Clean up attachments
for (Attachment attachment : attachments) {
if (!attachment.clean())
Log.e(Constants.ANDROID_LOG_TAG, "Can't clean up attachment: <" + attachment + ">");
}
return true;
} catch (PasswordException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (AddressException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (MessagingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (GeneralSecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;
}
private class IdentityAdapter extends ArrayAdapter<EmailIdentity> {
private LayoutInflater mInflater;
public IdentityAdapter(Context context) {
super(context, android.R.layout.simple_spinner_item);
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
try {
Collection<EmailIdentity> identities = I2PBote.getInstance().getIdentities().getAll();
mDefaultPos = 0;
String selectedIdentity = getActivity().getSharedPreferences(Constants.SHARED_PREFS, 0)
.getString(Constants.PREF_SELECTED_IDENTITY, null);
for (EmailIdentity identity : identities) {
add(identity);
boolean isDefaultIdentity = selectedIdentity == null ?
identity.isDefaultIdentity() :
identity.getKey().equals(selectedIdentity);
boolean selectByDefault = mSenderKey == null ?
isDefaultIdentity :
identity.getKey().equals(mSenderKey);
if (selectByDefault)
mDefaultPos = getPosition(identity);
}
} catch (PasswordException e) {
// TODO Handle
e.printStackTrace();
} catch (IOException e) {
// TODO Handle
e.printStackTrace();
} catch (GeneralSecurityException e) {
// TODO Handle
e.printStackTrace();
}
}
@Override
public EmailIdentity getItem(int position) {
if (position > 0)
return super.getItem(position - 1);
else
return null;
}
@Override
public int getPosition(EmailIdentity item) {
if (item != null)
return super.getPosition(item) + 1;
else
return 0;
}
@Override
public int getCount() {
return super.getCount() + 1;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v;
if (convertView == null)
v = mInflater.inflate(android.R.layout.simple_spinner_item, parent, false);
else
v = convertView;
setViewText(v, position);
return v;
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
View v;
if (convertView == null)
v = mInflater.inflate(android.R.layout.simple_spinner_dropdown_item, parent, false);
else
v = convertView;
setViewText(v, position);
return v;
}
private void setViewText(View v, int position) {
TextView text = (TextView) v.findViewById(android.R.id.text1);
EmailIdentity identity = getItem(position);
if (identity == null)
text.setText("Anonymous");
else
text.setText(identity.getPublicName());
}
}
public void onBackPressed() {
if (mDirty) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(R.string.stop_composing_email)
.setMessage(R.string.all_changes_will_be_discarded)
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
mCallbacks.onBackPressAllowed();
}
}).setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.cancel();
}
}).show();
} else
mCallbacks.onBackPressAllowed();
}
}

View File

@ -0,0 +1,215 @@
package i2p.bote.android;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v4.view.ViewPager;
import android.support.v7.widget.Toolbar;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.List;
import i2p.bote.android.util.BetterAsyncTaskLoader;
import i2p.bote.android.util.BoteHelper;
import i2p.bote.email.Email;
import i2p.bote.fileencryption.PasswordException;
import i2p.bote.folder.EmailFolder;
import i2p.bote.folder.FolderListener;
public class ViewEmailActivity extends BoteActivityBase implements
LoaderManager.LoaderCallbacks<List<String>> {
public static final String FOLDER_NAME = "folder_name";
public static final String MESSAGE_ID = "message_id";
private static final int MESSAGE_ID_LIST_LOADER = 1;
private EmailFolder mFolder;
// The messageId of the currently-viewed Email
private String mMessageId;
private ViewPager mPager;
private ViewEmailPagerAdapter mPagerAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_email);
// Set the action bar
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
setSupportActionBar(toolbar);
// Enable ActionBar app icon to behave as action to go back
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowTitleEnabled(false);
Intent i = getIntent();
String folderName = i.getStringExtra(FOLDER_NAME);
mFolder = BoteHelper.getMailFolder(
folderName == null ? "inbox" : folderName);
mMessageId = i.getStringExtra(MESSAGE_ID);
// Instantiate the ViewPager and PagerAdapter
mPager = (ViewPager) findViewById(R.id.pager);
mPagerAdapter = new ViewEmailPagerAdapter(getSupportFragmentManager());
mPager.setAdapter(mPagerAdapter);
mPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
mMessageId = mPagerAdapter.getMessageId(position);
// Mark the visible email as not new
if (mMessageId != null) {
try {
if (!BoteHelper.isOutbox(mFolder))
mFolder.setNew(mMessageId, false);
mFolder.setRecent(mMessageId, false);
} catch (PasswordException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (GeneralSecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
// Fire off a Loader to fetch the list of Emails
getSupportLoaderManager().initLoader(MESSAGE_ID_LIST_LOADER, null, this);
}
private class ViewEmailPagerAdapter extends FragmentStatePagerAdapter {
private List<String> mIds;
public ViewEmailPagerAdapter(FragmentManager fm) {
super(fm);
}
public void setData(List<String> data) {
mIds = data;
notifyDataSetChanged();
}
public int getPosition(String messageId) {
if (mIds == null)
return 0;
else
return mIds.indexOf(messageId);
}
public String getMessageId(int position) {
if (mIds == null)
return null;
else
return mIds.get(position);
}
@Override
public Fragment getItem(int position) {
if (mIds == null)
return null;
else
return ViewEmailFragment.newInstance(
mFolder.getName(), mIds.get(position));
}
@Override
public int getCount() {
if (mIds == null)
return 0;
else
return mIds.size();
}
}
// LoaderManager.LoaderCallbacks<List<String>>
public Loader<List<String>> onCreateLoader(int id, Bundle args) {
return new MessageIdListLoader(this, mFolder);
}
private static class MessageIdListLoader extends BetterAsyncTaskLoader<List<String>> implements
FolderListener {
private EmailFolder mFolder;
public MessageIdListLoader(Context context, EmailFolder folder) {
super(context);
mFolder = folder;
}
@Override
public List<String> loadInBackground() {
List<String> messageIds = null;
try {
List<Email> emails = BoteHelper.getEmails(mFolder, null, true);
messageIds = new ArrayList<String>();
for (Email email : emails)
messageIds.add(email.getMessageID());
} catch (PasswordException pe) {
// TODO: Handle this error properly (get user to log in)
}
return messageIds;
}
protected void onStartMonitoring() {
mFolder.addFolderListener(this);
}
protected void onStopMonitoring() {
mFolder.removeFolderListener(this);
}
protected void releaseResources(List<String> data) {
}
// FolderListener
@Override
public void elementAdded(String messageId) {
onContentChanged();
}
@Override
public void elementUpdated() {
onContentChanged();
}
@Override
public void elementRemoved(String messageId) {
onContentChanged();
}
}
public void onLoadFinished(Loader<List<String>> loader,
List<String> data) {
mPagerAdapter.setData(data);
mPager.setCurrentItem(
mPagerAdapter.getPosition(mMessageId));
// Mark the current email as not new
if (mMessageId != null) {
try {
if (!BoteHelper.isOutbox(mFolder))
mFolder.setNew(mMessageId, false);
mFolder.setRecent(mMessageId, false);
} catch (PasswordException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (GeneralSecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void onLoaderReset(Loader<List<String>> loader) {
mPagerAdapter.setData(null);
}
}

View File

@ -0,0 +1,324 @@
package i2p.bote.android;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Typeface;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.app.Fragment;
import android.support.v7.widget.PopupMenu;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.mikepenz.google_material_typeface_library.GoogleMaterial;
import com.mikepenz.iconics.IconicsDrawable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.text.DateFormat;
import java.util.List;
import javax.mail.Address;
import javax.mail.MessagingException;
import javax.mail.Part;
import i2p.bote.android.provider.AttachmentProvider;
import i2p.bote.android.util.BoteHelper;
import i2p.bote.android.util.ContentAttachment;
import i2p.bote.email.Attachment;
import i2p.bote.email.Email;
import i2p.bote.fileencryption.PasswordException;
public class ViewEmailFragment extends Fragment {
private String mFolderName;
private String mMessageId;
private boolean mIsAnonymous;
public static ViewEmailFragment newInstance(
String folderName, String messageId) {
ViewEmailFragment f = new ViewEmailFragment();
Bundle args = new Bundle();
args.putString("folderName", folderName);
args.putString("messageId", messageId);
f.setArguments(args);
return f;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
mFolderName = getArguments() != null ? getArguments().getString("folderName") : "inbox";
mMessageId = getArguments() != null ? getArguments().getString("messageId") : "1";
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_view_email, container, false);
try {
Email e = BoteHelper.getEmail(mFolderName, mMessageId);
if (e != null) {
displayEmail(e, v);
} else {
TextView subject = (TextView) v.findViewById(R.id.email_subject);
subject.setText(R.string.email_not_found);
}
} catch (PasswordException e) {
// TODO: Handle
e.printStackTrace();
}
return v;
}
private void displayEmail(Email email, View v) {
View sigInvalid = v.findViewById(R.id.signature_invalid);
TextView subject = (TextView) v.findViewById(R.id.email_subject);
ImageView picture = (ImageView) v.findViewById(R.id.picture);
TextView sender = (TextView) v.findViewById(R.id.email_sender);
LinearLayout toRecipients = (LinearLayout) v.findViewById(R.id.email_to);
TextView sent = (TextView) v.findViewById(R.id.email_sent);
TextView received = (TextView) v.findViewById(R.id.email_received);
TextView content = (TextView) v.findViewById(R.id.email_content);
LinearLayout attachments = (LinearLayout) v.findViewById(R.id.attachments);
try {
String fromAddress = email.getOneFromAddress();
subject.setText(email.getSubject());
Bitmap pic = BoteHelper.getPictureForAddress(fromAddress);
if (pic != null)
picture.setImageBitmap(pic);
else if (!email.isAnonymous()) {
ViewGroup.LayoutParams lp = picture.getLayoutParams();
picture.setImageBitmap(BoteHelper.getIdenticonForAddress(fromAddress, lp.width, lp.height));
}
final String senderDisplay = BoteHelper.getDisplayAddress(fromAddress);
if (!email.isSignatureValid() && !email.isAnonymous()) {
sigInvalid.setVisibility(View.VISIBLE);
sigInvalid.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(getActivity(), getString(R.string.signature_invalid, senderDisplay), Toast.LENGTH_LONG).show();
}
});
}
sender.setText(senderDisplay);
if (email.isAnonymous() && !BoteHelper.isSentEmail(email))
sender.setTypeface(Typeface.DEFAULT, Typeface.ITALIC);
Address[] emailToRecipients = email.getToAddresses();
if (emailToRecipients != null) {
for (Address recipient : emailToRecipients) {
TextView tv = new TextView(getActivity());
tv.setText(BoteHelper.getDisplayAddress(recipient.toString()));
tv.setTextAppearance(getActivity(), R.style.TextAppearance_AppCompat_Secondary);
toRecipients.addView(tv);
}
}
Address[] emailCcRecipients = email.getCCAddresses();
if (emailCcRecipients != null) {
v.findViewById(R.id.email_cc_row).setVisibility(View.VISIBLE);
LinearLayout ccRecipients = (LinearLayout) v.findViewById(R.id.email_cc);
for (Address recipient : emailCcRecipients) {
TextView tv = new TextView(getActivity());
tv.setText(BoteHelper.getDisplayAddress(recipient.toString()));
tv.setTextAppearance(getActivity(), R.style.TextAppearance_AppCompat_Secondary);
ccRecipients.addView(tv);
}
}
Address[] emailBccRecipients = email.getBCCAddresses();
if (emailBccRecipients != null) {
v.findViewById(R.id.email_bcc_row).setVisibility(View.VISIBLE);
LinearLayout bccRecipients = (LinearLayout) v.findViewById(R.id.email_bcc);
for (Address recipient : emailBccRecipients) {
TextView tv = new TextView(getActivity());
tv.setText(BoteHelper.getDisplayAddress(recipient.toString()));
tv.setTextAppearance(getActivity(), R.style.TextAppearance_AppCompat_Secondary);
bccRecipients.addView(tv);
}
}
if (email.getSentDate() != null)
sent.setText(DateFormat.getInstance().format(
email.getSentDate()));
if (email.getReceivedDate() != null)
received.setText(DateFormat.getInstance().format(
email.getReceivedDate()));
content.setText(email.getText());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
content.setTextIsSelectable(true);
List<Part> parts = email.getParts();
for (int partIndex = 0; partIndex < parts.size(); partIndex++) {
Part part = parts.get(partIndex);
if (Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition())) {
final ContentAttachment attachment = new ContentAttachment(getActivity(), part);
View a = getActivity().getLayoutInflater().inflate(R.layout.listitem_attachment, attachments, false);
((TextView) a.findViewById(R.id.filename)).setText(attachment.getFileName());
((TextView) a.findViewById(R.id.size)).setText(attachment.getHumanReadableSize());
final ImageView action = (ImageView) a.findViewById(R.id.attachment_action);
action.setImageDrawable(new IconicsDrawable(getActivity(), GoogleMaterial.Icon.gmd_more_vert).colorRes(R.color.md_grey_600).sizeDp(24).paddingDp(4));
action.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
PopupMenu popup = new PopupMenu(getActivity(), action);
popup.inflate(R.menu.attachment);
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.save_attachment:
saveAttachment(attachment);
return true;
default:
return false;
}
}
});
popup.show();
}
});
final Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(AttachmentProvider.getUriForAttachment(mFolderName, mMessageId, partIndex));
i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION |
Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
a.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivity(i);
}
});
attachments.addView(a);
}
}
// Prepare fields for replying
mIsAnonymous = email.isAnonymous();
} catch (MessagingException e) {
// TODO Handle
e.printStackTrace();
} catch (PasswordException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (GeneralSecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (BoteHelper.isOutbox(mFolderName)) {
((TextView) v.findViewById(R.id.email_status)).setText(
BoteHelper.getEmailStatusText(getActivity(), email, true));
v.findViewById(R.id.email_status_row).setVisibility(View.VISIBLE);
}
}
private void saveAttachment(Attachment attachment) {
File downloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
String fileName = attachment.getFileName();
int extInd = fileName.lastIndexOf('.');
String name = fileName.substring(0, extInd);
String ext = fileName.substring(extInd);
File outFile = new File(downloadsDir, fileName);
for (int i = 1; outFile.exists() && i < 32; i++) {
fileName = name + "-" + i + ext;
outFile = new File(downloadsDir, fileName);
}
if (outFile.exists()) {
Toast.makeText(getActivity(), R.string.file_exists_in_downloads, Toast.LENGTH_SHORT).show();
return;
}
FileOutputStream out = null;
try {
out = new FileOutputStream(outFile);
attachment.getDataHandler().writeTo(out);
Toast.makeText(getActivity(),
getResources().getString(R.string.saved_to_downloads, fileName),
Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(getActivity(), R.string.could_not_save_to_downloads, Toast.LENGTH_SHORT).show();
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
}
}
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.view_email, menu);
MenuItem reply = menu.findItem(R.id.action_reply);
reply.setIcon(BoteHelper.getMenuIcon(getActivity(), GoogleMaterial.Icon.gmd_reply));
menu.findItem(R.id.action_reply_all).setIcon(BoteHelper.getMenuIcon(getActivity(), GoogleMaterial.Icon.gmd_reply_all));
menu.findItem(R.id.action_forward).setIcon(BoteHelper.getMenuIcon(getActivity(), GoogleMaterial.Icon.gmd_forward));
if (mIsAnonymous)
reply.setVisible(false);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_reply:
case R.id.action_reply_all:
case R.id.action_forward:
Intent nei = new Intent(getActivity(), NewEmailActivity.class);
nei.putExtra(NewEmailFragment.QUOTE_MSG_FOLDER, mFolderName);
nei.putExtra(NewEmailFragment.QUOTE_MSG_ID, mMessageId);
NewEmailFragment.QuoteMsgType type = null;
switch (item.getItemId()) {
case R.id.action_reply:
type = NewEmailFragment.QuoteMsgType.REPLY;
break;
case R.id.action_reply_all:
type = NewEmailFragment.QuoteMsgType.REPLY_ALL;
break;
case R.id.action_forward:
type = NewEmailFragment.QuoteMsgType.FORWARD;
}
nei.putExtra(NewEmailFragment.QUOTE_MSG_TYPE, type);
startActivity(nei);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}

View File

@ -0,0 +1,73 @@
package i2p.bote.android.addressbook;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;
import i2p.bote.android.BoteActivityBase;
import i2p.bote.android.Constants;
import i2p.bote.android.R;
import i2p.bote.packet.dht.Contact;
public class AddressBookActivity extends BoteActivityBase implements
AddressBookFragment.OnContactSelectedListener {
static final int ALTER_CONTACT_LIST = 1;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_toolbar);
// Set the action bar
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
setSupportActionBar(toolbar);
// Enable ActionBar app icon to behave as action to go back
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
if (savedInstanceState == null) {
AddressBookFragment f = new AddressBookFragment();
getSupportFragmentManager().beginTransaction()
.add(R.id.container, f).commit();
}
}
@Override
public void onContactSelected(Contact contact) {
if (Intent.ACTION_PICK.equals(getIntent().getAction())) {
Intent result = new Intent();
result.putExtra(ViewContactFragment.ADDRESS, contact.getBase64Dest());
setResult(Activity.RESULT_OK, result);
finish();
} else {
Intent i = new Intent(this, ViewContactActivity.class);
i.putExtra(ViewContactFragment.ADDRESS, contact.getBase64Dest());
startActivityForResult(i, ALTER_CONTACT_LIST);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
if (scanResult != null) {
String content = scanResult.getContents();
if (content != null && content.startsWith(Constants.EMAILDEST_SCHEME)) {
String destination = content.substring(Constants.EMAILDEST_SCHEME.length() + 1);
Intent nci = new Intent(this, EditContactActivity.class);
nci.putExtra(EditContactFragment.NEW_DESTINATION, destination);
startActivityForResult(nci, ALTER_CONTACT_LIST);
}
} else if (requestCode == ALTER_CONTACT_LIST) {
if (resultCode == Activity.RESULT_OK) {
AddressBookFragment f = (AddressBookFragment) getSupportFragmentManager().findFragmentById(R.id.container);
f.updateContactList();
}
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
}

View File

@ -0,0 +1,219 @@
package i2p.bote.android.addressbook;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import com.google.zxing.integration.android.IntentIntegrator;
import com.pnikosis.materialishprogress.ProgressWheel;
import java.util.SortedSet;
import i2p.bote.I2PBote;
import i2p.bote.android.R;
import i2p.bote.android.util.AuthenticatedFragment;
import i2p.bote.android.util.BetterAsyncTaskLoader;
import i2p.bote.android.widget.DividerItemDecoration;
import i2p.bote.android.widget.LoadingRecyclerView;
import i2p.bote.fileencryption.PasswordException;
import i2p.bote.packet.dht.Contact;
public class AddressBookFragment extends AuthenticatedFragment implements
LoaderManager.LoaderCallbacks<SortedSet<Contact>> {
OnContactSelectedListener mCallback;
private LoadingRecyclerView mContactsList;
private ContactAdapter mAdapter;
private View mPromotedActions;
// Container Activity must implement this interface
public interface OnContactSelectedListener {
public void onContactSelected(Contact contact);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (OnContactSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnContactSelectedListener");
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Only so we can show/hide the FAM
setHasOptionsMenu(true);
}
@Override
public View onCreateAuthenticatedView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_list_contacts, container, false);
mContactsList = (LoadingRecyclerView) v.findViewById(R.id.contacts_list);
View empty = v.findViewById(R.id.empty);
ProgressWheel loading = (ProgressWheel) v.findViewById(R.id.loading);
mContactsList.setLoadingView(empty, loading);
mPromotedActions = v.findViewById(R.id.promoted_actions);
ImageButton b = (ImageButton) v.findViewById(R.id.action_new_contact);
b.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startNewContact();
}
});
b = (ImageButton) v.findViewById(R.id.action_scan_qr_code);
b.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startScanQrCode();
}
});
return v;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mContactsList.setHasFixedSize(true);
mContactsList.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST));
// Use a linear layout manager
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
mContactsList.setLayoutManager(mLayoutManager);
// Set the adapter for the list view
mAdapter = new ContactAdapter(getActivity(), mCallback);
mContactsList.setAdapter(mAdapter);
}
/**
* Start loading the address book.
* Only called when we have a password cached, or no
* password is required.
*/
protected void onInitializeFragment() {
getLoaderManager().initLoader(0, null, this);
}
protected void onDestroyFragment() {
getLoaderManager().destroyLoader(0);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.address_book, menu);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
boolean passwordRequired = I2PBote.getInstance().isPasswordRequired();
menu.findItem(R.id.export_address_book).setVisible(!passwordRequired);
menu.findItem(R.id.import_address_book).setVisible(!passwordRequired);
mPromotedActions.setVisibility(passwordRequired ? View.GONE : View.VISIBLE);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.export_address_book:
Intent ei = new Intent(getActivity(), AddressBookShipActivity.class);
ei.putExtra(AddressBookShipActivity.EXPORTING, true);
startActivity(ei);
return true;
case R.id.import_address_book:
Intent ii = new Intent(getActivity(), AddressBookShipActivity.class);
ii.putExtra(AddressBookShipActivity.EXPORTING, false);
startActivity(ii);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void startNewContact() {
Intent nci = new Intent(getActivity(), EditContactActivity.class);
getActivity().startActivityForResult(nci, AddressBookActivity.ALTER_CONTACT_LIST);
}
private void startScanQrCode() {
IntentIntegrator integrator = new IntentIntegrator(getActivity());
integrator.initiateScan(IntentIntegrator.QR_CODE_TYPES);
}
protected void updateContactList() {
getLoaderManager().restartLoader(0, null, this);
}
// LoaderManager.LoaderCallbacks<SortedSet<Contact>>
public Loader<SortedSet<Contact>> onCreateLoader(int id, Bundle args) {
return new AddressBookLoader(getActivity());
}
private static class AddressBookLoader extends BetterAsyncTaskLoader<SortedSet<Contact>> {
public AddressBookLoader(Context context) {
super(context);
}
@Override
public SortedSet<Contact> loadInBackground() {
SortedSet<Contact> contacts = null;
try {
contacts = I2PBote.getInstance().getAddressBook().getAll();
} catch (PasswordException e) {
// TODO handle, but should not get here
e.printStackTrace();
}
return contacts;
}
@Override
protected void onStartMonitoring() {
}
@Override
protected void onStopMonitoring() {
}
@Override
protected void releaseResources(SortedSet<Contact> data) {
}
}
@Override
public void onLoadFinished(Loader<SortedSet<Contact>> loader,
SortedSet<Contact> data) {
mAdapter.setContacts(data);
}
@Override
public void onLoaderReset(Loader<SortedSet<Contact>> loader) {
mAdapter.setContacts(null);
}
}

View File

@ -0,0 +1,26 @@
package i2p.bote.android.addressbook;
import android.widget.Toast;
import i2p.bote.android.R;
import i2p.bote.android.util.DataShipActivity;
import i2p.bote.android.util.DataShipFragment;
public class AddressBookShipActivity extends DataShipActivity {
@Override
protected DataShipFragment getDataShipFragment() {
return AddressBookShipFragment.newInstance(mExporting);
}
// DataShipFragment.Callbacks
public void onTaskFinished() {
Toast.makeText(this,
mExporting ?
R.string.address_book_exported:
R.string.address_book_imported,
Toast.LENGTH_SHORT).show();
setResult(RESULT_OK);
finish();
}
}

View File

@ -0,0 +1,95 @@
package i2p.bote.android.addressbook;
import android.os.Bundle;
import android.view.View;
import java.io.File;
import java.io.FileDescriptor;
import i2p.bote.I2PBote;
import i2p.bote.android.R;
import i2p.bote.android.util.DataShipFragment;
import i2p.bote.android.util.RobustAsyncTask;
import i2p.bote.fileencryption.PasswordException;
public abstract class AddressBookShipFragment extends DataShipFragment {
public static DataShipFragment newInstance(boolean exporting) {
return exporting ?
new ExportAddressBookFragment() :
new ImportAddressBookFragment();
}
public static class ExportAddressBookFragment extends ExportDataFragment {
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mExportFilename.setText("addressBook");
}
@Override
protected RobustAsyncTask<Object, String, String> getExportWaiter() {
return new ExportWaiter();
}
@Override
protected int getTitle() {
return R.string.export_address_book;
}
private class ExportWaiter extends RobustAsyncTask<Object, String, String> {
@Override
protected String doInBackground(Object... params) {
try {
publishProgress(getResources().getString(R.string.exporting_address_book));
I2PBote.getInstance().getAddressBook().export(
(File) params[0],
(String) params[1]);
return null;
} catch (Throwable e) {
cancel(false);
return e.getMessage();
}
}
}
}
public static class ImportAddressBookFragment extends ImportDataFragment {
@Override
protected RobustAsyncTask<Object, String, String> getImportWaiter() {
return new ImportWaiter();
}
@Override
protected int getTitle() {
return R.string.import_address_book;
}
private class ImportWaiter extends RobustAsyncTask<Object, String, String> {
@Override
protected String doInBackground(Object... params) {
try {
publishProgress(getResources().getString(R.string.importing_address_book));
boolean success = I2PBote.getInstance().getAddressBook().importFromFileDescriptor(
(FileDescriptor) params[0],
(String) params[1],
(Boolean) params[2],
(Boolean) params[3]);
if (success)
return null;
else {
cancel(false);
return (params[1] == null) ?
getResources().getString(R.string.no_contacts_found_maybe_encrypted) :
getResources().getString(R.string.no_contacts_found);
}
} catch (Throwable e) {
e.printStackTrace();
cancel(false);
if (e instanceof PasswordException)
return getResources().getString(R.string.password_incorrect);
return e.getLocalizedMessage();
}
}
}
}
}

View File

@ -0,0 +1,131 @@
package i2p.bote.android.addressbook;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedSet;
import i2p.bote.android.R;
import i2p.bote.android.util.BoteHelper;
import i2p.bote.packet.dht.Contact;
public class ContactAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context mCtx;
private List<Contact> mContacts;
private AddressBookFragment.OnContactSelectedListener mListener;
public static class SimpleViewHolder extends RecyclerView.ViewHolder {
public SimpleViewHolder(View itemView) {
super(itemView);
}
}
public static class ContactViewHolder extends RecyclerView.ViewHolder {
public ImageView mPicture;
public TextView mName;
public ContactViewHolder(View itemView) {
super(itemView);
mPicture = (ImageView) itemView.findViewById(R.id.contact_picture);
mName = (TextView) itemView.findViewById(R.id.contact_name);
}
}
public ContactAdapter(Context context, AddressBookFragment.OnContactSelectedListener listener) {
mCtx = context;
mListener = listener;
setHasStableIds(true);
}
public void setContacts(SortedSet<Contact> contacts) {
if (contacts != null) {
mContacts = new ArrayList<Contact>();
mContacts.addAll(contacts);
} else
mContacts = null;
notifyDataSetChanged();
}
@Override
public int getItemViewType(int position) {
if (mContacts == null || mContacts.isEmpty())
return R.layout.listitem_empty;
return R.layout.listitem_contact;
}
// Create new views (invoked by the layout manager)
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(viewType, parent, false);
switch (viewType) {
case R.layout.listitem_contact:
return new ContactViewHolder(v);
default:
return new SimpleViewHolder(v);
}
}
// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (holder.getItemViewType()) {
case R.layout.listitem_empty:
((TextView) holder.itemView).setText(
mCtx.getResources().getString(R.string.address_book_empty));
break;
case R.layout.listitem_contact:
final ContactViewHolder cvh = (ContactViewHolder) holder;
Contact contact = mContacts.get(position);
String pic = contact.getPictureBase64();
if (pic != null && !pic.isEmpty())
cvh.mPicture.setImageBitmap(BoteHelper.decodePicture(pic));
else {
ViewGroup.LayoutParams lp = cvh.mPicture.getLayoutParams();
cvh.mPicture.setImageBitmap(BoteHelper.getIdenticonForAddress(contact.getBase64Dest(), lp.width, lp.height));
}
cvh.mName.setText(contact.getName());
cvh.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mListener.onContactSelected(mContacts.get(cvh.getAdapterPosition()));
}
});
break;
default:
break;
}
}
// Return the size of the dataset (invoked by the layout manager)
@Override
public int getItemCount() {
if (mContacts == null || mContacts.isEmpty())
return 1;
return mContacts.size();
}
public long getItemId(int position) {
if (mContacts == null || mContacts.isEmpty())
return 0;
Contact contact = mContacts.get(position);
return contact.getDestination().getHash().hashCode();
}
}

View File

@ -0,0 +1,93 @@
package i2p.bote.android.addressbook;
import android.content.Intent;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.v7.widget.Toolbar;
import java.util.Arrays;
import i2p.bote.android.BoteActivityBase;
import i2p.bote.android.Constants;
import i2p.bote.android.R;
public class EditContactActivity extends BoteActivityBase {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_toolbar);
// Set the action bar
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
setSupportActionBar(toolbar);
// Enable ActionBar app icon to behave as action to go back
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
if (savedInstanceState == null) {
EditContactFragment f;
Bundle args = getIntent().getExtras();
if (args != null) {
String destination = args.getString(EditContactFragment.NEW_DESTINATION);
if (destination != null) {
String name = args.getString(EditContactFragment.NEW_NAME);
f = EditContactFragment.newInstance(name, destination);
} else {
destination = args.getString(EditContactFragment.CONTACT_DESTINATION);
f = EditContactFragment.newInstance(destination);
}
if (destination != null)
getSupportActionBar().setDisplayShowTitleEnabled(false);
} else
f = EditContactFragment.newInstance(null);
getSupportFragmentManager().beginTransaction()
.add(R.id.container, f).commit();
}
}
@Override
public void onResume() {
super.onResume();
// Check to see that the Activity started due to an Android Beam
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction()) ||
NfcAdapter.ACTION_TAG_DISCOVERED.equals(getIntent().getAction())) {
processIntent(getIntent());
}
}
@Override
public void onNewIntent(Intent intent) {
// onResume gets called after this to handle the intent
setIntent(intent);
}
/**
* Parses the NDEF Message from the intent
*/
private void processIntent(Intent intent) {
Parcelable[] rawMsgs = intent.getParcelableArrayExtra(
NfcAdapter.EXTRA_NDEF_MESSAGES);
if (rawMsgs == null || rawMsgs.length < 1)
return; // TODO notify user?
NdefMessage msg = (NdefMessage) rawMsgs[0];
NdefRecord[] records = msg.getRecords();
if (records.length != 2 ||
records[0].getTnf() != NdefRecord.TNF_EXTERNAL_TYPE ||
!Arrays.equals(records[0].getType(), Constants.NDEF_LEGACY_TYPE_CONTACT.getBytes()) ||
records[1].getTnf() != NdefRecord.TNF_EXTERNAL_TYPE ||
!Arrays.equals(records[1].getType(), Constants.NDEF_LEGACY_TYPE_CONTACT_DESTINATION.getBytes()))
return; // TODO notify user?
String name = new String(records[0].getPayload());
String destination = new String(records[1].getPayload());
EditContactFragment f = EditContactFragment.newInstance(
name, destination);
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, f).commit();
}
}

View File

@ -0,0 +1,237 @@
package i2p.bote.android.addressbook;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import com.mikepenz.google_material_typeface_library.GoogleMaterial;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.GeneralSecurityException;
import i2p.bote.I2PBote;
import i2p.bote.android.R;
import i2p.bote.android.util.BoteHelper;
import i2p.bote.android.util.EditPictureFragment;
import i2p.bote.fileencryption.PasswordException;
import i2p.bote.packet.dht.Contact;
public class EditContactFragment extends EditPictureFragment {
public static final String CONTACT_DESTINATION = "contact_destination";
public static final String NEW_NAME = "new_name";
public static final String NEW_DESTINATION = "new_destination";
static final int REQUEST_DESTINATION_FILE = 3;
EditText mNameField;
EditText mDestinationField;
EditText mTextField;
TextView mError;
private String mDestination;
public static EditContactFragment newInstance(String destination) {
EditContactFragment f = new EditContactFragment();
Bundle args = new Bundle();
args.putString(CONTACT_DESTINATION, destination);
f.setArguments(args);
return f;
}
public static EditContactFragment newInstance(String name, String destination) {
EditContactFragment f = new EditContactFragment();
Bundle args = new Bundle();
args.putString(NEW_NAME, name);
args.putString(NEW_DESTINATION, destination);
f.setArguments(args);
return f;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
mDestination = getArguments().getString(CONTACT_DESTINATION);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_edit_contact, container, false);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mNameField = (EditText) view.findViewById(R.id.contact_name);
mDestinationField = (EditText) view.findViewById(R.id.destination);
mTextField = (EditText) view.findViewById(R.id.text);
mError = (TextView) view.findViewById(R.id.error);
Button b = (Button) view.findViewById(R.id.import_destination_from_file);
if (mDestination != null) {
mDestinationField.setVisibility(View.GONE);
b.setVisibility(View.GONE);
} else
b.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.setType("text/plain");
i.addCategory(Intent.CATEGORY_OPENABLE);
try {
startActivityForResult(
Intent.createChooser(i,
getResources().getString(R.string.select_email_destination_file)),
REQUEST_DESTINATION_FILE);
} catch (android.content.ActivityNotFoundException ex) {
Toast.makeText(getActivity(), R.string.please_install_a_file_manager,
Toast.LENGTH_SHORT).show();
}
}
});
if (I2PBote.getInstance().isPasswordRequired()) {
// Request a password from the user.
BoteHelper.requestPassword(getActivity(), new BoteHelper.RequestPasswordListener() {
@Override
public void onPasswordVerified() {
initializeContact();
}
@Override
public void onPasswordCanceled() {
getActivity().setResult(Activity.RESULT_CANCELED);
getActivity().finish();
}
});
} else {
// Password is cached, or not set.
initializeContact();
}
}
private void initializeContact() {
String newDest = getArguments().getString(NEW_DESTINATION);
if (mDestination != null) {
try {
Contact contact = BoteHelper.getContact(mDestination);
String pic = contact.getPictureBase64();
if (pic != null && !pic.isEmpty()) {
setPictureB64(pic);
}
mNameField.setText(contact.getName());
mTextField.setText(contact.getText());
} catch (PasswordException e) {
// TODO Handle
e.printStackTrace();
}
} else if (newDest != null) {
mNameField.setText(getArguments().getString(NEW_NAME));
mDestinationField.setText(newDest);
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.edit_contact, menu);
menu.findItem(R.id.action_save_contact).setIcon(BoteHelper.getMenuIcon(getActivity(), GoogleMaterial.Icon.gmd_save));
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_save_contact:
String picture = getPictureB64();
String name = mNameField.getText().toString();
String destination = mDestination == null ?
mDestinationField.getText().toString() : mDestination;
String text = mTextField.getText().toString();
// Check fields
if (destination.isEmpty()) {
mDestinationField.setError(getActivity().getString(R.string.this_field_is_required));
mDestinationField.requestFocus();
return true;
} else {
mDestinationField.setError(null);
}
mError.setText("");
try {
String err = BoteHelper.saveContact(destination, name, picture, text);
if (err == null) {
if (mDestination == null) // Only set if adding new contact
getActivity().setResult(Activity.RESULT_OK);
getActivity().finish();
} else {
if (err.startsWith("No Email Destination found in string:") ||
err.startsWith("Not a valid Email Destination:")) {
mDestinationField.setError(getActivity().getString(R.string.not_a_valid_bote_address));
mDestinationField.requestFocus();
} else {
mError.setText(err);
}
}
} catch (PasswordException e) {
// TODO Auto-generated catch block
e.printStackTrace();
mError.setText(e.getLocalizedMessage());
} catch (GeneralSecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
mError.setText(e.getLocalizedMessage());
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_DESTINATION_FILE) {
if (resultCode == Activity.RESULT_OK) {
Uri result = data.getData();
BufferedReader br;
try {
ParcelFileDescriptor pfd = getActivity().getContentResolver().openFileDescriptor(result, "r");
br = new BufferedReader(
new InputStreamReader(
new FileInputStream(pfd.getFileDescriptor()))
);
try {
mDestinationField.setText(br.readLine());
} catch (IOException ioe) {
Toast.makeText(getActivity(), R.string.failed_to_read_email_destination_file,
Toast.LENGTH_SHORT).show();
}
} catch (FileNotFoundException fnfe) {
Toast.makeText(getActivity(), R.string.could_not_find_email_destination_file,
Toast.LENGTH_SHORT).show();
}
}
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
}

View File

@ -0,0 +1,78 @@
package i2p.bote.android.addressbook;
import android.annotation.SuppressLint;
import android.nfc.NdefMessage;
import android.nfc.NfcAdapter;
import android.nfc.NfcEvent;
import android.os.Build;
import android.os.Bundle;
import i2p.bote.android.BoteActivityBase;
public class ViewContactActivity extends BoteActivityBase {
NfcAdapter mNfcAdapter;
@SuppressLint("NewApi")
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
String destination = null;
Bundle args = getIntent().getExtras();
if (args != null)
destination = args.getString(ViewContactFragment.ADDRESS);
if (destination == null) {
setResult(RESULT_CANCELED);
finish();
return;
}
ViewContactFragment f = ViewContactFragment.newInstance(destination);
getSupportFragmentManager().beginTransaction()
.add(android.R.id.content, f).commit();
}
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (mNfcAdapter != null &&
Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
mNfcAdapter.setNdefPushMessageCallback(new NfcAdapter.CreateNdefMessageCallback() {
@Override
public NdefMessage createNdefMessage(NfcEvent nfcEvent) {
return getNdefMessage();
}
}, this);
}
}
@SuppressWarnings("deprecation")
@SuppressLint("NewApi")
@Override
public void onResume() {
super.onResume();
if (mNfcAdapter != null &&
Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
mNfcAdapter.enableForegroundNdefPush(this, getNdefMessage());
}
}
private NdefMessage getNdefMessage() {
ViewContactFragment f = (ViewContactFragment) getSupportFragmentManager()
.findFragmentById(android.R.id.content);
return f.createNdefMessage();
}
@SuppressWarnings("deprecation")
@SuppressLint("NewApi")
@Override
public void onPause() {
super.onPause();
if (mNfcAdapter != null &&
Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
mNfcAdapter.disableForegroundNdefPush(this);
}
}
}

View File

@ -0,0 +1,101 @@
package i2p.bote.android.addressbook;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import java.security.GeneralSecurityException;
import i2p.bote.android.R;
import i2p.bote.android.util.BoteHelper;
import i2p.bote.android.util.ViewAddressFragment;
import i2p.bote.fileencryption.PasswordException;
import i2p.bote.packet.dht.Contact;
public class ViewContactFragment extends ViewAddressFragment {
private Contact mContact;
public static ViewContactFragment newInstance(String destination) {
ViewContactFragment f = new ViewContactFragment();
Bundle args = new Bundle();
args.putString(ADDRESS, destination);
f.setArguments(args);
return f;
}
@Override
protected void loadAddress() {
try {
mContact = BoteHelper.getContact(mAddress);
if (mContact == null) {
// No contact found, finish
getActivity().setResult(Activity.RESULT_CANCELED);
getActivity().finish();
}
} catch (PasswordException e) {
// TODO Handle
e.printStackTrace();
}
}
@Override
protected String getPublicName() {
return mContact.getName();
}
@Override
protected int getDeleteAddressMessage() {
return R.string.delete_contact;
}
@Override
public void onResume() {
super.onResume();
Bitmap picture = BoteHelper.decodePicture(mContact.getPictureBase64());
if (picture != null)
mPicture.setImageBitmap(picture);
else {
ViewGroup.LayoutParams lp = mPicture.getLayoutParams();
mPicture.setImageBitmap(BoteHelper.getIdenticonForAddress(mAddress, lp.width, lp.height));
}
mPublicName.setText(mContact.getName());
if (mContact.getText().isEmpty())
mDescription.setVisibility(View.GONE);
else {
mDescription.setText(mContact.getText());
mDescription.setVisibility(View.VISIBLE);
}
mCryptoImplName.setText(mContact.getDestination().getCryptoImpl().getName());
}
@Override
protected void onEditAddress() {
Intent ei = new Intent(getActivity(), EditContactActivity.class);
ei.putExtra(EditContactFragment.CONTACT_DESTINATION, mAddress);
startActivity(ei);
}
@Override
public void onDeleteAddress() {
try {
String err = BoteHelper.deleteContact(mAddress);
if (err == null) {
getActivity().setResult(Activity.RESULT_OK);
getActivity().finish();
} else
Toast.makeText(getActivity(), err, Toast.LENGTH_SHORT).show();
} catch (PasswordException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (GeneralSecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,128 @@
package i2p.bote.android.config;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.preference.CheckBoxPreference;
import android.support.v7.preference.EditTextPreference;
import android.support.v7.preference.ListPreference;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceCategory;
import android.support.v7.preference.PreferenceFragmentCompat;
import i2p.bote.android.R;
import i2p.bote.android.config.util.SummaryEditTextPreference;
public class AdvancedPreferenceFragment extends PreferenceFragmentCompat {
@Override
public void onCreatePreferences(Bundle paramBundle, String s) {
addPreferencesFromResource(R.xml.settings_advanced);
setupAdvancedSettings();
}
@Override
public void onResume() {
super.onResume();
//noinspection ConstantConditions
((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_label_advanced);
}
private void setupAdvancedSettings() {
final Context ctx = getPreferenceManager().getContext();
final PreferenceCategory i2pCat = (PreferenceCategory) findPreference("i2pCategory");
CheckBoxPreference routerAuto = (CheckBoxPreference) findPreference("i2pbote.router.auto");
if (!routerAuto.isChecked()) {
setupI2PCategory(ctx, i2pCat);
}
routerAuto.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
final Boolean checked = (Boolean) newValue;
if (!checked) {
setupI2PCategory(ctx, i2pCat);
} else {
Preference p1 = i2pCat.findPreference("i2pbote.router.use");
Preference p2 = i2pCat.findPreference("i2pbote.i2cp.tcp.host");
Preference p3 = i2pCat.findPreference("i2pbote.i2cp.tcp.port");
if (p1 != null)
i2pCat.removePreference(p1);
if (p2 != null)
i2pCat.removePreference(p2);
if (p3 != null)
i2pCat.removePreference(p3);
}
return true;
}
});
}
private static void setupI2PCategory(Context context, PreferenceCategory i2pCat) {
final ListPreference routerChoice = createRouterChoice(context);
final EditTextPreference hostField = createHostField(context);
final EditTextPreference portField = createPortField(context);
i2pCat.addPreference(routerChoice);
i2pCat.addPreference(hostField);
i2pCat.addPreference(portField);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB)
routerChoice.setSummary(routerChoice.getEntry());
if ("remote".equals(routerChoice.getValue())) {
hostField.setEnabled(true);
portField.setEnabled(true);
}
routerChoice.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
final String val = newValue.toString();
int index = routerChoice.findIndexOfValue(val);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB)
routerChoice.setSummary(routerChoice.getEntries()[index]);
if (index == 2) {
hostField.setEnabled(true);
hostField.setText("127.0.0.1");
portField.setEnabled(true);
portField.setText("7654");
} else {
hostField.setEnabled(false);
hostField.setText("internal");
portField.setEnabled(false);
portField.setText("internal");
}
return true;
}
});
}
private static ListPreference createRouterChoice(Context context) {
ListPreference routerChoice = new ListPreference(context);
routerChoice.setKey("i2pbote.router.use");
routerChoice.setEntries(R.array.routerOptionNames);
routerChoice.setEntryValues(R.array.routerOptions);
routerChoice.setTitle(R.string.pref_title_router);
routerChoice.setSummary("%s");
routerChoice.setDialogTitle(R.string.pref_dialog_title_router);
routerChoice.setDefaultValue("internal");
return routerChoice;
}
private static EditTextPreference createHostField(Context context) {
EditTextPreference p = new SummaryEditTextPreference(context);
p.setKey("i2pbote.i2cp.tcp.host");
p.setTitle(R.string.pref_title_i2cp_host);
p.setSummary("%s");
p.setDefaultValue("internal");
p.setEnabled(false);
return p;
}
private static EditTextPreference createPortField(Context context) {
EditTextPreference p = new SummaryEditTextPreference(context);
p.setKey("i2pbote.i2cp.tcp.port");
p.setTitle(R.string.pref_title_i2cp_port);
p.setSummary("%s");
p.setDefaultValue("internal");
p.setEnabled(false);
return p;
}
}

View File

@ -0,0 +1,40 @@
package i2p.bote.android.config;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceFragmentCompat;
import i2p.bote.android.R;
public class AppProtectionPreferenceFragment extends PreferenceFragmentCompat {
@Override
public void onCreatePreferences(Bundle paramBundle, String s) {
addPreferencesFromResource(R.xml.settings_app_protection);
setupAppProtectionSettings();
}
@Override
public void onResume() {
super.onResume();
//noinspection ConstantConditions
((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_label_app_protection);
// Screen security only works from API 14
Preference screenSecurityPreference = findPreference("pref_screen_security");
if (screenSecurityPreference != null &&
Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH)
getPreferenceScreen().removePreference(screenSecurityPreference);
}
private void setupAppProtectionSettings() {
findPreference("pref_change_password").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
startActivity(new Intent(getActivity(), SetPasswordActivity.class));
return true;
}
});
}
}

View File

@ -0,0 +1,35 @@
package i2p.bote.android.config;
import android.os.Bundle;
import android.support.v7.preference.PreferenceFragmentCompat;
import i2p.bote.android.R;
public class AppearancePreferenceFragment extends PreferenceFragmentCompat {
@Override
public void onCreatePreferences(Bundle paramBundle, String s) {
addPreferencesFromResource(R.xml.settings_appearance);
}
@Override
public void onStart() {
super.onStart();
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(
(SettingsActivity) getActivity()
);
}
@Override
public void onResume() {
super.onResume();
((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_label_appearance);
}
@Override
public void onStop() {
super.onStop();
getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(
(SettingsActivity) getActivity()
);
}
}

View File

@ -0,0 +1,47 @@
package i2p.bote.android.config;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import java.util.Map;
import i2p.bote.Configuration;
import i2p.bote.I2PBote;
import i2p.bote.android.R;
import i2p.bote.android.config.util.CustomPreferenceFragment;
public class NetworkPreferenceFragment extends CustomPreferenceFragment {
@Override
public void onCreatePreferences(Bundle paramBundle, String s) {
addPreferencesFromResource(R.xml.settings_network);
}
@Override
public void onResume() {
super.onResume();
//noinspection ConstantConditions
((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_label_network);
}
@Override
public void onPause() {
Configuration config = I2PBote.getInstance().getConfiguration();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
Map<String, ?> all = prefs.getAll();
for (String x : all.keySet()) {
if ("autoMailCheckEnabled".equals(x))
config.setAutoMailCheckEnabled(prefs.getBoolean(x, true));
else if ("mailCheckInterval".equals(x))
config.setMailCheckInterval(prefs.getInt(x, 30));
else if ("deliveryCheckEnabled".equals(x))
config.setDeliveryCheckEnabled(prefs.getBoolean(x, true));
}
config.save();
// Store the settings in Android
super.onPause();
}
}

View File

@ -0,0 +1,70 @@
package i2p.bote.android.config;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.preference.ListPreference;
import android.support.v7.preference.Preference;
import java.util.Map;
import i2p.bote.Configuration;
import i2p.bote.I2PBote;
import i2p.bote.android.R;
import i2p.bote.android.config.util.CustomPreferenceFragment;
public class PrivacyPreferenceFragment extends CustomPreferenceFragment {
@Override
public void onCreatePreferences(Bundle paramBundle, String s) {
addPreferencesFromResource(R.xml.settings_privacy);
setupPrivacySettings();
}
@Override
public void onResume() {
super.onResume();
//noinspection ConstantConditions
((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.pref_title_privacy);
}
@Override
public void onPause() {
Configuration config = I2PBote.getInstance().getConfiguration();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
Map<String, ?> all = prefs.getAll();
for (String x : all.keySet()) {
if ("hideLocale".equals(x))
config.setHideLocale(prefs.getBoolean(x, true));
else if ("includeSentTime".equals(x))
config.setIncludeSentTime(prefs.getBoolean(x, true));
else if ("numSendHops".equals(x))
config.setNumStoreHops(Integer.parseInt(prefs.getString(x, "0")));
else if ("relayMinDelay".equals(x))
config.setRelayMinDelay(prefs.getInt(x, 5));
else if ("relayMaxDelay".equals(x))
config.setRelayMaxDelay(prefs.getInt(x, 40));
}
config.save();
// Store the settings in Android
super.onPause();
}
private void setupPrivacySettings() {
ListPreference numSendHops = (ListPreference) findPreference("numSendHops");
int value = Integer.valueOf(numSendHops.getValue());
numSendHops.setSummary(getResources().getQuantityString(R.plurals.pref_summ_numHops,
value, value));
numSendHops.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
int value = Integer.valueOf((String) newValue);
preference.setSummary(getResources().getQuantityString(R.plurals.pref_summ_numHops,
value, value));
return true;
}
});
}
}

View File

@ -0,0 +1,34 @@
package i2p.bote.android.config;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.widget.Toast;
import i2p.bote.android.BoteActivityBase;
import i2p.bote.android.R;
public class SetPasswordActivity extends BoteActivityBase implements
SetPasswordFragment.Callbacks {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_set_password);
// Set the action bar
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
setSupportActionBar(toolbar);
// Enable ActionBar app icon to behave as action to go back
//noinspection ConstantConditions
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
// SetPasswordFragment.Callbacks
public void onTaskFinished() {
Toast.makeText(this, R.string.password_changed,
Toast.LENGTH_SHORT).show();
setResult(RESULT_OK);
finish();
}
}

View File

@ -0,0 +1,266 @@
package i2p.bote.android.config;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.TextView;
import com.mikepenz.google_material_typeface_library.GoogleMaterial;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import i2p.bote.I2PBote;
import i2p.bote.fileencryption.PasswordIncorrectException;
import i2p.bote.fileencryption.PasswordMismatchException;
import i2p.bote.status.StatusListener;
import i2p.bote.android.R;
import i2p.bote.android.util.BoteHelper;
import i2p.bote.android.util.RobustAsyncTask;
import i2p.bote.android.util.TaskFragment;
import i2p.bote.status.ChangePasswordStatus;
public class SetPasswordFragment extends Fragment {
private Callbacks mCallbacks = sDummyCallbacks;
public interface Callbacks {
public void onTaskFinished();
}
private static Callbacks sDummyCallbacks = new Callbacks() {
public void onTaskFinished() {}
};
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (!(activity instanceof Callbacks))
throw new IllegalStateException("Activity must implement fragment's callbacks.");
mCallbacks = (Callbacks) activity;
}
@Override
public void onDetach() {
super.onDetach();
mCallbacks = sDummyCallbacks;
}
// Code to identify the fragment that is calling onActivityResult().
static final int PASSWORD_WAITER = 0;
// Tag so we can find the task fragment again, in another
// instance of this fragment after rotation.
static final String PASSWORD_WAITER_TAG = "passwordWaiterTask";
MenuItem mSave;
EditText mOldField;
EditText mNewField;
EditText mConfirmField;
TextView mError;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
PasswordWaiterFrag f = (PasswordWaiterFrag) getFragmentManager().findFragmentByTag(PASSWORD_WAITER_TAG);
if (f != null)
f.setTargetFragment(this, PASSWORD_WAITER);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_set_password, container);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mOldField = (EditText) view.findViewById(R.id.password_old);
mNewField = (EditText) view.findViewById(R.id.password_new);
mConfirmField = (EditText) view.findViewById(R.id.password_confirm);
mError = (TextView) view.findViewById(R.id.error);
if (!I2PBote.getInstance().getConfiguration().getPasswordFile().exists()) {
mOldField.setVisibility(View.GONE);
view.findViewById(R.id.msg_remove_password).setVisibility(View.GONE);
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.set_password, menu);
mSave = menu.findItem(R.id.action_set_password);
mSave.setIcon(BoteHelper.getMenuIcon(getActivity(), GoogleMaterial.Icon.gmd_save));
// If task is running, disable the save button.
PasswordWaiterFrag f = (PasswordWaiterFrag) getFragmentManager().findFragmentByTag(PASSWORD_WAITER_TAG);
if (f != null)
setInterfaceEnabled(false);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_set_password:
String oldPassword = mOldField.getText().toString();
String newPassword = mNewField.getText().toString();
String confirmNewPassword = mConfirmField.getText().toString();
InputMethodManager imm = (InputMethodManager)getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mNewField.getWindowToken(), 0);
setInterfaceEnabled(false);
mError.setText("");
PasswordWaiterFrag f = PasswordWaiterFrag.newInstance(oldPassword, newPassword, confirmNewPassword);
f.setTask(new PasswordWaiter());
f.setTargetFragment(SetPasswordFragment.this, PASSWORD_WAITER);
getFragmentManager().beginTransaction()
.replace(R.id.password_waiter_frag, f, PASSWORD_WAITER_TAG)
.commit();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == PASSWORD_WAITER) {
if (resultCode == Activity.RESULT_OK) {
mCallbacks.onTaskFinished();
} else if (resultCode == Activity.RESULT_CANCELED) {
setInterfaceEnabled(true);
mError.setText(data.getStringExtra("error"));
}
}
}
private void setInterfaceEnabled(boolean enabled) {
mSave.setVisible(enabled);
mOldField.setEnabled(enabled);
mNewField.setEnabled(enabled);
mConfirmField.setEnabled(enabled);
}
public static class PasswordWaiterFrag extends TaskFragment<String, String, Throwable> {
String currentStatus;
TextView mStatus;
public static PasswordWaiterFrag newInstance(String... params) {
PasswordWaiterFrag f = new PasswordWaiterFrag();
Bundle args = new Bundle();
args.putStringArray("params", params);
f.setArguments(args);
return f;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.dialog_status, container, false);
mStatus = (TextView) v.findViewById(R.id.status);
if (currentStatus != null && !currentStatus.isEmpty())
mStatus.setText(currentStatus);
return v;
}
@Override
public String[] getParams() {
Bundle args = getArguments();
return args.getStringArray("params");
}
@Override
public void updateProgress(String... values) {
ChangePasswordStatus status = ChangePasswordStatus.valueOf(values[0]);
switch (status) {
case CHECKING_PASSWORD:
currentStatus = getString(R.string.checking_password);
break;
case RE_ENCRYPTING_IDENTITIES:
currentStatus = getString(R.string.re_encrypting_identities);
break;
case RE_ENCRYPTING_ADDRESS_BOOK:
currentStatus = getString(R.string.re_encrypting_address_book);
break;
case RE_ENCRYPTING_FOLDER:
currentStatus = getString(R.string.re_encrypting_folder, values[1]);
break;
case UPDATING_PASSWORD_FILE:
currentStatus = getString(R.string.updating_password_file);
break;
}
mStatus.setText(currentStatus);
}
@Override
public void taskFinished(Throwable ignored) {
super.taskFinished(ignored);
if (getTargetFragment() != null) {
Intent i = new Intent();
getTargetFragment().onActivityResult(
getTargetRequestCode(), Activity.RESULT_OK, i);
}
}
@Override
public void taskCancelled(Throwable error) {
super.taskCancelled(error);
if (getTargetFragment() != null) {
Intent i = new Intent();
if (error instanceof PasswordIncorrectException) {
i.putExtra("error", getString(R.string.old_password_incorrect));
} else if (error instanceof PasswordMismatchException) {
i.putExtra("error", getString(R.string.new_password_mismatch));
} else {
i.putExtra("error", error.getLocalizedMessage());
}
System.out.println("Password error: " + error.toString());
getTargetFragment().onActivityResult(
getTargetRequestCode(), Activity.RESULT_CANCELED, i);
}
}
}
private class PasswordWaiter extends RobustAsyncTask<String, String, Throwable> {
protected Throwable doInBackground(String... params) {
StatusListener<ChangePasswordStatus> lsnr = new StatusListener<ChangePasswordStatus>() {
public void updateStatus(ChangePasswordStatus status, String... args) {
ArrayList<String> tmp = new ArrayList<>(Arrays.asList(args));
tmp.add(0, status.name());
publishProgress(tmp.toArray(new String[tmp.size()]));
}
};
try {
I2PBote.getInstance().changePassword(
params[0].getBytes(),
params[1].getBytes(),
params[2].getBytes(),
lsnr);
return null;
} catch (Throwable e) {
cancel(false);
return e;
}
}
}
}

View File

@ -0,0 +1,178 @@
package i2p.bote.android.config;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceFragmentCompat;
import android.support.v7.widget.Toolbar;
import i2p.bote.I2PBote;
import i2p.bote.android.BoteActivityBase;
import i2p.bote.android.EmailListActivity;
import i2p.bote.android.R;
import i2p.bote.android.identities.IdentityListActivity;
import i2p.bote.android.service.BoteService;
import i2p.bote.android.util.BoteHelper;
public class SettingsActivity extends BoteActivityBase implements
SharedPreferences.OnSharedPreferenceChangeListener {
public static final String PREFERENCE_CATEGORY = "preference_category";
public static final String PREFERENCE_CATEGORY_NETWORK = "preference_category_network";
public static final String PREFERENCE_CATEGORY_IDENTITIES = "preference_category_identities";
public static final String PREFERENCE_CATEGORY_PRIVACY = "preference_category_privacy";
public static final String PREFERENCE_CATEGORY_APP_PROTECTION = "preference_category_app_protection";
public static final String PREFERENCE_CATEGORY_APPEARANCE = "preference_category_appearance";
public static final String PREFERENCE_CATEGORY_ADVANCED = "preference_category_advanced";
//
// Android lifecycle
//
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_toolbar);
// Set the action bar
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
setSupportActionBar(toolbar);
//noinspection ConstantConditions
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
Fragment fragment;
String category = getIntent().getStringExtra(PREFERENCE_CATEGORY);
if (category != null)
fragment = getFragmentForCategory(category);
else
fragment = new SettingsFragment();
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, fragment)
.commit();
}
@Override
public boolean onSupportNavigateUp() {
FragmentManager fragmentManager = getSupportFragmentManager();
if (fragmentManager.getBackStackEntryCount() > 0) {
fragmentManager.popBackStack();
} else {
Intent intent = new Intent(this, EmailListActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
finish();
}
return true;
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals("pref_language")) {
notifyLocaleChanged();
Intent intent = new Intent(BoteService.LOCAL_BROADCAST_LOCALE_CHANGED);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
}
//
// Settings pages
//
public static class SettingsFragment extends PreferenceFragmentCompat {
@Override
public void onCreatePreferences(Bundle paramBundle, String s) {
addPreferencesFromResource(R.xml.settings);
findPreference(PREFERENCE_CATEGORY_NETWORK)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_NETWORK));
findPreference(PREFERENCE_CATEGORY_IDENTITIES)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_IDENTITIES));
findPreference(PREFERENCE_CATEGORY_PRIVACY)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_PRIVACY));
findPreference(PREFERENCE_CATEGORY_APP_PROTECTION)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_APP_PROTECTION));
findPreference(PREFERENCE_CATEGORY_APPEARANCE)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_APPEARANCE));
findPreference(PREFERENCE_CATEGORY_ADVANCED)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_ADVANCED));
}
@Override
public void onResume() {
super.onResume();
//noinspection ConstantConditions
((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.action_settings);
}
private class CategoryClickListener implements Preference.OnPreferenceClickListener {
private String category;
public CategoryClickListener(String category) {
this.category = category;
}
@Override
public boolean onPreferenceClick(Preference preference) {
switch (category) {
case PREFERENCE_CATEGORY_IDENTITIES:
Intent ili = new Intent(getActivity(), IdentityListActivity.class);
startActivity(ili);
break;
case PREFERENCE_CATEGORY_PRIVACY:
case PREFERENCE_CATEGORY_APP_PROTECTION:
if (I2PBote.getInstance().isPasswordRequired()) {
BoteHelper.requestPassword(getActivity(), new BoteHelper.RequestPasswordListener() {
@Override
public void onPasswordVerified() {
loadCategory();
}
@Override
public void onPasswordCanceled() {
}
});
} else
loadCategory();
break;
default:
loadCategory();
}
return true;
}
private void loadCategory() {
Fragment fragment = getFragmentForCategory(category);
getActivity().getSupportFragmentManager().beginTransaction()
.replace(R.id.container, fragment)
.addToBackStack(null)
.commit();
}
}
}
private static Fragment getFragmentForCategory(String category) {
switch (category) {
case PREFERENCE_CATEGORY_NETWORK:
return new NetworkPreferenceFragment();
case PREFERENCE_CATEGORY_PRIVACY:
return new PrivacyPreferenceFragment();
case PREFERENCE_CATEGORY_APP_PROTECTION:
return new AppProtectionPreferenceFragment();
case PREFERENCE_CATEGORY_APPEARANCE:
return new AppearancePreferenceFragment();
case PREFERENCE_CATEGORY_ADVANCED:
return new AdvancedPreferenceFragment();
default:
throw new AssertionError();
}
}
}

View File

@ -0,0 +1,32 @@
package i2p.bote.android.config.util;
import android.support.v4.app.DialogFragment;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceFragmentCompat;
/**
* Handles custom Preferences.
*/
public abstract class CustomPreferenceFragment extends PreferenceFragmentCompat {
private static final String DIALOG_FRAGMENT_TAG =
"android.support.v7.preference.PreferenceFragment.DIALOG";
@Override
public void onDisplayPreferenceDialog(Preference preference) {
// check if dialog is already showing
if (getFragmentManager().findFragmentByTag(DIALOG_FRAGMENT_TAG) != null) {
return;
}
DialogFragment f = null;
if (preference instanceof IntEditTextPreference) {
f = IntEditTextPreferenceDialog.newInstance(preference.getKey());
} else {
super.onDisplayPreferenceDialog(preference);
}
if (f != null) {
f.setTargetFragment(this, 0);
f.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
}
}
}

View File

@ -0,0 +1,51 @@
package i2p.bote.android.config.util;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import android.support.v7.preference.Preference;
import android.util.AttributeSet;
import com.mikepenz.iconics.IconicsDrawable;
import i2p.bote.android.R;
public class IconicsPreference extends Preference {
public IconicsPreference(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public IconicsPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void init(Context context, AttributeSet attrs) {
// Icons only work on API 11+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB)
return;
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.IconicsPreference, 0, 0);
String iconName = a.getString(R.styleable.IconicsPreference_ip_icon);
if (iconName == null)
return;
IconicsDrawable icon = new IconicsDrawable(context, iconName);
int color = a.getColor(R.styleable.IconicsPreference_ip_color, 0);
if (color != 0)
icon.color(color);
int size = a.getDimensionPixelSize(R.styleable.IconicsPreference_ip_size, 0);
if (size != 0)
icon.sizePx(size);
int padding = a.getDimensionPixelSize(R.styleable.IconicsPreference_ip_padding, 0);
if (padding != 0)
icon.paddingPx(padding);
a.recycle();
setIcon(icon);
}
}

View File

@ -0,0 +1,35 @@
package i2p.bote.android.config.util;
import android.content.Context;
import android.support.v7.preference.EditTextPreference;
import android.util.AttributeSet;
public class IntEditTextPreference extends EditTextPreference {
public IntEditTextPreference(Context context) {
super(context);
}
public IntEditTextPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public IntEditTextPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public CharSequence getSummary() {
return String.format((String) super.getSummary(), getText());
}
@Override
protected String getPersistedString(String defaultReturnValue) {
return String.valueOf(getPersistedInt(-1));
}
@Override
protected boolean persistString(String value) {
return persistInt(Integer.valueOf(value));
}
}

View File

@ -0,0 +1,24 @@
package i2p.bote.android.config.util;
import android.os.Bundle;
import android.support.v7.preference.EditTextPreferenceDialogFragmentCompat;
import android.text.InputType;
import android.view.View;
import android.widget.EditText;
public class IntEditTextPreferenceDialog extends EditTextPreferenceDialogFragmentCompat {
public static IntEditTextPreferenceDialog newInstance(String key) {
final IntEditTextPreferenceDialog fragment = new IntEditTextPreferenceDialog();
final Bundle b = new Bundle(1);
b.putString(ARG_KEY, key);
fragment.setArguments(b);
return fragment;
}
@Override
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
((EditText)view.findViewById(android.R.id.edit)).setInputType(
InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED);
}
}

View File

@ -0,0 +1,28 @@
package i2p.bote.android.config.util;
import android.content.Context;
import android.support.v7.preference.EditTextPreference;
import android.util.AttributeSet;
public class SummaryEditTextPreference extends EditTextPreference {
public SummaryEditTextPreference(Context context) {
super(context);
}
public SummaryEditTextPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SummaryEditTextPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public CharSequence getSummary() {
String summary = (String) super.getSummary();
if (summary == null)
summary = "%s";
return String.format(summary, getText());
}
}

View File

@ -0,0 +1,45 @@
package i2p.bote.android.identities;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.widget.Toast;
import i2p.bote.android.BoteActivityBase;
import i2p.bote.android.R;
public class EditIdentityActivity extends BoteActivityBase implements
EditIdentityFragment.Callbacks {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_edit_identity);
// Set the action bar
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
setSupportActionBar(toolbar);
// Enable ActionBar app icon to behave as action to go back
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
if (savedInstanceState == null) {
String key = null;
Bundle args = getIntent().getExtras();
if (args != null)
key = args.getString(EditIdentityFragment.IDENTITY_KEY);
if (key != null)
getSupportActionBar().setDisplayShowTitleEnabled(false);
EditIdentityFragment f = EditIdentityFragment.newInstance(key);
getSupportFragmentManager().beginTransaction()
.add(R.id.edit_identity_frag, f).commit();
}
}
// EditIdentityFragment.Callbacks
public void onTaskFinished() {
Toast.makeText(this, R.string.identity_saved,
Toast.LENGTH_SHORT).show();
setResult(RESULT_OK);
finish();
}
}

View File

@ -0,0 +1,428 @@
package i2p.bote.android.identities;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import com.mikepenz.google_material_typeface_library.GoogleMaterial;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import i2p.bote.I2PBote;
import i2p.bote.email.IllegalDestinationParametersException;
import i2p.bote.status.StatusListener;
import i2p.bote.android.R;
import i2p.bote.android.util.BoteHelper;
import i2p.bote.android.util.EditPictureFragment;
import i2p.bote.android.util.RobustAsyncTask;
import i2p.bote.android.util.TaskFragment;
import i2p.bote.crypto.CryptoFactory;
import i2p.bote.crypto.CryptoImplementation;
import i2p.bote.email.EmailIdentity;
import i2p.bote.fileencryption.PasswordException;
import i2p.bote.status.ChangeIdentityStatus;
public class EditIdentityFragment extends EditPictureFragment {
private Callbacks mCallbacks = sDummyCallbacks;
public interface Callbacks {
public void onTaskFinished();
}
private static Callbacks sDummyCallbacks = new Callbacks() {
public void onTaskFinished() {}
};
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (!(activity instanceof Callbacks))
throw new IllegalStateException("Activity must implement fragment's callbacks.");
mCallbacks = (Callbacks) activity;
}
@Override
public void onDetach() {
super.onDetach();
mCallbacks = sDummyCallbacks;
}
public static final String IDENTITY_KEY = "identity_key";
// Code to identify the fragment that is calling onActivityResult().
static final int IDENTITY_WAITER = 3;
// Tag so we can find the task fragment again, in another
// instance of this fragment after rotation.
static final String IDENTITY_WAITER_TAG = "identityWaiterTask";
static final int DEFAULT_CRYPTO_IMPL = 2;
private String mKey;
MenuItem mSave;
EditText mNameField;
EditText mDescField;
Spinner mCryptoField;
int mDefaultPos;
CheckBox mDefaultField;
TextView mError;
public static EditIdentityFragment newInstance(String key) {
EditIdentityFragment f = new EditIdentityFragment();
Bundle args = new Bundle();
args.putString(IDENTITY_KEY, key);
f.setArguments(args);
return f;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
IdentityWaiterFrag f = (IdentityWaiterFrag) getFragmentManager().findFragmentByTag(IDENTITY_WAITER_TAG);
if (f != null)
f.setTargetFragment(this, IDENTITY_WAITER);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_edit_identity, container, false);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mKey = getArguments().getString(IDENTITY_KEY);
mNameField = (EditText) view.findViewById(R.id.public_name);
mDescField = (EditText) view.findViewById(R.id.description);
mDefaultField = (CheckBox) view.findViewById(R.id.default_identity);
mError = (TextView) view.findViewById(R.id.error);
mCryptoField = (Spinner) view.findViewById(R.id.crypto_impl);
if (I2PBote.getInstance().isPasswordRequired()) {
// Request a password from the user.
BoteHelper.requestPassword(getActivity(), new BoteHelper.RequestPasswordListener() {
@Override
public void onPasswordVerified() {
initializeIdentity();
}
@Override
public void onPasswordCanceled() {
getActivity().setResult(Activity.RESULT_CANCELED);
getActivity().finish();
}
});
} else {
// Password is cached, or not set.
initializeIdentity();
}
}
private void initializeIdentity() {
if (mKey == null) {
// Show the encryption choice field
CryptoAdapter adapter = new CryptoAdapter(getActivity());
mCryptoField.setAdapter(adapter);
mCryptoField.setSelection(mDefaultPos);
mCryptoField.setVisibility(View.VISIBLE);
// If no identities, set this as default by default
try {
mDefaultField.setChecked(I2PBote.getInstance().getIdentities().size() == 0);
} catch (PasswordException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (GeneralSecurityException e) {
e.printStackTrace();
}
} else {
// Load the identity to edit
try {
EmailIdentity identity = BoteHelper.getIdentity(mKey);
String pic = identity.getPictureBase64();
if (pic != null && !pic.isEmpty()) {
setPictureB64(pic);
}
mNameField.setText(identity.getPublicName());
mDescField.setText(identity.getDescription());
mDefaultField.setChecked(identity.isDefaultIdentity());
} catch (PasswordException e) {
// TODO Handle
e.printStackTrace();
} catch (IOException e) {
// TODO Handle
e.printStackTrace();
} catch (GeneralSecurityException e) {
// TODO Handle
e.printStackTrace();
}
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.edit_identity, menu);
mSave = menu.findItem(R.id.action_save_identity);
mSave.setIcon(BoteHelper.getMenuIcon(getActivity(), GoogleMaterial.Icon.gmd_save));
IdentityWaiterFrag f = (IdentityWaiterFrag) getFragmentManager().findFragmentByTag(IDENTITY_WAITER_TAG);
if (f != null)
setInterfaceEnabled(false);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_save_identity:
String picture = getPictureB64();
String publicName = mNameField.getText().toString();
String description = mDescField.getText().toString();
boolean setDefault = mDefaultField.isChecked();
int cryptoImplId = -1;
if (mKey == null)
cryptoImplId = ((CryptoImplementation) mCryptoField.getSelectedItem()).getId();
InputMethodManager imm = (InputMethodManager)getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mNameField.getWindowToken(), 0);
setInterfaceEnabled(false);
mError.setText("");
IdentityWaiterFrag f = IdentityWaiterFrag.newInstance(
(mKey == null),
cryptoImplId,
null,
mKey,
publicName,
description,
picture,
null,
setDefault);
f.setTask(new IdentityWaiter());
f.setTargetFragment(EditIdentityFragment.this, IDENTITY_WAITER);
getFragmentManager().beginTransaction()
.replace(R.id.identity_waiter_frag, f, IDENTITY_WAITER_TAG)
.commit();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == IDENTITY_WAITER) {
if (resultCode == Activity.RESULT_OK) {
mCallbacks.onTaskFinished();
} else if (resultCode == Activity.RESULT_CANCELED) {
setInterfaceEnabled(true);
mError.setText(data.getStringExtra("error"));
}
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
private void setInterfaceEnabled(boolean enabled) {
mSave.setVisible(enabled);
mNameField.setEnabled(enabled);
mDescField.setEnabled(enabled);
mDefaultField.setEnabled(enabled);
}
private class CryptoAdapter extends ArrayAdapter<CryptoImplementation> {
public CryptoAdapter(Context context) {
super(context, android.R.layout.simple_spinner_item);
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
List<CryptoImplementation> instances = CryptoFactory.getInstances();
mDefaultPos = 0;
for (CryptoImplementation instance : instances) {
add(instance);
if (instance.getId() == DEFAULT_CRYPTO_IMPL)
mDefaultPos = getPosition(instance);
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = super.getView(position, convertView, parent);
setViewText(v, position);
return v;
}
@Override
public View getDropDownView (int position, View convertView, ViewGroup parent) {
View v = super.getDropDownView(position, convertView, parent);
setViewText(v, position);
return v;
}
private void setViewText(View v, int position) {
TextView text = (TextView) v.findViewById(android.R.id.text1);
text.setText(getItem(position).getName());
}
}
public static class IdentityWaiterFrag extends TaskFragment<Object, String, Throwable> {
static final String CREATE_NEW = "create_new";
static final String CRYPTO_IMPL_ID = "crypto_impl_id";
static final String VANITY_PREFIX = "vanity_prefix";
static final String KEY = "key";
static final String PUBLIC_NAME = "public_name";
static final String DESCRIPTION = "description";
static final String PICTURE_BASE64 = "picture_base64";
static final String EMAIL_ADDRESS = "email_address";
static final String SET_DEFAULT = "set_default";
String currentStatus;
TextView mStatus;
public static IdentityWaiterFrag newInstance(
boolean createNew, int cryptoImplId, String vanity_prefix,
String key, String publicName, String description,
String pictureBase64, String emailAddress, boolean setDefault) {
IdentityWaiterFrag f = new IdentityWaiterFrag();
Bundle args = new Bundle();
args.putBoolean(CREATE_NEW, createNew);
args.putInt(CRYPTO_IMPL_ID, cryptoImplId);
args.putString(VANITY_PREFIX, vanity_prefix);
args.putString(KEY, key);
args.putString(PUBLIC_NAME, publicName);
args.putString(DESCRIPTION, description);
args.putString(PICTURE_BASE64, pictureBase64);
args.putString(EMAIL_ADDRESS, emailAddress);
args.putBoolean(SET_DEFAULT, setDefault);
f.setArguments(args);
return f;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.dialog_status, container, false);
mStatus = (TextView) v.findViewById(R.id.status);
if (currentStatus != null && !currentStatus.isEmpty())
mStatus.setText(currentStatus);
return v;
}
@Override
public Object[] getParams() {
Bundle args = getArguments();
return new Object[] {
args.getBoolean(CREATE_NEW),
args.getInt(CRYPTO_IMPL_ID),
args.getString(VANITY_PREFIX),
args.getString(KEY),
args.getString(PUBLIC_NAME),
args.getString(DESCRIPTION),
args.getString(PICTURE_BASE64),
args.getString(EMAIL_ADDRESS),
args.getBoolean(SET_DEFAULT),
};
}
@Override
public void updateProgress(String... values) {
ChangeIdentityStatus status = ChangeIdentityStatus.valueOf(values[0]);
switch (status) {
case GENERATING_KEYS:
currentStatus = getString(R.string.generating_keys);
break;
case SAVING_IDENTITY:
currentStatus = getString(R.string.saving_identity);
break;
}
mStatus.setText(currentStatus);
}
@Override
public void taskFinished(Throwable ignored) {
super.taskFinished(ignored);
if (getTargetFragment() != null) {
Intent i = new Intent();
getTargetFragment().onActivityResult(
getTargetRequestCode(), Activity.RESULT_OK, i);
}
}
@Override
public void taskCancelled(Throwable error) {
super.taskCancelled(error);
if (getTargetFragment() != null) {
Intent i = new Intent();
if (error instanceof IllegalDestinationParametersException) {
IllegalDestinationParametersException e = (IllegalDestinationParametersException) error;
i.putExtra("error", getString(R.string.invalid_vanity_chars, e.getBadChar(), e.getValidChars()));
} else {
i.putExtra("error", error.getLocalizedMessage());
}
getTargetFragment().onActivityResult(
getTargetRequestCode(), Activity.RESULT_CANCELED, i);
}
}
}
private class IdentityWaiter extends RobustAsyncTask<Object, String, Throwable> {
protected Throwable doInBackground(Object... params) {
StatusListener<ChangeIdentityStatus> lsnr = new StatusListener<ChangeIdentityStatus>() {
public void updateStatus(ChangeIdentityStatus status, String... args) {
ArrayList<String> tmp = new ArrayList<>(Arrays.asList(args));
tmp.add(0, status.name());
publishProgress(tmp.toArray(new String[tmp.size()]));
}
};
try {
BoteHelper.createOrModifyIdentity(
(Boolean) params[0],
(Integer) params[1],
(String) params[2],
(String) params[3],
(String) params[4],
(String) params[5],
(String) params[6],
(String) params[7],
new Properties(),
(Boolean) params[8],
lsnr);
lsnr.updateStatus(ChangeIdentityStatus.SAVING_IDENTITY);
I2PBote.getInstance().getIdentities().save();
return null;
} catch (Throwable e) {
cancel(false);
return e;
}
}
}
}

View File

@ -0,0 +1,126 @@
package i2p.bote.android.identities;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import i2p.bote.android.R;
import i2p.bote.android.util.BoteHelper;
import i2p.bote.email.EmailIdentity;
public class IdentityAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context mCtx;
private List<EmailIdentity> mIdentities;
private IdentityListFragment.OnIdentitySelectedListener mListener;
public static class SimpleViewHolder extends RecyclerView.ViewHolder {
public SimpleViewHolder(View itemView) {
super(itemView);
}
}
public static class IdentityViewHolder extends RecyclerView.ViewHolder {
public ImageView mPicture;
public TextView mName;
public IdentityViewHolder(View itemView) {
super(itemView);
mPicture = (ImageView) itemView.findViewById(R.id.identity_picture);
mName = (TextView) itemView.findViewById(R.id.identity_name);
}
}
public IdentityAdapter(Context context, IdentityListFragment.OnIdentitySelectedListener listener) {
mCtx = context;
mListener = listener;
setHasStableIds(true);
}
public void setIdentities(Collection<EmailIdentity> identities) {
if (identities != null) {
mIdentities = new ArrayList<>();
mIdentities.addAll(identities);
} else
mIdentities = null;
notifyDataSetChanged();
}
@Override
public int getItemViewType(int position) {
if (mIdentities == null || mIdentities.isEmpty())
return R.layout.listitem_empty;
return R.layout.listitem_identity;
}
// Create new views (invoked by the layout manager)
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(viewType, parent, false);
switch (viewType) {
case R.layout.listitem_identity:
return new IdentityViewHolder(v);
default:
return new SimpleViewHolder(v);
}
}
// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (holder.getItemViewType()) {
case R.layout.listitem_empty:
((TextView) holder.itemView).setText(
mCtx.getResources().getString(R.string.no_identities));
break;
case R.layout.listitem_identity:
final IdentityViewHolder cvh = (IdentityViewHolder) holder;
EmailIdentity identity = mIdentities.get(position);
ViewGroup.LayoutParams lp = cvh.mPicture.getLayoutParams();
cvh.mPicture.setImageBitmap(BoteHelper.getIdentityPicture(identity, lp.width, lp.height));
cvh.mName.setText(identity.getPublicName());
cvh.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mListener.onIdentitySelected(mIdentities.get(cvh.getAdapterPosition()));
}
});
break;
default:
break;
}
}
// Return the size of the dataset (invoked by the layout manager)
@Override
public int getItemCount() {
if (mIdentities == null || mIdentities.isEmpty())
return 1;
return mIdentities.size();
}
public long getItemId(int position) {
if (mIdentities == null || mIdentities.isEmpty())
return 0;
EmailIdentity identity = mIdentities.get(position);
return identity.getHash().hashCode();
}
}

View File

@ -0,0 +1,53 @@
package i2p.bote.android.identities;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import i2p.bote.android.BoteActivityBase;
import i2p.bote.android.R;
import i2p.bote.email.EmailIdentity;
public class IdentityListActivity extends BoteActivityBase implements
IdentityListFragment.OnIdentitySelectedListener {
static final int ALTER_IDENTITY_LIST = 1;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_toolbar);
// Set the action bar
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
setSupportActionBar(toolbar);
// Enable ActionBar app icon to behave as action to go back
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
if (savedInstanceState == null) {
IdentityListFragment f = new IdentityListFragment();
getSupportFragmentManager().beginTransaction()
.add(R.id.container, f).commit();
}
}
@Override
public void onIdentitySelected(EmailIdentity identity) {
Intent i = new Intent(this, ViewIdentityActivity.class);
i.putExtra(ViewIdentityFragment.ADDRESS, identity.getKey());
startActivityForResult(i, ALTER_IDENTITY_LIST);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == ALTER_IDENTITY_LIST) {
if (resultCode == Activity.RESULT_OK) {
IdentityListFragment f = (IdentityListFragment) getSupportFragmentManager().findFragmentById(R.id.container);
f.updateIdentityList();
}
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
}

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