Compare commits

...

1213 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
814 changed files with 90581 additions and 22598 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/

View File

@ -1,22 +1,42 @@
[I2P.plugin_i2pbote]
source_file = locale/messages_en.po
source_lang = en
trans.ar = locale/messages_ar.po
trans.cs = locale/messages_cs.po
trans.de = locale/messages_de.po
trans.es = locale/messages_es.po
trans.fr = locale/messages_fr.po
trans.hu = locale/messages_hu.po
trans.it = locale/messages_it.po
trans.nl = locale/messages_nl.po
trans.nb = locale/messages_nb.po
trans.pl = locale/messages_pl.po
trans.pt = locale/messages_pt.po
trans.ru = locale/messages_ru.po
trans.sv_SE = locale/messages_sv.po
trans.uk_UA = locale/messages_uk.po
trans.vi = locale/messages_vi.po
trans.zh_CN = locale/messages_zh.po
[main]
host = http://www.transifex.net
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,462 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Frequently Asked Questions</title>
<meta http-equiv="Content-Language" content="English" />
<meta name="Robots" content="index,follow" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" href="css/style.css" media="screen" />
</head>
<body>
<div id="wrap">
<div id="top">
<div class="rights"> </div>
<div id="search">
</div>
<div class="lefts">
<h1>I2P-Bote</h1>
<h2>Secure Distributed Email</h2>
</div>
</div>
<div id="topmenu">
<div class="rights"></div>
<div class="lefts"><ul>
<li><a href="index.html" title="Home">HOME</a></li>
<li><a href="download.html" title="Download I2P-Bote">DOWNLOAD</a></li>
<li><a href="screenshots.html" title="Screenshots">SCREENSHOTS</a></li>
<li><a href="userguide.html" title="User Guide">USER GUIDE</a></li>
<li><a href="faq.html" title="Frequently Asked Questions">FAQ</a></li>
<li><a href="http://forum.i2p/viewforum.php?f=35" title="Forum">FORUM</a></li>
<li><a href="contact.html" title="Contact">CONTACT</a></li>
</ul></div>
</div>
<div style="text-align: justify;" id="main">
<div id="leftside">
<p>
<h2>Frequently Asked Questions</h2>
</p>
<p>
<div class="date"></div>
</p><p>
<strong>What is I2P-Bote?</strong><br/>
A peer-to-peer email program designed to protect your privacy.<br/>
I2P-Bote is an end-to-end encrypted, network-internal, fully decentralized
(serverless) e-mail system. It supports different identities and does
not expose e-mail headers. Currently, it is still alpha software and
can only by accessed via web console. It soon will have POP3 support,
and it is planned to guarantee additional anonymity by providing a
high-latency transport option. All bote-mails are automatically
end-to-end encrypted, so that there's no need to set up e-mail
encryption (though the option does exist), and bote-mails will be
authenticated automatically. As
it is decentralized, there is no e-mail server that could link
different e-mail identities as communicating with each other
(profiling): Even the nodes relaying the mails will not know the
sender and apart from sender and receiver, only the end of the
high-latency mail tunnel and the storing nodes will know to whom
(anonymous identity) the mail is destined. The original sender can
have gone offline, long before the mail becomes available on the
other side. This adds on the degree of anonymity that can be reached
with I2P-Bote. For those who do not want high delays: All these
settings are be user-adjustable, so each user decides on how much
anonymity he wants.
</p>
<div class="date"></div>
<p>
<strong>Why is it named I2P-Bote?</strong><br/>
Bote is the German word for messenger.
</p>
<div class="date"></div>
<p>
<strong>Why I2P-Bote?</strong><br/>
Because it's cool.
And because I2P was lacking a decentralized e-mail service, and seeing
the creation of such as an opportunity to improve on neglected
anonymity aspects, it was decided to add an optional high-latency
transport. You can use a normal e-mail account and end-to-end encrypt your mails, but
they are still not anonymous. You can use anonymous server-bound e-mails, yet they are not automatically
end-to-end encrypted.<br>
Or you can use I2P-Bote in which your mails
are anonymous and <i>automatically</i> end-to-end-encrypted.<br>In contrast
to standard e-mail systems there is no need to setup an additional
key management application. Everything you need is already there. <br/>But
despite it being simple and easy to use, it still offers military
grade encryption and options for extremely strong anonymity.
</p>
<div class="date"></div>
<p>
<strong>What happens with an email after I click "Send"?</strong><br/>
It is encrypted and stored on other I2P-Bote participants' computers. From there, it is delivered to the recipient when they check their email.
</p>
<div class="date"></div>
<p>
<strong>Wait a minute, all email I send is saved on some random person's hard drive? That sounds like a really dumb idea!</strong><br/>
All they see is garbage data because it is encrypted with "military-grade" encryption. Only you and the recipient know what is in the email. Additionally, if you send the email with relays enabled, it is not even possible to tell who sent it.<br/>
Between this and using an email account with a
<a href="http://www.theregister.co.uk/2009/12/07/schmidt_on_privacy/">company that doesn't respect your privacy</a>, over an internet line that <a href="http://www.eff.org/issues/nsa-spying">is being spied on by shady agencies</a>, which would you say is more trustworthy?
</p>
<div class="date"></div>
<p>
<strong>How does it work exactly?</strong><br/>
&#8211; see the section &#8220;Concept&#8221; of the Manual &#8211;<br/>In short:
I2P-Bote nodes form a p2p-network, relaying mail packets for one another and storing them into a DHT.
</p>
<div class="date"></div>
<p>
<strong>Why would I use it? I have nothing to hide &hellip;</strong><br/>
Because you wouldn't go out to the street naked either, would you?<br/>Well, maybe you would. But the point is, sometimes you want private e-mail communication to be secret and untraceable and not let the whole world know when you say what to whom.<br/>
And sometimes you simply want to communicate fully anonymously with others.<br/>Therefor I2P-Bote is the ideal tool, giving you a hell lot of protection while preserving a great deal of flexibility.<br/>
It aims at providing professional military grade security and n00b-proof usability:<br/>
You can have really paranoid settings, where it takes a mail an eternity to arrive; or have faster communication and still enjoy very high anonymity.<br/>
You decide &#8211; easily with a click of your mouse.</p>
<div class="date"></div>
<p>
<strong>What about <a href="http://www.pgp.com/">PGP</a> and <a href="http://gnupg.org/">GPG</a>?</strong><br/>
PGP and GPG let you encrypt email and send it through your existing email account. They offer strong encryption, but they only encrypt the email text, not the headers, which means the subject line, your computer name, and other information is not secure.<br/>
Another privacy issue is that PGP/GPG cannot prevent anybody from finding out who is talking to whom.<br/>
I2P-Bote, in contrast, encrypts everything but the recipient's Email Destination. (In fact even the recipient's destination is only visible to nodes who do not know who the sender of the mail was.) It also has the ability to send an email through several relays (similar to <a href="http://mixmaster.sourceforge.net/">Mixmaster</a>), so nobody can find out who is sending email to whom.
<br/>
</p>
<div class="date"></div>
<p>
<strong>Can I still use GPG/PGP with I2P-Bote?</strong><br/>
Of course. Either have GPG encrypt your e-mail's text before pasting it into the I2P-Bote mail composition field, or use a mail app with GPG support. [<font color="#ff0000">POP3 support not implemented</font>]
</p>
<div class="date"></div>
<p>
<strong>How does it compare to Susimail?</strong><br/>
I2P-Bote is better because it has a higher version number. Just kidding.<br/>
I2P-Bote offers more privacy, but Susimail has some features I2P-Bote doesn't have yet (see below), and Susimail is more bandwidth-efficient because it doesn't store emails redundantly.
</p>
<div class="date"></div>
<p>
<strong>Why is I2P-Bote better?</strong><br/>
<i>We</i> think it's better for <i>us</i> (and maybe for you, too; decide yourself!), than &#8230; <ul><li><p style="line-height: 150%;"><font face="Arial, sans-serif">mixminion as it is easy to use and as n00b-proof as we could get it.</font></p>
</li><li><p style="line-height: 150%;"><font face="Arial, sans-serif">anonymous e-mail services not based on destination key routing: as those do not deliver built-in end-to-end encryption.</font></p>
</li><li><p style="line-height: 150%;"><font face="Arial, sans-serif">centralized services, as the server could go down (due to attacks, legal problems, lack of funding or interest, &#8230;) and the server admin has too many means to do profiling.</font></p>
</li></ul></p>
<div class="date"></div>
<p>
<strong>How is my identity kept safe when I exchange mail with someone?</strong><br/>
Never is your ip number or even your I2P-destination included in any e-mail you send.<br/>
The high-latency transport counters timing attacks.<br/>
End-to-end encryption, per-hop encryption, relaying packets for other nodes, one single packet size* (padding), a constant rate of sending (test and dummy messages)*, and a rather balanced incoming/outgoing ratio* counter traffic analysis attacks, and in combination with per-hop delays, I2P-Bote offers good means against intersection attacks.<br/>
The open source nature of I2P-Bote guarantees that you yourself can see the implementation and check it for bugs.
<br/>*[<font color="#ff0000">not yet implemented</font>]</p>
<div class="date"></div>
<p>
<strong>How do I use it?</strong><br/>
Read the manual or see the other questions and answers here!<br>
If you still have unanswered questions, ask on the forum:
<a href="http://forum.i2p/viewforum.php?f=35">http://forum.i2p/viewforum.php?f=35</a>
</p>
<div class="date"></div>
<p>
<strong>Can I use an email program like Thunderbird?</strong><br/>
No, but it is on the roadmap.
<!--Yes, you can use the e-mail client of your choice, as long as it supports POP3 and SMTP<br/>[<font color="#ff0000">POP3 and SMTP not yet implemented</font>]-->
</p>
<div class="date"></div>
<p>
<strong>Can I send attachments, and what limits are there?</strong><br/>
Yes, attachments are supported as of release 0.2.5.<br/>The overall size of attached files should be kept small, preferably below 500kB.</p>
<div class="date"></div>
<p>
<strong>How do I create an email account?</strong><br/>
I2P-Bote calls them Email Identities. You can create one in the I2P-Bote web interface under the "Identities" link. The reason why it's not called an account is that there is no provider like GMail or GMX. You alone hold the (cryptographic) keys to the Email Identity.<br/>
When you create an Email Identity, I2P-Bote generates a string of numbers and letters called an Email Destination. This is the address you can be reached at.<br/>
Example: <tt><b>wsq-8u5bTWbaOsrS0JuXRKL-RsbTkckV4W7u2mIu0Yrlfetixq1F~03CArnvbd6tDWwjPHYEuoKyWqwxplSdix</b></tt>
</p>
<div class="date"></div>
<p>
<strong>What's an Email Destination? What about email addresses?</strong><br/>
Email destinations (aka bote dests) are between 86 and 512 characters long, depending on the type of encryption. Support for easy-to-remember, user-chosen addresses is planned for the near future.<br/>
The e-mail identities consist of public and private keys, as well as a name the user chooses for it. The public part is your e-mail destination, your pseudonymous identity. And one real user can have more than one of those identities. They serve for addressing mails to certain users &#8211;therefore it is referred to as a &#8220;destination&#8221 or short &#8220;dest&#8221; &#8211; as well as for encrypting the mails for them.
Hence, your e-mail destination is the key others use in order to encrypt mails which they send to you, and in
order to verify the authenticity and integrity of mails they receive from you.<br/>
It is save to give your e-mail destination to anybody you want to get e-mails from.<br/>
It is important to distinguish between the mail dest and the router id! Your I2P-Bote mail identity is not related to your I2P-Bote router/node id, which is used for I2P-Bote nodes to contact each other and this way for the Bote network.<br>
If you have problems with your I2P-Bote app &#8211; in the highly unlikely case it should be necessary &#8211; you can tell your I2P-Bote router id in irc2p, I2P's IRC channels, or the forum or manually add other peer's id's in order to connect, though until now this has never been necessary. <br>It is <b>not linked to your ip</b>. Nonetheless, do not relate your I2P-Bote router id with your I2P-Bote mail dests since this might destroy the additional anonymity I2P-Bote itself generates!
</p>
<div class="date"></div>
<p>
<strong>Why are the e-mail addresses so long?</strong><br/>
In I2P-Bote every mail is (automatically) encrypted. In order not to require you to exchange an e-mail address <b>and</b> a long key, we simply made that key the address. This comes with two additional benefits: You won't have
to worry if an e-mail address is already taken or not (at least not if you do not send or receive e-mails to or from the internet) and you don't need a key management app apart, for taking care of your keys.<br>
It is safe to give away this key, as it is only the public key which everybody may know about without compromising your e-mails' secrecy.<br/>
Using the ECC encryption as option will yield shorter e-mail destination keys.
</p>
<div class="date"></div>
<p>
<strong>But I cannot remember those long destinations &#8230;</strong><br/>
That's what the integrated addressbook is there. Once you have become more acquainted qith I2P-Bote, you will appreciate the built-in encrpytion and authentication, which can only be achieved using cryptographic keys.
<br/>Again, the alternatove would be to have short and easy addresses <b>plus</b> a long key for encryption and authentication, <b>and</b> to rely on some authority to map the e-mail addresses to some anonymous recipient.
</p>
<div class="date"></div>
<p>
<strong>What's the point of using multiple mail identities?</strong><br/>
I2P-Bote is not an instant messenger, so you can have several identities without having to keep many tunnels open. Only for fetching requests you'd use up more resources but at the same time provide more cover for others.<br/>
Now, imagine you communicate with your friends unobservedly (see: data retention laws) via I2P-Bote, and want to quickly send out a mail that you'll be meeting each other in a different location tonight.
Then, you need no super-anonymity and can renounce mail routes and delays. Your friends, on the other hand, would want to have a shorter check interval, so they will receive the mail in time. Yet you still want super high anonymity for some of your other communications &#8231; that's where a different mail identity with mail routes, delays and long check intervals comes in handy.
</p>
<div class="date"></div>
<p>
<strong>Which encryption type is best?</strong><br/>
256-bit <a href="http://en.wikipedia.org/wiki/Elliptic_curve_cryptography">ECC</a> produces short and handy Email Destinations, and it is considered stronger than 2048-bit ElGamal.<br/>
521-bit <a href="http://en.wikipedia.org/wiki/Elliptic_curve_cryptography">ECC</a> is stronger than 256-bit ECC, but it makes Email Destinations longer.<br/>
2048-bit <a href="http://en.wikipedia.org/wiki/ElGamal">ElGamal</a> produces even longer Email Destinations, and it is the cryptographically weakest of the three options. However, ElGamal is better researched than ECC, which makes it less likely that there is an unknown weakness in ElGamal than in ECC.
<br>
</p>
<div class="date"></div>
<p>
<strong>What algorithms are used for symmetric encryption, and for hashing?</strong><br/>
<a href="http://en.wikipedia.org/wiki/Advanced_Encryption_Standard">AES-256</a> in
<a href="http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29">CBC</a> mode and <a href="http://en.wikipedia.org/wiki/SHA-2">SHA-256</a>.
</p>
<div class="date"></div>
<p>
<strong>Are there any anti-spam measures?</strong><br/>
I2P-Bote does no active spam filtering, but the fact that mass emails have to be sent individually should discourage spammers. Another line of defense is <a href="http://www.hashcash.org/">HashCash</a> which is supported at the protocol level and may be implemented in a future version if spam becomes a problem.
</p>
<div class="date"></div>
<p>
<strong>How about HTML or styled text?</strong><br/>
The webinterface does not render html.
</p>
<div class="date"></div>
<p>
<strong>How long are emails kept around?</strong><br/>
Emails are available for 100 days after they have been sent. Emails that have not been downloaded by then are deleted.<br/>
Emails you have received stay on your local machine until you delete them.
</p>
<div class="date"></div>
<p>
<strong>When do Email Identities expire?</strong><br/>
Never.
</p>
<div class="date"></div>
<p>
<strong>Can I send email to, and receive email from normal internet email servers?</strong><br/>
No, but this is being worked on.
<!-- Yes. You can both send and receive mails to/from ordinary internet e-mail accounts -->
</p>
<div class="date"></div>
<p>
<strong>Can I send email to, and receive email from postman's traditional I2P mail accounts?</strong><br/>
No, but this too is being worked on.
<!-- Yes. You can both send and receive mails to/from some_name@mail.i2p addresses -->
</p>
<div class="date"></div>
<p>
<strong>What does it mean when Know is x'ed for a mail in my inbox?</strong><br/>
When the sender's destination is not known locally the mail is marked by an x in the &#8220;Know&#8221; column
<!--or by putting &#8220;[UNK]&#8221; before the sender's address in POP3-->.<br/>
This means that you have no proof this user is really who he claims to be, in his user name. Of course, if the signature is valid, you know he possesses the destination key with which the mail was signed, and that the mail content is from that person. But you cannot rely on the short name here. In case you had gotten a mail from a user with this name before, you cannot be sure it is the same user this time, even if the signature is valid. In this
case you must compare the destination keys or add them to your addressbook. A user not locally known, is not necessarily evil, but you shouldn't trust it's the user you might think it is. But, if verified against locally stored keys, you know it's the same user when you receive another mail from him and &#8220;Know&#8221; has a green check.
</p>
<div class="date"></div>
<p>
<strong>What do &#8220;BktPfx&#8221;, &#8220;Distance&#8221; and &#8220;Locked?&#8221; mean?</strong><br/>
&nbsp; &bull; &nbsp; BktPfx = BucketPrefix<br>
&nbsp; &bull; &nbsp; Distance: the distance of an I2P-Bote node to your own node in keyspace<br>
&nbsp; &bull; &nbsp; Locked: If a node is not reachable for whatever reason, it is marked as locked, plus the time it has been found unreachable.<br>
</p>
<div class="date"></div>
<p>
<strong>What can I do to be more anonymous?</strong><br/>
<ul>
<li><p>Don't send identifying information about you! (name, address, photos, geographic location, time zone, age, sex, websites, login names, I2P router id, I2P-Bote id, Files that contain author information about you, &#8230;)</p></li>
<li><p>don't send personal information or information that only you can possess,</p></li>
<li><p>leave I2P-Bote running 24/7,</p></li>
<li><p>use mailroutes with randomized per-hop delays and/or per-hop fixed send times, <font color="#ff0000">[not yet fully implemented]</font></p></li>
<li><p>use a long check interval,</p></li>
<li><p>use a long local delay for own packets,</p></li>
<li><p>use a big check interval randomization. <span style="color: red;">[not yet implemented]</span></p></li>
<li><p>suppress the sending of date and time in the e-mails' header,</p></li>
<li><p>suppress translation of markers like &#8220;Re:&#8221; into another language,</p></li>
<li><p>watch your language and writing style,</p></li>
<li><p>use different e-mail identities,</p></li>
<li><p>consider discarding e-mail identities after longer periods of usage,</p></li>
<li><p>...</p></li>
</ul>
</p>
<div class="date"></div>
<p>
<strong>How do I not send timestamps?</strong><br/>
Go to settings and disable sending of timestamps. This will have the effect that your mail will not contain a date or time of sending.
<!-- <br/>(When using mailroutes, the timestamps are automatically disabled.) <span style="color: red;">[not yet implemented]</span></p> -->
</p>
<div class="date"></div>
<p>
<strong>How do I migrate my settings and data to another computer, or back them up?</strong><br/>
I2P-Bote stores all email and other data in the <tt>i2pbote</tt> folder. On Windows, that folder can be found at
<tt>%APPDATA%\I2P\i2pbote</tt>; on Linux, it is <tt>$HOME/.i2p/i2pbote</tt>.<br/>
To back up or migrate everything, just copy the whole <tt>i2pbote</tt> folder.<br/>
If you are only interested in your Email Identities, copy the file <tt>identities.txt</tt>. For the address book, copy <tt>addressBook.txt</tt>.
</p>
<div class="date"></div>
<p>
<strong>What is a <i>mail route?</i></strong><br/>
see: What does high-latency transport mean?<br/>
<!-- (When using mail routes, the timestamps are automatically disabled.) [yet to be implemented] -->
</p>
<div class="date"></div>
<p>
<strong>What does high-latency transport mean?</strong><br/>
It means that you can enable an option where e-mail packets are not sent directly to storing nodes, but are relayed (forwarded) by other peers (who cannot read the e-mails, as they are encrypted with several layers and ripped into small parts), who do not send them on immediately but wait a user-specified time &#8211; in case of sending
specified by the sender, in case of receiving specified by recipient.<br/>
Therefore it takes the mail some time to arrive. Thus an attacker cannot simply run stats on node uptimes (who was connected when) and times a message was received to be stored (which in a low-latency environment would be about the time it was sent), in order to uncover the real life identities behind I2P-Bote e-mail identities.
</p>
<div class="date"></div>
<p>
<strong>What latencies are there, and how can they be controlled (if at all)?</strong><br/>
I2P-Bote is distributed and running on top of the I2P network, so it takes some time. Speed is not our strength, but we compare well with other anon mail systems. Without mail routes enabled it takes 3 to 10 minutes from hitting the
&#8220;Send&#8221; button to being displayed in the receiver's inbox.<br/>
If speed is what you want, fully disable mail routes or set them to the minimum number of hops and minimum per-hop delay you can live with.
</p>
<div class="date"></div>
<p>
<strong>If I2P-Bote generates its own anonymity, why does it need I2P?</strong><br/>
I2P-Bote is built on top of I2P mainly for five reasons:<br/>
&nbsp; &bull; &nbsp; I2P was lacking a decentralized e-mail service and HungryHobo is an I2P user.<br/>
&nbsp; &bull; &nbsp; I2P offers very good anonymity, is mature and incorporates years of experience.<br>
&nbsp; &bull; &nbsp; So being on top of it, kind of represents an anonymity fall-back even if there were some crucial bugs in I2P-Bote.<br/>
&nbsp; &bull; &nbsp; Flexibility: We want to offer an easy way to anonymous low-latency e-mail communication as well, with still a high level of protection.<br/>
&nbsp; &bull; &nbsp; I2P with it the many other apps running on top of it creates a lot of traffic that blends with I2P-Bote traffic.<br/>
&nbsp; &bull; &nbsp; Even I2P-Bote relays are thus location-hidden.
</p>
<div class="date"></div>
<p>
<strong>How anonymous/secure is I2P-Bote without mail routes?</strong><br/>
Pretty anonymous and very secure.<br/>
It then basically enjoys the same anonymity other apps have on I2P, the anonymity provided by the I2P router &#8211; which is rather strong anonymity already. However, I2P is a low-latency network, with all the shortcomings a
low-latency network comes with by its very nature. There are attacks against which I2P cannot protect you or not protect you very reliably. I2P-Bote does its best to augment I2P anonymity with its high-latency transport option, which make &#8211; if enabled &#8211;
I2P-Bote mails <i>paranoidly</i> anonymous.
</p>
<div class="date"></div>
<p>
<strong>Is I2P-Bote open source?</strong><br/>
Of course!<br/>&#8220; <i>
This software is licensed under the GPL version 3 (see licenses/GPLv3.txt), except for bcprov-ecc-jdk16-145.jar which is licensed under the Bouncy Castle License
(see licenses/BouncyCastle.txt).</i>&#8221; <br>(Both of which are free open source licences.)
</p>
<div class="date"></div>
<p>
<strong>Who made I2P-Bote?</strong><br/>
(See also Credits!)<br>
Conception, technical design, implementation and web user interface were/are done by HungryHobo, an anonymous developer. For feedback or if you want to offer help, you can contact him using I2P-Bote. His destination key is: <br><b>hobo37SEJsEMfQHwcpVlvEgnrERGFz34GC1yjVyuRvl1QHnTi0UAoOtrLP~qkFY0oL59BBqj5sCep0RA8I5G8n</b>
</p>
<div class="date"></div>
<p>
<strong>What languages are available?</strong><br/>
English, German, Russian, French, Spanish, Portuguese, Dutch, Norwegian, Swedish, Chinese, and Arabic.
</p>
<div class="date"></div>
<p>
<strong>How can I help translate I2P-Bote into my language?</strong><br/>
Translations are done the same way as the rest of I2P. If you would like to help and have questions, please
<a href=contact.html>contact the author.</a>
</p>
<div class="date"></div>
<p>
<strong>How does it work on a technical level?</strong><br/>
Have a look at the file <tt>doc/techdoc.txt</tt> in the source code.
</p>
<div class="date"></div>
<p>
<strong>What are some other ways I can help?</strong><br/><br/>
&nbsp; &bull; &nbsp; Use I2P-Bote and give feedback<br/><br/>
&nbsp; &bull; &nbsp; Tell your friends, family, collegues et al. about I2P-Bote and lend them a hand<br/><br/>
&nbsp; &bull; &nbsp; Mention I2P-Bote on your blog, eepsite or website<br/><br/>
&nbsp; &bull; &nbsp; Write a user's guide or improve the technical documentation<br/><br/>
&nbsp; &bull; &nbsp; Add features or fix bugs (<a href=contact.html>contact the author first</a>)<br/><br/>
</p>
<div class="date"></div>
<p>
<br/><br/><br/><br/><br/><br/><br/>
<strong>Credits:</strong><br/><br/>
Idea &amp; technical concept: <i>HungryHobo</i>, <i>Mixxy</i><br/><br/>
Implementation: <i>HungryHobo</i><br/><br/>
Plugin support: <i>zzz</i>, <i>HungryHobo</i><br/><br/>
User interface: <i>HungryHobo</i><br/><br/>
Seedless integration: <i>sponge</i><br/><br/>
German translation: <i>HungryHobo</i><br/><br/>
Russian translation: <i>suhr</i><br/><br/>
French translation: <i>albat</i>, <i>Redzara</i>, <i>Mixxy</i><br/><br/>, <i>magma</i><br/><br/>
Spanish translation: <i>Mixxy</i><br/><br/>
Portuguese translation: <i>Mixxy</i><br/><br/>
Dutch translation: <i>KwukDuck</i><br/><br/>
Norwegian translation: <i>hej</i><br/><br/>
Swedish translation: <i>hottuna</i><br/><br/>
Chinese translation: <i>walking</i><br/><br/>
Arabic translation: <i>hamada</i><br/><br/>
Alpha testing: <i>HungryHobo</i>, <i>Mixxy</i>, <i>Returning Novice</i>, <i>sponge</i>, and many others<br><br/>
Manual: <i>Mixxy</i><br/><br/>
<br><br/>
</p>
<div class="date"></div>
</div>
</div>
<div id="footer">
<div class="rside">CSS: <a href="http://www.free-css-templates.com" title="Design by David Herreman">David Herreman</a></div>
<p>
<a href="http://validator.w3.org/check?uri=referer" title="Validate">XHTML</a> - <a href="http://jigsaw.w3.org/css-validator/check/referer" title="Validate">CSS</a>
</p>
</div>
</div>
</body>
</html>

View File

@ -1,622 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head>
<title>Foire aux questions</title>
<meta http-equiv="Content-Language" content="English">
<meta name="Robots" content="index,follow">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" href="FAQ_fr_files/style.html"
media="screen">
</head><body>
<div id="wrap">
<div id="top">
<div class="rights"> </div>
<div id="search">
</div>
<div class="lefts">
<h1>I2P-Bote</h1>
<h2>Messagerie décentralisée sécurisée</h2>
</div>
</div>
<div id="topmenu">
<div class="rights"></div>
<div class="lefts"><ul>
<li><a
href="http://t5qds7twb7eyyvp2fchhbn74tgdzv774rlru5guit5jkn4a6do7a.b32.i2p/index.html"
title="Home">Accueil</a></li>
<li><a
href="http://t5qds7twb7eyyvp2fchhbn74tgdzv774rlru5guit5jkn4a6do7a.b32.i2p/download.html"
title="Download I2P-Bote">Téléchargement</a></li>
<li><a
href="http://t5qds7twb7eyyvp2fchhbn74tgdzv774rlru5guit5jkn4a6do7a.b32.i2p/screenshots.html"
title="Screenshots">Captures d'écrans</a></li>
<li><a
href="http://t5qds7twb7eyyvp2fchhbn74tgdzv774rlru5guit5jkn4a6do7a.b32.i2p/userguide.html"
title="User Guide">Guide d'utilisation</a></li>
<li><a
href="http://t5qds7twb7eyyvp2fchhbn74tgdzv774rlru5guit5jkn4a6do7a.b32.i2p/faq.html"
title="Frequently Asked Questions">Foire aux questions</a></li>
<li><a href="http://forum.i2p/viewforum.php?f=35" title="Forum">Forum</a></li>
<li><a
href="http://t5qds7twb7eyyvp2fchhbn74tgdzv774rlru5guit5jkn4a6do7a.b32.i2p/contact.html"
title="Contact">Contact</a></li>
</ul></div>
</div>
<div style="text-align: justify;" id="main">
<div id="leftside">
<p>
</p><h2>Foire aux questions</h2>
<p>
</p><div class="date"></div>
<p>
<strong>I2P-Bote, kesako?</strong><br>
C'est un programme de messagerie P2P conçu pour protéger votre vie privée.<br>
I2P-Bote un système de messagerie entièrement décentralisé (sans serveur)
qui fonctionne sur le réseau I2P.
Il prend en charge les identités multiples et n'expose pas les
en-têtes de messages. Il est encore en phase de développement et n'est
accessible que via la console du routeur I2P. Il sera bientôt doté de la prise en
charge de POP3, et de plus d'anonymat via une option de transport
à haute latence. Tous les messages bote sont automatiquement
cryptés de bout en bout sans avoir à régler de cryptage de mails
(bien que cette option soit disponible), et ils sont authentifiés
automatiquement. Comme il est décentralisé, aucun serveur de mail ne peut
faire de rapprochement entre expéditeur et destinataire (profilage):
même les nœuds relayant les messages ne peuvent identifier l'expéditeur
et en dehors de expéditeur et du destinataire, seuls le point terminal du
tunnel à haute latence et les nœuds de stockage connaissent à qui (l'identité
anonyme) est destiné le message. L'expéditeur peut se déconnecter bien avant que
le message arrive à destination.
Ceci augmente encore le niveau d'anonymat que peut atteindre I2P-Bote.
Pour ceux qui ne veulent pas de forts délais, tous ces paramètre sont
modifiables pour s'adapter au niveau de leur besoin d'anonymat.
</p>
<div class="date"></div>
<p>
<strong>Pourquoi ce nom d'I2P-Bote?</strong><br>
Bote est le mot allemand pour dire "messager".
</p>
<div class="date"></div>
<p>
<strong>Pourquoi I2P-Bote?</strong><br>
Parce que c'est sympa. Et parce qu'un système décentralisé faisait cruellement
défaut et qu'I2P n'avait pas de système de messagerie décentralisé, et qu'y voyant
une opportunité d'améliorer un aspect négligé des considérations sur l'anonymat,
il fut décidé d'ajouter une méthode de transport à latence élevée.
Vous pourriez utiliser un compte de messagerie normal et crypter vos messages,
mais ils ne seraient pas pour autant anonymes. Vous pourriez alors utiliser un serveur
d'envoi anonyme, mais ils ne seraient pas automatiquement cryptés de bout en bout.<br>
Ou vous pouvez utiliser I2P-Bote grâce auquel vos messages sont anonymes et
<i>automatiquement</i> cryptés de bout en bout.<br> À la différence des messageries
traditionnelles, il n'y a pas à s'occuper de gérer en plus un système de clés. Tout
ce dont vous avez besoin est déjà prêt.<br> Et ne vous y trompez pas, malgré son
apparente simplicité et sa réelle facilité d'utilisation, I2P-bote vous offre un niveau
de cryptage de qualité militaire et des options d'anonymat extrêmement efficaces.
</p>
<div class="date"></div>
<p>
<strong>Que se passe-t-il quand je clique sur "Envoyer"?</strong><br>
Le message est crypté et stocké sur des ordinateurs d'autres utilisateurs d'I2P-Bote.
À partir de là, il est expédié au destinataire quand il relève son courrier.
</p>
<div class="date"></div>
<p>
<strong>Une minute! Tous mes envois sont stockés dans les ordinateurs de personnes
choisies aléatoirement? C'est une idée de fou!</strong><br>
Tout ce qu'elles peuvent voir n'est qu'un tas d'ordures car les données sont cryptée
à un niveau militaire. Seuls vous et le destinataire savez ce qu'il y a dans le
message. De plus, si vous envoyez en ayant activé le relayage, il ne leur est même pas
possible de savoir qui l'a envoyé.<br> Entre ça et l'utilisation d'un compte fourni par
une <a href="http://www.theregister.co.uk/2009/12/07/schmidt_on_privacy/">société
qui ne respecte pas votre vie privée</a>, sur une liaison Internet qui
<a href="http://www.eff.org/issues/nsa-spying">peut être espionnée par des entités
occultes</a>, quelle est la solution la plus digne de confiance?
</p>
<div class="date"></div>
<p>
<strong>Comment cela marche-t-il exactement?</strong><br>
Voyez le chapitre “Concept” du Guide <br>En bref:
Les nœuds I2P-Bote constituent un réseau p2p relayant les paquets de messages
pour le compte des autres et les stockant dans une table de hachage décentralisée (DHT).
</p>
<div class="date"></div>
<p>
<strong>Pourquoi devrais-je m'en servir? Je n'ai rien à cacher&hellip;</strong><br>
Vous vous baladez à poil dans la rue, vous?<br>Bon, après tout c'est possible. Mais il
faut quand même noter qu'il peut parfois vous arriver d'avoir des besoins de correspondances
épistolaires secrètes et intraçables, et ne pas faire savoir au monde entier que vous
dites ceci ou cela et à qui vous le dites.<br>
Parfois, vous pouvez aussi vouloir communiquer de façon entièrement anonyme avec
un tiers.<br>I2P-Bote est alors le moyen idéal qui vous donne une protection d'enfer
tout en restant très facile à utiliser.<br>
Il parvient à combiner une sécurité de niveau militaire et une ergonomie de niveau
débutant:<br>
vous pouvez avoir réglé un niveau d'anonymat paranoïaque, qui retardera l'arrivée du message
d'une demi-éternité, ou avoir des échanges plus rapides et cependant garder un fort
niveau d'anonymat.<br>
À vous de décider, d'un simple clic de souris.</p>
<div class="date"></div>
<p>
<strong>Et au sujet de <a href="http://www.pgp.com/">PGP</a> et
<a href="http://gnupg.org/">GPG</a>?</strong><br>
PGP et GPG vous permettent de crypter le courrier et de l'envoyer par votre compte de
messagerie existant. Il offrent du cryptage fort, mais uniquement du corps du message,
pas des en-têtes, ce qui veut dire que les champs "Objet", le nom de votre ordinateur,
et d'autres informations ne sont pas protégées.<br> Une autre faiblesse de PGP/GPG
est qu'ils ne peuvent pas empêcher de trouver qui parle à qui.<br>
I2P-Bote, au contraire, crypte tout sauf la destination (en fait, même la destination
n'est visible que par les nœuds qui ignorent de qui vient le message). Il peut aussi
envoyer le message via plusieurs relais (comme
<a href="http://mixmaster.sourceforge.net/">Mixmaster</a>), de sorte que personne ne
puisse trouver qui envoie à qui.<br>
</p>
<div class="date"></div>
<p>
<strong>Puis-je toujours utiliser GPG/PGP avec I2P-Bote?</strong><br>
Bien sûr. Cryptez le texte de votre message avec GPG avant de le coller dans le corps
du message I2P-Bote, ou utilisez une application de messagerie compatible GPG
[<font color="#ff0000">Prise en charge POP3 non implémentée</font>].
</p>
<div class="date"></div>
<p>
<strong>Quelle différence y a-t-il avec Susimail?</strong><br>
I2P-Bote est meilleur parce que son numéro de version est plus élevé&hellip; blague.<br>
I2P-Bote apporte plus d'anonymat, mais Susimail dispose de fonctionnalités dont
I2P-Bote est pour l'instant dépourvu (voir plus bas), et Susimail est plus efficace en
terme de consommation de bande passante car il ne fait pas de stockage redondant
réparti.
</p>
<div class="date"></div>
<p>
<strong>Pourquoi I2P-Bote est-il meilleur?</strong><br>
<i>Nous</i> pensons qu'il est meilleur&hellip; <i>pour nous</i> (et peut-être pour vous, aussi;
décidez vous-même!), que&hellip;</p><ul><li><p style="line-height: 150%;">
<font face="Arial, sans-serif">mixminion car il est facile à utiliser et aussi simple à prendre
en main que nous avons pu.</font></p>
</li><li><p style="line-height: 150%;"><font face="Arial, sans-serif">les services de mail
anonymes n'utilisant pas le routage basé sur des clés de destination, car ils ne permettent pas
le cryptage de bout en bout.</font></p>
</li><li><p style="line-height: 150%;"><font face="Arial, sans-serif">les services centralisés,
car que le serveur pourrait s'arrêter (à cause d'attaques, de problèmes légaux, manque de
financement ou d'intérêt&hellip;) et car l'administrateur du serveur dispose de trop de moyens
pour faire de l'espionnage.</font></p>
</li></ul>
<div class="date"></div>
<p>
<strong>Comment mon identité est-elle protégée quand j'échange du courrier?</strong><br>
Jamais votre adresse IP ni même votre destination I2P n'est inclue dans les messages que vous
envoyez.<br>
Les compteurs de transports à haute latence protègent des attaques de timing.<br>
Le cryptage de bout en bout et par saut, le relayage des paquets pour d'autres nœuds, les
paquets à taille unique* (bourrage/padding), le débit d'envoi constant (messages de test
et bidons/dummy)*, et un rapport assez équilibré des taux d'envoi/réception* protègent des
attaques d'analyse de trafic, et en combinaison avec les delais par saut, I2P-Bote offre de
bons moyens de prévention des attaques d'intersection.<br>
La nature open source d'I2P-Bote garantit que vous pouvez vous-même voir la mise en œuvre et
la vérifier pour y chercher ou trouver des bogues.
<br>*[<font color="#ff0000">pas encore implémenté</font>]</p>
<div class="date"></div>
<p>
<strong>Comment faire pour l'utiliser?</strong><br>
Lisez le Guide ou les autres questions/réponses ici!<br>
Si vous avez toujours des questions, posez-les sur le forum:
<a href="http://forum.i2p/viewforum.php?f=35">http://forum.i2p/viewforum.php?f=35</a>
</p>
<div class="date"></div>
<p>
<strong>Puis-je utiliser un programme comme Thunderbird?</strong><br>
Non, mais c'est sur la feuille de route.
<!--Yes, you can use the e-mail client of your choice, as long as it supports POP3 and
SMTP<br/>[<font color="#ff0000">POP3 and SMTP not yet implemented</font>]-->
</p>
<div class="date"></div>
<p>
<strong>Puis-je envoyer des pièce-jointes, et quelles sont les limites?</strong><br>
Oui, depuis la version 0.2.5.<br>La taille cumulée des pièces jointes devrait être petite et de
préférence ne pas dépasser les 500ko.</p>
<div class="date"></div>
<p>
<strong>Comment créer un compte de messagerie?</strong><br>
I2P-Bote les appelle "Identités". Vous pouvez en créer une dans l'interface web d'I2P-Bote
via le lien "Identités". La raison pour laquelle on ne les appelle pas "comptes" est qu'il
n'y a pas de fournisseur comme GMail ou autre. Vous êtes le seul dépositaire du porte-clés
(cryptographique) de l'identité.<br>Quand vous la créez, I2P-bote génère une chaîne de
chiffres et de lettres appelée "Destination". C'est l'adresse à laquelle on peut vous joindre.
<br>
Exemple: <tt><b>wsq-8u5bTWbaOsrS0JuXRKL-RsbTkckV4W7u2mIu0Yrlfetixq1F~03CArnvbd6tDWwjPHYEuoKyWqwxplSdix</b></tt>
</p>
<div class="date"></div>
<p>
<strong>Qu'est-ce qu'une destination? Et l'adresse mail?</strong><br>
La longueur des destinations est comprise entre 86 et 512 caractères, suivant le type de
cryptage. La prise en charge d'adresses faciles à mémoriser et choisies par l'utilisateur est
au programme d'un futur proche.<br>Les identités sont une paire de clés privée et publique,
et d'un nom que l'utilisateur lui attribue. La clé publique est la destination,
votre identité pseudonymique. Un utilisateur réel peut en avoir plusieurs. Elles servent à
envoyer des messages à certains utilisateurs donc aussi appelés “destinations” tout comme
pour crypter les messages qui leur sont&hellip; destinés. Votre destination est donc pour les
autres aussi la clé qu'ils utilisent pour crypter les messages qu'ils vous envoient et pour
pour vérifier l'intégrité et l'authenticité de ceux qu'ils reçoivent&hellip; de vous.<br>
Il est sans danger (et indispensable) de donner votre destination à toute personne de laquelle
vous voulez recevoir du courrier.<br>Il est important de faire la distinction entre votre
destination de messagerie et la destination de votre routeur I2P! Votre identité de messagerie
n'a rien à voir avec l'identité de votre nœud/routeur I2P-Bote qui est utilisée pour permettre
aux nœuds I2P-Bote de se contacter les uns les autres pour constituer le réseau Bote.<br>
Si vous avez des problèmes avec l'application I2P-Bote dans le cas très improbable où ça
serait nécessaire vous pouvez indiquer votre destination (au sens d'application I2P) sur
irc2p, les canaux IRC ou le forum I2P, ou ajouter manuellement des identités d'autres pairs
pour pouvoir vous connecter, bien que ceci n'ait jamais été nécessaire à ce jour.<br>
Elle <b>n'est pas liée à votre adresse IP</b>. Néanmoins, ne révélez d'aucune façon la relation
entre votre identité de routeur I2P-Bote et celle d'une de vos propres destinations de courrier
I2P-Bote car cela réduirait à néant le niveau d'anonymat supplémentaire qu'I2P-Bote porte en
lui-même!
</p>
<div class="date"></div>
<p>
<strong>Pourquoi les adresses sont-elles si longues?</strong><br>
Dans I2P-Bote chaque message est (automatiquement) crypté. Pour vous éviter d'avoir à échanger
votre adresse <b>ET</b> de très longues clés publiques, on a simplement fait de ces clés les
adresses. Deux autres avantages en découlent: vous n'avez pas à vous soucier de la préexistence
de votre adresse (au moins tant que vous ne recevez ni n'envoyez courrier vers
l'Internet normal), et vous n'avez pas à avoir un système de gestion de clés séparé pour
s'occuper de vos clés.<br>
Il est sans danger (et indispensable) de donner cette clé, car elle n'est que la clé publique,
que certains peuvent avoir à connaître, sans pour autant que cela compromette la
confidentialité de vos messages.<br>
L'utilisation du cryptage ECC conduit à des clés plus courtes.
</p>
<div class="date"></div>
<p>
<strong>Mais je n'arrive pas à mémoriser de si longues destinations&hellip;</strong><br>
Le carnet d'adresses est là pour ça. Une fois convaincu par le principe d'I2P-Bote, vous
apprécierez le cryptage et l'authentification intégrés, qui ne peuvent être atteints que par
l'utilisation de clés cryptographiques.
<br>Encore une fois, l'alternative serait d'avoir des adresses courtes et simples <b>plus</b>
de longues clés de cryptage et authentification, <b>ET</b> de se reposer sur quelqu'autorité
pour faire la relation entre l'adresse et un destinataire anonyme.
</p>
<div class="date"></div>
<p>
<strong>Quel est l'intérêt d'utiliser de multiples identités?</strong><br>
I2P-Bote n'est pas une messagerie instantanée, donc vous pouvez avoir plusieurs identités sans
avoir à tenir plusieurs tunnels ouverts en même temps. Vous utiliserez seulement un peu plus de
ressources au moment de la relève mais en même temps vous fournirez une meilleure couverture
aux autres.<br>
Maintenant, imaginez que vous communiquez incognito avec des amis (voir: lois sur le stockage
de données) via I2P-Bote, et que vous voulez envoyer rapidement un message pour vous retrouver
ce soir ailleurs qu'à l'habituel Café des Arts. Vous n'avez pas besoin d'un super-anonymat et
pouvez renoncer aux relayages et délais de mails. Vos amis, d'autre part, devraient raccourcir
leur intervalle de vérification pour recevoir le message à temps. Mais pour d'autres types de
communications, vous avez besoin d'un super niveau d'anonymat, <!--et c'est là qu'une autre
identité avec un relayage et des délais différents peut être utile-->
</p>
<div class="date"></div>
<p>
<strong>Quel est le meilleur cryptage?</strong><br>
<a href="http://fr.wikipedia.org/wiki/Cryptographie_sur_les_courbes_elliptiques">ECC</a> à 256
bits produit des adresses destinations courtes et maniables, et est considéré comme plus solide
que l'ElGamal à 2048 bits.<br>
<a href="http://en.wikipedia.org/wiki/Elliptic_curve_cryptography">ECC</a> à 521 bits est plus
solide que sa version à 256 bits, mais il génère des destinations (clés) très longues.<br>
<a href="http://fr.wikipedia.org/wiki/Cryptosyst%C3%A8me_de_ElGamal">ElGamal</a> à 2048 bits
génère des clés encore plus longues, et il est le plus faible des trois algorithmes. Cependant,
ElGamal fait l'objet de plus recherches que ECC, ce qui rend moins probable qu'une faiblesse de
cet algorithme passe inaperçue pendant plus longtemps que dans ECC.<br>
</p>
<div class="date"></div>
<p>
<strong>Quels algorithmes sont-ils utilisés pour le cryptage symétrique et le hachage?</strong><br>
<a href="http://fr.wikipedia.org/wiki/Advanced_Encryption_Standard_process">AES</a>-
<a href="http://fr.wikipedia.org/wiki/Advanced_Encryption_Standard">256</a> en mode
<a href="http://fr.wikipedia.org/wiki/Mode_d%27op%C3%A9ration_%28cryptographie%29#Encha.C3.AEnement_des_blocs_:_.C2.AB_
Cipher_Block_Chaining_.C2.BB_.28CBC.29">CBC</a> et
<a href="http://http://fr.wikipedia.org/wiki/Secure_Hash_Algorithm">SHA-256</a>.
</p>
<div class="date"></div>
<p>
<strong>Y a-t-il des protections anti-spam?</strong><br>
I2P-Bote ne filtre pas le spam, mais le fait que l'envoi en masse doive être fait
individuellement devrait décourager les spammers. Un autre moyen de défense repose sur des
pénalités (<a href="http://www.hashcash.org/">HashCash</a>) qui sont un mécanisme supporté au
niveau du protocole, et qui pourrait être implémenté dans une version ultérieure si le spam
devenait un problème.
</p>
<div class="date"></div>
<p>
<strong>Deux mot sur HTML et le formatage de texte?</strong><br>
L'interface web ne restitue pas la mise en forme HTML.
</p>
<div class="date"></div>
<p>
<strong>Pendant combien de temps les messages restent-ils dans la nature?</strong><br>
Ils restent disponibles 100 jours après l'émission. Les messages n'ayant pas été relevés
au-delà de cette période sont supprimés.<br>
Les messages relevés restent sur votre ordinateur jusqu'à ce que vous les supprimiez.</p>
<div class="date"></div>
<p>
<strong>Quand les identités expirent-elle?</strong><br>
Jamais. Mais la votre peut devenir inutilisable si vous perdez le contenu de votre disque dur.
Vous seriez alors amené à en créer une ou plusieurs autres en remplacement, et en informer vos
correspondants habituels.
</p>
<div class="date"></div>
<p>
<strong>Puis-je recevoir et envoyer des messages de/vers des serveurs de l'Internet normal?
</strong><br>
Non, mais on y travaille.
<!-- Yes. You can both send and receive mails to/from ordinary internet e-mail accounts -->
</p>
<div class="date"></div>
<p>
<strong>Et de/vers les comptes traditionnels de postman?</strong><br>
Non plus, mais on y travaille aussi.
<!-- Yes. You can both send and receive mails to/from some_name@mail.i2p addresses -->
</p>
<div class="date"></div>
<p>
<strong>Que signifient les icônes dans la colonne "Connu" de la boîte de réception?</strong><br>
Quand la destination expéditrice est inconnue localement le message est marqué d'un X dans la
colonne "Connu"
<!--or by putting &#8220;[UNK]&#8221; before the sender's address in POP3-->.<br>
Cela veut dire que vous n'avez aucune preuve que cet utilisateur est vraiment qui il prétend
être dans son nom public "humain". Bien sûr, si la signature est valide, vous êtes assuré(e)
qu'il possède la clé privée de destination avec laquelle il a signé le message, et donc que le
contenu du message est bien issu de cette personne. Mais vous ne pouvez pas vous fier au nom
court malgré tout (il vous faudrait un autre moyen de vous en assurer: qu'il vous ait remis sa
destination en main propre par exemple, ou vérifiée au téléphone, etc&hellip;). Dans le cas où
vous avez déjà eu un message d'un utilisateur affichant ce nom, vous ne pouvez pas être sûr(e)
que c'est le même utilisateur cette fois, même si la signature est valide. Dans ce cas, vous
devez comparer les clés de destination ou les ajouter à votre carnet d'adresses. Un utilisateur
inconnu localement n'est pas forcément une vermine, mais vous ne devriez pas lui faire
confiance à priori sur la seule foi du nom qu'il annonce. Mais s'il passe avec succès le test de
comparaison avec la clé précédemment par vous stockée localement, vous avez au moins la preuve
qu'il s'agit bien du même utilisateur quand vous recevez un nouveau message de sa part, et la
colonne "Connu" est maintenant décorée d'une jolie coche verte.
</p>
<div class="date"></div>
<p>
<strong>“BktPfx”, “Distance” et “Verrouillé?"&hellips; c'est quoi?</strong><br>
&nbsp;&nbsp; BktPfx = BucketPrefix (préfixe de paquet)<br>
&nbsp;&nbsp; Distance: la distance d'un nœud I2P-Bote au votre propre dans l'espace de clés.
<br>
&nbsp;&nbsp; Verrouillé: si un nœud n'est pas joignable, quelle qu'en soit la raison, il est
marqué "Verrouillé", et le temps depuis lequel sa défection a été découverte est affiché.<br>
</p>
<div class="date"></div>
<p>
<strong>Comment rester plus anonyme?</strong><br>
</p><ul>
<li><p>N'envoyez aucune information à votre sujet! (nom, adresse, photos, situation
géographique, fuseau horaire, âge, sexe, sites web, identifiants, id de routeur I2P, id
I2P-Bote, fichiers contenant des informations sur l'auteur, &hellip;)</p></li>
<li><p>N'envoyez pas d'information personnelles c'est à dire des informations qui vous
identifient de façon unique en tant que vous seul les connaissez,</p></li>
<li><p>Laissez I2P-Bote tourner 24/24 7/7,</p></li>
<li><p>Utilisez des routes de messages avec des délais aléatoires par-saut et/ou des temps
fixes par saut <font color="#ff0000">[not yet fully implemented]</font>,</p></li>
<li><p>Utilisez de longs intervalles de relève,</p></li>
<li><p>Utilisez un délais long pour vos propres paquets,</p></li>
<li><p>Utilisez un fort niveau d'intervalle de relève aléatoire. <span style="color:
red;">[not yet implemented]</span></p></li>
<li><p>Supprimez l'envoi de la date et de l'heure dans les en-têtes de messages,</p></li>
<li><p>Renoncez aux marqueurs de suivi tels que “Tr:” traduits dans votre langue,</p></li>
<li><p>Surveillez votre style de langage et d'écriture,</p></li>
<li><p>Utilisez différentes identités,</p></li>
<li><p>Envisagez d'abandonner vos identités après de longues périodes d'utilisation,</p></li>
<li><p>...</p></li>
<li><p>Cette liste n'est pas destinée à vous rendre paranoïaque, mais à vous montrer par où
l'anonymat peut-être amoindri.</p></li>
</ul>
<div class="date"></div>
<p>
<strong>Comment ne pas envoyer de tampons horaires?</strong><br>
L'option est dans les "Préférences". Les messages n'auront pas de date/heure d'envoi;
<!-- <br/>(When using mailroutes, the timestamps are automatically disabled.)
<span style="color: red;">[not yet implemented]</span></p> -->
</p>
<div class="date"></div>
<p>
<strong>Comment migrer mes réglages et données vers un autre ordinateur, ou les
sauvegarder?</strong><br>
I2P-Bote stocke tout le courrier et autres données dans le dossier <tt>i2pbote</tt>. Sur
Windows, <tt>%APPDATA%\I2P\i2pbote</tt>; sur Linux, <tt>$HOME/.i2p/i2pbote</tt>.<br>
Une simple copie du dossier <tt>i2pbote</tt> fait l'affaire.<br>
Si seules vos identités vous intéressent, copiez le fichier <tt>identities.txt</tt>.
Pour le carnet d'adresses, copiez <tt>addressBook.txt</tt>.
</p>
<div class="date"></div>
<p>
<strong>qu'est-ce qu'une <i>route de messages</i>?</strong><br>
voir: Que signifie "Transport à haute latence"?<br>
<!-- (When using mail routes, the timestamps are automatically disabled.) [yet to be implemented] -->
</p>
<div class="date"></div>
<p>
<strong>Que signifie "Transport à haute latence"?</strong><br>
Cela signifie que (par une option que vous pouvez activer) les paquets de messages ne sont pas
envoyés directement aux nœuds de stockage, mais sont relayés (transférés) par d'autres pairs
(qui ne peuvent pas les lire car ils sont cryptés sur plusieurs niveaux et découpés en petits
morceaux) qui ne les traitent pas immédiatement mais attendent un certain temps défini au
niveau utilisateur pour l'envoi, par l'expéditeur, pour la réception, par le destinataire.<br>
En conséquence le message est long à arriver. L'attaquant ne peut pas utiliser de statistiques
sur les temps de fonctionnement des nœuds (qui s'est connecté à quel moment) ni les moments où
un message a été reçu pour être stocké (ce dans un environnement à faible latence serait à peu
près le moment où il a été envoyé), pour en déduire les identités réelles sous-jacentes aux
identités I2P-Bote.
</p>
<div class="date"></div>
<p>
<strong>Quelles latences y a-t-il, et comment les contrôler?</strong><br>
I2P-Bote est décentralisé et s'exécute sur le reéseau I2P, donc ça prend du temps. La vitesse
n'est pas notre force, mais il supporte bien la comparaison à d'autres systèmes de messagerie
anonyme. Sans apport des routes de messages, il faut 3 à 10 mn entre le clic sur "Envoyer" et
l'affichage chez le destinataire.<br>Si la vitesse vous préoccupe, désactivez les routes ou
réglez-les au nombre de sauts et de retard par saut minimums avec lesquels vous pouvez vivre.
</p>
<div class="date"></div>
<p>
<strong>Si I2P-Bote son propre anonymat, pourquoi a-t-il besoin d'I2P?</strong><br>
I2P-Bote est bâti sur I2P pour cinq raisons principales:<br>
&nbsp;&nbsp; I2P manquait d'une messagerie décentralisée et HungryHobo est un utilisateur
d'I2P.<br>
&nbsp;&nbsp; I2P offre un excellent anonymat, il est bien au point et bénéficie de
nombreuses années d'expérience.<br>
&nbsp;&nbsp; Donc, en faire ses fondations représente une assurance d'anonymat s'il y avait
des bogues critiques dans I2P-Bote.<br>
&nbsp;&nbsp; Souplesse: nous voulons aussi offrir une méthode de communication à faible
latence, tout en gardant un haut niveau de protection.<br>
&nbsp;&nbsp; I2P, avec toutes les autres applications qui l'utilisent, génère beaucoup de
trafic dans lequel le trafic I2P-Bote peut se camoufler.<br>
&nbsp;&nbsp; En conséquence, même les relais I2P-Bote sont masqués en terme de localisation.
</p>
<div class="date"></div>
<p>
<strong>Que valent l'anonymat et la sécurité d'I2P-Bote sans les routes?</strong><br>
Plutôt bonne pour l'anonymat et très sécurisé.<br>
Il bénéficie de base du même anonymat que les autres applications I2P, l'anonymat procuré par
le routeur 2P router qui est déjà fort conséquent. Cependant, I2P est un réseau à faible
latence, avec tous les défauts intrinsèques de ce genre de réseaux. Il a des attaques contre
lesquelles I2P ne peut pas vous protéger ou au moins très efficacement. I2P-Bote améliore
l'anonymat grâce à ses options de transport à haute latence, qui rendent si vous les
activez les messages I2P-Bote à un niveau d'anonymat <i>paranoïaque</i>.
</p>
<div class="date"></div>
<p>
<strong>I2P-Bote, open source?</strong><br>
Bien sûr!<br><i>Ce logiciel est sous licence GPL version 3 (voir licenses/GPLv3.txt),
sauf pour bcprov-ecc-jdk16-145.jar qui est sous licence Bouncy Castle (voir
licenses/BouncyCastle.txt).</i><br>(Toutes deux sont des licences libres et ouvertes).
</p>
<div class="date"></div>
<p>
<strong>Qui a créé I2P-Bote?</strong><br>
(Voir aussi les "Remerciements"!)<br>
La conception, la structure technique, l'implémentation et l'interface web user sont de
HungryHobo, un développeur anonyme. Pour les retours techniques ou proposer votre aide, vous
pouvez le contacter avec I2P-Bote. Sa destination est: <br>
<b>hobo37SEJsEMfQHwcpVlvEgnrERGFz34GC1yjVyuRvl1QHnTi0UAoOtrLP~qkFY0oL59BBqj5sCep0RA8I5G8n</b>
</p>
<div class="date"></div>
<p>
<strong>Langues disponibles</strong><br>
Anglais, allemand, russe, français, espagnol, portugais, hollandais, norvégien, suédois,
chinois et arabe.
</p>
<div class="date"></div>
<p>
<strong>Comment puis-je aider à la traduction d'I2P-Bote dans ma langue?</strong><br>
Comme pour I2P.
<a href="http://t5qds7twb7eyyvp2fchhbn74tgdzv774rlru5guit5jkn4a6do7a.b32.i2p/contact.html">
Contactez l'auteur.</a>
</p>
<div class="date"></div>
<p>
<strong>Fonctionnement technique?</strong><br>
Regardez <tt>doc/techdoc.txt</tt> dans le code source.
</p>
<div class="date"></div>
<p>
<strong>D'autres moyens pour aider?</strong><br><br>
&nbsp;&nbsp; Utilisez I2P-Bote et faites part de vos commentaires/<br><br>
&nbsp;&nbsp; Parlez d'I2P-Bote à vos amis, à votre famille et à vos collègues, et aidez-les.
<br><br>
&nbsp;&nbsp; Mentionnez I2P-Bote sur votre blog, votre eepsite et votre web.<br><br>
&nbsp;&nbsp; Rédigez un guide utilisateur ou améliorez la documentation technique.<br><br>
&nbsp;&nbsp; Ajoutez des fonctionnalités ou corrigez des bogues (<a
href="http://t5qds7twb7eyyvp2fchhbn74tgdzv774rlru5guit5jkn4a6do7a.b32.i2p/contact.html">contactez tout d'abord l'auteur
</a>).<br><br>
</p>
<div class="date"></div>
<p>
<br><br><br><br><br><br><br>
<strong>Remerciements:</strong><br><br>
Idée &amp; conception technique: <i>HungryHobo</i>, <i>Mixxy</i><br><br>
Implémentation: <i>HungryHobo</i><br><br>
Maintenance du greffon: <i>zzz</i>, <i>HungryHobo</i><br><br>
Interface utilisateur: <i>HungryHobo</i><br><br>
Intégration de Seedless: <i>sponge</i><br><br>
Traduction allemande: <i>HungryHobo</i><br><br>
Traduction russe: <i>suhr</i><br><br>
Traduction française: <i>albat</i>, <i>Redzara</i>, <i>Mixxy</i>, <i>magma</i><br><br>
Traduction espagnole: <i>Mixxy</i><br><br>
Traduction portugaise: <i>Mixxy</i><br><br>
Traduction hollandaise: <i>KwukDuck</i><br><br>
Traduction norvégienne: <i>hej</i><br><br>
Traduction suédoise: <i>hottuna</i><br><br>
Traduction chinoise: <i>walking</i><br><br>
Traduction arabe: <i>hamada</i><br><br>
Testeurs versions Alpha: <i>HungryHobo</i>, <i>Mixxy</i>, <i>Returning Novice</i>,
<i>sponge</i>, et de nombreux autres<br><br>
Manuel: <i>Mixxy</i><br><br>
<br><br>
</p>
<div class="date"></div>
</div>
</div>
<div id="footer">
<div class="rside">CSS: <a href="http://www.free-css-templates.com/"
title="Design by David Herreman">David Herreman</a></div>
<p>
<a href="http://validator.w3.org/check?uri=referer" title="Validate">XHTML</a>
- <a href="http://jigsaw.w3.org/css-validator/check/referer"
title="Validate">CSS</a>
</p>
</div>
</div>
</body></html>

View File

@ -1,879 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html><head>
<meta content="text/html; charset=ISO-8859-1" http-equiv="content-type"><title>User's Guide</title><meta http-equiv="CONTENT-TYPE" content="text/html; charset=utf-8"><meta name="GENERATOR" content="OpenOffice.org 3.2 (Unix)"><style type="text/css">
<!--
@page { margin: 2cm }
P { margin-bottom: 0.21cm }
A:link { so-language: zxx }
-->
</style><meta http-equiv="CONTENT-TYPE" content="text/html; charset=utf-8"><meta name="GENERATOR" content="OpenOffice.org 3.2 (Unix)"><style type="text/css">
<!--
@page { margin: 2cm }
P { margin-bottom: 0.21cm }
-->
</style></head><body>
<p style="line-height: 150%; font-weight: bold;" align="center"><font face="Arial, sans-serif">I2P-Bote
Manual</font></p>
<p style="line-height: 150%;"><br></p>
<p style="line-height: 150%;"><br>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif"><b>1.
Introduction</b></font></p>
<p style="line-height: 150%;"><br><br>
<font face="Arial, sans-serif">I2P-Bote
is an easy-to-use, highly anonymous secure e-mail application for
I2P. It is a serverless, fully decentralized system that
establishes/forms a peer-to-peer network built on top of
state-of-the-art low latency anonymizing network I2P; adding to it an
optional mixminion-like high-latency transport and thus avoiding the
shortcomings of low-latency networks.</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">Therefore,
I2P-Bote makes full use of the anonymity provided by I2P, plus it
generates its own anonymity by adding another anonymizing layer
(overlay network).</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">This
concept of layered anonymity is what makes I2P-Bote so flexible:</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">You can
configure it to be extremely anonymous and slow or less anonymous but
faster/more efficient. In any event, I2P-Bote<font color="#000000">
always provides a very good anonymity for both, sender and receiver,
as well as end-to-end encryption*. </font></font>
</p>
<p style="line-height: 150%;"><font color="#000000"><font face="Arial, sans-serif">I2P-Bote
offers the option to make your communications even more anonymous, by
enabling the high-latency mail routes &#8211; at cost of performance,
however. Users that want their anonymous e-mails to arrive as quickly
as possible, will want to disable the mail routes and use 'direct'
sending through I2P. It is guaranteed that you will never be less
anonymous than the anonymity provided by standard I2P connections. </font></font>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif"><font color="#000000">In
order to achieve high usability, we have enabled it to be used with
standard mail clients such as Thunderbird, Evolution or Kmail,
without having to worry about what extra information these
applications send in their headers. [</font><font color="#ff0000">YET
TO BE IMPLEMENTED</font><font color="#000000">] Furthermore there is
a web interface that lets you send and read e-mails or manage your
settings and identities.</font></font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">I2P-Bote
is easy to use: If you're
not yet using I2P, just install I2P from <a href="http://www.i2p2.de/">http://www.i2p2.de</a>
and then install the I2P-Bote plugin as described in this manual.
Otherwise simply read on!**</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif"><i>Current
version is 0.2.6.</i></font><br><br>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">*Unless
you send e-mails to or receive them from the regular internet, ALL
emails &#8211; the mail body, attachments and the header except
recipient's address (subject, date, time, sender address, ...) are
automatically and transparently end-to-end encrypted. The recipient's
address is only visible for the mail route's last node that stores
the packets into the kad network, and the respective storing nodes,
but they cannot read the mail's content nor who sent it nor who will
fetch it.</font></p>
<p style="line-height: 150%;"><br><br>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">**Of
course you can also compile from source.</font></p>
<p style="line-height: 150%;"><br>
</p>
<p style="line-height: 150%;"><br>
<br>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif"><b>2.
Howto</b></font></p>
<p style="line-height: 150%;"><br>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif"><u>2.1.
Installation</u></font></p>
<p style="line-height: 150%;"><br>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">In order
to install I2P-Bote, go to the bottom of the I2P Client Configuration
page at </font>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif"><a href="http://localhost:7657/configclients.jsp">http://localhost:7657/configclients.jsp</a>
</font>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">and
enter:</font></p>
<p style="line-height: 150%;"><a href="http://tjgidoycrw6s3guetge3kvrvynppqjmvqsosmtbmgqasa6vmsf6a.b32.i2p/i2pbote.xpi2p"><font face="Arial, sans-serif">http://tjgidoycrw6s3guetge3kvrvynppqjmvqsosmtbmgqasa6vmsf6a.b32.i2p/i2pbote.xpi2p</font></a></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">(
<a href="http://i2pbote.i2p/i2pbote.xpi2p">http://i2pbote.i2p/i2pbote.xpi2p</a>
might work, too, if you have i2pbote.i2p in your address book or a
subscription )</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">in the
&#8220;Plugin Installation Download Url&#8221; line, then hit &#8220;Install
Plugin&#8221;. Wait
until your sidebar says plugin installed and started. </font>
</p>
<p style="line-height: 150%;"><br>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">In order
to update your I2P-Bote instance, click 'Update' under I2P-Bote on
the I2P Client Configuration page at
<a href="http://localhost:7657/configclients.jsp">http://localhost:7657/configclients.jsp</a></font></p>
<p style="line-height: 150%;"><br><br>
</p><p style="line-height: 150%;"><font face="Arial, sans-serif"><u>2.2.
Using I2P-Bote</u></font></p>
<p style="line-height: 150%;"><br><font face="Arial, sans-serif">On your
router console <a href="http://127.0.0.1:7657/">http://127.0.0.1:7657/</a>
click on <span style="font-style: italic;">SecureMail</span> on the upper left (in the sidebar). Now you are
on I2P-Bote's web interface.</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">After
starting I2P-Bote (by default it is set to start automatically when
your I2P router starts up) it takes a bit more than three minutes for
everything to be up and running. </font>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">So have a
look at 'Network Status' on the left. It should state 'Connected'.</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">If you
want to use I2P-Bote for yourself, you first need to create an
identity.</font>
</p>
<p style="line-height: 150%;"><br><br>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif"><u>2.2.1
Creating an Identity</u></font></p>
<p style="line-height: 150%;"><br><font face="Arial, sans-serif">Click on
'Identities' on the left, then hit the &#8220;New Identity&#8221; button.</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">Enter at
least a 'Public Name' and hit 'Create'. That's all that's needed to
create an identity.</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">The
public name is the name you see for this identity (useful in case you
have different identities for different sets of users you communicate
with or different purposes) and it will be sent as &#8220;sender's name&#8221;
to the mail's recipient. There is no need for Public Name to be
unique. </font>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">(As you
can choose any name here &#8211; anyone could call himself HungryHobo
there &#8211; it is not suited to be used by the recipient for telling if
two mails come from the same sender. This is why the name saved in
the local addressbook (there can only by one name per destination
key) is displayed, if there is any, and you will see a green mark in
the &#8220;Know&#8221; column, stating it is the locally known name. If there
is no entry for a destination in local addressbook, the name
specified by the sender will be displayed with a prefixed [UNK] in
the mail clients). [<font color="#ff0000">POP3 NOT YET IMPLEMENTED</font>]</font></p>
<p style="line-height: 150%;"><br><font face="Arial, sans-serif">You can
also fill out the other fields, if you like:</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">Description
&#8211; this field is kept locally. It's just for your convenience: If
you want to add some additional information for yourself about that
identity, you can enter it here. </font>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">Email
Address &#8211; this field is not used yet.<font color="#ff0000"><br></font></font><br><font color="#000000"><font face="Arial, sans-serif">Choose
from one of the given encryption algorithms. If in doubt, stick to
the defaults.</font></font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">You click
on the name of one of your identities and copy the long key displayed
under 'Email Destination'. This is your I2P-Bote e-mail address. If
you want anybody to be able to send you a bote mail, he need to be
given this long key.</font><br>
<br><font face="Arial, sans-serif">Now you
can send and receive I2P-Bote mails.</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">But you
should also have a look at your I2P-Bote settings and see if they fit
your needs.</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">(You can
also create various identities and assign different settings to each
of them.)</font></p>
<p style="line-height: 150%;"><br></p><p style="line-height: 150%;"><font face="Arial, sans-serif"><u><br>
2.2.2
Sending and Receiving E-Mails</u></font></p>
<p style="line-height: 150%;"><br>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">You need
to have the I2P-Bote e-mail destination key of the user you to whom
you want to send a bote mail. </font>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">In order
to send a message, click on 'New', choose your own sender identity or
'Anonymous' under &#8220;From&#8221; and enter the recipient's e-mail
destination key or alternatively an address in the &#8220;To:&#8221; line.</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">Alternatively,
you can hit the &#8220;Addr. Book&#8221; button right under this very line,
in order to chose from e-mail dests stored locally in your address
book: Mark the user(s) to which you want your mail to be sent and hit
the &#8220;Add Recipients&#8221; button.)</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">You can
add several recipients and change the 'To:' to 'CC:' or 'BCC:'.</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">The &#8220;+&#8221;
button adds additional recipient lines.</font></p>
<p style="line-height: 150%;"><br>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">Now write
your bote mail and hit 'Send' for sending it, or 'Save' in order to
store it as a draft into your 'drafts' folder or any user-defined
folder. [<font color="#ff0000">not yet implemented</font>]</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">Hitting
&#8220;Send&#8221; will place your e-mail into the Outbox folder and you can
go on using I2P-Bote, e.g. writing another e-mail, or simply do other
things. I2P-Bote is now sending your e-mail. Once it is sent, it's
automatically removed from Outbox and stored into your Sent folder.
This means, your mail is entirely on the way to its destination
(unless you have set a delay time, which is disabled by default).</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">In
I2P-Bote e-mails are automatically signed (unless send without any
sender identity).<br>
You can
also send e-mails without specifying <i>any </i><span style="font-style: normal;">sender
identity/destination/address, just select &#8220;Anonymous&#8221; in the
scroll-down menu &#8220;From:&#8221;.</span></font></p>
<p style="line-height: 150%;"><br><font face="Arial, sans-serif">In the
default settings I2P-Bote will automatically check for new mails, and
all you need to do in order to see if you got e-mails is look into
your Inbox (link 'Inbox' on the left).<br>
You can
force a manual check by clicking the 'Check Mail' button. This is a
global checking, that tries to fetch new mails for all of your
identities, except for those you have excluded from global checking.
[<font color="#ff0000">not yet implemented</font>]</font></p>
<p style="line-height: 150%;"><br><font face="Arial, sans-serif">The
number of unread e-mails is shown in parenthesis next to the folder's
name in the sidebar. </font>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">Click on
&#8220;<span style="font-style: normal;">Inbox&#8221;</span> to have a list of
received e-mails displayed. You will see two columns with x's or
green checks. Those show you if a mail contains a valid signature and
is thus authentic (Sig) and if the sender's e-mail destination key is
locally known, i.e. in your addressbook (Know). Hence, two green
checks next to a mail entry mean that you already know that e-mail
identity and that the mail is signed by that identity. </font>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">If you
have a certain name in your address book and you get a mail from an
identity with that name, yet Know is <i><u>not</u></i><i> </i><span style="font-style: normal;">displaying
a green check, then it is a </span><i><u>different</u></i><span style="font-style: normal;">
destination that sent and signed this mail; he simply has chosen the
same name you have chosen for one of your contacts.</span></font></p>
<p style="font-style: normal; line-height: 150%;"><font face="Arial, sans-serif">Is
there a green check mark for &#8220;Sig&#8221;, then the mail is correctly
signed by the sender and you may add it to your addressbook under a
different name, which now will be displayed as the sender.</font></p>
<p style="font-style: normal; line-height: 150%;"><font face="Arial, sans-serif">Of
course, a mail without sender destination (&#8220;Anonymous&#8221; is
displayed as sender) will have two x's.</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">Clicking
one of the e-mails displayed in your inbox will open the mail. </font>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">The same
applies to all other folders.</font></p>
<p style="line-height: 150%;"><br><font face="Arial, sans-serif">(Due to
the distributed nature of I2P-Bote, sending as well as checking for
and retrieving e-mails takes a few minutes. With mail routes
activated respectively more. But you need not keep the browser open
for that, simply leave I2P-Bote running as a background process &#8211;
this is also benefits your anonymity)</font></p>
<p style="line-height: 150%;"><br>
</p>
<p style="line-height: 150%;"><br><font face="Arial, sans-serif"><u>2.2.3
Local Address Book</u></font></p>
<p style="line-height: 150%;"><br><font face="Arial, sans-serif">If you
have the I2P-Bote e-mail key from somebody you want to write to more
frequently, it is handy to store that key locally into your address
book (link on the left), specify a name of your own choosing for this
contact and paste his mail destination in the corresponding line,
<font color="#000000">then save.</font></font></p>
<p style="line-height: 150%;"><font color="#000000"><font face="Arial, sans-serif">You
should normally save destinations to your addressbook, so that next
time you get a mail from the same sender it will be shown to be from
the same, locally known sender (&#8220;Loc&#8221; is checked) and a mail sent
by someone else who is just using the same user name will be marked
as NOT known locally (an x in web-UI's 'Know' column or [UNK] before
the sender address in POP3), so you know it's a new/different one. </font></font>
</p>
<p style="line-height: 150%;"><br><br>
<font face="Arial, sans-serif"><u>2.2.4
Settings (and what they mean)</u></font></p>
<p style="line-height: 150%;"><br><font face="Arial, sans-serif">Under
settings you can choose the I2P-Bote interface's <i>language</i>
(currently English or German) and decide whether even with a
non-English language setting everything that will be automatically
added to an e-mail when replying will nonetheless stay in English, so
that the recipient does not know your I2P-Bote is set to a different
language.<br>Otherwise the recipient could guess about your
nationality which would decrease your anonymity. </font>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">Here you
can also adjust the interval for <i>automatic checking</i> of e-mails
and decide whether or not to send any <i>time stamp</i> with your
mails, indicating date and time when the mail was sent. The time
stamps are always in UTC.</font></p>
<p style="font-style: normal; line-height: 150%;"><font face="Arial, sans-serif">(When
using mail routes, the timestamps are automatically disabled.) [<font color="#ff0000">not
yet implemented</font>]</font></p>
<p style="font-style: normal; line-height: 150%;"><br>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif"><i>automatic
checking for e-mails:</i></font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">For more
comfort there is the &#8220;Check for mail every XX minutes&#8220; option. </font>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">Here you
can specify how often your I2P-Bote app should try to fetch unread
mails for your identities. This can be set on a per-identity basis
[<font color="#ff0000">not yet implemented</font>]</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">If you
specify a random offset, then it will not check _exactly_ every XX
minutes, but rather every (XX+-offset*XX)minutes, i.e. after a
randomly chosen time between (1-offset)XX minutes and (1+offset)XX
minutes. [<font color="#ff0000">not yet implemented</font>]</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">You can
also totally disable the automatic checking for a given identity.</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">(If you
are not sure about these settings, the defaults should be ok for
you.)</font></p>
<p style="line-height: 150%;"><br><br>
<font face="Arial, sans-serif"><i>Mail
routes</i> are chains of I2P-Bote nodes acting as relays/routers for
other peers and obeying to per-hop delays, thus providing the
high-latency transport for increased anonymity. </font>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">You can
specify the number of nodes (here called hops) that should be chained
to form a mail route. Then each of the e-mail packets sent by the
identity that has mail routes enabled will go through a mail route of
n hops before being stored. You can set a delay for each hop
individually, as no hop should know the time a packet will wait at
the next hop, making the timing unpredictable. [<font color="#ff0000">individual
per-hop and per-identity setting of delays not yet implemented</font>]</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">As delay
you can specify a time frame (e.g. 60-600 minutes) &#8211; then a random
wait time between the two values will be chosen for the packet at
that hop &#8211; or a fix time, then the packet will be forwarded at that
fix time, e. g. noon UTC, no matter when it arrived. [<font color="#ff0000">fix
time not yet implemented</font>] </font>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">(When
using mail routes, the timestamps are automatically disabled. [<font color="#ff0000">not
yet implemented</font>])</font></p>
<p style="line-height: 150%;"><br><font face="Arial, sans-serif">Under
&#8220;<i>mínimo en el bote</i>&#8221; (minimum threshold number of relay packets that
will be sent) you can specify a threshold. As your node can only act
reliably as a mix, if there are enough foreign packets to mix and to
blend own packets with, it will accumulate messages who's delay time
is over until reaching this lower limit. Only when it is surpassed,
your node starts sending them out in random order. [<font color="#ff0000">Not
yet implemented</font>]</font><br><br>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif"><i>exclude
identity from global checking</i> [<font color="#ff0000">Not yet
implemented</font>]</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">If you
enable this option for one of your identities, then this one will not
be affected by the global manual checking for mails nor by any global
automatic mail checking.</font></p>
<p style="line-height: 150%;"><br><br>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif"><u>2.2.5
E-mails to and from the Internet</u><span style="text-decoration: none;">
[</span><font color="#ff0000"><span style="text-decoration: none;">NOT
YET FULLY IMPLEMENTED!</span></font><span style="text-decoration: none;">]</span></font></p>
<p style="line-height: 150%;"><br><font face="Arial, sans-serif">In order
to be able to send bote mails to the internet and to receive e-mails
from the internet with your I2P-Bote application, you must first
register with an appropriate mail gateway. Currently there is only
one: postman. </font>
</p>
<p style="line-height: 150%;"><br><font face="Arial, sans-serif">1) First, go to:
<a href="http://hq.postman.i2p/?page_id=16">http://hq.postman.i2p/?page_id=16</a> and
register an account. If you already have an account or if you have
just created one as described, proceed with #2.</font></p>
<p style="line-height: 150%;"><br><font face="Arial, sans-serif">2) For an
existing account you can add your I2P-Bote mail destination, so that
e-mails coming from the internet are forwarded to your I2P-Bote app.
To do so go to: <a href="http://hq.postman.i2p/?page_id=74">http://hq.postman.i2p/?page_id=74</a>
and provide the requested information.</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">Now all
e-mails sent to that address (<a href="mailto:name@i2pmail.org">name@i2pmail.org</a>
from the outer internet or <a href="mailto:name@mail.i2p">name@mail.i2p</a>
for mails from other postman subscribers) will be forwarded via the
I2P-Bote network to your I2P-Bote app.</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">(N.B.
When using the <a href="mailto:name@mail.i2p">name@mail.i2p</a> or
<a href="mailto:name@i2pmail.org">name@i2pmail.org</a> addresses
instead of the long addresses, e-mails are no longer end-to-end
encrypted. Therefore, it is recommended to exchange the I2P-Bote mail
destination keys for communicating within the network. Postman has
offered high quality services in I2P for quite a while already, but
be aware that it's a centralized point that might go offline one day,
or worse be taken over by an evildoer that will manipulate mails. As
for network-internal e-mail communication, I2P-Bote makes sure that
if you use the address keys, nobody can tamper with the mails you
send or receive.)</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">If you
want not only to receive e-mails from the internet, but also enable
sending e-mails from I2P-Bote to the internet, you must provide your
I2P-Bote client with the gateway's mail destination key, so that your
I2P-Bote knows where to send those mails to.<br>
You can
do this under &#8220;settings&#8221;. </font><font face="Arial, sans-serif">This
gateway will allow I2P-Bote users to communicate with the standard
e-mail users on the internet as well as with users of postman's
classical i2pmail service (@mail.i2p).</font><br>
<font face="Arial, sans-serif"><br>
In order
to fight abuse, there will be a limitation of the number of e-mails
you can send out to the internet; just like for normal postman mail
service users: If an I2P-Bote user exceeds the quota with outgoing
e-mails, the additional e-mails will be sent back as bounce.</font></p>
<p style="line-height: 150%;"><br><br>
<br>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif"><b>3.
Considerations about Anonymity</b></font></p>
<p style="line-height: 150%;"><br>
<font face="Arial, sans-serif">Don't
send identifying information about you (name, address, geographic
location, time zone, age, websites you have just visited or blogged
about, user names, ip numbers, I2P router id, I2P-Bote id, social
security number, credit card number, &#8230;, copies of your passport,
driver's license, home rental contract, photos &#8211; nude or with
clothes &#8211;, documents that contain your username in author's
settings, and many many more)!</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif"><br>
If
possible, </font>
</p>
<ul><li><p style="line-height: 150%;"><font face="Arial, sans-serif">leave
I2P-Bote running 24/7,</font></p>
</li><li><p style="line-height: 150%;"><font face="Arial, sans-serif">use
mailroutes with randomized per-hop delays and/or per-hop fixed send
times, [<font color="#ff0000">not yet </font><font color="#ff0000"><i>fully</i></font><font color="#ff0000">
implemented</font>]</font></p>
</li><li><p style="line-height: 150%;"><font face="Arial, sans-serif">use
a long check interval,</font></p>
</li><li><p style="line-height: 150%;"><font face="Arial, sans-serif">use
a long local delay for own packets,</font></p>
</li><li><p style="line-height: 150%;"><font face="Arial, sans-serif">use
a big check interval randomization. [<font color="#ff0000">not yet
implemented</font>]</font></p>
</li></ul>
<p style="line-height: 150%;"><br><font face="Arial, sans-serif">You can
suppress the sending of date and time in the e-mails' header.</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">When you
reply to an e-mail, certain markers, such as &#8220;Re: [subject of the
mail you're replying to]&#8221; or &#8220;[username] wrote:&#8221;. Those are
different for the languages you can chose from in your language
settings. However, if you don't want the recipient to know what
language you have set, you can suppress translation of these markers,
so that they will be in English, no matter what you language setting
is. In order to do so, mark &#8220;Use English for text added to outgoing
email ('Re:', 'wrote:', etc.) &#8220;</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">Be
careful with the contents you send! Don't include personal
information or information that only you can possess. Don't write I'm
going to bed now, it's late when including time stamps.<br>
The
language in which your write your e-mails, your style and
formulations can also be of interest for an attacker.</font></p>
<p style="line-height: 150%;"><br><font face="Arial, sans-serif">I2P-Bote
also offers the possibility to use different e-mail
identities.<br>Suppose one of you contacts learns about your
identity, as you forgot to erase identifying information in a secret
document you have sent to him. Now if this e-mail's recipient was to
collaborate with others you are in contact with, he could tell them
the real world identity belonging to the Bote address he knows from
you. Thusly, if you communicate with those others using the same Bote
address, they will know who you are.<br>Not so, if you used a
different address for sending mails to them.</font></p>
<p style="line-height: 150%;"><br><br>
<font face="Arial, sans-serif"><b>4.
Technical Concept</b></font></p>
<p style="line-height: 150%;"><br>
<font face="Arial, sans-serif">I2P-Bote
is an end-to-end encrypted, network-internal, fully decentralized
(i.e. serverless) e-mail system. It supports different identities and
does not expose e-mail headers. Currently, it is still alpha software
and can only by accessed via web console. It soon will have POP3
support, and it is planned to guarantee additional anonymity by
providing a high-latency transport option. All bote-mails are
automatically end-to-end encrypted, so that there's no need to set up
extra e-mail encryption (though you can do that), and bote-mails will
be authenticated automatically. As it is decentralized, there is no
e-mail server that could link different e-mail identities as
communicating with each other (<i>profiling</i>): Even the nodes
relaying the mails will not know the sender and apart from sender and
receiver, only the end of the high-latency mail tunnel and the
storing nodes will know to whom (anonymous identity) the mail is
destined. The original sender can have gone offline, long before the
mail becomes available on the other side. This adds on the degree of
anonymity that can be reached with I2P-Bote. For those who do not
want high delays: All these settings are be user-adjustable, so each
user decides on how much anonymity he wants.</font></p><font face="Arial, sans-serif"><font color="#000000"><br>I2P-Bote
nodes store encrypted e-mails into a Kademlia DHT. Therefore, an
e-mail can be sent through a number of other
nodes (relays) for increased security, or directly to<font face="Arial, sans-serif"><font color="#000000"> a
set of storage nodes for faster delivery. The same applies to
retrieving email.<br>(When using mail routes, timestamps are
automatically disabled.)<br></font>[Retrieving via relays not yet
implemented]</font></font></font>
<p><br>
</p>
<p style="line-height: 150%;"><font color="#000000"><font face="Arial, sans-serif">All
nodes are created equal: There are no "supernodes" or
designated relay/storage nodes. Everybody acts as a potential relay
and storage node. The maximum amount of disk space used for
relayed/stored email packets can be configured by the user.</font></font></p>
<p style="line-height: 150%;"><font color="#000000"><font face="Arial, sans-serif">Before
an email is sent to a relay, it is broken up into packets and
encrypted with the recipient's public key. These packets are stored
redundantly in a distributed hash table (DHT).</font></font></p>
<p style="line-height: 150%;"><font color="#000000"><font face="Arial, sans-serif">They
are kept for at least 100 days, during which the recipient can
download them.</font></font></p>
<p style="line-height: 150%;"><font color="#000000"><font face="Arial, sans-serif">Relay
packets also expire after 100 days or more.</font></font></p>
<p style="line-height: 150%;"><font color="#000000"><font face="Arial, sans-serif">If
a node runs out of email storage space, and there are no old packets
that can be deleted, the node refuses storage requests.</font></font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">Furthermore,
I2P-Bote sanitizes the mail headers and does not allow any unneeded
information to be transmitted, thus allowing the use of e-mail
clients without prior checks of what this client sends in the mail
headers. [<font color="#ff0000">POP3 not yet implemented</font>]</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">All the
encryption, path choosing and profiling is done locally so that there
is no trusted party involved.</font></p>
<p style="font-weight: normal; line-height: 150%;"><font face="Arial, sans-serif">Using
I2P-Bote appropriately, that means keeping in mind the considerations
given above and showing some common sense, nobody will be able to
find out who or where you are. And if you are already being observed
and your internet connection sniffed, the observer will not be able
to find out what you send or receive or to whom you are sending to or
receiving from or where your contacts are located.</font></p>
<p style="line-height: 150%;"><br></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif"><br>
Let's go
a bit more into detail:</font></p>
<p style="line-height: 150%;"><br>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif"><u>What
I2P-Bote <i>does hide</i>:</u></font></p>
<p style="line-height: 150%;"><br><font face="Arial, sans-serif"><i>I2P-Bote
hides</i> both, the identity and location of sender and receiver, as
well as those of intermediary nodes (relays and storing nodes), the
content of your mails, their size, the number of mails you send.</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">Only the
recipient can know the sender's bote mail destination, and if he
choses not to send his destination, not even the recipient will know
it.</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">Even if
you send time stamps, your time zone will not be disclosed.</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">Furthermore,
I2P-Bote hides ...</font></p>
<p style="line-height: 150%;">&nbsp;&nbsp;&nbsp; <font face="Arial, sans-serif">- the
fact that you run I2P-Bote</font></p>
<p style="line-height: 150%;">&nbsp;&nbsp;&nbsp; <font face="Arial, sans-serif">- the
fact that you send a mail</font></p>
<p style="line-height: 150%;">&nbsp;&nbsp;&nbsp; <font face="Arial, sans-serif">- the
fact that you receive a mail</font></p>
<p style="line-height: 150%;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face="Arial, sans-serif">and
hence</font></p>
<p style="line-height: 150%;">&nbsp;&nbsp;&nbsp; <font face="Arial, sans-serif">- the
time you send a mail</font></p>
<p style="line-height: 150%;">&nbsp;&nbsp;&nbsp; <font face="Arial, sans-serif">- the
time you receive a mail</font></p>
<p style="line-height: 150%;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face="Arial, sans-serif">and</font></p>
<p style="line-height: 150%;">&nbsp;&nbsp;&nbsp; <font face="Arial, sans-serif">- the
upper limit of number of mails an unknown user receives, - nota bene:
an abstract <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; user, no concrete one, just concluding its
existence from the existence of the mail identity<br>&nbsp;&nbsp;&nbsp; - as he
could always have more than one e-mail identity; and the lower limit
as an <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; identity also sends out test and dummy messages</font></p>
<p style="line-height: 150%;"><br><br>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif"><u>What
I2P-Bote <i>hides partially</i>: </u></font>
</p>
<p style="line-height: 150%;"><br><font face="Arial, sans-serif">The
I2P-Bote address of the recipient will only be known to sender and
recipient(s). </font>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">In case
of multiple recipients, each one will see all other recipients that
the mail was addressed to via &#8220;To:&#8221; or &#8220;CC:&#8221;. </font>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">All
entries that were under &#8220;BCC:&#8221; will only be visible to the sender
and this very recipient. </font>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">The time
an sent time will, if at all, only be visible to sender and
recipient.</font></p>
<p style="line-height: 150%;"><br><br>
<font face="Arial, sans-serif"><u>What
I2P-Bote <i>can hide</i> optionally: </u></font>
</p>
<p style="line-height: 150%;"><br><br>
<font face="Arial, sans-serif">- If
mail routes are use, the time a bote mail is sent</font></p>
<p style="line-height: 150%;"> <font face="Arial, sans-serif">- If
mail routes are used, the time a bote mail is fetched. [<font color="#ff0000">not
yet implemented</font>]</font></p>
<p style="line-height: 150%;"> <font face="Arial, sans-serif">- If the
sender suppresses timestamps only the sender himself will know when
he sent a<br> mail.</font></p>
<p style="line-height: 150%;"><br><br>
<font face="Arial, sans-serif"><u>What
I2P-Bote <i>cannot hide</i>: </u></font>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif"><br>
I2P-Bote
cannot hide the frequency a given identity checks for new mails nor
the number of mails a given identity receives.</font></p>
<p style="line-height: 150%;"><br><br>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif"><br>
Not even
for bootstrapping I2P-Bote depends on a central node, as it uses
Seedless.<br>
</font></p>
<p style="line-height: 150%;"><br><br>
<br>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif"><b>5
Terminology/Glossary of Terms:</b></font></p>
<p style="line-height: 150%;"><br>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif"><i><u>I2P-Bote
(router/node) id</u>:</i></font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">This is
the id an I2P-Bote router is known as. It is used for contacting this
router, for storing, relaying and fetching mails, but also used in
the hop-to-hop encryption and for simply contacting it via I2P, as it
is at the same the I2P-Bote router's I2P tunnel destination. It is
displayed to represent an I2P-Bote node in the stats.</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">So the
router id corresponds to the I2P destination (the address of an
I2P-Bote node on the I2P network - there is no need to know it unless
you are having problems connecting to other I2P-Bote nodes.)</font></p>
<p style="font-weight: normal; line-height: 150%;"><br><font face="Arial, sans-serif"><i><u>I2P-Bote
e-mail destination:</u></i></font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">The
I2P-Bote e-mail destination (key) is an identifier by which somebody
can be reached via I2P-Bote, so as the name states: an e-mail
destination. Thus it is for I2P-Bote what an e-mail address is for
standard e-mail system: The e-mail destination is the actual address
for sending e-mails, for storing them into and for fetching them from
the DHT. <br>At the same time it used for the end-to-end encryption
of e-mails, header information and attachments. </font>
</p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">An
I2P-Bote e-mail destination is a Base64 string containing a public
encryption key and a signature verification key. Example:</font></p>
<p style="line-height: 150%;">
</p>
<p style="line-height: 150%;">
<font face="Arial, sans-serif">uQtdwFHqbWHGyxZN8wChjWbCcgWrKuoBRNoziEpE8XDt8koHdJiskYXeUyq7JmpG</font></p>
<p style="line-height: 150%;">
<font face="Arial, sans-serif">In8WKXY5LNue~62IXeZ-ppUYDdqi5V~9BZrcbpvgb5tjuu3ZRtHq9Vn6T9hOO1fa</font></p>
<p style="line-height: 150%;">
<font face="Arial, sans-serif">FYZbK-FqHRiKm~lewFjSmfbBf1e6Fb~FLwQqUBTMtKYrRdO1d3xVIm2XXK83k1Da</font></p>
<p style="line-height: 150%;">
<font face="Arial, sans-serif">-nufGASLaHJfsEkwMMDngg8uqRQmoj0THJb6vRfXzRw4qR5a0nj6dodeBfl2NgL9</font></p>
<p style="line-height: 150%;">
<font face="Arial, sans-serif">HfOLInwrD67haJqjFJ8r~vVyOxRDJYFE8~f9b7k3N0YeyUK4RJSoiPXtTBLQ2RFQ</font></p>
<p style="line-height: 150%;">
<font face="Arial, sans-serif">gOaKg4CuKHE0KCigBRU-Fhhc4weUzyU-g~rbTc2SWPlfvZ6n0voSvhvkZI9V52X3</font></p>
<p style="line-height: 150%;">
<font face="Arial, sans-serif">SptDXk3fAEcwnC7lZzza6RNHurSMDMyOTmppAVz6BD8PB4o4RuWq7MQcnF9znElp</font></p>
<p style="line-height: 150%;">
<font face="Arial, sans-serif">HX3Q10QdV3omVZJDNPxo-Wf~CpEd88C9ga4pS~QGIHSWtMPLFazeGeSHCnPzIRYD</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif"><br>I2P-Bote
router/node id and I2P-Bote e-mail destinations look similar, but are
completely independent of each other.</font></p>
<p style="line-height: 150%;"><br><font face="Arial, sans-serif"><i><u>E-mail
address:</u></i></font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">E-mail
addresses in I2P-Bote are shortcuts for e-mail destinations.</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">The
e-mail address &lt;--&gt; e-mail destination mappings are stored in
two places: the local address book and the distributed address
directory [<font color="#ff0000">the latter not yet implemented</font>].</font></p>
<p style="line-height: 150%;"><br><font face="Arial, sans-serif"><i><u>I2P-Bote
e-mail identity:</u></i></font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">The
I2P-Bote e-mail identity is a set of an I2P-Bote e-mail destination
key, the corresponding private keys and a name given to it by the
user. This name will be sent with the destination key if you do not
suppress sending information about the sender.</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">However
it will only be displayed for the recipient in case he does not have
a name for this destination in his local address book.</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">So
technically speaking, an e-mail identity consists of four things:</font></p>
<p style="line-height: 150%;"> <font face="Arial, sans-serif">* an
e-mail destination (i.e. two public keys)</font></p>
<p style="line-height: 150%;"> <font face="Arial, sans-serif">* two
private keys for the e-mail destination</font></p>
<p style="line-height: 150%;"> <font face="Arial, sans-serif">* a
public name which can be shown to other people in e-mails</font></p>
<p style="line-height: 150%;"> <font face="Arial, sans-serif">* a
description which is not shown to anybody but you.</font></p>
<p style="line-height: 150%;"> <font face="Arial, sans-serif">(It
helps you remember which e-mail identity you use for which purpose.)</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">An e-mail
identity is not required for sending emails (although then only
"Anonymous" can be selected for the "sender"
field).</font></p>
<p style="line-height: 150%;"><br></p><p style="line-height: 150%;"><font face="Arial, sans-serif"><span style="text-decoration: underline; font-style: italic;">Mail
routes</span>:</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">Mail
routes are an additional high-latency transport for I2P-Bote. For
this, a chain of I2P-Bote nodes is built, acting as relays/routers
for packets and obeying to individual per-hop delays; [<font color="#ff0000">still
no individual setting for delays implemented</font>]</font></p>
<p style="line-height: 150%; font-weight: bold;"><font face="Arial, sans-serif"><br>
BEWARE!</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">If you
choose this option - especially with many hops and / or long delay
times, don't be surprised if your mail does not reach its destination
too soon. It will, of course, take longer &#8211; up to several days!</font><br>
</p>
<p style="line-height: 150%;"><br>
<br>
</p>
<p style="line-height: 150%; font-weight: bold; font-style: italic;"><font face="Arial, sans-serif">6.&nbsp; Credits</font></p>
<p style="line-height: 150%;">
</p><p style="line-height: 150%;"><font face="Arial, sans-serif">Idea &amp;
technical concept: HungryHobo, Mixxy</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">Implementation:
HungryHobo</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">Plugin
support: zzz, HungryHobo</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">User
interface: HungryHobo</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">Seedless
integration: sponge</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">German
translation: HungryHobo</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">French
translation: albat, Redzara, Mixxy</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">Spanish
translation: Mixxy</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">Alpha
testing: HungryHobo, Mixxy, Returning Novice, sponge, and many others</font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif"><b><br>
<br>
</b></font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif"><b>7.
Technical Details</b></font></p>
<p style="line-height: 150%;"><font face="Arial, sans-serif">-- see
techdoc.txt --</font></p>
<p style="line-height: 150%;"><br><br>
</p>
<p style="line-height: 150%;"><br><br>
</p>
<p style="line-height: 150%; font-weight: bold; font-style: italic;"><font face="Arial, sans-serif">ENJOY THE
BOTE FEELING!!</font></p>
</body></html>

View File

@ -1,862 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=utf-8">
<TITLE>Guide de l'utilisateur</TITLE>
<META NAME="GENERATOR" CONTENT="LibreOffice 3.3 (Linux)">
<META NAME="CREATED" CONTENT="0;0">
<META NAME="CHANGED" CONTENT="20110407;10200100">
<STYLE TYPE="text/css">
<!--
@page { margin: 2cm }
P { margin-bottom: 0.21cm }
A:link { so-language: zxx }
-->
</STYLE>
</HEAD>
<BODY LANG="fr-FR" DIR="LTR">
<P ALIGN=CENTER STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif"><B>Guide
de l'utilisateur I2P-Bote</B></FONT></P>
<P STYLE="line-height: 150%"><BR><BR>
</P>
<P STYLE="line-height: 150%"><BR><BR>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif"><B>1.
Introduction</B></FONT></P>
<P STYLE="line-height: 150%"><BR><BR><FONT FACE="Arial, sans-serif">I2P-Bote
est une application de messagerie sécurisée pour I2P, à anonymat
élevé et simple à utiliser. C'est un système sans serveur,
entièrement décentralisé, qui constitue et génère un réseau
pairs à pairs sur le réseau d'anonymisation I2P, lui-même conçu
selon les règles de l'art ; l'adjonction optionnelle d'un transport
à la façon de Mixminion le débarrasse des faiblesses intrinsèques
des réseaux à basse latence.</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">I2P-Bote
tire donc le meilleur de l'anonymat fourni par I2P, à quoi il
apporte son propre niveau supplémentaire d'anonymat par l'ajout
d'une autre couche d'anonymisation (sur-réseau).</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Ce
concept d'anonymat étagé est ce rend I2P-Bote si souple :</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Vous
pouvez le configurer pour être extrêmement anonyme et lent, ou
moins anonyme mais plus rapide et efficace. Dans tous les cas,
I2P-Bote<FONT COLOR="#000000"> procure toujours un très bon anonymat
à l'expéditeur et au destinataire, ainsi qu'un cryptage de bout en
bout*. </FONT></FONT>
</P>
<P STYLE="line-height: 150%"><FONT COLOR="#000000"><FONT FACE="Arial, sans-serif">I2P-Bote
permet de rendre vos communications encore plus anonymes, en activant
les routes à haute latence au prix d'une baisse de performance,
cependant. Les utilisateurs qui souhaitent que leurs messages
arrivent au plus vite peuvent désactiver les routes de messages et
utiliser l'envoi «&nbsp;direct&nbsp;» sur I2P. Ils sont ainsi
assurés de bénéficier au minimum de l'anonymat d'origine des
connexions I2P. </FONT></FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif"><FONT COLOR="#000000">Pour
atteindre une ergonomie élevée, nous l'avons conçu pour qu'il
puisse fonctionner avec les logiciels clients de messagerie standards
tels que Thunderbird, Evolution ou Kmail, sans avoir à se préoccuper
des informations supplémentaires que ces applications envoient dans
leurs en-têtes [</FONT><FONT COLOR="#ff0000">PAS ENCORE IMPLÉMENTÉ</FONT><FONT COLOR="#000000">].
De plus, il y a une interface web qui vous permet d'envoyer et de
recevoir des messages, et de gérer vos préférences et vos
identités.</FONT></FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">I2P-Bote
est facile à utiliser : si vous n'utilisez pas déjà I2P, installez
le depuis <A HREF="http://www.i2p2.de/">http://www.i2p2.de</A> puis
installez le greffon I2P-Bote comme expliqué dans ce manuel. Sinon,
continuez la lecture!**</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif"><I>La
version actuelle est la 0.2.6.</I></FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">*Sauf si
vous recevez ou envoyez des messages de/vers l'Internet classique,
TOUS les messages le corps, les pièces jointes et les en-têtes
à part l'adresse du destinataire (objet, date et heure,
expéditeur,...) sont automatiquement cryptés de bout en bout de
façon transparente. L'adresse du destinataire n'est visible que par
le dernier nœud de la route qui stocke les paquets dans le réseau
kad, ainsi que par les nœuds de stockage, mais ils ne peuvent pas
lire les messages, ni qui l'a envoyé, ni qui le relèvera.</FONT></P>
<P STYLE="line-height: 150%"><BR><BR>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">**Bien
sûr, vous pouvez aussi le compiler à partir des sources.</FONT></P>
<P STYLE="line-height: 150%"><BR><BR>
</P>
<P STYLE="line-height: 150%"><BR><BR>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif"><B>2.
Procédures</B></FONT></P>
<P STYLE="line-height: 150%"><BR><BR>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif"><U>2.1.
Installation</U></FONT></P>
<P STYLE="line-height: 150%"><BR><BR>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Pour
installer I2P-Bote, rendez-vous en bas de la page de configuration du
client I2P sur </FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif"><A HREF="http://localhost:7657/configclients.jsp">http://localhost:7657/configclients.jsp</A>
</FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">en entrez
:</FONT></P>
<P STYLE="line-height: 150%"><A HREF="http://tjgidoycrw6s3guetge3kvrvynppqjmvqsosmtbmgqasa6vmsf6a.b32.i2p/i2pbote.xpi2p"><FONT FACE="Arial, sans-serif">http://tjgidoycrw6s3guetge3kvrvynppqjmvqsosmtbmgqasa6vmsf6a.b32.i2p/i2pbote.xpi2p</FONT></A></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">(<A HREF="http://i2pbote.i2p/i2pbote.xpi2p">http://i2pbote.i2p/i2pbote.xpi2p</A>
marche aussi, si vous avez i2pbote.i2p dans votre carnet d'adresses
ou un abonnement)</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">dans le
champ «&nbsp;…, URL de téléchargement&nbsp;:&nbsp;», puis
cliquez sur le bouton «&nbsp;Installer le greffon&nbsp;». Attendez
jusque à ce que le panneau de surveillance sur la gauche indique que
le greffon est installé et démarré. </FONT>
</P>
<P STYLE="line-height: 150%"><BR><BR>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Pour
mettre à jour I2P-Bote, dans la même page, cliquez sur «&nbsp;Mise
à jour&nbsp;»dans le cadre dédié au greffon une fois celui-ci
installé.</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif"><U>2.2.
Utiliser I2P-Bote</U></FONT></P>
<P STYLE="line-height: 150%"><BR><FONT FACE="Arial, sans-serif">Dans
la console du routeur <A HREF="http://127.0.0.1:7657/">http://127.0.0.1:7657/</A>
cliquez sur </FONT><FONT FACE="Arial, sans-serif"><I>SecureMail</I></FONT>
<FONT FACE="Arial, sans-serif">en haut à gauche dans le panneau de
surveillance. Vous êtes maintenant dans l'interface web d'I2P-Bote.</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Après
son lancement, I2P-Bote a besoin d'un peu plus de trois minutes pour
être entièrement opérationnel (Il est configuré pour démarrer
automatiquement en même temps que le routeur). </FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Donc,
quand le panneau de surveillance indique «&nbsp;Réseau&nbsp;: OK&nbsp;»,
c'est prêt..</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Si vous
utiliser I2P-Bote pour vous-même, il vous faut créer une identité.</FONT>
</P>
<P STYLE="line-height: 150%"><BR><BR>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif"><U>2.2.1
Création d'une identité</U></FONT></P>
<P STYLE="line-height: 150%"><BR><FONT FACE="Arial, sans-serif">Cliquez
sur «&nbsp;Identités&nbsp;» sur la gauche, puis sur le bouton
«&nbsp;Nouvelle identité&nbsp;».</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Entrez au
moins un «&nbsp;Nom public&nbsp;» et cliquez sur «&nbsp;Créer&nbsp;».
C'est tout ce qu'il faut faire pour créer une identité.</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Le nom
public est le nom que vous voyez pour cette identité (utile quand
vous en avez plusieurs, pour communiquer avec différents groupes
d'utilisateurs ou pour des besoins différents) et il sera envoyé
aux destinataires en tant que «&nbsp;nom d'expéditeur&nbsp;». Il
n'est pas nécessaire qu'il soit unique. </FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">(Comme on
peut choisir ici n'importe quel nom n'importe qui pourrait
choisir ici HungryHobo comme nom public ce champ n'est pas conçu
pour apporter au destinataire la certitude que deux messages
proviennent du même expéditeur. C'est pourquoi c'est le nom
sauvegardé dans le carnet d'adresses local [il ne peut y avoir qu'un
seul nom par clé de destination] qui est affiché dans la boîte de
réception si l'expéditeur en a sélectionné un lors de l'envoi
[envoi non anonyme], et vous verrez alors une marque verte dans la
colonne «&nbsp;Connu&nbsp;» même si le nom public spécifié par
l'expéditeur est différent pour cette sienne identité, indiquant
ainsi qu'il s'agit bien de la destination connue localement. S'il n'y
a pas d'entrée pour une destination donnée dans le carnet
d'adresses, le nom indiqué par l'expéditeur est affiché précédé
du préfixe [UNK] dans les logiciels clients de messagerie) [<FONT COLOR="#ff0000">POP3
N'EST PAS ENCORE IMPLÉMENTÉ</FONT>].</FONT></P>
<P STYLE="line-height: 150%"><BR><FONT FACE="Arial, sans-serif">Vous
pouvez aussi compléter les autres champs, si vous le souhaitez :</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Description
ce champ est conservé localement, pas diffusé. Il ne sert qu'à
vous : si vous voulez ajouter des informations supplémentaires à
votre usage au sujet de cette identité, ce champ est prévu pour
cela. </FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Adresse
e-mail champ pas encore utilisé.</FONT><FONT COLOR="#ff0000"><FONT FACE="Arial, sans-serif"><BR></FONT></FONT><BR><FONT COLOR="#000000"><FONT FACE="Arial, sans-serif">Choisissez
un algorithme de cryptage parmi ceux proposés. Dans le doute, gardez
celui proposé par défaut.</FONT></FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Vous
pouvez cliquer sur une de vos identités et copier la longue clé
affichée sous «&nbsp;Destination de messagerie&nbsp;». C'est votre
adresse e-mail I2P-Bote. Si vous voulez recevoir des messages de
quelqu'un, il faudra la lui transmettre.</FONT><BR><BR><FONT FACE="Arial, sans-serif">Vous
pouvez maintenant envoyer et recevoir des messages I2P-Bote.</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Mais vous
devriez regarder les réglages d'I2P-Bote et vous assurer qu'il sont
conformes à vos besoins.</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">(Vous
pouvez créer plusieurs identités et leur associer des réglages
différents.)</FONT></P>
<P STYLE="line-height: 150%"><BR><BR>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif"><U><BR>2.2.2
Envoyer et recevoir des messages</U></FONT></P>
<P STYLE="line-height: 150%"><BR><BR>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Vous avez
besoin de la clé de destination de message I2P-Bote du correspondant
à qui vous voulez envoyer un message bote. </FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Pour
envoyer un message, cliquez sur «&nbsp;Nouveau&nbsp;», choisissez
votre identité d'expéditeur ou «&nbsp;Anonyme&nbsp;» dans le
champ «&nbsp;De&nbsp;:&nbsp;» et entrez la destination du
destinataire dans le champ «&nbsp;À&nbsp;:&nbsp;», ou une adresse
mail.</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">(Vous
pouvez aussi cliquer sur le bouton «&nbsp;Carnet d'adresses&nbsp;»
tout proche de ce champ, pour choisir une destination stockée
localement dans votre carnet d'adresses&nbsp;: cochez le/les
utilisateur(s) destinataires et cliquez sur le bouton “Ajouter les
destinataires&nbsp;»)</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Vous
pouvez ajouter plusieurs destinataires et changer l'attribut «&nbsp;À&nbsp;:&nbsp;»
pour «&nbsp;Cc:&nbsp;» ou «&nbsp;Cci:&nbsp;».</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Le bouton
«&nbsp;+&nbsp;» ajoute des lignes destinataires supplémentaires.</FONT></P>
<P STYLE="line-height: 150%"><BR><BR>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Rédigez
maintenant votre message bote et cliquez sur «&nbsp;Envoyer&nbsp;»pour...
l'envoyer, ou «&nbsp;Enregistrer&nbsp;» pour le sauvegarder dans
votre dossier «&nbsp;Brouillons&nbsp;» ou tout autre dossier
personnel [<FONT COLOR="#ff0000">pas encore implémenté</FONT>].</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">L'action
d'envoi déplace le message dans le dossier «&nbsp;Boîte d'envoi&nbsp;»
et vous pouvez continuer à utiliser I2P-Bote, par exemple pour
écrire un autre message, gérer votre carnet d'adresses, etc...
I2P-Bote est alors en train d'envoyer votre message. Une fois envoyé,
il est déplacé de la boîte d'envoi vers le dossier «&nbsp;Courriers
envoyés&nbsp;». Votre message est en route (à moins que vous
n'ayez défini un retard, ce qui n'est pas activé pas défaut).</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Dans
I2P-Bote, les messages sont signés automatiquement (sauf ceux que
vous envoyez comme «&nbsp;Anonyme&nbsp;», c'est à dire sans
choisir d'identité d'expéditeur)&nbsp;: en effet, vous pouvez
décider de ne pas choisir d'identité d'expéditeur.</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">De par
les réglages par défaut, I2P-Bote relève le automatiquement
courrier, et tout ce que vous avez à faire pour savoir si vous avez
reçu de nouveaux messages est de regarder dans la boîte de
réception (lien sur la gauche).<BR>Vous pouvez forcer une relève en
cliquant cliquant sur le bouton «&nbsp;Relever le courrier&nbsp;».
C'est une vérification globale qui tente de récupérer les nouveaux
messages destinés à toutes vos identités à l'exception de celles
exclues de cette fonctionnalité [</FONT><FONT COLOR="#ff0000"><FONT FACE="Arial, sans-serif">pas
encore implémenté</FONT></FONT><FONT FACE="Arial, sans-serif">].</FONT></P>
<P STYLE="line-height: 150%"><BR><FONT FACE="Arial, sans-serif">Le
nombre de messages non lus est indiqué entre parenthèses à
proximité du nom du dossier dans le cadre de gauche. </FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Cliquez
sur «&nbsp;Boîte de réception&nbsp;» pour visualiser la liste des
messages reçus. Vous y voyez deux colonnes peuplées de «&nbsp;x&nbsp;»
et de coches vertes. Elles vous indiquent si le message en question
contient un signature valide et qu'il est donc certifié comme
authentique et intègre (Sig), et si la destination de l'expéditeur
est déjà connue, c'est à dire déjà enregistrée dans votre
carnet d'adresses (Connu). Donc, deux coches vertes pour un message
signalent que vous connaissez son expéditeur et que le message a été
signalé par lui. </FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Si vous
avez dans votre carnet d'adresses un certain nom et que vous recevez
un message de la part d'une identité affichant ce nom, alors si la
colonne «&nbsp;Connu&nbsp;» <U>n'indique pas</U> une <SPAN STYLE="font-style: normal">coche
verte, c'est que le message provient d'une destination </SPAN><SPAN STYLE="font-style: normal"><U>différente</U></SPAN>
<SPAN STYLE="font-style: normal">de celle contenue dans votre carnet
d'adresses&nbsp;; son propriétaire a simplement choisi le même nom
que celui d'un de vos contacts déjà enregistré.</SPAN></FONT></P>
<P STYLE="font-style: normal; line-height: 150%"><FONT FACE="Arial, sans-serif">S'il
y a une coche verte dans la colonne «&nbsp;Sig&nbsp;», c'est que le
message est correctement signé et vous pouvez ajouter l'expéditeur
à votre carnet d'adresses sous un nom différent qui sera dès lors
affiché en tant qu'expéditeur.</FONT></P>
<P STYLE="font-style: normal; line-height: 150%"><FONT FACE="Arial, sans-serif">Évidemment,
un message sans destination d'expéditeur (“Anonyme” affiché
comme expéditeur) sera repéré par deux marques «&nbsp;x&nbsp;».</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Un clic
sur un des messages de la liste ouvre celui-ci. </FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Ceci vaut
pour tous les autres dossiers.</FONT></P>
<P STYLE="line-height: 150%"><BR><FONT FACE="Arial, sans-serif">(De
par la nature décentralisée d'I2P-Bote, l'envoi comme la relève
prennent quelques minutes. Sensiblement plus lorsque les routes de
messages sont activées. Mais vous n'avez pas besoin de garder le
navigateur ouvert&nbsp;: il suffit de laisser tourner I2P-Bote en
tant que processus d'arrière plan ce dont profite aussi votre
anonymat)</FONT></P>
<P STYLE="line-height: 150%"><BR><BR>
</P>
<P STYLE="line-height: 150%"><BR><FONT FACE="Arial, sans-serif"><U>2.2.3
Carnet d'adresses local</U></FONT></P>
<P STYLE="line-height: 150%"><BR><FONT FACE="Arial, sans-serif">Si
vous avez la clé de destination I2P-Bote de quelqu'un à qui vous
voulez écrire souvent, il est commode de l'enregistrer dans votre
carnet d'adresses local (lien sur la gauche)&nbsp;: choisissez un nom
(de façon complètement arbitraire), collez la clé dans le champ
idoine et sauvegardez.</FONT></P>
<P STYLE="line-height: 150%"><FONT COLOR="#000000"><FONT FACE="Arial, sans-serif">C'est
normalement ce que vous devriez faire, en sorte que le prochain
message de ce destinataire soit marqué comme «&nbsp;Connu&nbsp;»,
alors qu'un message d'un autre expéditeur de même nom serait marqué
d'un «&nbsp;x&nbsp;» (ou préfixé [UNK] dans un client de
messagerie), vous prévenant ainsi qu'il provient de quelqu'un
d'autre. </FONT></FONT>
</P>
<P STYLE="line-height: 150%"><BR><BR><FONT FACE="Arial, sans-serif"><U>2.2.4
Les préférences de réglages (et ce qu'elles signifient)</U></FONT></P>
<P STYLE="line-height: 150%"><BR><FONT FACE="Arial, sans-serif">Dans
les préférences vous pouvez choisir la langue de l'interface
d'I2P-Bote et décider que même si vous choisissez une autre langue,
tout ce qui sera automatiquement ajouté comme titres d'en-têtes
restera cependant en anglais pour ne pas dévoiler votre langue (et
par là même, peut-être en déduire votre nationalité et
compromettre votre anonymat à ce sujet, la FAQ I2P-Bote et le
chapitre 3 de ce manuel donnent d'autres conseils).</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Vous
pouvez aussi régler l'intervalle de relève automatique, et
l'inclusion ou l'exclusion des horodatages pour vos messages (les
horodatages sont toujours en UTC).</FONT></P>
<P STYLE="font-style: normal; line-height: 150%"><FONT FACE="Arial, sans-serif">(Quand
vous utilisez les routes de messages, les horodatages sont
automatiquement désactivés [<FONT COLOR="#ff0000">pas encore
implémenté</FONT>]).</FONT></P>
<P STYLE="line-height: 150%"><BR><BR>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif"><I>Relève
automatique:</I></FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Pour le
confort d'utilisation, cochez «&nbsp;Relever le courrier toutes les
XX minutes&nbsp;:&nbsp;» et saisissez la valeur de l'intervalle. </FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Ceci
définit la fréquence de tentatives de téléchargement de nouveaux
messages pour vos identités. Ça peut être différentié par
identité [<FONT COLOR="#ff0000">pas encore implémenté</FONT>].</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Si vous
définissez un décalage aléatoire, la relève n'aura pas lieu
exactement toutes les XX minutes, mais toutes les
(XX+/-décalage*XX)minutes, c'est à dire après un temps aléatoire
compris entre (1-décalage)*XX minutes et (1+décalage)*XX minutes
[<FONT COLOR="#ff0000">pas encore implémenté</FONT>].</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Vous
pouvez également désactiver complètement la relève automatique
pour une identité donnée.</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">(Si vous
n'êtes pas à l'aise avec ces réglages, les valeurs par défaut
vous conviendront très bien)</FONT></P>
<P STYLE="line-height: 150%"><BR><BR><FONT FACE="Arial, sans-serif">Les
</FONT><FONT FACE="Arial, sans-serif"><I>Routes de messagerie </I></FONT><FONT FACE="Arial, sans-serif">sont
des chînes de nœuds I2P-Bote agissant en tant que routeurs relais
pour le compte d'autres pairs et obéissant à des délais par saut
définis par l'expéditeur, fournissant ainsi le transport à haute
latence garant d'un anonymat plus élevé. </FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Vous
pouvez spécifier le nombre de nœuds (sauts) nécessaires à la
constitution d'une route de messagerie. Ensuite, chacun des paquets
de message envoyés par l'identité qui a activé la fonction routes
de messagerie passera par une route de n sauts avant d'être stocké.
Vous pouvez définir un délai pour chaque saut individuel, pour
qu'aucun saut ne puisse savoir combien de temps le paquet attendra au
niveau du saut suivant, ce qui rend la prédiction horaire impossible
[<FONT COLOR="#ff0000">les définitions des retards par saut et par
identité ne sont pas encore implémentées</FONT>].</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Vous
pouvez définir une tranche de temps (p.e. 60-600 minutes) puis
un retard aléatoire entre ces deux valeurs sera choisi pour le
paquet au niveau de tel saut ou une heure fixe à laquelle le
paquet sera transféré, p.e. cet après-midi UTC, indépendamment du
moment où il est arrivé [<FONT COLOR="#ff0000">heure fixe pas
encore implémentée</FONT>]. </FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">(Quand
vous utilisez les routes de messagerie, les horodatages sont
automatiquement désactivés [<FONT COLOR="#ff0000">pas encore
implémenté</FONT>]).</FONT></P>
<P STYLE="line-height: 150%"><BR><FONT FACE="Arial, sans-serif">Sous
«&nbsp; </FONT><FONT FACE="Arial, sans-serif"><I>Minimum dans la
messagerie</I></FONT>&nbsp;<FONT FACE="Arial, sans-serif">» (nombre
minimum de paquets relayés à envoyer), vous pouvez indiquer un
seuil. Comme votre instance I2P-Bote peut fonctionner comme un relais
mélangeur, s'il y a suffisamment de paquets étrangers à mélanger
et avec lesquels camoufler les vôtres, elle va accumuler les
messages pour lesquels le retard demandé n'est pas échu jusqu'à
atteindre ce seuil. Une fois atteint, votre bote commence à les
transférer aléatoirement [</FONT><FONT COLOR="#ff0000"><FONT FACE="Arial, sans-serif">pas
encore implémenté</FONT></FONT><FONT FACE="Arial, sans-serif">].</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif"><I>Exclure
l'identité de la relève globale</I> [<FONT COLOR="#ff0000">pas
encore implémenté</FONT>].</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Si vous
activez cette option pour une de vos identités, elle ne sera pas
prise en compte ni par la fonction de relève globale automatique
planifiée, ni par la relève manuelle globale.</FONT></P>
<P STYLE="line-height: 150%"><BR><BR>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif"><U>2.2.5
E-mails de et vers l'Internet</U><SPAN STYLE="text-decoration: none">
[</SPAN><FONT COLOR="#ff0000"><SPAN STYLE="text-decoration: none">NOT
YET FULLY IMPLEMENTED!</SPAN></FONT><SPAN STYLE="text-decoration: none">]</SPAN></FONT></P>
<P STYLE="line-height: 150%"><BR><FONT FACE="Arial, sans-serif">Pour
pouvoir envoyer des messages bote vers l'Internet et recevoir des
e-mails venant de l'Internet dans l'application I2P-Bote, vous devez
d'abord vous enregistrer auprès d'une passerelle de messagerie. Il
n'y en a actuellement qu'une seule&nbsp;: postman. </FONT>
</P>
<P STYLE="line-height: 150%"><BR><FONT FACE="Arial, sans-serif">1)
D'abord, allez sur : <A HREF="http://hq.postman.i2p/?page_id=16">http://hq.postman.i2p/?page_id=16</A>
et créez un compte. Si vous en avez déjà un, ou si vous venez de
le créer, passer à l'étape 2).</FONT></P>
<P STYLE="line-height: 150%"><BR><FONT FACE="Arial, sans-serif">2)
Pour un compte donné, vous pouvez ajouter votre clé de destination
de messagerie, pour que les messages arrivant d'Internet puissent
être transférés vers votre application I2P-Bote. Pour ce faire
allez sur : <A HREF="http://hq.postman.i2p/?page_id=74">http://hq.postman.i2p/?page_id=74</A>
et indiquez les informations demandées.</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Maintenant,
tous les e-mails envoyés à cette adresse (<A HREF="mailto:name@i2pmail.org">nom@i2pmail.org</A>
depuis l'Internet externe ou depuis <A HREF="mailto:name@mail.i2p">blaze@mail.i2p</A>
pour les messages d'autres comptes postman) seront transférés par
le réseau I2P-Bote à votre application I2P-Bote.</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">(N.B. En
utilisant les adresses <A HREF="mailto:name@mail.i2p">nom@mail.i2p</A>
ou <A HREF="mailto:name@i2pmail.org">nom@i2pmail.org</A> au lieu des
longues clés de destination, les messages ne sont plus cryptés de
bout en bout. En conséquence, il est recommandé d'échanger les
clés I2P-Bote pour la communication intra réseau. Postman offre des
services de haute qualité sur I2P depuis déjà un bon moment, mais
soyez conscient qu'il s'agit d'un point centralisé qui pourrait un
jour cesser de fonctionner, ou pire, tomber sous le contrôle d'un
nuisible qui pourrait manipuler les courriers. Comme pour la
messagerie intra réseau, I2P-Bote vous assure que si vous utilisez
des clés de destination, personne ne peut bricoler les messages que
vous recevez ou envoyez.)</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Si vous
voulez non seulement recevoir des messages d'Internet, mais aussi
pouvoir en envoyer vers Internet depuis I2P-Bote, vous devez indiquer
à votre application cliente I2P-Bote la clé de destination de la
passerelle de messagerie, pour que votre I2P-Bote sache où envoyer
les messages.<BR>Ceci se fait dans les «&nbsp;Préférences&nbsp;».
Cette passerelle permet aux utilisateurs d'I2P-Bote de communiquer
avec les utilisateurs de messagerie standard sur l'Internet classique
et avec ceux qui utilisent le service i2pmail de postman
(@mail.i2p).</FONT><BR><FONT FACE="Arial, sans-serif"><BR>Pour
combattre les abus, il y a une limitation au nombre de messages que
vous pouvez envoyer vers Internet, comme avec le service normal de
postman&nbsp;: si un utilisateur d'I2P-Bote dépasse le quota de
messages envoyés, les messages supplémentaires sont retournés à
l'envoyeur.</FONT></P>
<P STYLE="line-height: 150%"><BR><BR><BR>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif"><B>3.
Considérations sur l'anonymat</B></FONT></P>
<P STYLE="line-height: 150%"><BR><FONT FACE="Arial, sans-serif">N'envoyez
pas d'informations susceptibles de vous identifier (nom, adresse,
situation géographique, fuseau horaire, horodatages, âge, sites web
que vous venez de visiter ou sur lesquels vous venez de parler sur un
blog, noms d'utilisateurs, adresses IP, identités de routeur I2P
routeur, de nœud I2P-Bote, numéros de sécurité sociale, de carte
de crédit, …, copies de passeport, de permis de conduire, de
contrats de location, de photos nu ou habillé , documents qui
contiennent votre nom comme auteur dans les réglages, et une
multitude d'autres) !</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif"><BR>Si
possible, </FONT>
</P>
<UL>
<LI><P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">laissez
I2P-Bote tourner 24h/24 7j/7,</FONT></P>
<LI><P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">utilisez
les routes de messagerie en conjonction avec les retards aléatoires
ou fixes par saut [<FONT COLOR="#ff0000">pas encore </FONT><FONT COLOR="#ff0000"><I>complètement</I></FONT><FONT COLOR="#ff0000">
implémenté</FONT>],</FONT></P>
<LI><P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">utilisez
un intervalle de relève long,</FONT></P>
<LI><P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">utilisez
un long retard pour vos propres paquets,</FONT></P>
<LI><P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">utilisez
un large intervalle de relève aléatoire [<FONT COLOR="#ff0000">pas
encore implémenté</FONT>].</FONT></P>
</UL>
<P STYLE="line-height: 150%"><BR><FONT FACE="Arial, sans-serif">Vous
pouvez supprimer l'envoi des horodatages des en-têtes de messages.</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Quand
vous répondez, certains marqueurs comme, «&nbsp;Rép: [Top
secret]&nbsp;» ou «&nbsp;[mégaphone] a écrit :&nbsp;» sont
ajoutés à votre réponse. Ils diffèrent d'une langue à l'autre,
suivant vos préférences de réglage de langue de l'interface
d'I2P-Bote. Cependant, si vous ne voulez pas que vos destinataires
puissent savoir comment vous avez effectué ce réglage (révélateur
de votre langue et peut-être de votre nationalité), vous pouvez
désactiver la traduction de ces marqueurs pour qu'il restent en
anglais, quelle que soit votre réglage de langue&nbsp;: Cochez la
case «&nbsp;Utiliser l'anglais pour les titres d'en-têtes de
messages sortants ('Re', 'wrote', etc.)&nbsp;».</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Soyez
prudent sur le contenu de vos envois! N'incluez pas d'information
personnelles ou des informations que vous seul pouvez posséder.
N'écrivez pas «&nbsp;je vais me coucher, il est tard&nbsp;»,
lorsque les tampons horaires ne sont pas désactivés.<BR>La langue
dans laquelle vous écrivez, votre style et vos particularités
d'expression sont autant d'autres éléments qui pourraient
intéresser un attaquant.</FONT></P>
<P STYLE="line-height: 150%"><BR><FONT FACE="Arial, sans-serif">I2P-Bote
permet aussi l'utilisation d'identités multiples. Supposez qu'un de
vos contacts découvre votre identité, car vous avez oublié
d'enlever une information vous concernant d'un document confidentiel
que vous lui avez transmis (par erreur ou pas). Maintenant, si ce
correspondant est amené a communiquer/collaborer avec d'autres de
vos contacts, il pourrait leur révéler l'identité réelle liée à
l'identité Bote qu'il connait de vous. Donc, si vous communiquez
avec ces autres avec la même adresse il vous reconnaitrons. Ils ne
pourraient pas si vous aviez utilisé une identité différente pour
vous adresser à eux.</FONT></P>
<P STYLE="line-height: 150%"><BR><BR><FONT FACE="Arial, sans-serif"><B>4.
Concept technique</B></FONT></P>
<P STYLE="line-height: 150%"><BR><FONT FACE="Arial, sans-serif">I2P-Bote
est un système de messagerie totalement décentralisé (sans
serveur) intra réseau crypté de bout en bout. Il supporte
l'utilisation d'identités multiples et n'expose pas les en-têtes de
messages. Il est actuellement encore en version alpha et ne peut être
utilisé que par son interface web intégré à la console du routeur
I2P. Il disposera bientôt du support de POP3, et il est prévu qu'il
garantisse un niveau d'anonymat supplémentaire via une option de
transport à haute latence. Tous les messages bote sont cryptés
automatiquement de bout en bout, de telle sorte qu'il ne soit pas
nécessaire d'utiliser un autre système de cryptage (bien que ça
soit possible), et les messages sont automatiquement authentifiés.
Comme il est décentralisé, il n'y a pas de serveur de messagerie
qui puisse établir de relation entre différentes identités en tant
qu'elles communiqueraient les unes avec les autres (profilage) : même
les nœuds relayant les messages ne connaissent pas l'expéditeur, et
hormis l'expéditeur et le destinataire, seuls le point terminal du
tunnel de messages à haute latence et les nœuds de stockage savent
à qui (identité anonyme) le message est destiné. L'expéditeur
peut être passé hors ligne depuis longtemps lorsque le message
devient disponible côté destinataire. Ceci améliore la classe
d'anonymat qui peut être atteinte par I2P-Bote. Pour ceux qui ne
veulent pas de longs retards, tous ces réglages sont accessibles à
l'utilisateur et chacun peut ainsi ajuster le niveau d'anonymat à
ses désirs.</FONT></P>
<P><FONT COLOR="#000000"><FONT FACE="Arial, sans-serif"><BR>Les
nœudsI2P-Bote stockent les messages cryptés dans une table de
hachage décentralisée (DHT) Kademlia. En conséquence, un message
peut être envoyé via de nombreux autres nœuds (relais) pour
augmenter la sécurité, ou directement sur un groupe de nœuds de
stockage pour une livraison plus rapide. Le même scenario s'applique
aussi à le relève des messages.<BR>(L'utilisation des routes de
message désactive automatiquement les tampons horaires)<BR>[</FONT></FONT><FONT COLOR="#ff0000"><FONT FACE="Arial, sans-serif">La
relève via les relais n'est pas encore implémentée</FONT></FONT><FONT COLOR="#000000"><FONT FACE="Arial, sans-serif">].</FONT></FONT></P>
<P><BR><BR>
</P>
<P STYLE="line-height: 150%"><FONT COLOR="#000000"><FONT FACE="Arial, sans-serif">Tous
les nœuds sont créés égaux : il n'y a pas de «&nbsp;super-nœuds&nbsp;»
ni de nœuds désignés comme relais ou stockeurs. Tout le monde agit
en tant que relais et stockage potentiels. L'utilisation d'espace
disque est configurable par l'utilisateur.</FONT></FONT></P>
<P STYLE="line-height: 150%"><FONT COLOR="#000000"><FONT FACE="Arial, sans-serif">Avant
qu'un message ne soit envoyé à un relais, il est découpé en
paquets et crypté avec la clé publique du destinataire. Ces paquets
sont stockés de façon redondante dans la DHT.</FONT></FONT></P>
<P STYLE="line-height: 150%"><FONT COLOR="#000000"><FONT FACE="Arial, sans-serif">Il
sont conservés au moins 100 jours, pendant lesquels le destinataire
peut les télécharger.</FONT></FONT></P>
<P STYLE="line-height: 150%"><FONT COLOR="#000000"><FONT FACE="Arial, sans-serif">Les
paquets relayés expirent aussi après 100 jours ou plus.</FONT></FONT></P>
<P STYLE="line-height: 150%"><FONT COLOR="#000000"><FONT FACE="Arial, sans-serif">Si
un nœud manque d'espace de stockage et qu'il n'y a pas de vieux
paquets à supprimer, ce nœud refuse les requêtes de stockage.</FONT></FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">De plus,
I2P-Bote nettoie les en-têtes de messages en n'autorisant que les
informations indispensables à la transmission, permettant ainsi
l'utilisation de clients de messagerie sans vérification préalable
de ce qu'ils ajoutent comme en-têtes bavardes [<FONT COLOR="#ff0000">POP3
pas encore implémenté</FONT>].</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Tout le
cryptage, le choix des chemins et le profilage sont effectués
localement en sorte qu'aucune tierce partie de confiance ne soit mise
à contribution.</FONT></P>
<P STYLE="font-weight: normal; line-height: 150%"><FONT FACE="Arial, sans-serif">Par
une utilisation appropriée d'I2P-Bote, c'est à dire en gardant à
l'esprit les considérations signalées plus haut relatives au bon
sens, personne ne saura qui vous êtes ni où vous vous trouvez. Et
si vous êtes déjà surveillé et que votre connexion Internet est
sur écoutes, le nuisible ne pourra pas savoir ce que vous envoyez ou
recevez, ni avec qui vous communiquez et où sont situés vos
correspondants.</FONT></P>
<P STYLE="line-height: 150%"><BR><BR>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif"><BR>Rentrons
un peu dans les détails&nbsp;:</FONT></P>
<P STYLE="line-height: 150%"><BR><BR>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif"><U>Ce
qu'I2P-Bote <I>cache</I> :</U></FONT></P>
<P STYLE="line-height: 150%"><BR><FONT FACE="Arial, sans-serif"><I>I2P-Bote
cache à la fois, l'identité et la situation de l'expéditeur et du
ou destinataires, celles des nœuds intermédiaires (relais et
stockage), le contenu des messages, leur taille, le nombre que vous
envoyez.</I></FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Seul le
destinataire peut connaître l'adresse de l'expéditeur du message
bote, et si ce dernier l'a décidé, même le destinataire peut être
tenu dans l'ignorance de cette donnée.</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Même si
envoyez des horodatages, votre fuseau horaire ne sera pas dévoilé.</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">De plus,
I2P-Bote masque ...</FONT></P>
<P STYLE="line-height: 150%">&nbsp;&nbsp;&nbsp; <FONT FACE="Arial, sans-serif">-
le fait que vous utilisez... I2P-Bote</FONT></P>
<P STYLE="line-height: 150%">&nbsp;&nbsp;&nbsp; <FONT FACE="Arial, sans-serif">-
le fait que vous envoyez un message</FONT></P>
<P STYLE="line-height: 150%">&nbsp;&nbsp;&nbsp; <FONT FACE="Arial, sans-serif">-
le fait que vous recevez un message</FONT></P>
<P STYLE="line-height: 150%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <FONT FACE="Arial, sans-serif">et
donc</FONT></P>
<P STYLE="line-height: 150%">&nbsp;&nbsp;&nbsp; <FONT FACE="Arial, sans-serif">-
le moment d'envoi d'un message</FONT></P>
<P STYLE="line-height: 150%">&nbsp;&nbsp;&nbsp; <FONT FACE="Arial, sans-serif">-
le moment de réception d'un message</FONT></P>
<P STYLE="line-height: 150%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <FONT FACE="Arial, sans-serif">ainsi
que</FONT></P>
<P STYLE="line-height: 150%">&nbsp;&nbsp;&nbsp; <FONT FACE="Arial, sans-serif">-
la limite supérieure du nombre de messages qu'un utilisateur inconnu
reçoit, - nota bene : un utilisateur abstrait, pas un concret,
extrapolant simplement son existence de celle de la destination de
messagerie<BR>&nbsp;&nbsp;&nbsp; - car il pourrait toujours avoir
plus d'une identité ; et la limite basse, car une identité envoie
également des messages de test et des messages leurres/«&nbsp;bidons&nbsp;»</FONT></P>
<P STYLE="line-height: 150%"><BR><BR>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif"><U>Ce
qu'I2P-Bote <I>masque partiellement</I> : </U></FONT>
</P>
<P STYLE="line-height: 150%"><BR><FONT FACE="Arial, sans-serif">L'adresse
I2P-Bote du destinataire n'est connue que par l'expéditeur et le(s)
destinataire(s). </FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">En cas de
destinataires multiples, chacun verra tous les autres destinataires
dont les adresses ont été indiquées en «&nbsp;A&nbsp;:» OU
«&nbsp;Cc&nbsp;:&nbsp;».</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Toutes
les entrées «&nbsp;Cci&nbsp;:&nbsp;» ne sont visibles qu'à
l'expéditeur et au destinataire spécifique. </FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Le moment
de l'envoi, s'il est précisé, n'est visible que par l'expéditeur
et le destinataire.</FONT></P>
<P STYLE="line-height: 150%"><BR><BR><FONT FACE="Arial, sans-serif"><U>Ce
qu'I2P-Bote </U></FONT><FONT FACE="Arial, sans-serif"><I><U>peut
masquer optionnellement </U></I></FONT><FONT FACE="Arial, sans-serif"><U>:
</U></FONT>
</P>
<P STYLE="line-height: 150%"><BR><FONT FACE="Arial, sans-serif">- Si
les routes de messagerie sont utilisées, le moment de l'envoi d'un
message</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">- Si les
routes de messagerie sont utilisées, le moment de relève d'un
message [<FONT COLOR="#ff0000">pas encore implémenté</FONT>].</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">- Si
l'expéditeur supprime les horodatages, seul l'expéditeur sait quand
il a envoyé un message.</FONT></P>
<P STYLE="line-height: 150%"><BR><BR><FONT FACE="Arial, sans-serif"><U>Ce
qu'I2P-Bote </U></FONT><FONT FACE="Arial, sans-serif"><I><U>ne peut
pas cacher </U></I></FONT><FONT FACE="Arial, sans-serif"><U>: </U></FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif"><BR>I2P-Bote
ne peut pas masquer la fréquence à laquelle une identité donnée
relève les nouveaux messages, ni ne nombre de messages reçus par
une identité donnée.</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif"><BR>Pour
son démarrage, I2P-Bote ne dépend pas non-plus d'un nœud central
car il utilise Seedless.</FONT></P>
<P STYLE="line-height: 150%"><BR><BR><BR>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif"><B>5
Terminologie/Glossaire:</B></FONT></P>
<P STYLE="line-height: 150%"><BR><BR>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif"><I><U>Identité
du routeur/nœud I2P-Bote </U>:</I></FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">C'est
l'identité par laquelle le routeur I2P-Bote est connu. Elle est
utilisée pour contacter ce routeur, pour stocker, relayer et relever
les messages, mais aussi pour le cryptage par saut et le contacter
via I2P, car c'est celle là même de la destination du tunnel I2P.
Elle est affichée pour représenter le nœud I2P-Bote dans les
statistiques.</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Ainsi
donc, l'identité du routeur (I2P-Bote) correspond à la destination
I2P (l'adresse d'un nœud I2P-Bote sur le réseau I2P il n'y a
pas de nécessité de la connaître, sauf en cas de problèmes de
connexion aux autres nœuds I2P-Bote).</FONT></P>
<P STYLE="font-weight: normal; line-height: 150%"><BR><FONT FACE="Arial, sans-serif"><I><U>Destination
de messagerie I2P-Bote :</U></I></FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">La
destination de messagerie I2P-Bote (clé) est un identifiant par
lequel quelqu'un peut être atteint via I2P-Bote, comme son nom
l'indique&nbsp;: une destination e-mail. C'est donc pour I2P-Bote ce
qu'est une adresse e-mail pour un système de messagerie standard&nbsp;:
la destination de messagerie est l'adresse effective d'envoi de
messages, de stockage et de relève dans la DHT. <BR>Elle est aussi
utilisée pour le cryptage de bout en bout des messages, pour les
informations d'en-têtes et les pièces jointes. </FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Une
destination de messagerie I2P-Bote est une chaîne en base 64
contenant une clé publique de cryptage et une clé de vérification
de signature, par exemple&nbsp;: </FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">uQtdwFHqbWHGyxZN8wChjWbCcgWrKuoBRNoziEpE8XDt8koHdJiskYXeUyq7JmpG</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">In8WKXY5LNue~62IXeZ-ppUYDdqi5V~9BZrcbpvgb5tjuu3ZRtHq9Vn6T9hOO1fa</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">FYZbK-FqHRiKm~lewFjSmfbBf1e6Fb~FLwQqUBTMtKYrRdO1d3xVIm2XXK83k1Da</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">-nufGASLaHJfsEkwMMDngg8uqRQmoj0THJb6vRfXzRw4qR5a0nj6dodeBfl2NgL9</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">HfOLInwrD67haJqjFJ8r~vVyOxRDJYFE8~f9b7k3N0YeyUK4RJSoiPXtTBLQ2RFQ</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">gOaKg4CuKHE0KCigBRU-Fhhc4weUzyU-g~rbTc2SWPlfvZ6n0voSvhvkZI9V52X3</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">SptDXk3fAEcwnC7lZzza6RNHurSMDMyOTmppAVz6BD8PB4o4RuWq7MQcnF9znElp</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">HX3Q10QdV3omVZJDNPxo-Wf~CpEd88C9ga4pS~QGIHSWtMPLFazeGeSHCnPzIRYD</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif"><BR>L'identité
de routeur/nœud I2P-Bote et la destination de messagerie I2P-Bote se
ressemblent mais elles sont complètement indépendantes l'une de
l'autre.</FONT></P>
<P STYLE="line-height: 150%"><BR><FONT FACE="Arial, sans-serif"><I><U>Adresse
e-mail :</U></I></FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Les
adresses e-mail dans I2P-Bote sont des raccourcis des destinations de
messagerie.</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Les
correspondances entre elles sont stockées à deux endroits&nbsp;: le
carnet d'adresses local et l'annuaire d'adresses décentralisé [<FONT COLOR="#ff0000">ce
dernier n'est pas encore implémenté</FONT>].</FONT></P>
<P STYLE="line-height: 150%"><BR><FONT FACE="Arial, sans-serif"><I><U>Identité
de messagerie I2P-Bote :</U></I></FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">L'identité
de messagerie I2P-Bote est un porte clé de destination de messagerie
I2P-Bote avec la clé privée correspondante et le nom que lui a
donné l'utilisateur qui l'a créé. Ce nom sera envoyé avec la clé
de destination si vous ne désactivez pas l'envoi de cette
information sur l'expéditeur.</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Elle ne
sera visible que par le destinataire s'il n'a pas déjà un nom pour
cette destination dans son propre carnet d'adresses local.</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Donc en
termes techniques, une identité de messagerie est constituée de
quatre éléments&nbsp;:</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">* une
destination de messagerie (c'est à dire deux clés publiques)</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">* les
deux clés privées correspondantes</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">* un nom
public qui peut être montré aux correspondants</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">* une
description qui n'est accessible qu'à vous même, le créateur
l'identité.</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">(Pour
vous aider à vous souvenir de l'usage que vous assignez à cette
identité).</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Une
identité n'est pas pas nécessaire pour envoyer des messages (dans
ce cas, seul «&nbsp;Anonyme&nbsp;» est proposé comme expéditeur
dans le champ de sélection).</FONT></P>
<P STYLE="line-height: 150%"><BR><BR>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif"><I><U>Routes
de messagerie </U></I>:</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Les
routes de messagerie sont un mode de transport à haute latence
supplémentaire pour I2P-Bote. Pour son fonctionnement, une chaîne
de nœuds I2P-Bote est construite, agissant en tant que routeurs
relais pour les paquets et obéissant à des retards par saut [<FONT COLOR="#ff0000">les
retards individuels par saut ne sont pas encore implémentés</FONT>]&nbsp;;
</FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif"><B><BR>AVERTISSEMENT!</B></FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Si vous
choisissez cette option particulièrement avec de nombreux sauts
et/ou de longs retards, ne soyez pas surpris si vos messages
n'arrivent pas rapidement à destination. Ça prendra évidemment
plus de temps jusqu'à plusieurs jours !</FONT></P>
<P STYLE="line-height: 150%"><BR><BR>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif"><I><B>6.&nbsp;
Remerciements</B></I></FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Idée &amp;
conception technique : HungryHobo, Mixxy </FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Implémentation
: HungryHobo </FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Maintenance
du greffon : zzz, HungryHobo </FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Interface
utilisateur : HungryHobo </FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Intégration
de Seedless : sponge </FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Traduction
allemande : HungryHobo </FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Traduction
russe : suhr</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Traduction
française : albat, Redzara, Mixxy, magma </FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Traduction
espagnole : Mixxy</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Traduction
portugaise : Mixxy </FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Traduction
hollandaise : KwukDuck </FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Traduction
norvégienne : hej </FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Traduction
suédoise : hottuna</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Traduction
chinoise : walking </FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Traduction
arabe : hamada </FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Testeurs
versions Alpha : HungryHobo, Mixxy, Returning Novice, sponge, et de
nombreux autres. </FONT>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Manuel :
Mixxy</FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">Testeurs
versions Alpha : HungryHobo, Mixxy, Returning Novice, sponge, and
many others</FONT></P>
<P STYLE="line-height: 150%">Traduction française du manuel&nbsp;:
magma (avril 2011) (grâce à l'aide inestimable de user pour la
restitution correcte des concepts techniques et l'utilisation des
outils techniques de l'équipe I2P).</P>
<P STYLE="line-height: 150%"><BR><BR>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif"><B>7.
Détails techniques</B></FONT></P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif">-- voir
techdoc.txt --</FONT></P>
<P STYLE="line-height: 150%"><BR><BR>
</P>
<P STYLE="line-height: 150%"><BR><BR>
</P>
<P STYLE="line-height: 150%"><FONT FACE="Arial, sans-serif"><I><B>ENJOY
THE BOTE FEELING!!</B></I></FONT></P>
</BODY>
</HTML>

View File

@ -1,2 +0,0 @@
REM This scrypt only works if I2P is installed in the standard directory
@java -cp "%PROGRAMFILES%\\i2p\\lib\\i2p.jar;i2pbote.jar" i2p.bote.fileencryption.Decrypt %*

View File

@ -1,2 +0,0 @@
#!/bin/sh
java -cp $I2P/lib/i2p.jar:`dirname $0`/i2pbote.jar i2p.bote.fileencryption.Decrypt $@

View File

@ -1,2 +0,0 @@
REM This scrypt only works if I2P is installed in the standard directory
@java -cp "%PROGRAMFILES%\\i2p\\lib\\i2p.jar;i2pbote.jar" i2p.bote.fileencryption.Encrypt %*

View File

@ -1,2 +0,0 @@
#!/bin/sh
java -cp $I2P/lib/i2p.jar:`dirname $0`/i2pbote.jar i2p.bote.fileencryption.Encrypt $@

Binary file not shown.

Binary file not shown.

View File

@ -1,133 +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="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="ib" uri="I2pBoteTags" %>
<%--
JSP variables that can be passed into this JSP:
title - The page title to set
refreshInterval - If this parameter is set, do an HTTP refresh every refreshInterval seconds
refreshUrl - If refreshInterval is set, load this URL when refreshing
infoMessage - Display an informational message
errorMessage - Display an error message
infoMessage and errorMessage can also be passed in as an HTTP parameter.
--%>
<fmt:requestEncoding value="UTF-8"/>
<jsp:useBean id="jspHelperBean" class="i2p.bote.web.JSPHelper"/>
<c:set var="themeDir" value="themes/${jspHelperBean.configuration.theme}" scope="request"/>
<fmt:setLocale value="${jspHelperBean.language}" scope="request"/>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<%-- Refresh --%>
<c:if test="${not empty refreshInterval}">
<meta http-equiv="refresh" content="${refreshInterval};url=${refreshUrl}" />
</c:if>
<link rel="stylesheet" href="themes/${jspHelperBean.configuration.theme}/i2pbote.css" />
<link rel="icon" type="image/png" href="${themeDir}/images/favicon.png" />
<c:if test="${!empty title}">
<title>${title} <ib:message key="- I2P-Bote"/></title>
</c:if>
</head>
<body>
<div class="titlebar" onclick="document.location='.'">
<c:if test="${jspHelperBean.passwordInCache}">
<div class="password">
<ib:message key="Password is cached. Click to clear the password cache." var="linkTitle"/>
<a href="clearPasswordCache.jsp" title="${linkTitle}"><img src="${themeDir}/images/clear_password.png"/></a>
</div>
</c:if>
<div class="title"><ib:message key="I2P-Bote"/></div>
<br/>
<div class="subtitle"><ib:message key="Secure Distributed Email"/></div>
</div>
<c:if test="${jspHelperBean.updateAvailable}">
<div class="updateMessage">
<a href="http://localhost:7657/configclients.jsp#plugin" target="_parent"><ib:message key="A new version is available. Click here for the configuration page."/></a>
</div>
</c:if>
<div class="menubox">
<iframe src="buttonFrame.jsp" class="button-frame"></iframe>
</div>
<div class="menubox">
<iframe src="folders.jsp" class="folders-frame"></iframe>
</div>
<div class="menubox">
<h2><ib:message key="Addresses"/></h2>
<a href="identities.jsp"><ib:message key="Identities"/></a><br/>
<a href="addressBook.jsp"><ib:message key="Address Book"/></a><br/>
Public Address Directory<br/>
</div>
<div class="menubox">
<h2><ib:message key="Configuration"/></h2>
<a href="settings.jsp"><ib:message key="Settings"/></a><br/>
</div>
<div class="menubox">
<h2><a href="network.jsp"><ib:message key="Network Status"/></a></h2>
<iframe src="statusFrame.jsp" class="status-frame"></iframe>
</div>
<div class="menubox">
<h2><ib:message key="Help"/></h2>
<a href="${ib:getLocalizedFilename('User\'s Guide.html', pageContext.servletContext)}"><ib:message key="User Guide"/></a><br/>
<a href="${ib:getLocalizedFilename('FAQ.html', pageContext.servletContext)}"><ib:message key="FAQ"/></a><br/>
<a href="about.jsp"><ib:message key="About"/></a><br/>
</div>
<div class="infoMessage">
<c:if test="${not empty infoMessage}">
${fn:escapeXml(infoMessage)}
</c:if>
<c:if test="${empty infoMessage}">
${fn:escapeXml(param.infoMessage)}
</c:if>
</div>
<div class="errorMessage">
<c:if test="${not empty errorMessage}">
${fn:escapeXml(errorMessage)}
</c:if>
<c:if test="${empty errorMessage}">
${fn:escapeXml(param.errorMessage)}
</c:if>
</div>

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: 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,73 +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" %>
<ib:message key="Set Password" var="title" scope="request"/>
<c:if test="${param.action eq 'set'}">
<c:set var="errorMessage" value="${ib:changePassword(param.old, param.new, param.confirm)}" scope="request"/>
<c:if test="${empty errorMessage}">
<ib:message key="The password has been changed." var="infoMessage" scope="request"/>
<jsp:forward page="index.jsp"/>
</c:if>
</c:if>
<jsp:include page="header.jsp"/>
<div class="main">
<h2><ib:message key="Set a new Password"/></h2>
<p>
<ib:message key="If you have not set a password, leave the old password blank."/>
</p><p>
<ib:message>
Please note that if a password is set, emails cannot be checked automatically
but only when the Check Mail button is clicked.
</ib:message>
</p><br/>
<form name="form" action="setPassword.jsp" method="POST">
<input type="hidden" name="action" value="set"/>
<div class="password-label"><ib:message key="Old password:"/></div>
<div class="password-field"><input type="password" name="old"/></div>
<div class="password-label"><ib:message key="New password:"/></div>
<div class="password-field"><input type="password" name="new"/></div>
<div class="password-label"><ib:message key="Confirm:"/></div>
<div class="password-field"><input type="password" name="confirm"/></div>
<p/>
<ib:message key="OK" var="ok"/>
<input type="submit" value="${ok}"/>
</form>
<script type="text/javascript" language="JavaScript">
document.forms['form'].elements['old'].focus();
</script>
</div>
<jsp:include page="footer.jsp"/>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 B

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