Compare commits

...

511 Commits

Author SHA1 Message Date
f2b8b0950a Merge branch 'master' of i2pgit.org:I2P_Developers/i2p.android.base
Some checks failed
Sync Primary Repository to GitHub Mirror / sync (push) Has been cancelled
Java CI / build (push) Has been cancelled
Java CI / trunk (push) Has been cancelled
Java Signed CI / build-signed (push) Has been cancelled
Java Signed CI / trunk-signed (push) Has been cancelled
2025-06-03 13:25:01 -04:00
fa668711bf Release I2P 2.9.0
Some checks failed
Java CI / build (push) Has been cancelled
Java CI / trunk (push) Has been cancelled
Release / build (push) Has been cancelled
Java Signed CI / build-signed (push) Has been cancelled
Java Signed CI / trunk-signed (push) Has been cancelled
2025-06-03 13:16:20 -04:00
f06e0e1ca8 Add github sync for i2p.android.base
Some checks failed
Java CI / build (push) Failing after 3s
Java CI / trunk (push) Failing after 4s
Java Signed CI / build-signed (push) Failing after 3s
Java Signed CI / trunk-signed (push) Failing after 3s
Sync Primary Repository to GitHub Mirror / sync (push) Has been cancelled
2025-05-10 18:51:17 -04:00
d25aa29fee disable git sync job
Some checks failed
Java CI / build (push) Has been cancelled
Java CI / trunk (push) Has been cancelled
Java Signed CI / build-signed (push) Has been cancelled
Java Signed CI / trunk-signed (push) Has been cancelled
2025-04-27 00:11:30 -04:00
48d9afea41 tag new version 2025-04-19 11:06:46 -04:00
da4ed503c8 add magicindicator license 2025-03-21 18:18:32 -04:00
78a0a2b5f4 bump test 2025-03-18 14:47:24 -04:00
42202cc4d2 Fix exploratory tunnel fragment crash 2025-03-18 14:22:46 -04:00
49f4562bf7 Revert "force it into landscape mode for now until I can fix tunnel display in portrait mode"
This reverts commit 96bb2cf161.
2025-03-18 14:08:13 -04:00
ca6031ab47 update changelog 2025-03-18 14:06:58 -04:00
6c83285e04 Increment version numbers 2025-03-15 14:28:22 -04:00
96bb2cf161 force it into landscape mode for now until I can fix tunnel display in portrait mode 2025-03-15 14:25:22 -04:00
ddb2482623 Fixes tunnel listing in landscape mode bot not in portrait mode 2025-03-14 18:28:53 -04:00
bf2dea5e0a Merge branch 'master' of i2pgit.org:i2p-hackers/i2p.android.base into i2p-android-2.8.0-androidx 2025-03-14 16:22:28 -04:00
2d784dbef2 fix crashes in landscape mode 2025-03-10 19:29:53 -04:00
554a65987b Fix the SAM lifecycle error. Migrate the other MagicIndicator components 2025-03-10 18:40:29 -04:00
846795dea6 Update the whole thing to use Androidx and MagicIndicator because it's marginally more comprehensible than trying to get ViewPageIndicator 2.4.4 to compile 2025-03-10 17:35:47 -04:00
7a22ce4bee Add new version of CustomViewPager.java 2025-03-05 21:48:23 -05:00
0e25d89bd8 Finish most migrating to androidx. Dependency issues remain 2025-03-05 21:46:18 -05:00
61010b2c93 migrating to androidx 2025-03-05 21:12:15 -05:00
zzz
5fac82a915 pull translations from tx 2024-11-26 12:33:56 -05:00
zzz
c8c36540eb Add complete Gan Chinese translation 2024-11-26 12:32:12 -05:00
59b1da6950 Fix version number and re-create tag 2024-10-16 18:51:39 -04:00
40894d8d04 Fix version number and re-create tag 2024-10-16 18:50:38 -04:00
d0f38397cd Fix version number and re-create tag 2024-10-16 18:48:59 -04:00
17b86fffdd Fix version number and re-create tag 2024-10-16 18:48:19 -04:00
a06b449bfb Fix permissions, Fix ForegroundService on Android>=34, remove all TitlePageIndicators. 2024-10-16 14:01:56 -04:00
128f1df0ac update gradle plugin version 2024-10-11 18:09:43 -04:00
ed085194e6 Update for GPlay compliance issues 2024-10-11 18:06:56 -04:00
52a4c2f430 Increment version code 2024-10-11 17:46:39 -04:00
784918d220 Update libraries and fix unit test 2024-10-11 17:44:07 -04:00
f129773255 Merge branch 'master' of i2pgit.org:i2p-hackers/i2p.android.base 2024-10-11 15:46:46 -04:00
b304771eca Update build.gradle and I2P dependencies, move es_rAR to es-rAR 2024-10-11 15:45:12 -04:00
f87e8d036b Release: bump I2P version, I2P for Android version, and Build Number 2024-07-18 15:31:05 -04:00
zzz
16c1277935 Add new translation: Argentinian Spanish 2024-07-14 10:25:40 -04:00
zzz
a6aa76f9dd Pull translations from tx 2024-06-10 17:55:27 -04:00
zzz
a0ced4133d Pull translations from tx 2024-05-16 09:23:37 -04:00
e024950567 Update pull translation instructions 2024-05-16 09:09:28 -04:00
ea68f116e1 Increment build 2024-05-15 20:04:41 -04:00
3cb746594c Fix url text 2024-05-05 10:23:46 -04:00
27adae63eb Update forum URLs 2024-05-05 10:11:45 -04:00
85890d3b68 update CI files 2024-04-29 20:49:41 -04:00
96b045eb2a update CI files 2024-04-29 20:45:05 -04:00
8d00c5fa31 fix sync CI file 2024-04-29 20:28:08 -04:00
fc93a71552 Use CI to sync with gitlab 2024-04-29 18:59:34 -04:00
18e4aef4ca I2P for Android 2.5.0 2024-04-08 23:31:40 -04:00
5315e35adc I2P for Android 2.5.0 2024-04-08 23:23:24 -04:00
9ad9bc07ff Fix keystore name to produce signed build 2024-04-06 23:18:15 -04:00
bde92be29d Fix keystore path to produce signed build 2024-04-06 23:13:25 -04:00
849150ffa6 list keystore location at final step of CI so I can fix properties file 2024-04-06 23:08:51 -04:00
52dad19c7a list keystore location at final step of CI so I can fix properties file 2024-04-06 23:03:52 -04:00
e84a51097d fix base64 decode syntax 2024-04-05 16:13:54 -04:00
5289f5068a fix base64 decode syntax 2024-04-05 16:10:02 -04:00
09c7d9cfd8 fix base64 decode syntax 2024-04-05 16:06:12 -04:00
14d293fe6f Move keystore directory to runner user 2024-04-05 16:01:37 -04:00
7cd08cfd4c Assure keystore directory exists 2024-04-05 15:58:05 -04:00
aa22d83a44 fix syntax error 2024-04-05 15:10:11 -04:00
a1fd8d41b2 fix syntax error 2024-04-05 15:09:13 -04:00
9d24e68b57 remove secrets from sign.yml 2024-04-05 15:02:42 -04:00
d5e2804ea4 fix secret access to sign.yml 2024-04-05 14:58:33 -04:00
1edabd7252 fix secret access to sign.yml 2024-04-05 14:56:42 -04:00
52c1901eba fix secret access to sign.yml 2024-04-05 14:55:41 -04:00
a6f5221975 add secret access to sign.yml 2024-04-05 14:52:41 -04:00
caa57bbc3a add secret access to sign.yml 2024-04-05 14:51:34 -04:00
b28e5d741e add secret access to sign.yml 2024-04-05 14:50:20 -04:00
0f3a61390c fix typo in sign.yml 2024-04-05 14:46:58 -04:00
30a1f1d800 Create signing keys for dev-build specific builds 2024-04-05 14:41:38 -04:00
22d5d126f6 Create signing keys for dev-build specific builds 2024-04-05 14:36:18 -04:00
70e4ea810f Attempt signed CI builds(will fail right now) 2024-04-03 15:38:53 -04:00
b5c6e1489a Attempt signed CI builds(will fail right now) 2024-04-03 15:37:35 -04:00
333a1a49b1 Attempt CI builds 2024-04-03 15:26:58 -04:00
2ea93f106e unzip only tagged apk 2024-03-25 15:05:28 -04:00
d99ed0e5da Reduce timeout for release uploader 2024-03-25 14:53:34 -04:00
3881966ddb see if I can get trunk builds to work 2024-03-25 14:46:55 -04:00
9de9f46489 when a release is tagged copy over artifacts 2024-03-25 14:30:17 -04:00
149881522e specify a tag for non-trunk CI builds 2024-03-25 14:25:52 -04:00
df26d89310 attempt a trunk build too 2024-03-25 14:18:37 -04:00
707dcbbc78 create local.properties for routerjars 2024-03-25 14:08:25 -04:00
812aea9873 clone i2p.i2p in CI 2024-03-25 14:04:49 -04:00
aeb7614b62 clone i2p.i2p in CI 2024-03-25 14:00:30 -04:00
9bf9d27c5a use Java 11 in CI 2024-03-25 13:58:07 -04:00
f2cfe4cee5 start a CI build of the Android APK 2024-03-25 13:55:38 -04:00
45865ff5b9 Increment version code and upload to GPlay 2023-12-19 13:45:24 -05:00
96f9905952 Update I2P library version to 2.4.0 2023-12-19 13:44:04 -05:00
idk
41ca3fe527 tx pull 2023-07-05 18:18:32 -04:00
idk
c5468e4829 migrate transifex configs 2023-07-05 18:17:16 -04:00
idk
174bb7f827 increment version code 2023-07-05 15:51:33 -04:00
idk
3d92f32c13 update I2P library 2023-07-05 13:54:21 -04:00
idk
2fae27a3b3 bump version code 2023-04-15 05:46:17 +00:00
idk
71d38c64e0 fix version 2023-04-15 02:37:48 +00:00
idk
ddb651a602 pull in new translations 2023-03-18 04:04:56 +00:00
idk
a8ad1d8d47 update changelog 2023-03-18 04:02:13 +00:00
idk
912602bfc0 bump versioncode 2023-03-18 04:01:16 +00:00
idk
24e3358ffa check in stats page translation fix, update for release 2023-03-18 01:29:12 +00:00
idk
fcc51c429d update version code again 2023-03-04 23:09:41 +00:00
idk
de0ee87db3 bump versionCode and I2P_ANDROID_VERSION 2023-03-04 23:05:24 +00:00
idk
2102d55315 update javadoc on long copypasta section 2023-02-10 00:21:36 +00:00
idk
2f900ebe2d blocklist entries first attempt 2023-02-10 00:20:48 +00:00
idk
5c03981a61 don't overwrite subscriptions.txt TODO look into possible permissions issues obscuring the behavior of cRTFIA 2023-01-31 22:34:12 +00:00
idk
c68f0ff23a Add broadcast intent change from @RyeMantis to changelog 2023-01-23 00:36:14 +00:00
idk
b01f44ae1f enable adding base32 destinations from the addressbook interface. Allow base32 addresses which contain ports to validate 2023-01-23 00:32:26 +00:00
idk
05aeb0cf37 Merge branch 'remote-start' into 'master'
Allow third-party apps to start I2P on Android via a Bradcast Intent

Closes #39

See merge request i2p-hackers/i2p.android.base!3
2023-01-22 23:28:43 +00:00
fd8d596064 Allow third-party apps to start I2P on Android via a Bradcast Intent 2023-01-22 23:28:43 +00:00
idk
ea3fe136e4 bump versionCode for gplay release 2023-01-11 16:38:32 +00:00
idk
47f4530f6f bump version for release, add jcenter repository to app/build.gradle to resolve missing packages 2023-01-11 16:00:52 +00:00
idk
016a7a47fd check in new translations 2023-01-07 19:54:33 +00:00
idk
f8410deeba -f pull just in case, at some point translation got out-of-sync on my end and needed refreshed, probably good to be sure at release time 2022-12-11 16:25:55 +00:00
idk
7268132ddd Add tag freeze note to RELEASE_PROCESS.md 2022-12-11 02:55:40 +00:00
idk
62545f1247 Target latest SDK version, which requires updating gradle plugin. Add TODO: for maven plugin which breaks on newer versions of gradle 2022-12-09 21:32:59 +00:00
zzz
99b6992b5c Build: Disable lint which fails with a class conflict.
As recommended by idk
2022-12-09 12:56:28 -05:00
zzz
e8ebb0a569 Pull translations from tx 2022-12-06 13:45:55 -05:00
idk
3ab2c83b7b bump release version and version code 2022-11-23 14:53:47 -05:00
idk
cb46abca82 Always compile with the TARGET_SDK_VERSION even if we're compatible with lower versions 2022-11-23 14:09:28 -05:00
idk
63b345e329 Add FLAG_IMMUTABLE recent Android versions, should fix #49 but still untested 2022-11-23 13:30:36 -05:00
idk
794b8433d8 update changelog 2022-11-22 22:12:59 -05:00
idk
a7891a3674 check in version 2.0.0 2022-11-22 11:53:02 -05:00
idk
7076785ec1 add instructions for Privacy Browser 2022-11-09 01:16:17 -05:00
idk
f8eda45409 promote privacybrowser back to supported, demote non-fennec firefoxes to unsupported 2022-11-09 00:53:52 -05:00
idk
8d0f1689c1 Merge branch 'android-build-updates' into 'master'
Android build updates

See merge request idk/i2p.android.base!1
2022-10-19 01:38:22 +00:00
idk
25ef37cddf Merge branch 'master' into 'android-build-updates'
# Conflicts:
#   gradle.properties
2022-10-19 01:38:08 +00:00
idk
c5936858a2 fix Android 12 builds 2022-10-18 21:33:43 -04:00
idk
17d2e2fc93 list all browsers in prep for VPN mode 2022-10-18 20:32:43 -04:00
idk
a0e41e5171 update the changelog 2022-09-08 11:51:50 -04:00
idk
2d1244f339 update versions for F-Droid main 2022-09-04 11:23:37 -04:00
idk
9eec1d3348 checkin android build updates 2022-08-30 11:07:40 -04:00
idk
5824fd4efa update the version code 2022-08-26 22:29:07 -04:00
idk
9c87bfcb51 After un-breaking the gradle build, bump the version to 1.9.0 2022-08-26 22:27:43 -04:00
idk
0c380f2ef5 Fix backward base32 validator condition 2022-08-21 12:10:37 -04:00
idk
dafe3d0002 make base32's greater than 64 chars in length validate 2022-08-08 14:23:20 -04:00
idk
255ac60691 update changelog and fix string 2022-08-08 14:08:59 -04:00
idk
23f681dd7d start retroactively fixing the changelog which I did not update for a while. 2022-07-18 12:47:46 -04:00
idk
d6a55a122a switch back I2P_VERSION 2022-06-26 18:41:02 -04:00
idk
454937d345 checkin release update 2022-06-25 10:16:47 -04:00
idk
ec8cf76fc4 reverse changes to settings_net.xml 2022-06-24 13:34:05 -04:00
idk
6788f9a663 make speed_values an integer array 2022-06-24 01:34:40 -04:00
idk
dc96c6251f remove less-good family keys workaround 2022-06-17 03:13:30 -04:00
idk
c2bced68bb fix missing cert issue 2022-06-16 15:59:40 -04:00
idk
e21b4e5cbd fix .gitignore 2022-05-28 02:13:42 -04:00
idk
1f434d8a05 re-do the AndroidSAMSecureSession so it doesn't require me to ask permission to draw over other apps. Permission to draw over other apps gives apps the power to take screenshots, so it's a special permission on Android. 2022-05-28 02:12:23 -04:00
idk
cd12d84d47 get rid of buggy new-version check which prevents merging of new hosts files. More refactoring of interactive SAM Auth interface. 2022-05-19 18:29:31 -04:00
idk
82b92caa55 only loop for 1 minute before returning false by default 2022-05-19 15:51:56 -04:00
idk
49e4fcc49c refactor SAMSecureSession implementation 2022-05-19 15:41:48 -04:00
idk
a9abe05325 check in new turkish strings 2022-05-19 13:29:24 -04:00
idk
ea6705374f Make the SAM approver do UI stuff on the main thread 2022-04-22 00:46:31 -04:00
idk
3e28f2bf93 A do even more early logging. Certs need to be kept up to date and should be updated regardless of whether it's a new version or not 2022-04-21 17:10:57 -04:00
idk
4c881525f9 Use localized strings for interactive SAM auth 2022-04-21 12:30:34 -04:00
idk
9280c80a53 Add SAM interstitial 2022-04-20 14:11:40 -04:00
idk
e465a6d232 Check in translations 2022-04-18 14:29:54 -04:00
idk
d7f79e7b0b do much more logging in the initialization process. Account for differences in application path when seen from aab vs apk viewpoint. 2022-03-02 17:22:55 -05:00
idk
68f44b8ec1 Bump for release and tag 2022-02-22 15:27:48 -05:00
idk
0835a42fbd Replace donate references with gitlab references 2022-01-07 15:52:02 -05:00
idk
78e649dc9b Bump GPlay version 2022-01-06 20:02:04 -05:00
idk
84e73a693c Bump 1.6.1 2022-01-06 19:32:45 -05:00
idk
c0ad7dfc09 Fix zip path traversal issue(we are likely unaffected since we do not unzip arbitrary zip files, just certificates_zip. Update certificates_zip. 2022-01-06 19:31:25 -05:00
idk
dec68432bc Check in translations. Fix dockerfile. Bump I2P_VERSION. Bump versionCode. 2021-11-30 19:57:51 -05:00
idk
f93fc155ef Merge branch 'Battery-Optimization-Fix' into 'master'
Battery optimization fix

See merge request i2p-hackers/i2p.android.base!2
2021-09-05 20:19:42 +00:00
e348af340a Battery optimization fix 2021-09-05 20:19:41 +00:00
idk
aa3631ed33 Merge branch 'start-on-boot-fix' into 'master'
Fixed start on boot

See merge request i2p-hackers/i2p.android.base!1
2021-09-02 16:04:07 +00:00
idk
f2088ad1e5 bump versions and tag 2021-08-30 09:58:27 -04:00
008e9b7961 Fixed start on boot 2021-08-27 04:22:06 -07:00
idk
90a46bcc3f make sure gitignore and dockerignore are checked in 2021-08-15 14:49:54 -04:00
idk
f69f748064 Checkin release 0.9.50 2021-05-19 09:32:41 -04:00
idk
640a2e1918 Add Docker release instructions, since it takes a bunch of the toolchain-management difficulty and automates it 2021-05-07 02:00:36 -04:00
idk
8aead91700 Add GPG to docker container builds 2021-05-06 17:06:16 -04:00
idk
b18a2e6241 Dockerize the build process to save time setting up releases in the future 2021-05-04 15:02:37 -04:00
idk
09c3e6e12c Remove docker instructions until I have time to write them better 2021-05-03 21:35:28 -04:00
idk
74bf0eade2 Merge branch 'master' of 127.0.0.1:i2p-hackers/i2p.android.base 2021-05-03 21:33:40 -04:00
idk
2fa6e7f3cb Enable Docker builds 2021-05-03 21:32:53 -04:00
idk
24b741be1e Enable Docker builds 2021-05-03 21:32:02 -04:00
zzz
ac8ce6f916 Update and remove old links in welcome_html 2021-05-02 11:50:14 -04:00
idk
cd7d0ad723 Android release 2021-02-28
-----BEGIN PGP SIGNATURE-----
 
 iQFMBAABCgA2FiEEcNIGBzi++AUjrK/311wDs5teFOEFAmAugowYHGhhbmtoaWxs
 MTk1ODBAZ21haWwuY29tAAoJENdcA7ObXhThwQsIAK0A284NXvJsJ7an3CCIf1Ws
 DEYmG1fibJA3BwgSYxgFUy5bgN3tEAEvwldo8PyRq6EBxe419/Ab9awoepx1wmN+
 NtfOOTuB0kHTRKUreZNuhwYUKddNrr7o10WJgHhyj6PPGIau/4XYvTVuQ70hOoI4
 Vw809u7bk5mM4cGCAnxnsgNus7khcwzbDeYZI5CbbEClk+QcKHTDkC1sMCo9vqTo
 bbS+zIoy9G+4k6LCaiC0QL6GeLcwns2vtRfDaXgImjoDGx9TL0nm/USBwsllkkmW
 +GZAvbck43yh8DixuERHN6NncuF0EI01etgvl4H8no31tQxVU+7UsZWTkz1uyKQ=
 =tJK3
 -----END PGP SIGNATURE-----
gpgsig -----BEGIN PGP SIGNATURE-----
 
 iQFMBAABCgA2FiEEcNIGBzi++AUjrK/311wDs5teFOEFAmCFoG4YHGhhbmtoaWxs
 MTk1ODBAZ21haWwuY29tAAoJENdcA7ObXhThM08H/2+D0RCbP0/2QxYxFsf3NuZp
 UizMCnVqKCoomN1dChLs1kw00dJXAEAGtFZqzT8nbUHD8W98pljFc5j0qJ3BLQNU
 xrNHkE3KiwHU0ACZvrIPN9X1bj3uhNnziblPlpEBpMq/p3W8oKUWnLr9Zv8Gyetu
 Cpqjr/p4OaYpTUHLzp9NWltUpDDul0Mgy+lmhtUbYfNW4NtlVMAfOpNvBwFDcYGn
 FW4cP7t8nPzv4Cdzn5SD19KFCgGxHEFkWCF2uiJTiXOdi7LNsljKEgdMVNokO5Av
 zF97Cr+alLhg/GnxYeUB/Tgn4mTOBqzM4ir8EhIb+APWYowoWUossr68mgZ5aco=
 =BGmd
 -----END PGP SIGNATURE-----

Merge tag 'android-0.9.49'

Android release 2021-02-28
2021-04-25 13:01:31 -04:00
zzz
c5a0c3608d Drop .mtn-ignore 2021-04-25 07:11:49 -04:00
idk
3863c0e183 Increase version numbers for release 2021-02-18 10:05:20 -05:00
idk
69ea3d8bde remove trailing slash from gradle.properties 2021-02-10 17:25:32 -05:00
idk
7cafe6da48 add gitlab SSH and HTTP to i2ptunnel_config. Invalidate the view containing the I2P tunnel lists, in order to force them to redraw when a new tunnel is created. Closes #4 2021-02-04 12:41:03 -05:00
idk
9464b46ad0 Change the foreground service notification priorities from PRIORITY_MIN to PRIORITY_LOW. Don't check if we're restarting on Android versions greater than O since it uses the notificationChannel. Closes #5 2021-02-03 14:25:08 -05:00
idk
c230a5a101 Create the notification channel right before updating the statusbar notification. 2021-02-02 01:22:37 -05:00
idk
3d37f2ae07 use a toast for clipboard notification instead 2021-02-02 00:38:50 -05:00
idk
f88aafe292 fix the notification channel for the main status 2021-02-01 21:22:51 -05:00
idk
aa2dab1d3c Re-enable NTCP because it fixes the firewalled but inbound TCP not enabled issue 2021-01-29 16:45:07 -05:00
idk
8e85eaa2f0 SAM needs run() instead of start() to work correctly 2021-01-29 16:05:41 -05:00
zzz
6a1848caf6 Startup: Don't sleep in startup Jobs, requeue instead, to not clog the job queue 2021-01-27 09:09:52 -05:00
idk
aa36b4cb14 add facility to copy base32 URL of a tunnel by long-pressing the tunnel name 2021-01-18 11:58:48 -05:00
idk
78d4b12142 Remove nonsense comment at the end of the line which was covered up by the terminal 2021-01-11 10:46:15 -05:00
idk
c2f3a80dec Correctly fetch new SAM API preference when starting the SAM API job 2021-01-11 10:45:19 -05:00
idk
a679784aab Fix some build issues(Stemming from resources moving to wars) and add a SAM API option 2021-01-10 09:23:43 -05:00
idk
835667437b Fix some build issues(Stemming from resources moving to wars) and add a SAM API option 2021-01-10 09:20:45 -05:00
idk
f016edec7a add logo 2021-01-09 17:26:18 -05:00
1e3b517219 Bump target requirement to 29 for GPlay 2020-12-03 04:19:24 +00:00
f4e3b15fcf add new translation 'tk' 2020-12-02 22:51:06 +00:00
fc4154be67 Bump version for release 2020-12-02 22:48:07 +00:00
5bf0b18767 Add IceRaven configuration recommendation. 2020-11-11 05:28:17 +00:00
eb5ef3129b Add IceRaven configuration recommendation. 2020-11-11 05:26:05 +00:00
6e87d248c0 update changelog for 0.9.47-1 2020-10-30 12:39:27 +00:00
c9b0aff142 Add pushing mtn tags to the RELEASE-PROCESS.md so nobody confuses gitisms and mtnisms again 2020-10-30 02:44:26 +00:00
5acac0dbc4 increment version code 2020-10-29 19:38:28 +00:00
0b42a7ee64 add an example override.properties 2020-10-29 04:00:53 +00:00
c3a798ee3d add an example override.properties 2020-10-29 04:00:27 +00:00
e682369311 slightly better bootclasspath example in RELEASE-PROCESS.md 2020-10-28 21:40:28 +00:00
14b953f145 add classpath and Java Version requirements to RELEASE-PROCESS.md 2020-10-28 20:29:19 +00:00
036c807d6b Explicitly enable NTCP2 2020-10-28 02:00:52 +00:00
a41fca95df disable ntcp 2020-10-27 23:13:33 +00:00
26fdf40f25 merge of '5285d38e1dec9ca4af6fed9b10029de9c622a443'
and '890c3b02b48b2ff11946ccea164dad683aa2e8fd'
2020-10-27 20:29:03 +00:00
070af6529c Only ever pop the batter interstitial one time, no matter what, since we can't rely on the Android API to tell us the right thing 2020-10-27 20:28:41 +00:00
zzz
7ba0892351 Remove outproxy that's down 2020-10-26 17:30:52 +00:00
5b9cdb9f9f update release process again 2020-09-13 14:45:07 +00:00
zzz
b79d39a74a date format fixes 2020-09-03 15:07:09 +00:00
5fc5aed0c9 update the maven release instructions 2020-09-03 14:13:56 +00:00
aec25ab374 update build files for the release 2020-08-26 20:07:21 +00:00
zzz
9d5c495936 remove old subscription 2020-07-04 12:31:16 +00:00
4dc2bb6b01 check in new string for new foreground task 2020-06-29 23:28:18 +00:00
373e013911 fix foreground service notification issue on android 8.1 and greater 2020-06-29 20:12:32 +00:00
83bb7096a7 merge of '412007995e44aa866ff698a168742c4c330a62ef'
and '5bb8ab81cd3ab734f3c6b6d67d53eff28c5f5558'
2020-06-07 16:11:38 +00:00
cc1c4690a2 fix unchecked-in version code bump 2020-06-05 16:13:01 +00:00
69ad581235 update the changelog 2020-06-03 04:51:36 +00:00
4be227631d Bump version to 0.9.46 2020-06-03 00:20:01 +00:00
3f3f1f8e3d Catch the AndroidRuntimeException and warn if we can't change the power save permission 2020-06-02 22:40:58 +00:00
22290da1a4 merge of '01b01ebb27ab903f97975265856546c244185909'
and 'ac15ee2b1ce53342c8bfea5ccd3360220e615471'
2020-03-28 17:16:03 +00:00
7caf21d552 I2P Android 0.9.45 release commit 2020-03-08 04:51:08 +00:00
701860a525 Translations update in helper 2020-03-08 04:50:35 +00:00
7615aca89e Translations update 2020-03-08 04:50:07 +00:00
cae8ed2ae3 Remove arch x86 and mips due to google requirements 2020-03-08 04:49:49 +00:00
zzz
a523e1cb4a update default irc server list 2020-03-04 13:29:13 +00:00
5e048af9c1 Google said no. The 0.9.44 release would be based off this commit instead as it's has the current minimum target sdk. 2019-12-03 15:42:13 +00:00
77b6c4d30f Release commit for Android I2P 0.9.44 2019-12-03 15:33:29 +00:00
zzz
983a94e1c4 Update changelog after revert of:
Revision: 034402ca4ed4243122bd88857c7d306e295c9fbe
  Parent:   9f9a905cf75324471b3a8eb41da2feebf620207c
  Author:   meeh@mail.i2p
  Date:     10/27/2019 15:00:33
  Branch:   i2p.android.base
  Changelog:
  Merged two heads
2019-10-28 15:09:23 +00:00
zzz
1f79323d66 merge of '1d4693c3e133c94344da0418287d05613bfe34f5'
and 'cab71a0c3af65109945e3b7e4f927825e8e7deb4'
2019-10-28 15:05:50 +00:00
zzz
d86d3ad5ae disapproval of revision '034402ca4ed4243122bd88857c7d306e295c9fbe' 2019-10-28 15:05:37 +00:00
c41b064045 Android version 0.9.43 2019-10-27 15:01:28 +00:00
4c299ecda3 Merged two heads 2019-10-27 15:00:33 +00:00
761f427366 Translations update 2019-10-27 15:00:12 +00:00
f4c4bfe8be string improvements and remove redundancy in battery optimization fix 2019-10-12 17:49:06 +00:00
7f15a6f1e1 Inform the user of improved perfomance by excepting us from battery optimizations and prompt for permission to do so 2019-10-11 21:34:46 +00:00
zzz
5734760d58 Hide tunnel actions while TCG is starting 2019-10-11 17:12:33 +00:00
zzz
3244adfcd2 Rework fix for ticket #2629 to catch the null TCG sooner
javadocs
2019-10-11 16:52:43 +00:00
zzz
ce62b0fb97 Force logo to stopped if no router
Set logo for graceful shutdown
2019-10-11 16:25:43 +00:00
zzz
64673ee185 Change message for tunnel list when TCG not running yet 2019-10-11 16:20:06 +00:00
zzz
a36cabdcc8 Fix ISE in language dialog (ticket #2631) 2019-10-10 12:48:14 +00:00
1e8531c731 Fix for #2629, at least this is what we can do in the android end without adding new strings. 2019-10-09 20:19:24 +00:00
zzz
0935659d6d Save state to preferences in background thread (tickets #2595, #2632) 2019-10-08 15:45:34 +00:00
zzz
bef5f7e746 more README updates 2019-10-08 14:16:32 +00:00
zzz
d64e8359c1 Add dev build info to README 2019-10-08 14:11:20 +00:00
5fd77ea62d changelog reminder in RELEASE-PROCESS.md 2019-08-31 00:22:36 +00:00
8626ac2913 Changelog update 2019-08-31 00:21:05 +00:00
1258f18bc3 Android 0.9.42 2019-08-30 17:32:06 +00:00
8b677abd3c Hopefully a fix for #2598 2019-08-27 13:40:45 +00:00
f3d1e89002 Release commit for 0.9.41 2019-07-05 09:29:04 +00:00
zzz
f39c9a0fc4 changelog
one final
2019-06-30 16:00:48 +00:00
3186caa6bb use a timed task.get to avoid the issue in 2491 2019-06-30 15:43:35 +00:00
fc1259d8a8 fix gradle.properties 2019-06-21 14:22:01 +00:00
848d07331e fix the missing import 2019-06-21 03:28:33 +00:00
zzz
9794d26d0c Update client and helper lib min SDK to 14
Update helper support lib dependency version
2019-06-20 17:43:16 +00:00
76cd9c85ef merge of '668d7d7863ed98df6cac1ba0fe2d435570a36efb'
and 'adf749373c41853aa682a705dae3a5da0c52713b'
2019-06-20 16:30:22 +00:00
d4c7c480fb merge of '36107760d6d841ea86b860db02529fb4d089febc'
and '6f181c44ff5071894c19222047b2e90ad3ab4af5'
2019-06-20 16:24:54 +00:00
00aa80d104 fixed 2522, browsers may be installed while the application is running 2019-06-20 16:24:47 +00:00
zzz
20086685aa lint 2019-06-20 14:21:48 +00:00
zzz
86a8effd82 lint: This broadcast receiver declares an intent-filter for
a protected broadcast action string, which can only be sent by the system,
not third-party applications. However, the receiver's onReceive method
does not appear to call getAction to ensure that the received Intent's action string
matches the expected value, potentially making it possible
for another actor to send a spoofed intent with no action string
or a different action string and cause undesired behavior.
2019-06-20 14:13:10 +00:00
zzz
55d6e6d24e suppress lint 2019-06-20 13:52:37 +00:00
zzz
3b05046df3 Catch WindowManager$BadTokenException in WebViewClient (ticket #2390) 2019-06-20 13:03:37 +00:00
zzz
b8587cd0ab Don't store ContentResolver with a Context ref in AppCache static ref;
pass the Context to each call instead.
This may be the cause of some WebView leaks/crashes
2019-06-20 12:46:37 +00:00
zzz
61dd550040 lint: Remove NewsFetcher static ref;
Turn it into a ClientApp and register with the ClientAppManager
so NewsFragment can find it.
Let ClientAppManager start and stop it.
2019-06-19 15:06:38 +00:00
478ff63889 remove ! emphasis 2019-06-19 14:03:21 +00:00
e747619b85 merge of '0d35cf2746cae62a4bec33eaaf8f58302c3485aa'
and '327097d70c3dd5045a07be4e0367d91e3073a321'
2019-06-19 14:01:18 +00:00
zzz
f043bb3d72 lint: Bump espresso-core dependency from 2.2.2 to 3.0.2 2019-06-19 12:45:30 +00:00
zzz
98d8106f56 lint: Replace HashMap with SparseArray
finals
2019-06-19 12:37:01 +00:00
zzz
74eb0ce4f6 add lint notes 2019-06-18 19:37:12 +00:00
zzz
d79813d6d1 lint:
Consider using apply() instead; commit writes its data to persistent storage immediately,
whereas apply will handle it in the background

javadocs:
Unlike commit(), which writes its preferences out to persistent storage synchronously,
apply() commits its changes to the in-memory SharedPreferences immediately
but starts an asynchronous commit to disk and you won't be notified of any failures.
If another editor on this SharedPreferences does a regular commit() while a apply() is still outstanding,
the commit() will block until all async commits are completed as well as the commit itself.

As SharedPreferences instances are singletons within a process,
it's safe to replace any instance of commit() with apply() if you were already ignoring the return value.

You don't need to worry about Android component lifecycles and their interaction with apply() writing to disk.
The framework makes sure in-flight disk writes from apply() complete before switching states.
2019-06-18 19:25:44 +00:00
zzz
8f60c6ce9e lint: Specify locale in case conversion 2019-06-18 19:12:32 +00:00
zzz
7e0d017858 lint:
Must be one or more of: Service.START_FLAG_REDELIVERY, Service.START_FLAG_RETRY
2019-06-18 19:05:12 +00:00
zzz
8470435ee2 lint:
The WIFI_SERVICE must be looked up on the Application context or memory will leak on devices < Android N.
Try changing context to context.getApplicationContext()
2019-06-18 18:54:25 +00:00
5023d69222 This solves #2552 hopefully. 2019-06-18 18:09:03 +00:00
34c7464f5b fix inconsistency 2019-06-18 01:44:54 +00:00
2da6fe9c62 experimental browser documentation 2019-06-18 01:08:03 +00:00
1927c9e5a3 extend instructions for Firefox 2019-06-17 21:50:07 +00:00
1ace085d13 updating browser documentation 2019-06-17 20:09:31 +00:00
zzz
95e6c1f7a6 New 64 bit libjbigi (ticket #2503), update 32 bit jbigi
With GMP 6.1.2 and Android NDK r19c
From i2p.i2p/core/c/jbigi:
TARGET=android BITS=64 mbuild_all.sh
TARGET=android BITS=32 mbuild_all.sh
Both tested on phones.
Note lib/client/src/main/jniLibs/build.sh is broken
2019-06-05 18:37:19 +00:00
zzz
03bdd575a7 New translations: Add cs, da, el
Add missing translations to menu: ar, fi, gl, hu, zh_TW
2019-06-02 12:23:10 +00:00
zzz
98c5313d75 Add spacer between addresses in RI output 2019-06-02 11:41:45 +00:00
zzz
c047bdf085 Use new 0.9.41 TCG.getInstance() method for Android
so instance with stale context is not returned.
2019-06-02 11:10:04 +00:00
zzz
e113ef0002 build: Add streaming dependencies
(fixed streaming not found at runtime)
2019-06-01 18:40:26 +00:00
zzz
3e9b47307d i2ptunnel: Drop welterde IRC tunnel 2019-06-01 17:57:48 +00:00
zzz
2d864ad8b1 Don't start Addressbook and NewsFetcher until i2ptunnel starts
Changelog updates
2019-06-01 15:00:14 +00:00
zzz
5b7f9bd452 If router stops before ready, stop RunI2PTunnel job 2019-06-01 14:04:00 +00:00
zzz
3c89749f94 Set disableInterface property string like the others 2019-06-01 12:07:06 +00:00
zzz
edbd5fd7ea Don't zero-pad slider value 2019-06-01 12:05:45 +00:00
zzz
faf8bf74af add some padding to the tables 2019-06-01 00:25:38 +00:00
zzz
851e774e7a Update home page floating menu visibility when clicked, not just when page is loaded
Put our router info at the top of the list
Release process additions
2019-06-01 00:07:23 +00:00
zzz
dcea801116 UPnP: Add multicast permissions, use callback for MulticastLock (ticket #2499) 2019-05-31 21:51:12 +00:00
zzz
cb5235e6da add new outproxy 2019-05-31 21:26:32 +00:00
zzz
5c7eaf2484 remove BOB 2019-05-31 21:16:15 +00:00
764cfc91ec Instructions for development builds 2019-05-29 18:11:51 +00:00
177a2c6dc1 Required changes to gradle for development builds. 2019-05-29 17:37:48 +00:00
4dccd1dbae Clarify that the client library should always be released 2019-05-16 08:18:32 +00:00
50141eb24b release docs update 2019-05-13 20:03:34 +00:00
4dab632bc8 Release process docs update 2019-05-13 14:21:12 +00:00
350515041a I2P Android release commit for 0.9.40 2019-05-10 22:26:04 +00:00
5ba294c2c2 Updated the release docs with a lot more information. 2019-05-10 22:23:57 +00:00
a215363206 merge of '252f48089a9d496c609cf1b886d8d56c65836851'
and '2c8389bae9c93fb46e31edc66f1c292a95bf87eb'
2019-05-10 22:16:39 +00:00
4bd647c67c Translation updates. 2019-05-10 22:16:29 +00:00
zzz
b951892c05 Util: Check for NTCP2 in getNetStatus() 2019-05-02 14:29:59 +00:00
zzz
16e05e0dd8 Fix NPE in ViewPager (ticket #2488)
See code comments for references
2019-05-01 17:34:45 +00:00
5bdf119b81 merge of '67e6f911bbdcd72a5910ab653cf40eca13f08659'
and 'f846922ebab446b76ccee6c4d10f873af2d3d4ac'
2019-05-01 13:00:34 +00:00
b73b72c9c8 Fix for trac ticket #2485. 2019-05-01 12:59:56 +00:00
zzz
0bae211da5 Fix addressbook crash (ticket #2489) 2019-04-30 21:14:52 +00:00
zzz
709392e8b6 UI: Classify NTCP2 as NTCP
Don't crash on unknown transport configuration (ticket #2482)
2019-04-30 14:25:29 +00:00
zzz
80ed1e71da i2ptunnel: Possible NPE fix (ticket #2483) 2019-04-29 16:33:32 +00:00
6c0a60892f Fix for trac issue #2481. 2019-04-29 16:18:43 +00:00
zzz
7f13aa26fb i2ptunnel: more finals 2019-04-29 13:34:11 +00:00
zzz
7167a11844 i2ptunnel: finals 2019-04-29 13:28:18 +00:00
zzz
1f140bf95a Settings: Fix saving I2CP config (hopefully) 2019-04-29 12:47:21 +00:00
zzz
940b2b83a1 New translations: ar, gl, hu 2019-04-24 14:10:53 +00:00
zzz
bdbebe11c4 reduce min translate percentage 2019-04-24 14:07:07 +00:00
zzz
51ca137102 Update news URLs 2019-04-23 18:34:52 +00:00
f0ff4eeab7 Temporary fix for Javadoc errors 2019-04-22 07:35:37 +00:00
zzz
93d103e5ad Re-enable the advanced config for i2cp.disable,
so third-party apps can connect via the standard socket.
Domain sockets don't work on recent androids for 3rd party apps.
Bote can continue to use the domain socket if it's signed by the same key
and has the same ID.
2019-04-12 17:05:25 +00:00
b87d77d5e3 Android release commit 0.9.39
* translation updates.
* changed IPC uid to match bote.
2019-03-23 12:18:13 +00:00
ffbd8cfb76 I2P Android 0.9.38 Release commit. 2019-01-27 14:23:08 +00:00
2d1664574d "Fix" for ticket #2404 2019-01-24 18:33:26 +00:00
5d3aa1f625 Strings update 2019-01-24 18:21:20 +00:00
3fa53c7654 Expanded code to hunt NPE - ticket #2389 2019-01-24 18:21:01 +00:00
84ecf55ff8 Play store required a new android number even the first wasn't published. 2018-10-10 18:07:28 +00:00
72ad40ecfc Hotfix update cause of play console warnings.
Short: We had to upgrade to sdk 26 to ensure the app can run on all devices.
2018-10-10 17:56:38 +00:00
2f48898235 Release commit for I2P Android 0.9.37 2018-10-10 17:00:45 +00:00
39758c8cf4 Disable the SSL option for now. See trac issue #2296 2018-10-10 16:09:19 +00:00
ecc5509007 Gradle wrapper update 2018-10-10 16:07:28 +00:00
0e75b3e957 Android studio gave me some warnings against using ellipsize and maxLines together and suggested singleLine. 2018-10-10 16:06:39 +00:00
7b4c80216d Translations update. 2018-10-10 16:03:39 +00:00
70bbc18054 merge of 'bc6eaecbdac3f21b425ed335e348b1c505094d70'
and 'cd8c7bf753e03b16fd44f0516d6fd4fbd649fc5f'
2018-08-27 16:14:56 +00:00
09fcef23a4 Updates for the new i2p android 0.9.36 release. 2018-08-26 15:45:25 +00:00
zzz
333f09073a minor updates from tx 2018-07-04 13:58:17 +00:00
58cb33aa77 Updates for the new i2p android 0.9.35 release. 2018-07-03 21:42:09 +00:00
9654fa24cc Pushing 0.9.34 changes. This is the release commit. 2018-04-25 14:34:08 +00:00
7843b37a7e Release process document 2018-02-18 21:17:41 +00:00
1db9128afc 0.9.33 2018-02-17 14:57:43 +00:00
c03d3a8b92 Updated translations 2018-02-17 14:07:52 +00:00
80b7455602 Add release date to CHANGELOG 2017-11-28 10:03:51 +00:00
1fcf5aa49b 0.9.32, helper 0.9.5 2017-11-28 09:44:50 +00:00
640803418d Bump to I2P 0.9.32 2017-11-28 09:42:39 +00:00
56fa0b0302 Rename _() for translation to _t() for Java 9 compatibility 2017-11-26 13:25:58 +00:00
9c10eef0e3 Attempt to fix WindowManager$BadTokenException crash
Use the parent fragment's Context, which should be more reliable than the one
from the WebView.
2017-11-26 13:17:00 +00:00
9fd5e43115 NPE fix 2017-11-25 23:58:38 +00:00
d5bd9b8eaa Unused import 2017-11-25 23:42:01 +00:00
5b1203a1c6 IAE fix 2017-11-25 23:39:58 +00:00
fbe79eee2e Replace PNG icons with SVG
This doesn't shrink the APK size (yet), as we still generate the PNGs for older
API levels.
2017-11-25 23:31:32 +00:00
ffa21fc1e0 Drop unused icons 2017-11-25 22:39:59 +00:00
5faf1f5bb0 Add a "sync" icon to more clearly indicate tunnel "starting" status 2017-11-25 22:33:10 +00:00
e15efb6537 ActivityNotFoundException fix 2017-11-25 22:06:47 +00:00
cd1702d53c Update classname in logger.config 2017-11-25 21:06:38 +00:00
5a6ca8a0a4 Add note about possible ANR to watch for 2017-11-25 19:58:35 +00:00
82d184cf90 Fix ANR when restarting all tunnels 2017-11-25 19:43:14 +00:00
5162bb604b Delay loading of tunnels and addressbook until router is running
This should help avoid a race condition where the RouterContext is available
before the Router is fully-initialised (see ticket #2092).
2017-11-25 19:22:49 +00:00
3aff2a7a9d SecurityException fix 2017-11-25 17:01:55 +00:00
cae565761d IAE fix 2017-11-25 16:00:46 +00:00
c2b6cee9a2 ActivityNotFoundException fix 2017-11-25 15:44:47 +00:00
84e7b1f41c Update Firefox browser config instructions for Firefox Quantum 2017-11-25 15:37:48 +00:00
7ebed1e6d2 NetworkOnMainThreadException fixes 2017-11-25 14:27:10 +00:00
2e68122b8d Updated translations 2017-11-25 14:18:52 +00:00
899a3f4cfc ISE fix 2017-11-24 17:33:38 +00:00
52d49a4ab6 NPE fix 2017-11-24 17:07:49 +00:00
75f705125f ISE fix 2017-11-24 17:00:11 +00:00
722ddf8a47 NPE fix
The advanced menu options used to be in a sub-toolbar, so onPrepareOptionsMenu
was called to set their visibility. When we moved to a FAM, we didn't remove
this call, and in some newer Android versions this results in a race condition.
2017-11-24 16:21:56 +00:00
524d21631e ISE fix
Switch to using Router directly, which means we don't have a potential race
condition looking up the RouterContext.
2017-11-24 16:08:56 +00:00
86e6060217 NPE fix 2017-11-24 15:54:24 +00:00
b5c7fad876 ISE fix 2017-11-24 15:50:49 +00:00
5f3ca0fe69 NPE fix 2017-11-24 15:34:10 +00:00
ddd9bea786 Upgrade test dependencies 2017-11-24 15:13:41 +00:00
6aac99e7ea Upgrade to Gradle 4.1 and Android Gradle Tools 3.0.1
Required dropping gradle-witness because it does not support modern Gradle.
2017-11-24 14:35:07 +00:00
da763a7c81 0.9.31 2017-08-19 22:22:56 +00:00
6cb46c3168 Helper library 0.9.4 2017-08-19 22:01:34 +00:00
610e963d22 Update translations 2017-08-19 21:54:46 +00:00
96b8ed43e0 Fix NFEs 2017-08-19 00:07:22 +00:00
812c28cd33 Fix tunnel details IOOBE, expose errors to user 2017-08-18 23:20:42 +00:00
3f7312653a Update translations 2017-08-18 23:10:45 +00:00
c89d3992c7 Add Firefox Focus to unsupported browsers list 2017-08-18 23:08:48 +00:00
d9394685c9 Show tunnel status on details page, use it for transition in place of text 2017-08-18 23:04:46 +00:00
b140158b24 Fix tunnel details IAE, expose errors to user 2017-08-18 21:30:53 +00:00
4f5b0bd21a Correct Ed25519 name 2017-08-18 21:14:00 +00:00
1d17d89ccb Change default tunnel SigType to Ed25519 2017-08-18 21:13:05 +00:00
b6074da7c4 NPE fix 2017-08-18 20:41:22 +00:00
c0fdb4aff7 Validate numbers in tunnel wizard
This shouldn't be necessary, because the TextView is constrained to only accept
numbers, but someone on Google Play ran into a NumberFormatException, so...
2017-08-18 20:28:53 +00:00
e00c9cc449 Try to fix obscure NPE where none should occur 2017-08-18 19:34:55 +00:00
423ca46672 Target SDK 25
Initial testing doesn't show any permissions problems, and enabling this will
help uncover user issues.
2017-08-18 19:18:45 +00:00
66ed9d94a7 Update Firefox browser config instructions
Closes #1977.
2017-08-18 18:03:04 +00:00
a68ef9d372 Update translations 2017-08-18 16:10:26 +00:00
cb389123c5 Hide peers FAM item
From 0.9.31 the backend used to render the HTML table has been moved. We either
need to create a dedicated Android UI for this at some point, or remove it.
2017-08-18 15:39:09 +00:00
62cca0ed50 Bump I2P dependencies to 0.9.31 2017-08-18 15:36:07 +00:00
c99e3c0b41 0.9.30 2017-05-20 14:35:50 +00:00
302c51ccfa Handle IAE 2017-05-20 10:59:12 +00:00
1e34bc2159 Ensure debug build is debuggable 2017-05-20 10:39:14 +00:00
24f6f4789d Fetch WebView Context on UI thread 2017-05-20 10:31:33 +00:00
2de11a4067 Update translations 2017-05-20 09:32:23 +00:00
d83a2f9919 Updated translations 2017-05-15 12:09:43 +00:00
bf36b4c2e6 Replace anonymous DialogFragment subclasses with full subclasses
Closes #1916, #1917, #1923.
2017-05-15 12:05:10 +00:00
daa0b739a3 Use NamingService.requestUpdate()
Closes #1972.
2017-05-15 01:07:41 +00:00
5e1b0d9b50 Upgrade Android build tools, Android and I2P dependencies 2017-05-14 23:58:44 +00:00
917742847a 0.9.29 2017-03-27 03:31:16 +00:00
9460e3202f Helper library 0.9.3 2017-03-27 01:36:52 +00:00
5f388a7c6b Updated translations 2017-03-27 01:22:51 +00:00
39d5de7eb4 Upgrade I2P dependencies to 0.9.29 2017-03-27 01:20:24 +00:00
0fb1ef881c Upgrade Android dependencies 2017-03-27 00:40:05 +00:00
8230769191 Upgrade Android support libraries to 25.3.0 2017-03-26 09:22:22 +00:00
19036a71cb 0.9.28 2017-01-02 12:33:56 +00:00
40f3fbf9c5 Add -dontobfuscate to ProGuard rules 2017-01-02 12:08:30 +00:00
1127fb0195 Helper library 0.9.2 2017-01-02 12:06:26 +00:00
df81efe6bc Fix height of address book list entries 2017-01-02 10:57:40 +00:00
784ca3691b Collate TODO items 2017-01-02 10:07:26 +00:00
0fa4241ce6 New translations 2017-01-02 09:44:58 +00:00
5063d276de Updated translations 2017-01-02 09:44:39 +00:00
81d0e43f0f Upgrade Android Gradle tools to 2.2.3 2017-01-02 09:42:40 +00:00
1637a9007d Upgrade Android support libraries to 25.0.1 2017-01-02 09:41:58 +00:00
ce0f01cf46 Update I2P dependencies to 0.9.28 2017-01-02 09:35:15 +00:00
dd579d4f5b 0.9.27 2016-11-20 07:12:16 +00:00
5703d8cc6d Updated translations 2016-11-20 06:06:09 +00:00
b8768ae9fe Helper library 0.9.1 2016-11-20 05:50:53 +00:00
54dc2c88bf Make it easier to test helper library against debug I2P Android builds 2016-11-20 05:50:35 +00:00
dba01b8c18 Remove debugging lines 2016-11-20 00:18:34 +00:00
b7b3eb7019 Refactor libraries:
- Remove client library dependency on helper library
- Separate client and helper library versions             
- Bump client library version to track I2P library version
2016-11-20 00:00:03 +00:00
430e2ab826 Remove kytv's IRC server from default tunnel list 2016-11-12 19:27:39 +00:00
87383a2ec8 Upgrade Espresso to 2.2.2, update tests 2016-11-12 19:19:38 +00:00
f63bfe1dea Bump build tools to 25.0.0 2016-11-08 06:45:08 +00:00
ff2021c0aa Upgrade Android support libraries to 25.0.0 2016-11-08 06:20:05 +00:00
51f7e07080 Remove unnecessary dependency from client library 2016-11-08 06:02:07 +00:00
7797e067a5 Upgrade I2P dependencies to 0.9.27
The router JAR is now fetched instead of built locally
2016-11-08 05:52:40 +00:00
cf09a21f1e Upgrade Android Gradle plugin 2016-11-08 05:51:40 +00:00
914294927d Updated CHANGELOG 2016-06-13 13:12:50 +00:00
bd0455c413 0.9.26 2016-06-13 13:12:28 +00:00
97f3d937ee Client library 0.9 2016-06-13 12:26:32 +00:00
ff102bfe73 Update dependencies 2016-06-13 12:12:37 +00:00
e31a350398 Enable use of debug builds of I2P with release versions of helper library 2016-06-13 12:03:59 +00:00
43a8f29794 New translations 2016-06-13 11:59:59 +00:00
bbca783b20 Updated translations 2016-06-13 11:59:14 +00:00
6d4fe52f8e Fixed crash when adding tunnel to empty list 2016-06-13 09:57:58 +00:00
ecb08a54fb Upgrade Android support libraries 2016-06-13 09:56:41 +00:00
7bd4524fd8 Update README 2016-06-13 08:41:08 +00:00
40f08d56f6 Use uploaded I2P client libraries instead of locally-built 2016-06-13 08:38:43 +00:00
fe61e35146 Undo accidental checkin 2016-05-29 11:50:34 +00:00
be3f74d71f Fixed "I2CP already listening" bug 2016-05-29 05:09:51 +00:00
8dcfa816e3 Migrate to dynamically-loaded Android-specific classes
Requires i2p.i2p revision fc46f2d84625265a3899b5ad50af5e91d396ba01 or upcoming
release 0.9.26
2016-05-28 23:52:37 +00:00
ae05e22670 Fix legacy package name in helper lib 2016-05-28 23:50:46 +00:00
79a4fa0407 Upgrade Android Gradle plugin 2016-05-28 23:49:50 +00:00
bb958b969a Updated CHANGELOG 2016-04-17 14:05:34 +00:00
02030454d1 0.9.25 2016-04-17 14:00:47 +00:00
e396b0b614 Client library 0.8
i2p.i2p tag: i2p-0.9.25
2016-04-17 13:08:33 +00:00
91cac6b743 Updated CHANGELOG 2016-04-17 12:53:16 +00:00
2a6015d890 Update README 2016-04-17 12:50:47 +00:00
049b094627 Updated translations 2016-04-17 12:41:59 +00:00
077d062e19 Update ignores 2016-04-17 12:39:33 +00:00
4ad483db71 Set colour of news notification to match persistent one 2016-04-17 12:29:47 +00:00
3edb8ad0c2 Fix client lib build system 2016-04-17 12:27:52 +00:00
769f41afe6 Update Gradle wrapper to 2.12 2016-03-28 10:30:49 +00:00
5a9d943a6c Upgrade support libs in client and helper libs, partially fix ProGuard issue
A release build can now be made (ProGuard no longer complains about duplicate
JARs), but the first build attempt after a clean fails because the I2P JARs
needed by the client library aren't on the javac classpath.
2016-03-28 10:27:16 +00:00
44fb246288 Upgrade support libraries to 23.2.1 2016-03-28 03:06:46 +00:00
3cc7498e66 Upgrade Androidplot, sort remote dependencies 2016-03-28 02:33:27 +00:00
84ce883285 Missing witness hash from previous commit 2016-03-28 02:17:43 +00:00
f8dd9df285 Migrate to support library PreferenceFragmentCompat 2016-03-28 02:15:32 +00:00
860cf6a658 Update Samsung 4.2 workaround for support lib 23.1.1 2016-03-09 20:39:15 +00:00
a24a50ce44 Missing files 2016-03-05 15:34:47 +00:00
fc9187297b Update CHANGELOG 2016-03-05 00:34:00 +00:00
eb26df874d NPE fix 2016-03-05 00:33:08 +00:00
27cbb1e57b Cut off speed at KBps 2016-03-05 00:10:16 +00:00
66aa79f90f Missing from previous commit 2016-03-04 23:44:31 +00:00
8b9a70b386 Fix localisation path 2016-03-04 23:35:17 +00:00
872a2d15e2 Update translations 2016-03-04 23:27:12 +00:00
7085567a08 Try to fix ClassNotFoundException 2016-03-04 23:15:58 +00:00
19b07a8a8c Add UI for copying server tunnel B32 2016-03-04 04:49:34 +00:00
0ed78a4806 Extract UI helper and AIDL interfaces to a helper library
For apps that only want to use e.g. HTTP proxy tunnel, this will decrease the
size of their APK by over 1.3 MB.
2015-11-23 03:44:22 +00:00
ca8fb4663f Missing from previous commit 2015-11-23 03:14:33 +00:00
ec34ce481e Moved client library into subdir 2015-11-23 03:04:43 +00:00
b0c4089e26 NPE fix 2015-11-22 04:52:13 +00:00
b23148c71d Updated translations 2015-11-22 04:37:39 +00:00
c9a336a0a5 Updated dependencies 2015-11-22 04:34:43 +00:00
0744426c15 Workaround for Samsung Android 4.2 bug 2015-11-22 03:39:54 +00:00
e6f4bd5531 Updated CHANGELOG 2015-10-10 22:16:07 +00:00
ab6f4799c9 0.9.22 2015-10-10 22:10:41 +00:00
f4beecead3 Added new client library translations 2015-10-10 11:43:12 +00:00
17e6d56bb7 Add false.i2p as SSL outproxy to default httpclient tunnel 2015-10-10 11:25:16 +00:00
29881b73f9 Fixed elevation of tabs 2015-10-10 11:16:50 +00:00
8c30582b9a Updated dependencies 2015-10-10 11:07:48 +00:00
06de8abb44 Re-add fixed translations for big text notification 2015-10-10 08:43:55 +00:00
a65ee65606 Updated translations 2015-10-09 22:56:07 +00:00
a6f49168b7 Add color to notification icon 2015-10-09 11:06:40 +00:00
1d1e6121fa Show addressbook tab when selecting address 2015-10-09 02:42:49 +00:00
30b86499cd Fix tab changing on rotate 2015-10-09 01:38:10 +00:00
bb8daa81d2 Translation bugfix 2015-10-09 00:12:13 +00:00
763ce08902 Updated CHANGELOG 2015-10-08 23:43:39 +00:00
a4e1055d86 Fixed bandwidth units in notification 2015-10-08 23:43:29 +00:00
ed89afd1bd Updated translations 2015-10-08 23:38:26 +00:00
13e26b4a1c Fix Activity transitions on Lollipop 2015-10-08 23:04:26 +00:00
3be56767a1 Move Lightning to recommended, Orweb to supported 2015-10-04 05:36:45 +00:00
d184019a8e Fix CalledFromWrongThreadException 2015-06-21 00:42:58 +00:00
6df542a162 Only linkify "irc://" in first start dialog if user can open them 2015-06-20 22:36:44 +00:00
96ca1d1a37 Updated CHANGELOG 2015-06-18 05:27:54 +00:00
6b585822f1 0.9.20
i2p.i2p revision: 747e9d409223a108623b0b38d084097335d6c195
2015-06-18 05:26:44 +00:00
c81c57daa0 Updated translations 2015-06-16 11:39:36 +00:00
f28be9cb02 Updated CHANGELOG 2015-06-16 11:35:28 +00:00
6582f67ed5 Fix tunnel backup quantity fetching in advanced tunnel prefs 2015-06-16 11:35:10 +00:00
d138c482d9 Change to bar plot, update range step when plot updates 2015-06-16 09:12:28 +00:00
fc2d962cad Hide stats in final shutdown 2015-06-16 07:32:48 +00:00
c541ae0347 Remove HTML entities from clock skew error, code style 2015-06-15 11:32:19 +00:00
d6e79ed0a7 Improved graph axes 2015-06-15 11:30:54 +00:00
7ec20fe60c Updated TODO 2015-06-15 11:30:13 +00:00
f3464c5095 SU3 news 2015-06-06 10:36:12 +00:00
32512fecbc Code style 2015-06-06 07:36:16 +00:00
1d7fcd47ef Filter proxy error page resources to remove routerconsole links 2015-06-06 03:56:44 +00:00
f156c591ad Disable countries table in netDB page (because no GeoIP data) 2015-06-06 00:07:18 +00:00
fb6ca0d61e Fixed NPE (linked to previous commit) 2015-06-05 23:50:33 +00:00
a4662984a7 Added missing setContentView() 2015-06-05 23:38:49 +00:00
6cccf1fb23 Don't change lights for graceful shutdown 2015-06-05 23:13:09 +00:00
39f32acd5b Update release notes 2015-06-04 10:45:21 +00:00
3d60d10f8e Make tunnel length default 2 in tunnel prefs, to match default i2ptunnel.config 2015-06-04 10:43:23 +00:00
8cbc11dff0 Updated translations 2015-06-03 00:03:14 +00:00
485 changed files with 11956 additions and 3407 deletions

3
.dockerignore Normal file
View File

@ -0,0 +1,3 @@
app/pkg-temp
app/build
app/pkg-mavencentral

73
.github/workflows/ant.yml vendored Normal file
View File

@ -0,0 +1,73 @@
# Mostly copied from https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-ant
# zlatinb
name: Java CI
on: [push]
permissions:
contents: read
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: GetText
run: sudo apt install gettext git
- uses: actions/checkout@v4
- name: Set up JDK 11
uses: actions/setup-java@v4
with:
java-version: '11'
distribution: 'temurin'
- name : Generate override.properties
run: |
rm -f override.properties
echo "build.built-by=GitHub Actions" >> override.properties
echo "noExe=true" >> override.properties
- name: build with Gradle
run: |
echo "i2psrc=$HOME/i2p.i2p" > routerjars/local.properties
git clone -b i2p-2.4.0 https://github.com/i2p/i2p.i2p "$HOME/i2p.i2p"
./gradlew assembleDebug
find . -name '*.apk'
- name: Upload i2p-debug-${{ github.sha }}.apk
uses: actions/upload-artifact@v4
with:
name: i2p-debug.apk
path: ./app/build/outputs/apk/free/debug/app-free-debug.apk
trunk:
runs-on: ubuntu-latest
steps:
- name: GetText
run: sudo apt install gettext git
- uses: actions/checkout@v4
- name: Set up JDK 11
uses: actions/setup-java@v4
with:
java-version: '11'
distribution: 'temurin'
- name : Generate override.properties
run: |
rm -f override.properties
echo "build.built-by=GitHub Actions" >> override.properties
echo "noExe=true" >> override.properties
grep -v I2P_ gradle.properties > gradle.properties.update
echo "I2P_VERSION=2.5.0-1" >> gradle.properties.update
echo "I2P_ANDROID_VERSION=2.5.0-1" >> gradle.properties.update
cp -v gradle.properties.update gradle.properties
- name: build with Gradle
run: |
echo "i2psrc=$HOME/i2p.i2p" > routerjars/local.properties
git clone -b master https://github.com/i2p/i2p.i2p "$HOME/i2p.i2p"
bash -c "cd $HOME/i2p.i2p && ./installer/resources/maven-dev-release.sh 1"
./gradlew assembleDebug
find . -name '*.apk'
- name: Upload i2p-debug-${{ github.sha }}.apk
uses: actions/upload-artifact@v4
with:
name: i2p-debug-trunk-${{ github.sha }}.apk
path: ./app/build/outputs/apk/free/debug/app-free-debug.apk

62
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,62 @@
name: Release
#on: [push]
on:
push:
# Sequence of patterns matched against refs/tags
tags:
- 'i2p-android-*.*.*' # Release i2p-firefox-1.2.3
- 'i2p-android-*.*.*-*' # Release i2p-firefox-1.2.3
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
sparse-checkout: |
CHANGELOG.md
sparse-checkout-cone-mode: false
- name: sleep 6 minutes
run: |
echo "sleeping 6 minutes to wait for artifacts"
sleep 1m
echo "sleeping 5 minutes to wait for artifacts"
sleep 1m
echo "sleeping 4 minutes to wait for artifacts"
sleep 1m
echo "sleeping 3 minutes to wait for artifacts"
sleep 1m
echo "sleeping 2 minutes to wait for artifacts"
sleep 1m
echo "sleeping 1 minutes to wait for artifacts"
sleep 1m
- name: Download artifacts
id: download-artifact
uses: dawidd6/action-download-artifact@v3
with:
skip_unpack: true
workflow: ant.yml
if_no_artifact_found: fail
# remove .zip file extension
#- run: for f in *.zip; do unzip "$f"; rm "$f"; done
- run: unzip i2p-debug.apk.zip
- run: echo "" | tee -a RELEASE.md
- run: echo "## Checksums" | tee -a RELEASE.md
- run: echo "" | tee -a RELEASE.md
- run: echo '```' | tee -a RELEASE.md
- run: sha256sum * | tee -a RELEASE.md
- run: echo '```' | tee -a RELEASE.md
- run: echo "" | tee -a RELEASE.md
- run: echo '```' | tee -a RELEASE.md
- run: file * | tee -a RELEASE.md
- run: echo '```' | tee -a RELEASE.md
- run: echo "" | tee -a RELEASE.md
- name: Upload artifacts
uses: ncipollo/release-action@v1
with:
artifacts: "*"
bodyFile: "RELEASE.md"

95
.github/workflows/sign.yml vendored Normal file
View File

@ -0,0 +1,95 @@
# Mostly copied from https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-ant
# zlatinb
name: Java Signed CI
on: [push]
permissions:
contents: read
pages: write
id-token: write
jobs:
build-signed:
runs-on: ubuntu-latest
steps:
- name: GetText
run: sudo apt install gettext git
- uses: actions/checkout@v4
- name: Set up JDK 11
uses: actions/setup-java@v4
with:
java-version: '11'
distribution: 'temurin'
- name: Generate override.properties
env:
DEV_SIGNING_KEY: ${{ secrets.DEV_SIGNING_KEY }}
DEV_PASSWORD: ${{ secrets.DEV_PASSWORD }}
run: |
rm -f override.properties
mv etc/github.gradle.properties gradle.properties
mv etc/github.signing.properties signing.properties
mkdir -p $HOME/keystores/
echo $DEV_SIGNING_KEY | base64 --decode > $HOME/keystores/android-release.keystore
echo "KEY_ALIAS=mykey" >> signing.properties
echo "KEY_PASSWORD=$DEV_PASSWORD" >> signing.properties
echo "build.built-by=GitHub Actions" >> override.properties
echo "noExe=true" >> override.properties
- name: build with Gradle
run: |
echo "i2psrc=$HOME/i2p.i2p" > routerjars/local.properties
git clone -b i2p-2.4.0 https://github.com/i2p/i2p.i2p "$HOME/i2p.i2p"
./gradlew assembleRelease
find . -name '*.apk'
ls -lah $HOME/keystores/android-release.keystore
ls -d $HOME
- name: Upload i2p-debug-${{ github.sha }}.apk
uses: actions/upload-artifact@v4
with:
name: i2p-debug.apk
path: ./app/build/outputs/apk/free/debug/app-free-debug.apk
trunk-signed:
runs-on: ubuntu-latest
steps:
- name: GetText
run: sudo apt install gettext git
- uses: actions/checkout@v4
- name: Set up JDK 11
uses: actions/setup-java@v4
with:
java-version: '11'
distribution: 'temurin'
- name: Generate override.properties
env:
DEV_SIGNING_KEY: ${{ secrets.DEV_SIGNING_KEY }}
DEV_PASSWORD: ${{ secrets.DEV_PASSWORD }}
run: |
rm -f override.properties
echo "build.built-by=GitHub Actions" >> override.properties
echo "noExe=true" >> override.properties
mv etc/github.gradle.properties gradle.properties
mv etc/github.signing.properties signing.properties
mkdir -p $HOME/keystores/
echo $DEV_SIGNING_KEY | base64 --decode > $HOME/keystores/android-release.keystore
echo "KEY_ALIAS=mykey" >> signing.properties
echo "KEY_PASSWORD=$DEV_PASSWORD" >> signing.properties
grep -v I2P_ gradle.properties > gradle.properties.update
echo "I2P_VERSION=2.5.0-1" >> gradle.properties.update
echo "I2P_ANDROID_VERSION=2.5.0-1" >> gradle.properties.update
cp -v gradle.properties.update gradle.properties
- name: build with Gradle
run: |
echo "i2psrc=$HOME/i2p.i2p" > routerjars/local.properties
git clone -b master https://github.com/i2p/i2p.i2p "$HOME/i2p.i2p"
bash -c "cd $HOME/i2p.i2p && ./installer/resources/maven-dev-release.sh 1"
./gradlew assembleRelease
find . -name '*.apk'
ls -lah $HOME/keystores/android-release.keystore
ls -d $HOME
- name: Upload i2p-debug-${{ github.sha }}.apk
uses: actions/upload-artifact@v4
with:
name: i2p-debug-trunk-${{ github.sha }}.apk
path: ./app/build/outputs/apk/free/debug/app-free-debug.apk

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.android.base.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: {}

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
etc/docker.signing.properties
signing.properties
.idea
.idea/
.gradle/
local.properties
override.properties
build

View File

@ -1,52 +0,0 @@
# Just to try and prevent some noob disasters.
# Use mtn add --no-respect-ignore foo.jar to ignore this ignore list
_jsp\.java$
\.bz2$
\.class$
\.diff$
\.exe$
\.fba$
\.gz$
\.jar$
\.out$
\.patch$
\.sig$
\.sud$
\.su2$
\.tar$
\.war$
\.zip$
^\.
^build/
^pkg-temp/
~$
/build/
/classes/
# Android-specific ignores
^routerjars/libs
local.properties
signing.properties
#IntelliJ IDEA
^.idea
.*.iml
.*.ipr
.*.iws
#Gradle
^.gradle
build
# I2P-specific ignores
^app/src/main/res/drawable/i2plogo.png
^app/src/main/res/raw/blocklist_txt
^app/src/main/res/raw/hosts_txt
^app/src/main/res/raw/.*_ht
^app/src/main/res/raw/license_
^app/src/main/res/raw/certificates_zip
^app/src/main/assets/themes/console/images
^app/src/main/assets/themes/console/light/console.css
^app/src/main/assets/themes/console/light/images/header.png
^scripts/build.number
^scripts/version.properties

View File

@ -1,18 +1,18 @@
[main]
host = https://www.transifex.com
lang_map = he: iw, id: in, pt_BR: pt-rBR, ru_RU: ru, sv_SE: sv, tr_TR: tr, uk_UA: uk, yi: ji, zh_CN: zh
host = https://www.transifex.com
lang_map = pt_BR: pt-rBR, yi: ji, zh_CN: zh, zh_TW: zh-rTW, id: in, ru_RU: ru, sv_SE: sv, tr_TR: tr, uk_UA: uk, he: iw, es_AR: es-rAR
[I2P.android]
file_filter = app/src/main/res/values-<lang>/strings.xml
source_file = app/src/main/res/values/strings.xml
source_lang = en
type = ANDROID
minimum_perc = 50
[I2P.android_lib_client]
file_filter = client/src/main/res/values-<lang>/strings.xml
source_file = client/src/main/res/values/strings.xml
source_lang = en
type = ANDROID
[o:otf:p:I2P:r:android]
file_filter = app/src/main/res/values-<lang>/strings.xml
source_file = app/src/main/res/values/strings.xml
source_lang = en
type = ANDROID
minimum_perc = 22
[o:otf:p:I2P:r:android_lib_helper]
file_filter = lib/helper/src/main/res/values-<lang>/strings.xml
source_file = lib/helper/src/main/res/values/strings.xml
source_lang = en
type = ANDROID
minimum_perc = 50

233
CHANGELOG
View File

@ -1,9 +1,240 @@
0.9.20
2.9.0
* Update I2P Library
2.8.0
* Update I2P Library
* Migrate to AndroidX
* Migrate to MagicIndicator
2.7.1
* Update I2P Library
* Roll back ViewPageIndicator version, fixes crash, causes other issues
2.7.0
* Update I2P Library
2.6.0
* Update I2P Library
2.5.0
* Update I2P Library
2.4.0
* Update I2P Library
2.3.0
* Update I2P Library
2.2.0
* Add blocklist feed support
* Fix translations on stats page
* Various bugfixes
2.1.0-1
* adds support for adding base32 addresses to the local addressbook
* adds support for destinations with ports in the string to i2ptunnel
* adds support for remote applications launching I2P, via MR#3 from @RyeMantis
2.1.0
* Upgrades router to version 2.1.0
* Adds jcenter repository back to app/ target, removes jcenter from other targets
* explicitly set android.useAndroidX
2.0.1
* Fixes gitlab#49 by applying MUTABLE or IMMUTABLE flags across all PendingIntents
2.0.0
* Updates router to version 2.0.0
* Updates gradle plugin
* Remove Firefox from supported browsers list due to lack of about:config support
* Add Privacy Browser to supported browsers list and write guide for it
1.9.1
* Switches back to mavenCentral builds
* Fixes build process for F-Droid main
* Using tag i2p.i2p:i2p-android-1.9.0
* Allow b32 and blinded b32 to validate in I2PTunnel UI
1.9.0
* Updates router to version 1.9.0
* Using tag i2p.i2p:i2p-android-1.9.0
1.8.2
* This update fixes a bug where Family Keys are incorrectly handled by the Android router, leading
to a crash.
* This update fixes a bug wherein incorrect type-checking caused a crash when using the Network
Config page for some users.
1.8.1
* This update fixes a bug where certificates were unpacked to the wrong location on new installs,
resulting in the router failing to start.
* Decouples the Android version from the I2P library version to allow for android point
releases with less pain.
1.8.0
* This release updates the underlying I2P libraries to I2P version 1.8.0
* The SAM API now operates in "Interactive Authentication" mode. Users will be prompted via
the notificationArea to approve or ignore new SAM connections initiated by external apps.
* The network config menu now features a drop-down configuration instead of a slider configuration
(since reversed)
1.7.1
* This release updates the underlying I2P libraries to I2P version 1.7.1
1.7.0
* This release updates the underlying I2P libraries to I2P version 1.7.0
* Refactor of the unzipResourceToDir function
1.6.0
* This release updates the underlying I2P libraries to I2P version 1.6.0
1.5.0
* This release updates the underlying I2P libraries to I2P version 1.5.0
0.9.50 2021-05-18
* This release updates the underlying I2P libraries to I2P version 0.9.50
0.9.48 2020-12-02
* This release updates the underlying I2P libraries to I2P version 0.9.48
* Updates to browser configuration documentation
0.9.47-1 2020-10-29
* This release fixes a number of bugs arising from a misconfigured bootclasspath
* Fix a battery-management issue arising on some phones
0.9.47 / 2020-08-26
* Notification bug-fixes on platforms >8.0
0.9.46 / 2020-06-03
* catch ActivityNotFound exception in MainActivity
0.9.45 / 2020-03-07
* No significant changes
0.9.44 / 2019-12-03
* Updated translations
* Bumped target sdk version to 28, enforced by google
0.9.43-1 / 2019-10-28
* Fix crash at startup in TCG
0.9.43 / 2019-10-27
* Save state in background thread (tickets #2595, #2632)
* Fix ISE in language dialog (ticket #2631)
* Fix NPE in create tunnel (ticket #2629)
* Update logo after router killed in background
* Fix message in tunnels tabs when tunnels not up yet
* Hide tunnel actions while TCG is starting
* Add battery permissions dialog (ticket #2607)
0.9.42 / 2019-08-28
* Possible fix for tunnel edit dialog crash (ticket #2598)
0.9.41 / 2019-07-03
* New 64 bit libjbigi (ticket #2503)
* Update 32 bit jbigi to GMP 6.1.2
* Fix for client tunnels not starting after reseed
* UPnP Fixes (ticket #2499)
* WebView crash fixes (ticket #2390)
* i2ptunnel crash fix (ticket #2552)
* i2ptunnel ANR fix (ticket #2491)
* Browser help pages improvements (tickets #2521, 2523)
* Code cleanups and fixes
* Update visibility of floating menu items
* Put our router info at top of list
* Table layout cleanups
* Fixes for threads remaining after router stop
* Remove BOB
* Remove welterde IRC tunnel
* Various lint fixes
* New translations: Add cs, da, el
* Add missing translations to menu: ar, fi, gl, hu, zh_TW
0.9.40 / 2019-05-10
* Open local I2CP socket for 3rd party apps
* Fix News URLs
* Numerous bug fixes (see trac)
0.9.39 / 2019-03-23
* Set App ID
0.9.38 / 2019-01-27
0.9.37 / 2018-10-10
0.9.36 / 2018-08-27
0.9.35 / 2018-07-03
0.9.34 / 2018-04-25
0.9.33 / 2018-02-18
* Translation updates
0.9.32 / 2017-11-28
* Fixed "Application Not Responding" error when restarting all tunnels
* Fixed crashes when:
* opening the console menu
* starting the router
* starting the router for the first time
* viewing tunnels
* viewing the addressbook
* opening the addressbook menu
* configuring addressbook subscriptions
* using a configuration wizard
* loading a B64 Destination from file
* viewing tunnel settings
* saving tunnel settings
* installing a tunnel's recommended app without a market app
* installing a browser without a market app
* rotating the screen while using the built-in browser
* Added a "sync" icon to more clearly indicate tunnel "starting" status
* Updated Firefox browser config instructions for Firefox Quantum
* Translation updates
0.9.31 / 2017-08-19
* Fixed various crashes in the Tunnels UI
* Updated Firefox browser config instructions
* Minor bug fixes
* Dependency and translation updates
0.9.30 / 2017-05-20
* Fixed crashes when creating or deleting tunnels, or adding names to the
private addressbook
* Minor bug fixes
* Dependency and translation updates
0.9.29 / 2017-03-27
* Dependency and translation updates
0.9.28 / 2017-01-02
* Bug fixes and translation updates
0.9.27 / 2016-11-20 / 64ff68efe98c345acb6ba1d0432fa49d1d650358
* Removed kytv's IRC server from default tunnel list
* Translation updates
0.9.26 / 2016-06-13 / b003272c8b504bb0d904edca2e95359a57c9a52c
* Fixed "I2CP already listening" bug
* Fixed crash when adding tunnel to empty list
* Translation updates
0.9.25 / 2016-04-17 / 46d45a878a2b73394b26ca27dbe6c696dedcf1c3
* Fixed a bug on Samsung Android 4.2 devices
* Dependency improvements
* Translation updates
0.9.22 / 2015-10-10 / 0f73ef90b81e2cf3d55f0ea2b0a16e1f10da40ad
* Updated browser config guide
* Bug fixes and translation updates
0.9.20 / 2015-06-18 / 5fdaabeb5fa955caac90f1390adbdeaeae42fdf1
* Simplified the main interface
* Language can be configured
* Tunnels can now be edited
* Material design improvements
* Better support for tablets
* Improved graph rendering
* Bug fixes and translation updates
0.9.19.1 / 2015-04-15 / ed86e7e85161dbe3f15932fd4d195c551f8e2c71

11
DEVELOPMENT-README.md Normal file
View File

@ -0,0 +1,11 @@
# Development Readme
## How to build development builds of the router core for android?
Check the RELEASE-PROCESS.md file for general information about how to build and to bump the version.
In your i2p.i2p codebase checkout, execute `./installer/resources/maven-dev-release.sh` with your build number as first argument.
The script locates itself and uses the same codebase as it's in, to produce the maven builds which will be locally installed.
Next, add the build number to the gradle.properties and build the android build as usual.

91
DOCKER.md Normal file
View File

@ -0,0 +1,91 @@
Docker Build Instructions
=========================
It is possible to build a container with a pre-installed environment for
correctly compiling an I2P for Android development build. Unlike the i2p.i2p
container, zero attempt has been made to optimize the size of the container,
as it contains a copy of the latest Android SDK, toolchains, and Android NDK,
which it must download. To save time, this is cached locally. It is likely to
take up to 30 GB of disk space to compile in this way, however, it is very easy
and convenient compared to the steps in RELEASE-PROCESS.md and may make
building Android reproducibly easier in the future.
Container dependencies
----------------------
- `menny/android_ndk` (third-party image) (reviewed by idk) (depends on menny/android_sdk
- `menny/android_sdk` (third-party image) (reviewed by idk) (depends on ubuntu/18.04)
- `ubuntu/18.04` (official docker container) (base container)
Build the container locally
---------------------------
Run:
docker build -t i2p.android.base .
To build the container. It will have a lot to download the first time, so it may take
a while to complete.
Run an Android build in the container
-------------------------------------
Copy the `etc/docker.signing.example.proprties` file to `etc/docker.signing.proprties`,
edit it to match your key information and rebuild the container.
Run:
docker run -it \
-u $(id -u):$(id -g) \
--name i2p.android.base \
-v $HOME/.gnupg/:/.gnupg/:ro \
-v $HOME/.i2p-plugin-keys/:/.i2p-plugin-keys/:ro \
-v /run/user/$(id -u)/:/run/user/$(id -u)/:ro \
i2p.android.base
To get the build artifacts for uploading to Maven out of the container, use:
docker cp i2p.android.base:/opt/workspace/i2p.i2p/pkg-mavencentral app/pkg-mavencentral
docker cp i2p.android.base:/opt/workspace/i2p.i2p/mavencentral-i2p.jar app/pkg-mavencentral
docker cp i2p.android.base:/opt/workspace/i2p.i2p/mavencentral-mstreaming.jar app/pkg-mavencentral
docker cp i2p.android.base:/opt/workspace/i2p.i2p/mavencentral-router.jar app/pkg-mavencentral
docker cp i2p.android.base:/opt/workspace/i2p.i2p/mavencentral-servlet-i2p.jar app/pkg-mavencentral
docker cp i2p.android.base:/opt/workspace/i2p.i2p/mavencentral-streaming.jar app/pkg-mavencentral
To get the Android build artifacts out of the container, use:
docker cp i2p.android.base:/opt/workspace/i2p.android.base/app/build/ app/build
And your android applications will appear in the `app/build` directory, in the same
place where non-container builds would go.
If you encounter a permissions error when rebuilding, delete the `app/build`,
`app/pkg-mavencentral` and `app/pkg-temp` path.
rm -rf app/pkg-temp app/build app/pkg-mavencentral
Copypasta
---------
Once you have set up builds for the first time, from then on you can update the container and
build a fresh set of Maven jars and a new I2P for Android app by copy-pasting the following
commands:
``` sh
rm -rf app/pkg-temp app/build app/pkg-mavencentral
docker build -t i2p.android.base .
docker run -it \
-u $(id -u):$(id -g) \
--name i2p.android.base \
-v $HOME/.gnupg/:/.gnupg/:ro \
-v $HOME/.i2p-plugin-keys/:/.i2p-plugin-keys/:ro \
-v /run/user/$(id -u)/:/run/user/$(id -u)/:ro \
i2p.android.base
docker cp i2p.android.base:/opt/workspace/i2p.i2p/pkg-mavencentral app/pkg-mavencentral
docker cp i2p.android.base:/opt/workspace/i2p.i2p/mavencentral-i2p.jar app/pkg-mavencentral
docker cp i2p.android.base:/opt/workspace/i2p.i2p/mavencentral-mstreaming.jar app/pkg-mavencentral
docker cp i2p.android.base:/opt/workspace/i2p.i2p/mavencentral-router.jar app/pkg-mavencentral
docker cp i2p.android.base:/opt/workspace/i2p.i2p/mavencentral-servlet-i2p.jar app/pkg-mavencentral
docker cp i2p.android.base:/opt/workspace/i2p.i2p/mavencentral-streaming.jar app/pkg-mavencentral
docker cp i2p.android.base:/opt/workspace/i2p.android.base/app/build/ app/build
```

39
Dockerfile Normal file
View File

@ -0,0 +1,39 @@
FROM menny/android_ndk
ENV VERSION=0.9.50
ENV JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64/
RUN echo 'deb http://deb.i2p2.de/ sid main' >> /etc/apt/sources.list
RUN echo 'deb-src http://deb.i2p2.de/ sid main' >> /etc/apt/sources.list
RUN echo 'deb http://archive.ubuntu.com/ubuntu trusty universe' >> /etc/apt/sources.list
RUN wget -O /etc/apt/trusted.gpg.d/i2p-debian-repo.key.asc https://geti2p.net/_static/i2p-debian-repo.key.asc
COPY etc/debian-jessie-repo.key.asc /etc/apt/trusted.gpg.d
RUN mkdir -p /opt/packages && wget -O /opt/packages/openjdk-7-jre-headless.deb http://security.debian.org/debian-security/pool/updates/main/o/openjdk-7/openjdk-7-jre-headless_7u261-2.6.22-1~deb8u1_amd64.deb
RUN apt-get update
RUN apt-get build-dep -y i2p i2p-router
RUN apt-get install -y ant openjdk-8* libxml2-utils junit4 libhamcrest-java libmockito-java libmaven-ant-tasks-java dpkg-sig maven
RUN cd /opt/packages && dpkg-sig -l openjdk-7-jre-headless.deb && dpkg -x openjdk-7-jre-headless.deb /opt/packages/openjdk-7-jre
RUN git clone https://github.com/i2p/i2p.i2p --depth=1 -b i2p-$VERSION /opt/workspace/i2p.i2p
RUN update-java-alternatives --jre-headless --set java-1.8.0-openjdk-amd64
RUN update-java-alternatives --set java-1.8.0-openjdk-amd64
RUN update-alternatives --set javac /usr/lib/jvm/java-8-openjdk-amd64/bin/javac
RUN update-alternatives --set java /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java
RUN rm /opt/java/openjdk/ -rfv
COPY . /opt/workspace/i2p.android.base
COPY etc/docker.local.ndk.properties /opt/workspace/i2p.android.base/client/local.properties
COPY etc/docker.local.router.properties /opt/workspace/i2p.android.base/routerjars/local.properties
COPY etc/docker.local.sdk.properties /opt/workspace/i2p.android.base/local.properties
COPY etc/docker.override.properties /opt/workspace/i2p.android.base/override.properties
COPY etc/docker.override.properties /opt/workspace/i2p.i2p/override.properties
COPY etc/docker.signing.properties /opt/workspace/i2p.android.base/signing.properties
WORKDIR /opt/workspace/i2p.android.base
RUN find /opt/android-sdk-linux -type d -print0 | xargs -0 chown -R 1000:1000
RUN find /opt/android-sdk-linux -type d -print0 | xargs -0 chmod -Rc o+rw
RUN find /opt/android-sdk-linux -type d -print0 | xargs -0 chmod -c 0755
RUN find /opt/workspace -type d -print0 | xargs -0 chown -R 1000:1000
RUN find /opt/workspace -type d -print0 | xargs -0 chmod -Rc o+rw
RUN find /opt/workspace -type d -print0 | xargs -0 chmod -c 0755
CMD cd /opt/workspace/i2p.i2p && \
ant -k mavenCentral; \
cp -v *.jar pkg-mavencentral/; \
cd /opt/workspace/i2p.android.base && \
./gradlew --continue dependencies || true ; \
./gradlew --continue assembleRelease; tail -f README.md

View File

@ -43,8 +43,34 @@ License for the Android App:
See the License for the specific language governing permissions and
limitations under the License.
See the file licenses/LICENSE-Apache2.0.txt
===================================
License for the MagicIndicator library:
MIT License
Copyright (c) 2016 hackware1993
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
===================================
See the file licenses/LICENSE-Apache2.0.txt

View File

@ -7,8 +7,8 @@
- Java SDK (preferably Oracle/Sun or OpenJDK) 1.6.0 or higher
- Apache Ant 1.8.0 or higher
- I2P source
- Android SDK for API 21
- Android Build Tools 21.1.2
- Android SDK for API 28 or higher
- Android Build Tools 28.0.0 or higher
- Android Support Repository
- Gradle 2.2.1
@ -48,6 +48,12 @@ systemProp.socksProxyPort=9150
2. Check out the [`i2p.i2p`](https://github.com/i2p/i2p.i2p) repository.
3. Create a `local.properties` file in `i2p.android.base/lib/client` containing:
```
ndk.dir=/path/to/ndk
```
3. Create a `local.properties` file in `i2p.android.base/routerjars` containing:
```
@ -61,10 +67,18 @@ systemProp.socksProxyPort=9150
```
sdk.dir=/path/to/android-studio/sdk
```
1a. For building with a local router development build:
cd ../i2p.i2p
installer/resources/maven-dev-release.sh x // x is the build number, e.g. 6
cd back here
edit gradle.properties, add the build number x to I2P_PROPERTIES=0.9.xx-x
2. `gradle assembleDebug`
3. The APK will be placed in `i2p.android.base/app/build/outputs/apk`.
3. The APK files will be placed in `i2p.android.base/app/build/outputs/apk` subdirectories.
4. Install debug build on phone in USB debugging mode
adb install app/build/outputs/apk/free/debug/app-free-debug.apk
### Building with Android Studio
@ -107,8 +121,8 @@ systemProp.socksProxyPort=9150
signing.keyId=
signing.password=
signing.secretKeyRingFile=/path/to/secring.gpg
ossrhUsername=
ossrhPassword=
NEXUS_USERNAME=
NEXUS_PASSWORD=
```
2. `gradle :client:uploadArchives`

128
RELEASE-PROCESS.md Normal file
View File

@ -0,0 +1,128 @@
# Release Process
Note to all future maintainers: We have 4 channels that need to be updated in order to have a successful
Android release. Many of these channels are updated at different rates, and at times you must wait on a
third-party service to complete it's tasks before you may continue. When completing an Android release,
keep in mind that you must update 1) Maven 2) Google Play 3) f-droid.i2p.io and 4) F-Droid main
repository.
At the time of this revision, 2020/09/13, the main Android maintainer is idk. idk updates Maven, Google
Play, and f-droid.i2p.io, and nextl00p handles working with the F-Droid project to provide an I2P release
in their main repository.
NOTE: The docker container built by the Dockerfile in this repostory ensures that the Pre-requisites and
Dependencies are properly met by obtaining them from the Debian package in `oldoldstable` and pre-configuring
the override.properties that is used in the Docker container.
## Tag Freezes and Translations
1-2 weeks before the software is released, I2P Desktop will have a "Tag Freeze" which is the deadline for
translations to be checked in. **When** that time comes, someone who has Transifex privileges should use
the command: `tx push -s -R I2P.android` to push any and all changed string resources to transifex for
translation.
**>> Beginning of Docker-enabled Steps <<**
## Prerequirements
0. Update the changelog!
1. Ensure you have the deprecated maven ant tasks. ( https://maven.apache.org/ant-tasks/download.cgi )
2. It should exist at `~/.ant/lib/maven-ant-tasks-2.1.3.jar`
3. Ensure you have hamcrest-integration, hamcrest-library, hamcrest-core in the hamcrest.home directory.
4. Ensure junit 4.12 at least in junit.home, ensure the jar file is named `junit4.jar`.
5. Ensure you have the Mockito framework and accompanying documentation in your $JAVA_HOME
6. Ensure to have updated the changelog with the changes done.
7. Ensure that you are configured to build i2p.i2p with Java 8. On Debian it is easiest to set with
`update-java-alternatives --set java-1.8.0-openjdk-amd64` and picking Java 8. **TODO:** add instructions for non-Debian-based
systems.
8. Ensure that you have a Java 1.7 bootclasspath available. (See **Maven Central** step 2A.)
## Get all the dependencies ready
### Maven Central
1. Check out a clean copy of i2p.i2p at the correct release version. (Make a clean checkout)
2. Build the maven packages via `ant mavenCentral` where you end up with mavencentral-*.jar files in the
current directory.
2. **A)** I2P for Android requires a Java 1.7 bootclasspath, but the servlet jar requires Java 8. So, to do the builds:
- First set `javac.compilerargs=-bootclasspath /path/to/java/7/rt.jar:/path/to/java/7/jce.jar` in override.properties
- Build with `ant mavenCentral`
**>> End of Docker-enabled Steps for Maven <<**
3. Login to http://oss.sonatype.org for uploading the mavencentral-*.jar bundles.
4. In nexus, choose "Staging Upload" and upload all of the files with upload mode set to "Artifacts with POM".
When uploading the files to nexus, you *must* upload the pom.xml files, and all of their artifacts. For each
component, you will need to upload a *.jar, a *.jar.asc, a *sources.jar, a *sources.jar.asc, a javadoc.jar,
and a javadoc.jar.asc, and a pom.xml and a pom.xml.asc from the pkg-mavencentral directory during the "Upload
Artifacts with POM" operation. You will need to do this once for each component you upload to Nexus.
5. Under "Staging Repositories" ensure all where uploaded correctly, select them all and press "Release"
in the toolbar.
#### Example override.properties:
javac.version=1.7
javac.target=1.7
javac.source=1.8
javac.compilerargs=-bootclasspath /home/user/StudioProjects/java7bootclasspath/rt.jar:/home/user/StudioProjects/java7bootclasspath/jce.jar
javac.compilerargs7=-bootclasspath /home/user/StudioProjects/java7bootclasspath/rt.jar:/home/user/StudioProjects/java7bootclasspath/jce.jar
build.built-by=name
### Android Common Build
Using Docker: in order to use Docker to generate a new Android apk for release, you will
need to run the build twice, once for the mavenCentral jars, and once for the actual Android
app. After doing the Maven release, follow these steps in the i2p.android.base repository, and re-run
the `docker run` step described in `DOCKER.md`
1. Edit `routerjars/local.properties` to use the clean i2p.i2p copy.
2. Pull the latest translations with `tx pull --use-git-timestamps` and commit them. (If you don't have the `tx` command,
do `pip install transifex-client` ). If there are broken translations, exclude them and only them.
- If there are any new translations, `mtn add` them, and add them to `app/src/main/res/values/arrays.xml`
(two places, alphabetical order please)
3. Ensure that `signing.properties` contains the details of the release key. If you are using Docker, see
`DOCKER.md` to perform this step for Docker builds by editing `etc/docker.signing.properties` instead.
4. Edit `gradle.properties` to bump the I2P version.
5. Edit `app/build.gradle` to bump the Android version number.
6. Edit `CHANGELOG` to add the release and date.
7. If the helper has changed since the last release, edit
`lib/helper/gradle.properties` to bump the version.
8. `./gradlew clean assembleRelease`
### Libraries
1. `./gradlew :lib:client:uploadArchives`
2. If the helper version was changed and should be released: `./gradlew :lib:helper:uploadArchives`
3. Check on Sonatype that everything worked, and close/release.
## Release Packages
### F-Droid Guide
This guide is for f-droid.i2p.io, not for F-Droid's main repository. The repository keystore **and** the
config.py used to generate the repository are required to complete this process successfully.
1. Ensure you have the release keys, the keyfile must be placed at `~/.local/share/fdroidserver/keystore.jks`
2. If it's the first time, or you have reinstalled anything, ensure `path/to/fdroid/config.py` has correct
information.
3. Assuming you already have ran `./gradlew clean assembleRelease` from a earlier step, continue.
4. `cp app/build/outputs/apk/free/release/app-free-release.apk path/to/fdroid/repo/I2P-VERSION.apk`
5. Update `path/to/fdroid/metadata/net.i2p.android.txt` (The versions at the bottom of the file)
6. Run `fdroid update` from inside the fdroid path (install fdroid command via `pip install fdroidserver`)
7. Zip/tar the local fdroid repo and archive. `rm fdroid.tgz && tar czf fdroid.tgz archive/ repo/` from the
fdroid directory.
8. Push to download server and put in place. (via SSH for example, `scp fdroid.tgz download.i2p2.de:~/`)
9. On the server run `bin-fd/update-fdroid` and `sudo bin-fd/update-app i2p 0.9.40` (This ensures we use the
exact same apk file for the download page as in fdroid and gplay)
10. Check F-Droid repo works, and app works.
### Google Play and finishing up
1. Verify which files that are changed via `mtn ls cha`. It shouldn't be much more than those bellow this
line and possible translations (`mtn ls unk`).
2. Commit your release changes, `mtn ci gradle.properties lib/helper/gradle.properties app/build.gradle`
3. Push free and donate builds to Google Play via https://play.google.com/apps/publish/
4. Tag the new release. Example `mtn tag h: android-0.9.36`
5. Push the monotone changes. Make sure that they are there at the next git sync.
6. Update download page (version and hash, including F-Droid)

33
TODO
View File

@ -6,45 +6,74 @@
- Browser
<zzzccc> Bug report: i2p browser treats 302 as an error
<zzzccc> Bug 2: rotate screen in i2p browser seems to go back one page
- Console text change
<zzz> "download" and "upload" at the bottom of the status is a little misleading..
<zzz> maybe 'downstream bandwidth' or 'inbound usage' ?
- Fix visibility of advanced tunnel parameter changes
<zzz> when I change an advanced tunnel param e.g. length or variance, the change isn't displayed, I have to go back and forward again to see the change
# New UI fixes
- Addressbook action items are in tunnel overflow menu after moving from console to tunnels
- Material design:
- Style for addressbook headers
- Change console FAM icon when possible
<zzz> on the bottom right, the + and x icons might be better as a double-up arrow and double-down arrow?
- Use Material design for LongPressButton
- Highlight selected tunnel in two-pane mode
# Short-term
- Remove peers page (HTML version)
- Add firewall help page showing current port settings
- GMP 6
- Fetch all JARs from Maven Central (ie. upload everything that I2P Android uses)
- Disable uPnP when on cell networks
<zzz> spewing UPnP out into cell networks is a waste of time at best and a security risk at worst, but you really want it for wifi
- Rewrite settings config handling
- Rewrite InitActivities
- I2PTunnel
- Improve tunnel list status indicators
- Icon overlay to indicate which tunnels are shared
- Or reorder / group tunnels?
- Show all messages somewhere
- Bottom toolbar?
- Icons/header images for tunnel types on details page
- Setting to close when not on WiFi
- Progress feedback for addressbook subscriptions reload
- Display release notes directly on new router version
- Fill out help pages
- Fix navigation to specific settings pages
- Rewrite release notes to be release-specific
- Fix release notes UI, either make back button use clear or add buttons
- Notify user when autostart fails?
- NetDB tablet view fixes
- Refresh detail fragment when changing tab
- Move list to correct item when changing tab
- Create nav history when viewing RI from LS
- Handle NetDB null cases (failed lookup of requested hash in detail page)
- Include GeoIP db for country info
- Maybe change router-off mechanic for various pages? Enable as they become available?
# Medium-term
- SQLite naming service backend to store addresses more effectively
- Leverage for name completion in e.g. browsers
- Create/edit tunnels while router is not running
- Separate out shared tunnel config
- Convey to users that one config controls all shared tunnels
- Network profiles
- User selects profile in settings
- Change network participation etc. based on profile
- Also look at connection type: Connectivity.isConnectionFast()
- Expose log level overrides
- Bug report feature
- Replace peers page (native version)
- Improve graphs
- Show time on bottom axis
- Show fixed x range, not only available data
- Think about pan/zoom
- How to persist data across restarts?
- Enable apps to specify when they don't need the router anymore
# Silent Store approval checks to confirm/implement
@ -92,6 +121,8 @@
# Long-term
- Reproducible builds
- Extract RouterService into a library
- Remote router support
- Implement a "router wrapper" that can represent a local or remote router
- Implement/use client APIs to talk to remote router

View File

@ -1,13 +1,18 @@
apply plugin: 'com.android.application'
apply plugin: 'witness'
repositories {
mavenLocal()
mavenCentral()
maven { url 'https://jitpack.io' }
}
android {
compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION as String)
buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION as String
namespace 'net.i2p.android.router'
compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION as String)
defaultConfig {
versionCode 4745229
versionName '0.9.19.1'
minSdkVersion 9
versionCode Integer.parseInt(project.I2P_ANDROID_VERSION_CODE as String)
versionName "$I2P_ANDROID_VERSION"
minSdkVersion 21
targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION as String)
// For Espresso
@ -19,78 +24,108 @@ android {
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
debuggable true
applicationIdSuffix '.debug'
versionNameSuffix '-DEBUG'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
lintOptions {
abortOnError false
checkReleaseBuilds false
disable 'MissingDefaultResource'
}
packagingOptions {
exclude 'LICENSE.txt'
resources {
excludes += ['LICENSE.txt']
}
}
flavorDimensions 'tier'
productFlavors {
free {
dimension 'tier'
applicationId 'net.i2p.android'
}
donate {
dimension 'tier'
applicationId 'net.i2p.android.donate'
}
legacy {
dimension 'tier'
applicationId 'net.i2p.android.router'
}
}
}
dependencies {
implementation "androidx.appcompat:appcompat:1.5.1"
implementation "androidx.preference:preference:1.2.0"
implementation "androidx.annotation:annotation:1.5.0"
implementation 'androidx.recyclerview:recyclerview:1.0.0'
// Local dependencies
compile project(':routerjars')
compile project(':client')
implementation project(':lib:client')
implementation project(':lib:helper')
implementation project(path: ':routerjars', configuration: 'routerjars')
// Android Support Repository dependencies
compile 'com.android.support:support-v4:22.2.0'
compile 'com.android.support:appcompat-v7:22.2.0'
compile 'com.android.support:recyclerview-v7:22.2.0'
/*def supportVersion = '28.0.0'
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"*/
implementation 'com.google.android.material:material:1.9.0'
// Remote dependencies
compile 'net.i2p.android.ext:floatingactionbutton:1.9.0'
compile files('libs/androidplot-core-0.6.1.jar')
compile ('com.android.support:support-v4-preferencefragment:1.0.0@aar'){
exclude module: 'support-v4'
}
compile 'com.pnikosis:materialish-progress:1.5'
compile 'com.eowise:recyclerview-stickyheaders:0.5.2@aar'
compile ('com.mcxiaoke.viewpagerindicator:library:2.4.1') {
exclude group: 'com.android.support', module: 'support-v4'
}
implementation 'com.androidplot:androidplot-core:1.5.11'
implementation 'com.eowise:recyclerview-stickyheaders:0.5.2@aar'
//implementation 'com.inkapplications.viewpageindicator:library:2.4.4'
implementation 'com.github.hackware1993:MagicIndicator:1.7.0' // for androidx
implementation 'com.pnikosis:materialish-progress:1.7'
implementation "net.i2p:router:$I2P_VERSION"
implementation "net.i2p:i2p:$I2P_VERSION"
implementation "net.i2p.client:mstreaming:$I2P_VERSION"
implementation "net.i2p.client:streaming:$I2P_VERSION"
implementation 'net.i2p.android.ext:floatingactionbutton:1.10.1'
implementation 'com.github.SufficientlySecure:html-textview:v3.6'
// Testing-only dependencies
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.0'
androidTestCompile 'com.android.support.test:testing-support-lib:0.1'
androidTestImplementation('com.android.support.test.espresso:espresso-core:3.0.2') {
exclude group: 'com.android.support', module: 'support-annotations'
}
}
dependencyVerification {
verify = [
'com.android.support:support-v4:7bb6e40a18774aa2595e4d8f9fe0ae14e61670f71a1279272fb0b79b8be71180',
'com.android.support:appcompat-v7:2d5867698410b41f75140c91d6c1e58da74ae0f97baf6e0bdd1f7cc1017ceb2c',
'com.android.support:recyclerview-v7:3a8da14585fa1c81f06e7cef4d93a7641f0323d8f984ff9a7bd7a6e416b46888',
'net.i2p.android.ext:floatingactionbutton:b41eae5fe6be599e3fade00273521b0914f2e199d5f04c50fa34cfe935347f76',
'com.android.support:support-v4-preferencefragment:5470f5872514a6226fa1fc6f4e000991f38805691c534cf0bd2778911fc773ad',
'com.pnikosis:materialish-progress:d71d80e00717a096784482aee21001a9d299fec3833e4ebd87739ed36cf77c54',
'com.eowise:recyclerview-stickyheaders:7b236da49b33b840e9ba6e7e4182218d1a2d9047236fdbc3ca947352f9b0883b',
'com.mcxiaoke.viewpagerindicator:library:1e8aad664137f68abdfee94889f6da3dc98be652a235176a403965a07a25de62',
]
dependencies {
implementation "androidx.appcompat:appcompat:1.5.1"
implementation "androidx.preference:preference:1.2.0"
implementation "androidx.annotation:annotation:1.5.0"
implementation 'androidx.test.espresso:espresso-core:3.6.1'
implementation 'androidx.test.ext:junit:1.2.1'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
}
project.ext.i2pbase = '../i2p.i2p'
dependencies {
// ...existing code...
// Force consistent lifecycle versions
implementation('androidx.lifecycle:lifecycle-viewmodel:2.5.1')
implementation('androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1')
// Exclude older versions
configurations.all {
resolutionStrategy {
force 'androidx.lifecycle:lifecycle-viewmodel:2.5.1'
force 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
}
}
}
project.ext.i2pbase = "../i2p.i2p"
def Properties props = new Properties()
def propFile = new File(project(':routerjars').projectDir, 'local.properties')
@ -116,7 +151,7 @@ task copyI2PResources(type: Copy) {
outputs.upToDateWhen { false }
into 'src/main/res'
into('drawable') {
from file(i2pbase + '/installer/resources/themes/console/images/i2plogo.png')
from file(i2pbase + '/apps/routerconsole/jsp/themes/console/images/i2plogo.png')
}
into('raw') {
from(i2pbase + '/installer/resources/blocklist.txt') { rename { 'blocklist_txt' } }
@ -128,6 +163,24 @@ task copyI2PResources(type: Copy) {
rename { String name ->
name.toLowerCase(Locale.US).replace('-', '_').replace('.', '_')
}
filter { String line ->
// Remove links to routerconsole
def m = line =~ /127.0.0.1:7657/
if (m.getCount()) {
// Links around content
line = line.replaceAll(/<a href="http:\/\/127.0.0.1:7657[^>]*>(.+?)<\/a>/) { fullmatch, content ->
content
}
// Links in translation substitutions
line = line.replaceAll(/"<a href=\\"http:\/\/127.0.0.1:7657[^>]*>", "<\/a>"/, '"", ""')
}
// Remove "Configuration - Help - Addressbook" heading
def n = line =~ /Configuration.+Help.+Addressbook/
if (n.getCount())
""
else
line
}
}
from('../LICENSE.txt') { rename { 'license_app_txt' } }
from('../licenses/LICENSE-Apache2.0.txt') { rename { 'license_apache20_txt' } }
@ -161,15 +214,15 @@ task copyI2PAssets(type: Copy) {
outputs.upToDateWhen { false }
into 'src/main/assets/themes/console'
into('images') {
from file(i2pbase + '/installer/resources/themes/console/images/i2plogo.png')
from file(i2pbase + '/installer/resources/themes/console/images/inbound.png')
from file(i2pbase + '/installer/resources/themes/console/images/outbound.png')
from file(i2pbase + '/apps/routerconsole/jsp/themes/console/images/i2plogo.png')
from file(i2pbase + '/apps/routerconsole/jsp/themes/console/images/inbound.png')
from file(i2pbase + '/apps/routerconsole/jsp/themes/console/images/outbound.png')
}
into('light') {
from file(i2pbase + '/installer/resources/themes/console/light/console.css')
from file(i2pbase + '/apps/routerconsole/jsp/themes/console/light/console.css')
}
into('light/images') {
from file(i2pbase + '/installer/resources/themes/console/light/images/header.png')
from file(i2pbase + '/apps/routerconsole/jsp/themes/console/light/images/header.png')
}
}

Binary file not shown.

29
app/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,29 @@
# Add project specific ProGuard rules here.
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
-dontobfuscate
-dontoptimize
-dontpreverify
-dontshrink
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Workaround for Samsung Android 4.2 bug
# https://code.google.com/p/android/issues/detail?id=78377
# https://code.google.com/p/android/issues/detail?id=78377#c188
# https://code.google.com/p/android/issues/detail?id=78377#c302
-keepattributes **
-keep class !android.support.v7.view.menu.**,** {*;}
-dontwarn **
-dontnote **

View File

@ -1,38 +1,43 @@
package net.i2p.android;
import android.test.ActivityInstrumentationTestCase2;
import androidx.test.core.app.ActivityScenario;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import net.i2p.android.router.R;
import static android.support.test.espresso.Espresso.closeSoftKeyboard;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu;
import static android.support.test.espresso.Espresso.pressBack;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.swipeLeft;
import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.hasSibling;
import static android.support.test.espresso.matcher.ViewMatchers.isDescendantOfA;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static androidx.test.espresso.Espresso.closeSoftKeyboard;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu;
import static androidx.test.espresso.Espresso.pressBack;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.swipeLeft;
import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.hasSibling;
import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withParent;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.not;
public class I2PActivityTest extends ActivityInstrumentationTestCase2<I2PActivity> {
public I2PActivityTest() {
super(I2PActivity.class);
}
@Override
protected void setUp() throws Exception {
super.setUp();
// For each test method invocation, the Activity will not actually be created
// until the first time this method is called.
getActivity();
@RunWith(AndroidJUnit4.class)
public class I2PActivityTest {
private ActivityScenario<I2PActivity> scenario;
@Before
public void setUp() {
scenario = ActivityScenario.launch(I2PActivity.class);
}
@Test
public void testMainTabs() {
onView(withId(R.id.router_onoff_button)).check(matches(isDisplayed()));
@ -54,24 +59,26 @@ public class I2PActivityTest extends ActivityInstrumentationTestCase2<I2PActivit
onView(withId(R.id.router_onoff_button)).check(matches(isDisplayed()));
}
@Test
public void testMainSwipe() {
onView(withId(R.id.router_onoff_button)).check(matches(isDisplayed()));
onView(allOf(withId(R.id.pager), hasSibling(withId(R.id.main_toolbar)))).perform(swipeLeft());
onView(allOf(withId(R.id.pager), withParent(hasSibling(withId(R.id.main_toolbar))))).perform(swipeLeft());
onView(withId(R.id.router_onoff_button)).check(matches(not(isDisplayed())));
onView(withText(R.string.label_i2ptunnel_client)).check(matches(isDisplayed()));
onView(allOf(withId(R.id.pager), hasSibling(withId(R.id.main_toolbar)))).perform(swipeLeft());
onView(allOf(withId(R.id.pager), withParent(hasSibling(withId(R.id.main_toolbar))))).perform(swipeLeft());
// TODO: test tunnels ViewPager
onView(allOf(withId(R.id.pager), hasSibling(withId(R.id.main_toolbar)))).perform(swipeLeft());
onView(allOf(withId(R.id.pager), withParent(hasSibling(withId(R.id.main_toolbar))))).perform(swipeLeft());
onView(withText(R.string.label_i2ptunnel_client)).check(matches(not(isDisplayed())));
onView(withText(R.string.label_router)).check(matches(isDisplayed()));
// TODO: test addressbook ViewPager
}
@Test
public void testSettingsNavigation() {
// Open settings menu
openActionBarOverflowOrOptionsMenu(getActivity());
openActionBarOverflowOrOptionsMenu(getInstrumentation().getTargetContext());
onView(withText(R.string.menu_settings)).perform(click());
// Open bandwidth page
@ -105,4 +112,4 @@ public class I2PActivityTest extends ActivityInstrumentationTestCase2<I2PActivit
pressBack();
onView(withText(R.string.settings_label_advanced)).check(doesNotExist());
}
}
}

View File

@ -1,11 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.i2p.android.router"
android:installLocation="auto">
android:installLocation="auto"
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.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<!-- following two are for UPnP -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<!-- required for reliable core functionality on Android, see:
https://geti2p.net/en/docs/applications/embedding
heading: "Design for and Encourage Long Uptimes"
-->
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<application
android:icon="@drawable/ic_launcher_itoopie"
@ -14,7 +28,12 @@
<service
android:name=".service.RouterService"
android:icon="@drawable/ic_launcher_itoopie"
android:label="@string/app_name">
android:enabled="true"
android:exported="true"
android:label="@string/app_name"
android:foregroundServiceType="specialUse">
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:value="i2p_router_background_process_required_network_operation"/>
<intent-filter>
<action android:name="net.i2p.android.router.service.IRouterState" />
</intent-filter>
@ -22,17 +41,28 @@
<provider
android:name=".provider.CacheProvider"
android:authorities="${applicationId}.provider" />
<receiver android:name=".receiver.OnBootReceiver">
<receiver
android:name=".receiver.OnBootReceiver"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver
android:name=".receiver.RemoteStartReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="net.i2p.android.router.receiver.START_I2P" />
</intent-filter>
</receiver>
<activity
android:name="net.i2p.android.I2PActivity"
android:icon="@drawable/ic_launcher_itoopie"
android:label="@string/app_name"
android:launchMode="singleTop">
android:launchMode="singleTop"
android:exported="true">
<!-- Console filters -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@ -42,6 +72,10 @@
<action android:name="net.i2p.android.router.START_I2P" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="net.i2p.android.router.service.APPROVE_SAM" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<!-- Addressbook filters -->
<intent-filter>
@ -51,6 +85,7 @@
<action android:name="android.intent.action.PICK" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="@xml/searchable_addressbook" />
@ -91,6 +126,7 @@
<activity
android:name=".web.WebActivity"
android:configChanges="orientation|keyboardHidden"
android:exported="true"
android:label="I2P Web Browser">
<!-- Disabled, this browser is not very secure
Temporarily enabled until an alternative browser is ready -->

View File

@ -0,0 +1,152 @@
//package android.support.v4.view;
package androidx.viewpager.widget;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
//import android.support.v4.os.ParcelableCompat;
import androidx.core.os.ParcelableCompat;
//import android.support.v4.os.ParcelableCompatCreatorCallbacks;
import androidx.core.os.ParcelableCompatCreatorCallbacks;
import androidx.viewpager.widget.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.Toast;
import net.i2p.android.router.R;
import net.i2p.android.router.util.Util;
public class CustomViewPager extends ViewPager {
private boolean mEnabled;
private int mFixedPage;
private int mFixedPageString;
public CustomViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
mEnabled = false;
mFixedPage = -1;
mFixedPageString = 0;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return mEnabled && mFixedPage < 0 && super.onTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
// See Nov. 20, 2013 comment at:
// https://github.com/JakeWharton/ViewPagerIndicator/pull/257
// Our ticket #2488
// prevent NPE if fake dragging and touching ViewPager
if(isFakeDragging()) return false;
return mEnabled && mFixedPage < 0 && super.onInterceptTouchEvent(event);
}
@Override
public void setCurrentItem(int item) {
if ((mEnabled && (mFixedPage < 0 || item == mFixedPage))
|| (!mEnabled && item == 0))
super.setCurrentItem(item);
else if (!mEnabled)
Toast.makeText(getContext(), Util.getRouterContext() == null ?
R.string.router_not_running : R.string.router_shutting_down,
Toast.LENGTH_SHORT).show();
else if (mFixedPageString > 0)
Toast.makeText(getContext(), getContext().getString(mFixedPageString),
Toast.LENGTH_SHORT).show();
}
public void setPagingEnabled(boolean enabled) {
mEnabled = enabled;
updatePagingState();
}
public void setFixedPage(int page, int res) {
mFixedPage = page;
mFixedPageString = res;
updatePagingState();
}
public void updatePagingState() {
if (mEnabled) {
if (mFixedPage >= 0 && getCurrentItem() != mFixedPage)
setCurrentItem(mFixedPage);
} else if (getCurrentItem() != 0)
setCurrentItem(0);
}
public static class SavedState extends ViewPager.SavedState {
boolean enabled;
int fixedPage;
int fixedPageString;
public SavedState(Parcelable superState) {
super(superState);
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(enabled ? 1 : 0);
out.writeInt(fixedPage);
out.writeInt(fixedPageString);
}
@Override
public String toString() {
return "CustomViewPager.SavedState{"
+ Integer.toHexString(System.identityHashCode(this))
+ " enabled=" + enabled + " fixedPage=" + fixedPage + "}";
}
public static final Parcelable.Creator<SavedState> CREATOR
= ParcelableCompat.newCreator(new ParcelableCompatCreatorCallbacks<SavedState>() {
@Override
public SavedState createFromParcel(Parcel in, ClassLoader loader) {
return new SavedState(in, loader);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
});
SavedState(Parcel in, ClassLoader loader) {
super(in, loader);
enabled = in.readInt() != 0;
fixedPage = in.readInt();
fixedPageString = in.readInt();
}
}
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.enabled = mEnabled;
ss.fixedPage = mFixedPage;
ss.fixedPageString = mFixedPageString;
return ss;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState)state;
super.onRestoreInstanceState(ss.getSuperState());
mEnabled = ss.enabled;
mFixedPage = ss.fixedPage;
mFixedPageString = ss.fixedPageString;
}
}

View File

@ -1,162 +0,0 @@
/*
* The following code was written by Matthew Wiggins
* and is released under the APACHE 2.0 license
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Slight modifications and bugfixes by Sponge <sponge@mail.i2p>
* These modifications are released under the WTFPL (any version)
*
* We don't need negative numbers yet, and may never need to.
*
* XML Usage example:
*
* <com.hlidskialf.android.preference.SeekBarPreference android:key="duration"
* android:title="Duration of something"
* android:summary="How long something will last"
* android:dialogMessage="Something duration"
* android:defaultValue="5"
* android:text=" minutes"
* android:max="60"
* />
*
*/
package com.hlidskialf.android.preference;
import android.content.Context;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;
public class SeekBarPreference extends DialogPreference implements SeekBar.OnSeekBarChangeListener {
private static final String androidns = "http://schemas.android.com/apk/res/android";
private SeekBar mSeekBar;
private TextView mSplashText;
private TextView mValueText;
private Context mContext;
private String mDialogMessage, mSuffix;
private String mDefault = "0";
private int mMax = 0;
private int mValue = 0;
private int mDirection = LinearLayout.HORIZONTAL;
public SeekBarPreference(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
int dialogMessageR = attrs.getAttributeResourceValue(androidns, "dialogMessage", 0);
mDialogMessage = (dialogMessageR == 0)
? attrs.getAttributeValue(androidns, "dialogMessage")
: context.getResources().getString(dialogMessageR);
int textR = attrs.getAttributeResourceValue(androidns, "text", 0);
mSuffix = (textR == 0)
? attrs.getAttributeValue(androidns, "text")
: context.getResources().getString(textR);
mDefault = attrs.getAttributeValue(androidns, "defaultValue");
mMax = Integer.parseInt(attrs.getAttributeValue(androidns, "max"));
if (attrs.getAttributeValue(androidns, "direction") != null) {
mDirection = Integer.parseInt(attrs.getAttributeValue(androidns, "direction"));
}
}
@Override
protected View onCreateDialogView() {
LinearLayout.LayoutParams params;
LinearLayout layout = new LinearLayout(mContext);
layout.setOrientation(LinearLayout.VERTICAL);
layout.setPadding(6, 6, 6, 10);
// Set the width so that it is as usable as possible.
// We multiplymMax so that the smaller ranges will get a bigger area.
if (mDirection == LinearLayout.HORIZONTAL) {
layout.setMinimumWidth(mMax*5);
} else {
layout.setMinimumHeight(mMax*5);
}
mSplashText = new TextView(mContext);
if (mDialogMessage != null) {
mSplashText.setText(mDialogMessage);
}
layout.addView(mSplashText);
mValueText = new TextView(mContext);
mValueText.setGravity(Gravity.CENTER_HORIZONTAL);
mValueText.setTextSize(32);
params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
layout.addView(mValueText, params);
mSeekBar = new SeekBar(mContext);
mSeekBar.setOnSeekBarChangeListener(this);
// Move the bar away from the changing text, so you can see it, and
// move it away from the edges to improve usability for the end-ranges.
mSeekBar.setPadding(6, 30, 6, 6);
layout.addView(mSeekBar, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
if (shouldPersist()) {
mValue = Integer.parseInt(getPersistedString(mDefault));
}
mSeekBar.setMax(mMax);
mSeekBar.setProgress(mValue);
return layout;
}
@Override
protected void onBindDialogView(View v) {
super.onBindDialogView(v);
mSeekBar.setMax(mMax);
mSeekBar.setProgress(mValue);
}
@Override
protected void onSetInitialValue(boolean restore, Object defaultValue) {
super.onSetInitialValue(restore, defaultValue);
if (restore) {
mValue = shouldPersist() ? Integer.parseInt(getPersistedString(mDefault)) : 0;
} else {
mValue = (Integer) defaultValue;
}
}
public void onProgressChanged(SeekBar seek, int value, boolean fromTouch) {
String t = String.valueOf(value);
mValueText.setText(mSuffix == null ? t : t.concat(mSuffix));
if (shouldPersist()) {
persistString(t);
}
callChangeListener(value);
}
public void onStartTrackingTouch(SeekBar seek) {
}
public void onStopTrackingTouch(SeekBar seek) {
}
public void setMax(int max) {
mMax = max;
}
public int getMax() {
return mMax;
}
public void setProgress(int progress) {
mValue = progress;
if (mSeekBar != null) {
mSeekBar.setProgress(progress);
}
}
public int getProgress() {
return mValue;
}
}

View File

@ -0,0 +1,163 @@
package com.pavelsikun.seekbarpreference;
import android.content.Context;
import android.content.res.TypedArray;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
//import android.support.v7.preference.Preference;
import androidx.preference.Preference;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.SeekBar;
import android.widget.TextView;
import net.i2p.android.router.R;
/**
* Based on MaterialSeekBarController created by mrbimc on 30.09.15.
*/
public class MaterialSeekBarController implements SeekBar.OnSeekBarChangeListener {
private final String TAG = getClass().getName();
public static final int DEFAULT_CURRENT_VALUE = 50;
private static final int DEFAULT_MAX_VALUE = 100;
private static final String DEFAULT_MEASUREMENT_UNIT = "";
private int mMaxValue;
private int mMaxDigits;
private int mCurrentValue;
private String mMeasurementUnit;
private SeekBar mSeekBar;
private TextView mSeekBarValue;
private TextView mMeasurementUnitView;
private Context mContext;
private Persistable mPersistable;
public MaterialSeekBarController(Context context, AttributeSet attrs, Persistable persistable) {
mContext = context;
mPersistable = persistable;
init(attrs, null);
}
private void init(AttributeSet attrs, View view) {
setValuesFromXml(attrs);
if(view != null) onBindView(view);
}
private void setValuesFromXml(@Nullable AttributeSet attrs) {
if (attrs == null) {
mCurrentValue = DEFAULT_CURRENT_VALUE;
mMaxValue = DEFAULT_MAX_VALUE;
mMeasurementUnit = DEFAULT_MEASUREMENT_UNIT;
} else {
TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.SeekBarPreference);
try {
mMaxValue = a.getInt(R.styleable.SeekBarPreference_msbp_maxValue, DEFAULT_MAX_VALUE);
mCurrentValue = a.getInt(R.styleable.SeekBarPreference_msbp_defaultValue, DEFAULT_CURRENT_VALUE);
if(mCurrentValue > mMaxValue) mCurrentValue = mMaxValue / 2;
mMeasurementUnit = a.getString(R.styleable.SeekBarPreference_msbp_measurementUnit);
if (mMeasurementUnit == null)
mMeasurementUnit = DEFAULT_MEASUREMENT_UNIT;
} finally {
a.recycle();
}
}
mMaxDigits = (int) Math.log10(mMaxValue) + 1;
}
public void onBindView(@NonNull View view) {
mSeekBar = (SeekBar) view.findViewById(R.id.seekbar);
mSeekBar.setMax(mMaxValue);
mSeekBar.setOnSeekBarChangeListener(this);
mSeekBarValue = (TextView) view.findViewById(R.id.seekbar_value);
setPaddedValue(mCurrentValue);
mMeasurementUnitView = (TextView) view.findViewById(R.id.measurement_unit);
mMeasurementUnitView.setText(mMeasurementUnit);
mSeekBar.setProgress(mCurrentValue);
if (!view.isEnabled()) {
mSeekBar.setEnabled(false);
mSeekBarValue.setEnabled(false);
}
}
protected void onSetInitialValue(boolean restoreValue, @NonNull Object defaultValue) {
mCurrentValue = mMaxValue / 2;
try {
mCurrentValue = (Integer) defaultValue;
} catch (Exception ex) {
Log.e(TAG, "Invalid default value: " + defaultValue.toString());
}
}
public void setEnabled(boolean enabled) {
if (mSeekBar != null) mSeekBar.setEnabled(enabled);
if (mSeekBarValue != null) mSeekBarValue.setEnabled(enabled);
}
public void onDependencyChanged(Preference dependency, boolean disableDependent) {
if (mSeekBar != null) mSeekBar.setEnabled(!disableDependent);
if (mSeekBarValue != null) mSeekBarValue.setEnabled(!disableDependent);
}
//SeekBarListener:
@Override
public void onProgressChanged(@NonNull SeekBar seekBar, int progress, boolean fromUser) {
mCurrentValue = progress;
setPaddedValue(progress);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(@NonNull SeekBar seekBar) {
setCurrentValue(mCurrentValue);
}
private void setPaddedValue(int value) {
//mSeekBarValue.setText(String.format("%0" + mMaxDigits +"d", value));
mSeekBarValue.setText(String.format("%" + mMaxDigits +"d", value));
}
//public methods for manipulating this widget from java:
public void setCurrentValue(int value) {
mCurrentValue = value;
if (mPersistable != null) mPersistable.onPersist(value);
}
public int getCurrentValue() {
return mCurrentValue;
}
public void setMaxValue(int maxValue) {
mMaxValue = maxValue;
if (mSeekBar != null) mSeekBar.setMax(mMaxValue);
}
public int getMaxValue() {
return mMaxValue;
}
public void setMeasurementUnit(String measurementUnit) {
mMeasurementUnit = measurementUnit;
if (mMeasurementUnitView != null) mMeasurementUnitView.setText(mMeasurementUnit);
}
public String getMeasurementUnit() {
return mMeasurementUnit;
}
}

View File

@ -0,0 +1,8 @@
package com.pavelsikun.seekbarpreference;
/**
* Created by mrbimc on 04.10.15.
*/
public interface Persistable {
void onPersist(int value);
}

View File

@ -0,0 +1,106 @@
package com.pavelsikun.seekbarpreference;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import androidx.annotation.NonNull;
//import android.support.v7.preference.Preference;
import androidx.preference.Preference;
//import android.support.v7.preference.PreferenceViewHolder;
import androidx.preference.PreferenceViewHolder;
import android.util.AttributeSet;
import net.i2p.android.router.R;
public class SeekBarPreference extends Preference implements Persistable {
private MaterialSeekBarController mController;
public SeekBarPreference(Context context) {
super(context);
init(null);
}
public SeekBarPreference(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public SeekBarPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public SeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(attrs);
}
private void init(AttributeSet attrs) {
setLayoutResource(R.layout.seekbar_preference);
mController = new MaterialSeekBarController(getContext(), attrs, this);
}
@Override
public void onBindViewHolder(@NonNull PreferenceViewHolder viewHolder) {
super.onBindViewHolder(viewHolder);
mController.onBindView(viewHolder.itemView);
}
@Override
protected Object onGetDefaultValue(@NonNull TypedArray ta, int index) {
if(mController != null) return ta.getInt(index, mController.getCurrentValue());
else return null;
}
@Override
protected void onSetInitialValue(boolean restoreValue, @NonNull Object defaultValue) {
int average = mController.getMaxValue() / 2;
if(restoreValue) mController.setCurrentValue(getPersistedInt(average));
else mController.onSetInitialValue(restoreValue, defaultValue);
}
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
mController.setEnabled(enabled);
}
@Override
public void onDependencyChanged(Preference dependency, boolean disableDependent) {
super.onDependencyChanged(dependency, disableDependent);
mController.onDependencyChanged(dependency, disableDependent);
}
@Override
public void onPersist(int value) {
persistInt(value);
notifyChanged();
}
public String getMeasurementUnit() {
return mController.getMeasurementUnit();
}
public void setMeasurementUnit(String measurementUnit) {
mController.setMeasurementUnit(measurementUnit);
}
public int getMaxValue() {
return mController.getMaxValue();
}
public void setMaxValue(int maxValue) {
mController.setMaxValue(maxValue);
}
public int getCurrentValue() {
return mController.getCurrentValue();
}
public void setCurrentValue(int value) {
mController.setCurrentValue(value);
}
}

View File

@ -5,10 +5,14 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
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.widget.Toolbar;
//import android.support.v4.app.Fragment;
import androidx.fragment.app.Fragment;
//import android.support.v4.app.FragmentManager;
import androidx.fragment.app.FragmentManager;
//import android.support.v4.content.LocalBroadcastManager;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
//import android.support.v7.widget.Toolbar;
import androidx.appcompat.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
@ -19,12 +23,13 @@ import net.i2p.android.router.MainFragment;
import net.i2p.android.router.R;
import net.i2p.android.router.SettingsActivity;
import net.i2p.android.router.addressbook.AddressbookContainer;
import net.i2p.android.router.service.AndroidSAMSecureSession;
import net.i2p.android.router.service.RouterService;
import net.i2p.android.router.service.State;
import net.i2p.android.router.util.Connectivity;
import net.i2p.android.router.util.Util;
import net.i2p.android.util.MemoryFragmentPagerAdapter;
import net.i2p.android.widget.CustomViewPager;
import androidx.viewpager.widget.CustomViewPager;
import net.i2p.android.widget.SlidingTabLayout;
import net.i2p.router.RouterContext;
@ -134,6 +139,7 @@ public class I2PActivity extends I2PActivityBase implements
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
handleIntents();
}
@ -143,14 +149,32 @@ public class I2PActivity extends I2PActivityBase implements
Intent intent = getIntent();
String action = intent.getAction();
Util.d("handleIntent: intent=" + intent.toString());
if (action == null)
return;
if (action.equals("net.i2p.android.router.START_I2P")) {
if (mViewPager.getCurrentItem() != 0)
mViewPager.setCurrentItem(0, false);
autoStart();
Util.d("handleIntent: action=" + action);
Bundle extra = intent.getExtras();
if (extra != null)
Util.d("handleIntent extra=" + extra.toString());
switch (action) {
case "net.i2p.android.router.START_I2P":
if (mViewPager.getCurrentItem() != 0)
mViewPager.setCurrentItem(0, false);
autoStart();
break;
case "net.i2p.android.router.service.APPROVE_SAM":
Util.w("Affirmed SAM Connection");
String ID = extra.getString("ID");
Util.d("SAM ID was: " + ID);
AndroidSAMSecureSession.affirmResult(ID);
break;
case Intent.ACTION_PICK:
mViewPager.setFixedPage(2, R.string.select_an_address);
break;
}
}

View File

@ -6,7 +6,8 @@ import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
//import android.support.v7.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatActivity;
import net.i2p.android.router.service.RouterBinder;
import net.i2p.android.router.service.RouterService;

View File

@ -35,6 +35,7 @@ class InitActivities {
public InitActivities(Context c) {
ctx = c;
myDir = Util.getFileDir(c);
Util.i("My app directory is "+myDir);
_ourVersion = Util.getOurVersion(c);
}
@ -59,90 +60,89 @@ class InitActivities {
}
void initialize() {
Util.i("Initializing the I2P resources");
if (checkNewVersion()) {
List<Properties> lProps = Util.getPropertiesFromPreferences(ctx);
Properties props = lProps.get(0);
List<Properties> lProps = Util.getPropertiesFromPreferences(ctx);
Properties props = lProps.get(0);
props.setProperty("i2p.dir.temp", myDir + "/tmp");
props.setProperty("i2p.dir.pid", myDir + "/tmp");
// Time disabled in default router.config
// But lots of time problems on Android, not all carriers support NITZ
// and there was no NTP before 3.0. Tablets should be fine?
// Phones in airplane mode with wifi enabled still a problem.
// Deactivated phones in airplane mode definitely won't have correct time.
if (Build.VERSION.SDK_INT < 11) // Honeycomb 3.0
props.setProperty("time.disabled", "false");
mergeResourceToFile(R.raw.router_config, "router.config", props);
mergeResourceToFile(R.raw.logger_config, "logger.config", lProps.get(1));
// This is not needed for now, i2ptunnel.config only contains tunnel
// settings, which can now be configured manually. We don't want to
// overwrite the user's tunnels.
//mergeResourceToFile(R.raw.i2ptunnel_config, "i2ptunnel.config", null);
copyResourceToFileIfAbsent(R.raw.i2ptunnel_config, "i2ptunnel.config");
// FIXME this is a memory hog to merge this way
mergeResourceToFile(R.raw.hosts_txt, "hosts.txt", null);
mergeResourceToFile(R.raw.more_hosts_txt, "hosts.txt", null);
copyResourceToFile(R.raw.blocklist_txt, "blocklist.txt");
props.setProperty("i2p.dir.temp", myDir + "/tmp");
props.setProperty("i2p.dir.pid", myDir + "/tmp");
// Time disabled in default router.config
// But lots of time problems on Android, not all carriers support NITZ
// and there was no NTP before 3.0. Tablets should be fine?
// Phones in airplane mode with wifi enabled still a problem.
// Deactivated phones in airplane mode definitely won't have correct time.
if (Build.VERSION.SDK_INT < 11) // Honeycomb 3.0
props.setProperty("time.disabled", "false");
mergeResourceToFile(R.raw.router_config, "router.config", props);
mergeResourceToFile(R.raw.logger_config, "logger.config", lProps.get(1));
// This is not needed for now, i2ptunnel.config only contains tunnel
// settings, which can now be configured manually. We don't want to
// overwrite the user's tunnels.
//mergeResourceToFile(R.raw.i2ptunnel_config, "i2ptunnel.config", null);
copyResourceToFileIfAbsent(R.raw.i2ptunnel_config, "i2ptunnel.config");
// FIXME this is a memory hog to merge this way
mergeResourceToFile(R.raw.hosts_txt, "hosts.txt", null);
mergeResourceToFile(R.raw.more_hosts_txt, "hosts.txt", null);
copyResourceToFile(R.raw.blocklist_txt, "blocklist.txt");
File abDir = new File(myDir, "addressbook");
abDir.mkdir();
copyResourceToFile(R.raw.subscriptions_txt, "addressbook/subscriptions.txt");
mergeResourceToFile(R.raw.addressbook_config_txt, "addressbook/config.txt", null);
File abDir = new File(myDir, "addressbook");
abDir.mkdir();
copyResourceToFileIfAbsent(R.raw.subscriptions_txt, "addressbook/subscriptions.txt");
mergeResourceToFile(R.raw.addressbook_config_txt, "addressbook/config.txt", null);
File docsDir = new File(myDir, "docs");
docsDir.mkdir();
copyResourceToFile(R.raw.ahelper_conflict_header_ht, "docs/ahelper-conflict-header.ht");
copyResourceToFile(R.raw.ahelper_new_header_ht, "docs/ahelper-new-header.ht");
copyResourceToFile(R.raw.ahelper_notfound_header_ht, "docs/ahelper-notfound-header.ht");
copyResourceToFile(R.raw.auth_header_ht, "docs/auth-header.ht");
copyResourceToFile(R.raw.baduri_header_ht, "docs/baduri-header.ht");
copyResourceToFile(R.raw.denied_header_ht, "docs/denied-header.ht");
copyResourceToFile(R.raw.dnf_header_ht, "docs/dnf-header.ht");
copyResourceToFile(R.raw.dnfb_header_ht, "docs/dnfb-header.ht");
copyResourceToFile(R.raw.dnfh_header_ht, "docs/dnfh-header.ht");
copyResourceToFile(R.raw.dnfp_header_ht, "docs/dnfp-header.ht");
copyResourceToFile(R.raw.enc_header_ht, "docs/enc-header.ht");
copyResourceToFile(R.raw.encp_header_ht, "docs/encp-header.ht");
copyResourceToFile(R.raw.localhost_header_ht, "docs/localhost-header.ht");
copyResourceToFile(R.raw.nols_header_ht, "docs/nols-header.ht");
copyResourceToFile(R.raw.nolsp_header_ht, "docs/nolsp-header.ht");
copyResourceToFile(R.raw.noproxy_header_ht, "docs/noproxy-header.ht");
copyResourceToFile(R.raw.protocol_header_ht, "docs/protocol-header.ht");
copyResourceToFile(R.raw.reset_header_ht, "docs/reset-header.ht");
copyResourceToFile(R.raw.resetp_header_ht, "docs/resetp-header.ht");
File docsDir = new File(myDir, "docs");
docsDir.mkdir();
/*copyResourceToFile(R.raw.ahelper_conflict_header_ht, "docs/ahelper-conflict-header.ht");
copyResourceToFile(R.raw.ahelper_new_header_ht, "docs/ahelper-new-header.ht");
copyResourceToFile(R.raw.ahelper_notfound_header_ht, "docs/ahelper-notfound-header.ht");
copyResourceToFile(R.raw.auth_header_ht, "docs/auth-header.ht");
copyResourceToFile(R.raw.baduri_header_ht, "docs/baduri-header.ht");
copyResourceToFile(R.raw.denied_header_ht, "docs/denied-header.ht");
copyResourceToFile(R.raw.dnf_header_ht, "docs/dnf-header.ht");
copyResourceToFile(R.raw.dnfb_header_ht, "docs/dnfb-header.ht");
copyResourceToFile(R.raw.dnfh_header_ht, "docs/dnfh-header.ht");
copyResourceToFile(R.raw.dnfp_header_ht, "docs/dnfp-header.ht");
copyResourceToFile(R.raw.enc_header_ht, "docs/enc-header.ht");
copyResourceToFile(R.raw.encp_header_ht, "docs/encp-header.ht");
copyResourceToFile(R.raw.localhost_header_ht, "docs/localhost-header.ht");
copyResourceToFile(R.raw.nols_header_ht, "docs/nols-header.ht");
copyResourceToFile(R.raw.nolsp_header_ht, "docs/nolsp-header.ht");
copyResourceToFile(R.raw.noproxy_header_ht, "docs/noproxy-header.ht");
copyResourceToFile(R.raw.protocol_header_ht, "docs/protocol-header.ht");
copyResourceToFile(R.raw.reset_header_ht, "docs/reset-header.ht");
copyResourceToFile(R.raw.resetp_header_ht, "docs/resetp-header.ht");*/
File cssDir = new File(docsDir, "themes/console/light");
cssDir.mkdirs();
//copyResourceToFile(R.raw.console_css, "docs/themes/console/light/console.css");
//copyResourceToFile(R.raw.android_css, "docs/themes/console/light/android.css");
File cssDir = new File(docsDir, "themes/console/light");
cssDir.mkdirs();
//copyResourceToFile(R.raw.console_css, "docs/themes/console/light/console.css");
//copyResourceToFile(R.raw.android_css, "docs/themes/console/light/android.css");
File imgDir = new File(docsDir, "themes/console/images");
imgDir.mkdir();
copyResourceToFile(R.drawable.i2plogo, "docs/themes/console/images/i2plogo.png");
copyResourceToFile(R.drawable.itoopie_sm, "docs/themes/console/images/itoopie_sm.png");
//copyResourceToFile(R.drawable.outbound, "docs/themes/console/images/outbound.png");
//copyResourceToFile(R.drawable.inbound, "docs/themes/console/images/inbound.png");
File imgDir = new File(docsDir, "themes/console/images");
imgDir.mkdir();
copyResourceToFile(R.drawable.i2plogo, "docs/themes/console/images/i2plogo.png");
copyResourceToFile(R.drawable.itoopie_sm, "docs/themes/console/images/itoopie_sm.png");
//copyResourceToFile(R.drawable.outbound, "docs/themes/console/images/outbound.png");
//copyResourceToFile(R.drawable.inbound, "docs/themes/console/images/inbound.png");
File img2Dir = new File(cssDir, "images");
img2Dir.mkdir();
//copyResourceToFile(R.drawable.header, "docs/themes/console/light/images/header.png");
File img2Dir = new File(cssDir, "images");
img2Dir.mkdir();
//copyResourceToFile(R.drawable.header, "docs/themes/console/light/images/header.png");
File certDir = new File(myDir, "certificates");
certDir.mkdir();
File certificates = new File(myDir, "certificates");
File[] allcertificates = certificates.listFiles();
if ( allcertificates != null) {
for (File f : allcertificates) {
Util.d("Deleting old certificate file/dir " + f);
FileUtil.rmdir(f, false);
}
File certDir = new File(myDir, "certificates");
certDir.mkdir();
File certificates = new File(myDir, "certificates");
File[] allCertificates = certificates.listFiles();
if ( allCertificates != null) {
for (File f : allCertificates) {
Util.d("Deleting old certificate file/dir " + f);
FileUtil.rmdir(f, false);
}
unzipResourceToDir(R.raw.certificates_zip, "certificates");
//File netDBDir = new File(myDir, "netDB");
//netDBDir.mkdir();
//unzipResourceToDir(R.raw.netdb_zip, "netDB");
}
File netDBDir = new File(myDir, "netDB");
netDBDir.mkdir();
//unzipResourceToDir(R.raw.netdb_zip, "netDB");
unzipResourceToDir(R.raw.certificates_zip, "certificates");
// Set up the locations so settings can find them
System.setProperty("i2p.dir.base", myDir);
@ -178,10 +178,16 @@ class InitActivities {
out.write(buf, 0, read);
} catch (IOException ioe) {
Util.e("copyResourceToFile" + "IOE: ", ioe);
} catch (Resources.NotFoundException nfe) {
Util.e("copyResourceToFile" + "NFE: ", nfe);
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
if (out != null) try { out.close(); } catch (IOException ioe) {}
if (in != null) try { in.close(); } catch (IOException ioe) {
Util.e("copyResourceToFile" + "IOE in.close(): ", ioe);
}
if (out != null) try { out.close(); } catch (IOException ioe) {
Util.e("copyResourceToFile" + "IOE out.close(): ", ioe);
}
}
}
/**
@ -192,7 +198,7 @@ class InitActivities {
FileOutputStream out = null;
ZipInputStream zis = null;
Util.d("Creating files in '" + myDir + "/" + folder + "/' from resource");
Util.i("Creating files in '" + myDir + "/" + folder + "/' from resource");
try {
// Context methods
in = ctx.getResources().openRawResource(resID);
@ -200,6 +206,7 @@ class InitActivities {
ZipEntry ze;
while ((ze = zis.getNextEntry()) != null) {
out = null;
Util.i("unzipping "+ze);
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
@ -209,26 +216,51 @@ class InitActivities {
}
String name = ze.getName();
File f = new File(myDir + "/" + folder +"/" + name);
if (ze.isDirectory()) {
Util.d("Creating directory " + myDir + "/" + folder +"/" + name + " from resource");
String canonicalPath = f.getCanonicalPath().replace("/user/0/", "/data/");
// account for canonical path differences when using .aab bundles
if (!canonicalPath.startsWith(myDir.replace("/user/0/", "/data/"))) {
// If these don't match, there's a path-traversal possibility.
// So ignore it.
Util.e("Path mismatch bug " + canonicalPath.toString() + " " + myDir.toString());
} else if (ze.isDirectory()) {
Util.i("Creating directory " + myDir + "/" + folder +"/" + name + " from resource");
f.mkdir();
} else {
Util.d("Creating file " + myDir + "/" + folder +"/" + name + " from resource");
Util.i("Creating file " + myDir + "/" + folder +"/" + name + " from resource");
//create all the leading directories
File newFile = new File(myDir+"/"+folder+"/"+name);
newFile.getParentFile().mkdirs();
byte[] bytes = baos.toByteArray();
out = new FileOutputStream(f);
out.write(bytes);
}
} catch (IOException ioe) {
Util.e("unzipResourceToDir" + "IOE: ", ioe);
} finally {
if (out != null) { try { out.close(); } catch (IOException ioe) {} out = null; }
if (out != null) {
try {
out.close();
} catch (IOException ioe) {
Util.e("unzipResourceToDir" + "IOE: interior out.close ", ioe);
}
out = null;
}
}
}
} catch (IOException ioe) {
Util.e("unzipResourceToDir" + "IOE: ", ioe);
} catch (Resources.NotFoundException nfe) {
Util.e("unzipResourceToDir" + "NFE: ", nfe);
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
if (out != null) try { out.close(); } catch (IOException ioe) {}
if (zis != null) try { zis.close(); } catch (IOException ioe) {}
if (in != null) try { in.close(); } catch (IOException ioe) {
Util.e("unzipResourceToDir" + "IOE: in.close() ", ioe);
}
if (out != null) try { out.close(); } catch (IOException ioe) {
Util.e("unzipResourceToDir" + "IOE: out.close() ", ioe);
}
if (zis != null) try { zis.close(); } catch (IOException ioe) {
Util.e("unzipResourceToDir" + "IOE: zis.close() ", ioe);
}
}
}
@ -252,18 +284,31 @@ class InitActivities {
private boolean checkNewVersion() {
Properties props = new Properties();
Util.i("Checking for a new install/version");
InputStream fin = null;
try {
fin = ctx.openFileInput(CONFIG_FILE);
DataHelper.loadProps(props, fin);
} catch (IOException ioe) {
Util.d("Looks like a new install");
Util.i("Looks like a new install");
} finally {
if (fin != null) try { fin.close(); } catch (IOException ioe) {}
if (fin != null) {
try {
Util.i("fin was not null "+CONFIG_FILE);
fin.close();
} catch (IOException ioe) {
Util.i("Error loading config:", ioe);
}
}else {
Util.i("fin was null");
}
}
String oldVersion = props.getProperty(PROP_INSTALLED_VERSION);
Util.i("Old version is:"+oldVersion);
boolean newInstall = oldVersion == null;
if (newInstall)
return true;
boolean newVersion = !_ourVersion.equals(oldVersion);
if (newVersion) {

View File

@ -5,6 +5,8 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Locale;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.util.EepGet;
@ -83,7 +85,7 @@ public class EepGetFetcher implements EepGet.StatusListener {
int semi = rv.indexOf(";");
if (semi > 0)
rv = rv.substring(0, semi);
return rv.toLowerCase();
return rv.toLowerCase(Locale.US);
}
/**

View File

@ -1,24 +1,62 @@
package net.i2p.android.apps;
import static net.i2p.app.ClientAppState.INITIALIZED;
import static net.i2p.app.ClientAppState.RUNNING;
import static net.i2p.app.ClientAppState.STARTING;
import static net.i2p.app.ClientAppState.STOPPED;
import static net.i2p.app.ClientAppState.STOPPING;
import static net.i2p.app.ClientAppState.UNINITIALIZED;
import static net.i2p.update.UpdateType.BLOCKLIST;
import android.content.Context;
import net.i2p.android.router.NewsActivity;
import net.i2p.android.router.R;
import net.i2p.android.router.util.Notifications;
import net.i2p.app.ClientApp;
import net.i2p.app.ClientAppManager;
import net.i2p.app.ClientAppState;
import net.i2p.crypto.SU3File;
import net.i2p.data.Base64;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.router.Banlist;
import net.i2p.router.Blocklist;
import net.i2p.router.RouterContext;
import net.i2p.router.util.RFC822Date;
import net.i2p.router.news.BlocklistEntries;
import net.i2p.router.news.NewsEntry;
import net.i2p.router.news.NewsMetadata;
import net.i2p.router.news.NewsXMLParser;
import net.i2p.util.Addresses;
import net.i2p.util.EepGet;
import net.i2p.util.FileUtil;
import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
import net.i2p.util.RFC822Date;
import net.i2p.util.ReusableGZIPInputStream;
import net.i2p.util.SecureFile;
import net.i2p.util.SecureFileOutputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
/**
* From router console, simplified since we don't deal with router versions
* or updates.
*
* As of 0.9.41, implements ClientApp to hang us off the ClientAppManager,
* so we can remove the static reference.
*/
public class NewsFetcher implements Runnable, EepGet.StatusListener {
public class NewsFetcher implements Runnable, EepGet.StatusListener, ClientApp {
private final Context mCtx;
private final RouterContext _context;
private final Notifications _notif;
@ -29,20 +67,22 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
private boolean _invalidated;
private File _newsFile;
private File _tempFile;
private static NewsFetcher _instance;
private volatile boolean _isRunning = true;
private Thread _thread;
private final ClientAppManager _mgr;
private volatile ClientAppState _state = UNINITIALIZED;
public static final String APP_NAME = "NewsFetcher";
public static /*final */ NewsFetcher getInstance() {
return _instance;
}
static final String PROP_BLOCKLIST_TIME = "router.blocklistVersion";
private static final String BLOCKLIST_DIR = "docs/feed/blocklist";
private static final String BLOCKLIST_FILE = "blocklist.txt";
/**
* As of 0.9.41, returns a new one every time. Only call once.
*/
public static /* final */ synchronized NewsFetcher getInstance(
Context context, RouterContext ctx, Notifications notif) {
if (_instance != null)
return _instance;
_instance = new NewsFetcher(context, ctx, notif);
return _instance;
return new NewsFetcher(context, ctx, notif);
}
private static final String NEWS_DIR = "docs";
@ -50,36 +90,39 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
private static final String TEMP_NEWS_FILE = "news.xml.temp";
/**
* Changed in 0.9.11 to the b32 for psi.i2p, run by psi.
* We may be able to change it to psi.i2p in a future release after
* the hostname propagates.
* Changed in 0.9.11 to the b32 for psi.i2p, run by psi.
* We may be able to change it to psi.i2p in a future release after
* the hostname propagates.
*
* @since 0.7.14 not configurable
* @since 0.7.14 not configurable
*/
private static final String BACKUP_NEWS_URL = "http://avviiexdngd32ccoy4kuckvc3mkf53ycvzbz6vz75vzhv4tbpk5a.b32.i2p/news.xml";
private static final String BACKUP_NEWS_URL_SU3 = "http://dn3tvalnjz432qkqsvpfdqrwpqkw3ye4n4i2uyfr4jexvo3sp5ka.b32.i2p/news/news.su3";
private static final String PROP_LAST_CHECKED = "router.newsLastChecked";
private static final String PROP_REFRESH_FREQUENCY = "router.newsRefreshFrequency";
private static final String DEFAULT_REFRESH_FREQUENCY = 24*60*60*1000 + "";
private static final String DEFAULT_REFRESH_FREQUENCY = 24 * 60 * 60 * 1000 + "";
private static final String PROP_NEWS_URL = "router.newsURL";
private static final String DEFAULT_NEWS_URL = "http://echelon.i2p/i2p/news.xml";
public static final String DEFAULT_NEWS_URL_SU3 = "http://tc73n4kivdroccekirco7rhgxdg5f3cjvbaapabupeyzrqwv5guq.b32.i2p/news.su3";
private NewsFetcher(Context context, RouterContext ctx, Notifications notif) {
mCtx = context;
_context = ctx;
_notif = notif;
_context.addShutdownTask(new Shutdown());
_log = ctx.logManager().getLog(NewsFetcher.class);
try {
String last = ctx.getProperty(PROP_LAST_CHECKED);
if (last != null)
_lastFetch = Long.parseLong(last);
} catch (NumberFormatException nfe) {}
} catch (NumberFormatException nfe) {
}
File newsDir = new File(_context.getRouterDir(), NEWS_DIR);
// isn't already there on android
newsDir.mkdir();
_newsFile = new File(newsDir, NEWS_FILE);
_tempFile = new File(_context.getTempDir(), TEMP_NEWS_FILE);
updateLastFetched();
_mgr = ctx.clientAppManager();
changeState(INITIALIZED);
_mgr.register(this);
}
private void updateLastFetched() {
@ -98,25 +141,37 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
}
public String status() {
StringBuilder buf = new StringBuilder(128);
long now = _context.clock().now();
if (_lastUpdated > 0) {
buf.append(mCtx.getString(R.string.news_last_updated,
DataHelper.formatDuration2(now - _lastUpdated)))
.append('\n');
}
if (_lastFetch > _lastUpdated) {
buf.append(mCtx.getString(R.string.news_last_checked,
DataHelper.formatDuration2(now - _lastFetch)));
}
return buf.toString();
StringBuilder buf = new StringBuilder(128);
long now = _context.clock().now();
if (_lastUpdated > 0) {
buf.append(mCtx.getString(R.string.news_last_updated,
DataHelper.formatDuration2(now - _lastUpdated)))
.append('\n');
}
if (_lastFetch > _lastUpdated) {
buf.append(mCtx.getString(R.string.news_last_checked,
DataHelper.formatDuration2(now - _lastFetch)));
}
return buf.toString();
}
private static final long INITIAL_DELAY = 5*60*1000;
private static final long RUN_DELAY = 30*60*1000;
// Runnable
private static final long INITIAL_DELAY = 5 * 60 * 1000;
private static final long RUN_DELAY = 30 * 60 * 1000;
public void run() {
_thread = Thread.currentThread();
changeState(RUNNING);
try {
run2();
} finally {
_mgr.unregister(this);
changeState(STOPPED);
}
}
private void run2() {
try {
Thread.sleep(INITIAL_DELAY);
} catch (InterruptedException ie) {
@ -139,7 +194,7 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
return true;
updateLastFetched();
String freq = _context.getProperty(PROP_REFRESH_FREQUENCY,
DEFAULT_REFRESH_FREQUENCY);
DEFAULT_REFRESH_FREQUENCY);
try {
long ms = Long.parseLong(freq);
if (ms <= 0)
@ -160,8 +215,9 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
}
/**
* Call this when changing news URLs to force an update next time the timer fires.
* @since 0.8.7
* Call this when changing news URLs to force an update next time the timer fires.
*
* @since 0.8.7
*/
void invalidateNews() {
_lastModified = null;
@ -169,7 +225,7 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
}
public void fetchNews() {
String newsURL = _context.getProperty(PROP_NEWS_URL, DEFAULT_NEWS_URL);
String newsURL = _context.getProperty(PROP_NEWS_URL, DEFAULT_NEWS_URL_SU3);
String proxyHost = "127.0.0.1";
int proxyPort = 4444;
if (_tempFile.exists())
@ -185,7 +241,7 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
} else {
// backup news location - always proxied
_tempFile.delete();
get = new EepGet(_context, true, proxyHost, proxyPort, 0, _tempFile.getAbsolutePath(), BACKUP_NEWS_URL, true, null, _lastModified);
get = new EepGet(_context, true, proxyHost, proxyPort, 0, _tempFile.getAbsolutePath(), BACKUP_NEWS_URL_SU3, true, null, _lastModified);
get.addStatusListener(this);
if (get.fetch())
_lastModified = get.getLastModified();
@ -195,22 +251,34 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
}
}
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
// ignore
}
// EepGet.StatusListener
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {
// ignore
}
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {
if (_log.shouldLog(Log.INFO))
_log.info("News fetched from " + url + " with " + (alreadyTransferred+bytesTransferred));
_log.info("News fetched from " + url + " with " + (alreadyTransferred + bytesTransferred));
long now = _context.clock().now();
if (_tempFile.exists()) {
boolean copied = FileUtil.copy(_tempFile.getAbsolutePath(), _newsFile.getAbsolutePath(), true);
if (_tempFile.exists() && _tempFile.length() > 0) {
File from;
if (url.endsWith(".su3")) {
try {
from = processSU3();
} catch (IOException ioe) {
_log.error("Failed to extract the news file", ioe);
_tempFile.delete();
return;
}
} else {
from = _tempFile;
}
boolean copied = FileUtil.rename(from, _newsFile);
_tempFile.delete();
if (copied) {
_lastUpdated = now;
_tempFile.delete();
// Notify user
_notif.notify(mCtx.getString(R.string.news_updated),
@ -229,19 +297,321 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
_context.router().saveConfig();
}
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
// ignore
}
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
if (_log.shouldLog(Log.WARN))
_log.warn("Failed to fetch the news from " + url);
_tempFile.delete();
}
public void headerReceived(String url, int attemptNum, String key, String val) {}
public void attempting(String url) {}
private class Shutdown implements Runnable {
public void run() {
_isRunning = false;
if (_thread != null)
_thread.interrupt();
public void headerReceived(String url, int attemptNum, String key, String val) {
}
public void attempting(String url) {
}
//
// SU3 handlers
//
/**
* Process the fetched su3 news file _tempFile.
* Handles 3 types of contained files: xml.gz (preferred), xml, and html (old format fake xml)
*
* @return the temp file contining the HTML-format news.xml
* @since 0.9.20
*/
private File processSU3() throws IOException {
SU3File su3 = new SU3File(_context, _tempFile);
// real xml, maybe gz, maybe not
File to1 = new File(_context.getTempDir(), "tmp-" + _context.random().nextInt() + ".xml");
// real xml
File to2 = new File(_context.getTempDir(), "tmp2-" + _context.random().nextInt() + ".xml");
try {
su3.verifyAndMigrate(to1);
int type = su3.getFileType();
if (su3.getContentType() != SU3File.CONTENT_NEWS)
throw new IOException("bad content type: " + su3.getContentType());
if (type == SU3File.TYPE_HTML)
return to1;
if (type != SU3File.TYPE_XML && type != SU3File.TYPE_XML_GZ)
throw new IOException("bad file type: " + type);
File xml;
if (type == SU3File.TYPE_XML_GZ) {
gunzip(to1, to2);
xml = to2;
to1.delete();
} else {
xml = to1;
}
NewsXMLParser parser = new NewsXMLParser(_context);
parser.parse(xml);
xml.delete();
NewsMetadata data = parser.getMetadata();
List<NewsEntry> entries = parser.getEntries();
BlocklistEntries ble = parser.getBlocklistEntries();
if (ble != null && ble.isVerified())
processBlocklistEntries(ble);
else
_log.info("No blocklist entries found in news feed");
String sudVersion = su3.getVersionString();
String signingKeyName = su3.getSignerString();
File to3 = new File(_context.getTempDir(), "tmp3-" + _context.random().nextInt() + ".xml");
outputOldNewsXML(data, entries, sudVersion, signingKeyName, to3);
return to3;
} finally {
to2.delete();
}
}
/**
* Process blocklist entries
*
* @since 0.9.57
*/
private void processBlocklistEntries(BlocklistEntries ble) {
long oldTime = _context.getProperty(PROP_BLOCKLIST_TIME, 0L);
if (ble.updated <= oldTime) {
if (_log.shouldWarn())
_log.warn("Not processing blocklist " + DataHelper.formatDate(ble.updated) +
", already have " + DataHelper.formatDate(oldTime));
return;
}
Blocklist bl = _context.blocklist();
Banlist ban = _context.banlist();
String reason = "Blocklist feed " + DataHelper.formatDate(ble.updated);
int banned = 0;
for (Iterator<String> iter = ble.entries.iterator(); iter.hasNext(); ) {
String s = iter.next();
if (s.length() == 44) {
byte[] b = Base64.decode(s);
if (b == null || b.length != Hash.HASH_LENGTH) {
iter.remove();
continue;
}
Hash h = Hash.create(b);
if (!ban.isBanlistedForever(h)) {
ban.banlistRouterForever(h, reason);
_context.commSystem().forceDisconnect(h);
}
} else {
byte[] ip = Addresses.getIP(s);
if (ip == null) {
iter.remove();
continue;
}
if (!bl.isBlocklisted(ip))
bl.add(ip);
}
if (++banned >= BlocklistEntries.MAX_ENTRIES) {
// prevent somebody from destroying the whole network
break;
}
}
for (String s : ble.removes) {
if (s.length() == 44) {
byte[] b = Base64.decode(s);
if (b == null || b.length != Hash.HASH_LENGTH)
continue;
Hash h = Hash.create(b);
if (ban.isBanlistedForever(h))
ban.unbanlistRouter(h);
} else {
byte[] ip = Addresses.getIP(s);
if (ip == null)
continue;
if (bl.isBlocklisted(ip))
bl.remove(ip);
}
}
// Save the blocks. We do not save the unblocks.
File f = new SecureFile(_context.getConfigDir(), BLOCKLIST_DIR);
f.mkdirs();
f = new File(f, BLOCKLIST_FILE);
boolean fail = false;
BufferedWriter out = null;
try {
out = new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(f), "UTF-8"));
out.write("# ");
out.write(ble.supdated);
out.newLine();
banned = 0;
for (String s : ble.entries) {
s = s.replace(':', ';'); // IPv6
out.write(reason);
out.write(':');
out.write(s);
out.newLine();
if (++banned >= BlocklistEntries.MAX_ENTRIES)
break;
}
} catch (IOException ioe) {
_log.error("Error writing blocklist", ioe);
fail = true;
} finally {
if (out != null) try {
out.close();
} catch (IOException ioe) {}
}
if (!fail) {
f.setLastModified(ble.updated);
String upd = Long.toString(ble.updated);
_context.router().saveConfig(PROP_BLOCKLIST_TIME, upd);
}
if (_log.shouldWarn())
_log.warn("Processed " + ble.entries.size() + " blocks and " + ble.removes.size() + " unblocks from news feed");
}
/**
* Gunzip the file
*
* @since 0.9.20
*/
private static void gunzip(File from, File to) throws IOException {
ReusableGZIPInputStream in = ReusableGZIPInputStream.acquire();
OutputStream out = null;
try {
in.initialize(new FileInputStream(from));
out = new SecureFileOutputStream(to);
byte buf[] = new byte[4096];
int read;
while ((read = in.read(buf)) != -1) {
out.write(buf, 0, read);
}
} finally {
if (out != null) try {
out.close();
} catch (IOException ioe) {}
ReusableGZIPInputStream.release(in);
}
}
/**
* Output in the old format.
*
* @since 0.9.20
*/
private void outputOldNewsXML(NewsMetadata data, List<NewsEntry> entries,
String sudVersion, String signingKeyName, File to) throws IOException {
NewsMetadata.Release latestRelease = data.releases.get(0);
Writer out = null;
try {
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(to), "UTF-8"));
out.write("<!--\n");
// update metadata in old format
out.write("<i2p.release ");
if (latestRelease.i2pVersion != null)
out.write(" version=\"" + latestRelease.i2pVersion + '"');
if (latestRelease.minVersion != null)
out.write(" minVersion=\"" + latestRelease.minVersion + '"');
if (latestRelease.minJavaVersion != null)
out.write(" minJavaVersion=\"" + latestRelease.minJavaVersion + '"');
String su3Torrent = "";
String su2Torrent = "";
for (NewsMetadata.Update update : latestRelease.updates) {
if (update.torrent != null) {
if ("su3".equals(update.type))
su3Torrent = update.torrent;
else if ("su2".equals(update.type))
su2Torrent = update.torrent;
}
}
if (!su2Torrent.isEmpty())
out.write(" su2Torrent=\"" + su2Torrent + '"');
if (!su3Torrent.isEmpty())
out.write(" su3Torrent=\"" + su3Torrent + '"');
out.write("/>\n");
// su3 and feed metadata for debugging
out.write("** News version:\t" + DataHelper.stripHTML(sudVersion) + '\n');
out.write("** Signed by:\t" + signingKeyName + '\n');
out.write("** Feed:\t" + DataHelper.stripHTML(data.feedTitle) + '\n');
out.write("** Feed ID:\t" + DataHelper.stripHTML(data.feedID) + '\n');
out.write("** Feed Date:\t" + (new Date(data.feedUpdated)) + '\n');
out.write("-->\n");
if (entries == null)
return;
for (NewsEntry e : entries) {
if (e.title == null || e.content == null)
continue;
out.write("<!-- Entry Date: " + (new Date(e.updated)) + " -->\n");
out.write("<h3>");
out.write(e.title);
out.write("</h3>\n");
out.write(e.content);
out.write("\n\n");
}
} finally {
if (out != null) try {
out.close();
} catch (IOException ioe) {}
}
}
////// begin ClientApp interface
/**
* @since 0.9.41
*/
public synchronized void startup() {
changeState(STARTING);
_thread = new I2PAppThread(this, "NewsFetcher", true);
_thread.start();
}
/**
* @since 0.9.41
*/
public synchronized void shutdown(String[] args) {
if (_state != RUNNING)
return;
changeState(STOPPING);
_isRunning = false;
if (_thread != null)
_thread.interrupt();
changeState(STOPPED);
}
/**
* @since 0.9.41
*/
public ClientAppState getState() {
return _state;
}
/**
* @since 0.9.41
*/
public String getName() {
return APP_NAME;
}
/**
* @since 0.9.41
*/
public String getDisplayName() {
return APP_NAME;
}
////// end ClientApp interface
////// begin ClientApp helpers
/**
* @since 0.9.41
*/
private void changeState(ClientAppState state) {
changeState(state, null);
}
/**
* @since 0.9.41
*/
private synchronized void changeState(ClientAppState state, Exception e) {
_state = state;
_mgr.notify(this, state, null, e);
}
////// end ClientApp helpers
}

View File

@ -1,19 +1,26 @@
package net.i2p.android.help;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.net.Uri;
import androidx.annotation.NonNull;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
public class Browser implements Comparable<Browser> {
public final String packageName;
public final CharSequence label;
public final Drawable icon;
public final boolean isInstalled;
public final boolean isKnown;
public final boolean isSupported;
public final boolean isRecommended;
private boolean isInstalled;
/**
* A browser that we don't know about.
*
@ -80,4 +87,23 @@ public class Browser implements Comparable<Browser> {
} else
return 2;
}
public boolean isInstalled(Context context){
if (isInstalled) {
return true;
}
// Find all installed browsers that listen for ".i2p"
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://stats.i2p"));
final PackageManager pm = context.getPackageManager();
List<ResolveInfo> installedBrowsers = pm.queryIntentActivities(intent, 0);
for (ResolveInfo browser : installedBrowsers) {
if (browser.activityInfo.packageName.equals(packageName)) {
isInstalled = true;
break;
}
}
return isInstalled;
}
}

View File

@ -1,16 +1,19 @@
package net.i2p.android.help;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.net.Uri;
import android.support.v7.widget.RecyclerView;
//import android.support.v7.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import net.i2p.android.router.R;
@ -71,11 +74,15 @@ public class BrowserAdapter extends RecyclerView.Adapter<BrowserAdapter.ViewHold
holder.mLabel.setText(browser.label);
if (browser.isKnown) {
if (browser.isRecommended && browser.isInstalled) {
if (browser.isRecommended && browser.isInstalled(mCtx)) {
holder.mStatus.setImageDrawable(
mCtx.getResources().getDrawable(R.drawable.ic_stars_white_24dp));
holder.mStatus.setVisibility(View.VISIBLE);
} else if (browser.isSupported && !browser.isInstalled) {
} else if (browser.isSupported && browser.isInstalled(mCtx)) {
holder.mStatus.setImageDrawable(
mCtx.getResources().getDrawable(R.drawable.ic_stars_white_24dp));
holder.mStatus.setVisibility(View.INVISIBLE);
} else if (browser.isSupported && !browser.isInstalled(mCtx)) {
holder.mStatus.setImageDrawable(
mCtx.getResources().getDrawable(R.drawable.ic_shop_white_24dp));
holder.mStatus.setOnClickListener(new View.OnClickListener() {
@ -84,11 +91,15 @@ public class BrowserAdapter extends RecyclerView.Adapter<BrowserAdapter.ViewHold
String uriMarket = "market://search?q=pname:" + browser.packageName;
Uri uri = Uri.parse(uriMarket);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
mCtx.startActivity(intent);
try {
mCtx.startActivity(intent);
} catch (ActivityNotFoundException e) {
Toast.makeText(mCtx, R.string.no_market_app, Toast.LENGTH_LONG).show();
}
}
});
holder.mStatus.setVisibility(View.VISIBLE);
} else if (!browser.isSupported) {
} else if (browser.isInstalled(mCtx) && !browser.isSupported) {
// Make the icon gray-scale to show it is unsupported
ColorMatrix matrix = new ColorMatrix();
matrix.setSaturation(0);

View File

@ -1,8 +1,10 @@
package net.i2p.android.help;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
//import android.support.v7.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatActivity;
//import android.support.v7.widget.Toolbar;
import androidx.appcompat.widget.Toolbar;
import android.view.MenuItem;
import net.i2p.android.router.R;

View File

@ -8,11 +8,16 @@ import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
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.support.v4.app.Fragment;
import androidx.fragment.app.Fragment;
//import android.support.v4.app.LoaderManager;
import androidx.loader.app.LoaderManager;
//import android.support.v4.content.Loader;
import androidx.loader.content.Loader;
//import android.support.v7.widget.LinearLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
//import android.support.v7.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -26,6 +31,8 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.List;
import java.util.Map;
@ -100,8 +107,28 @@ public class BrowserListFragment extends Fragment implements
getContext().getResources().getStringArray(R.array.supported_browsers));
supportedLabels = Arrays.asList(
getContext().getResources().getStringArray(R.array.supported_browser_labels));
unsupported = Arrays.asList(
context.getResources().getStringArray(R.array.unsupported_browsers));
unsupported = allBrowsers(context);//Arrays.asList(
//context.getResources().getStringArray(R.array.unsupported_browsers));
}
public List<String> allBrowsers(Context context){
//try {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.google.com"));
List<ResolveInfo> browserList;
PackageManager pm = context.getPackageManager();
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
// gets all
browserList = pm.queryIntentActivities(intent, PackageManager.MATCH_ALL);
} else {
browserList = pm.queryIntentActivities(intent, 0);
}
//}catch()
List<String> finalResult = new ArrayList<String>();
for (ResolveInfo ri : browserList){
finalResult.add(ri.resolvePackageName);
}
return finalResult;
}
@Override
@ -121,8 +148,9 @@ public class BrowserListFragment extends Fragment implements
intent.setData(Uri.parse("http://stats.i2p"));
final PackageManager pm = getContext().getPackageManager();
List<ResolveInfo> installedBrowsers = pm.queryIntentActivities(intent, 0);
Set<ResolveInfo> installedBrowsers = new HashSet<>(pm.queryIntentActivities(intent, 0));
// Compare installed browsers to supported browsers
for (ResolveInfo browser : installedBrowsers) {
if (recommended.contains(browser.activityInfo.packageName)) {
browsers.add(new Browser(pm, browser, true, true));

View File

@ -2,10 +2,14 @@ package net.i2p.android.help;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.NavUtils;
import android.support.v4.app.TaskStackBuilder;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
//import android.support.v4.app.NavUtils;
import androidx.core.app.NavUtils;
//import android.support.v4.app.TaskStackBuilder;
import androidx.core.app.TaskStackBuilder;
//import android.support.v7.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatActivity;
//import android.support.v7.widget.Toolbar;
import androidx.appcompat.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;

View File

@ -1,7 +1,8 @@
package net.i2p.android.help;
import android.os.Bundle;
import android.support.v4.app.Fragment;
//import android.support.v4.app.Fragment;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -30,7 +31,7 @@ public class HelpHtmlFragment extends Fragment {
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.setHtmlFromRawResource(getActivity(), getArguments().getInt(ARG_HTML_FILE), true);
text.setHtml(getArguments().getInt(ARG_HTML_FILE));
return scroller;
}
}

View File

@ -2,7 +2,8 @@ package net.i2p.android.help;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
//import android.support.v4.app.ListFragment;
import androidx.fragment.app.ListFragment;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;

View File

@ -2,8 +2,10 @@ package net.i2p.android.i2ptunnel;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v7.widget.Toolbar;
//import android.support.v4.app.ActivityCompat;
import androidx.core.app.ActivityCompat;
//import android.support.v7.widget.Toolbar;
import androidx.appcompat.widget.Toolbar;
import android.view.View;
import net.i2p.android.I2PActivityBase;

View File

@ -1,31 +1,43 @@
package net.i2p.android.i2ptunnel;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Dialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.Toolbar;
import androidx.annotation.NonNull;
//import android.support.v4.app.DialogFragment;
import androidx.fragment.app.DialogFragment;
//import android.support.v4.app.Fragment;
import androidx.fragment.app.Fragment;
//import android.support.v4.view.ViewCompat;
import androidx.core.view.ViewCompat;
//import android.support.v7.app.AlertDialog;
import androidx.appcompat.app.AlertDialog;
//import android.support.v7.widget.Toolbar;
import androidx.appcompat.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import net.i2p.I2PAppContext;
import net.i2p.android.i2ptunnel.util.TunnelUtil;
import net.i2p.android.router.R;
import net.i2p.android.router.util.Util;
import net.i2p.android.util.FragmentUtils;
import net.i2p.app.ClientAppState;
import net.i2p.i2ptunnel.TunnelController;
import net.i2p.i2ptunnel.TunnelControllerGroup;
import java.util.List;
@ -37,6 +49,7 @@ public class TunnelDetailFragment extends Fragment {
private TunnelControllerGroup mGroup;
private TunnelEntry mTunnel;
private Toolbar mToolbar;
private ImageView mStatus;
public static TunnelDetailFragment newInstance(int tunnelId) {
TunnelDetailFragment f = new TunnelDetailFragment();
@ -69,21 +82,33 @@ public class TunnelDetailFragment extends Fragment {
super.onCreate(savedInstanceState);
String error;
List<TunnelController> controllers;
try {
mGroup = TunnelControllerGroup.getInstance();
error = mGroup == null ? getResources().getString(R.string.i2ptunnel_not_initialized) : null;
controllers = mGroup == null ? null : mGroup.getControllers();
} catch (IllegalArgumentException iae) {
mGroup = null;
controllers = null;
error = iae.toString();
}
if (mGroup == null) {
// Show error
Toast.makeText(getActivity().getApplicationContext(),
error, Toast.LENGTH_LONG).show();
getActivity().finish();
} else if (getArguments().containsKey(TUNNEL_ID)) {
int tunnelId = getArguments().getInt(TUNNEL_ID);
mTunnel = new TunnelEntry(getActivity(),
mGroup.getControllers().get(tunnelId),
tunnelId);
try {
TunnelController controller = controllers.get(tunnelId);
mTunnel = new TunnelEntry(getActivity(), controller, tunnelId);
} catch (IndexOutOfBoundsException e) {
// Tunnel doesn't exist
Util.e("Could not load tunnel details", e);
Toast.makeText(getActivity().getApplicationContext(),
R.string.i2ptunnel_no_tunnel_details, Toast.LENGTH_LONG).show();
getActivity().finish();
}
}
}
@ -103,6 +128,10 @@ public class TunnelDetailFragment extends Fragment {
updateToolbar();
if (mTunnel != null) {
mStatus = (ImageView) v.findViewById(R.id.tunnel_status);
updateStatus();
ViewCompat.setTransitionName(mStatus, "status" + mTunnel.getId());
TextView name = (TextView) v.findViewById(R.id.tunnel_name);
name.setText(mTunnel.getName());
@ -112,8 +141,26 @@ public class TunnelDetailFragment extends Fragment {
TextView description = (TextView) v.findViewById(R.id.tunnel_description);
description.setText(mTunnel.getDescription());
TextView details = (TextView) v.findViewById(R.id.tunnel_details);
details.setText(mTunnel.getDetails());
if (!mTunnel.getDetails().isEmpty()) {
v.findViewById(R.id.tunnel_details_container).setVisibility(View.VISIBLE);
TextView details = (TextView) v.findViewById(R.id.tunnel_details);
View copyDetails = v.findViewById(R.id.tunnel_details_copy);
details.setText(mTunnel.getDetails());
if (!mTunnel.isClient()) {
copyDetails.setVisibility(View.VISIBLE);
copyDetails.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB)
copyToClipbardLegacy();
else
copyToClipboardHoneycomb();
Toast.makeText(getActivity(), R.string.address_copied_to_clipboard, Toast.LENGTH_SHORT).show();
}
});
}
}
View accessIfacePortItem = v.findViewById(R.id.tunnel_access_interface_port_item);
TextView accessIfacePort = (TextView) v.findViewById(R.id.tunnel_access_interface_port);
@ -178,11 +225,17 @@ public class TunnelDetailFragment extends Fragment {
Uri uri = mTunnel.getRecommendedAppForTunnel();
if (uri != null) {
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
Toast.makeText(getContext(),
R.string.no_market_app,
Toast.LENGTH_LONG).show();
}
}
}
})
.setNegativeButton(net.i2p.android.lib.client.R.string.no, new DialogInterface.OnClickListener() {
.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialogInterface, int i) {
}
});
@ -218,6 +271,14 @@ public class TunnelDetailFragment extends Fragment {
}
}
private void updateStatus() {
mStatus.setImageDrawable(mTunnel.getStatusIcon());
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
mStatus.setBackgroundDrawable(mTunnel.getStatusBackground());
else
mStatus.setBackground(mTunnel.getStatusBackground());
}
private boolean onToolbarItemSelected(MenuItem item) {
if (mTunnel == null)
return false;
@ -231,6 +292,8 @@ public class TunnelDetailFragment extends Fragment {
+ ' ' + mTunnel.getName(), Toast.LENGTH_LONG).show();
// Reload the toolbar to change the start/stop action
updateToolbar();
// Update the status icon
updateStatus();
return true;
case R.id.action_stop_tunnel:
mTunnel.getController().stopTunnel();
@ -239,39 +302,84 @@ public class TunnelDetailFragment extends Fragment {
+ ' ' + mTunnel.getName(), Toast.LENGTH_LONG).show();
// Reload the toolbar to change the start/stop action
updateToolbar();
// Update the status icon
updateStatus();
return true;
case R.id.action_edit_tunnel:
mCallback.onEditTunnel(mTunnel.getId());
return true;
case R.id.action_delete_tunnel:
DialogFragment dg = new DialogFragment() {
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity())
.setMessage(R.string.i2ptunnel_delete_confirm_message)
.setPositiveButton(R.string.i2ptunnel_delete_confirm_button,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
List<String> msgs = TunnelUtil.deleteTunnel(
I2PAppContext.getGlobalContext(),
mGroup, mTunnel.getId(), null);
dialog.dismiss();
Toast.makeText(getActivity().getApplicationContext(),
msgs.get(0), Toast.LENGTH_LONG).show();
mCallback.onTunnelDeleted(mTunnel.getId(),
mGroup.getControllers().size());
}
})
.setNegativeButton(android.R.string.cancel, null)
.create();
}
};
dg.show(getFragmentManager(), "delete_tunnel_dialog");
DialogFragment dg = DeleteTunnelDialogFragment.newInstance();
dg.show(getChildFragmentManager(), "delete_tunnel_dialog");
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void onDeleteTunnel() {
List<String> msgs = TunnelUtil.deleteTunnel(
I2PAppContext.getGlobalContext(),
mGroup, mTunnel.getId(), null);
Toast.makeText(getActivity().getApplicationContext(),
msgs.get(0), Toast.LENGTH_LONG).show();
mCallback.onTunnelDeleted(mTunnel.getId(),
mGroup.getControllers().size());
}
private void copyToClipbardLegacy() {
android.text.ClipboardManager clipboard = (android.text.ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
clipboard.setText(mTunnel.getDetails());
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void copyToClipboardHoneycomb() {
android.content.ClipboardManager clipboard = (android.content.ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
android.content.ClipData clip = android.content.ClipData.newPlainText(
mTunnel.getName(), mTunnel.getDetails());
clipboard.setPrimaryClip(clip);
}
public static class DeleteTunnelDialogFragment extends DialogFragment {
TunnelDetailFragment mListener;
public static DialogFragment newInstance() {
return new DeleteTunnelDialogFragment();
}
private void onAttachToParentFragment(Fragment fragment) {
// Verify that the host fragment implements the callback interface
try {
// Instantiate the TunnelDetailFragment so we can send events to the host
mListener = (TunnelDetailFragment) fragment;
} catch (ClassCastException e) {
// The fragment doesn't implement the interface, throw exception
throw new ClassCastException(fragment.toString()
+ " must be TunnelDetailFragment");
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
onAttachToParentFragment(getParentFragment());
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity())
.setMessage(R.string.i2ptunnel_delete_confirm_message)
.setPositiveButton(R.string.i2ptunnel_delete_confirm_button,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
mListener.onDeleteTunnel();
}
})
.setNegativeButton(android.R.string.cancel, null)
.create();
}
}
}

View File

@ -6,16 +6,24 @@ import android.net.Uri;
import android.widget.Toast;
import net.i2p.I2PAppContext;
import net.i2p.android.i2ptunnel.util.SaveTunnelTask;
import net.i2p.android.i2ptunnel.util.TunnelUtil;
import net.i2p.android.router.R;
import net.i2p.android.router.util.Util;
import net.i2p.data.Destination;
import net.i2p.data.PrivateKeyFile;
import net.i2p.i2ptunnel.TunnelController;
import net.i2p.i2ptunnel.TunnelControllerGroup;
import net.i2p.i2ptunnel.ui.TunnelConfig;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
/**
* A single tunnel.
* Stored by the TunnelEntryAdapter.
*/
public class TunnelEntry {
public static final int RUNNING = 1;
public static final int STARTING = 2;
@ -26,18 +34,33 @@ public class TunnelEntry {
private final TunnelController mController;
private final int mId;
/**
* @param tcg non-null
* @return the new TunnelEntry, or null if there was an error.
*/
public static TunnelEntry createNewTunnel(
Context ctx,
TunnelControllerGroup tcg,
TunnelConfig cfg) {
int tunnelId = tcg.getControllers().size();
List<String> msgs = TunnelUtil.saveTunnel(
I2PAppContext.getGlobalContext(), tcg, -1, cfg);
TunnelEntry ret = null;
List<String> msgs = new ArrayList<>();
SaveTunnelTask task = new SaveTunnelTask(tcg, -1, cfg);
try {
msgs.addAll(task.execute().get());
TunnelController cur = TunnelUtil.getController(tcg, tunnelId);
ret = new TunnelEntry(ctx, cur, tunnelId);
} catch (InterruptedException e) {
Util.e("Interrupted while saving tunnel config", e);
msgs.add(ctx.getString(R.string.i2ptunnel_msg_config_save_failed));
} catch (ExecutionException e) {
Util.e("Error while saving tunnel config", e);
msgs.add(ctx.getString(R.string.i2ptunnel_msg_config_save_failed));
}
// TODO: Do something else with the other messages.
Toast.makeText(ctx.getApplicationContext(),
msgs.get(0), Toast.LENGTH_LONG).show();
TunnelController cur = TunnelUtil.getController(tcg, tunnelId);
return new TunnelEntry(ctx, cur, tunnelId);
return ret;
}
public TunnelEntry(Context context, TunnelController controller, int id) {
@ -252,7 +275,7 @@ public class TunnelEntry {
if (isClient())
details = getClientDestination();
else
details = "";
details = getDestHashBase32();
return details;
}
@ -262,6 +285,8 @@ public class TunnelEntry {
return mContext.getResources()
.getDrawable(R.drawable.ic_schedule_black_24dp);
case STARTING:
return mContext.getResources()
.getDrawable(R.drawable.ic_sync_black_24dp);
case RUNNING:
case NOT_RUNNING:
default:

View File

@ -2,24 +2,37 @@ package net.i2p.android.i2ptunnel;
import android.content.Context;
import android.os.Build;
import android.support.v4.util.Pair;
import android.support.v7.widget.RecyclerView;
//import android.support.v4.util.Pair;
import androidx.core.util.Pair;
//import android.support.v4.view.ViewCompat;
import androidx.core.view.ViewCompat;
//import android.support.v7.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import net.i2p.android.router.R;
import net.i2p.android.util.FragmentUtils;
import java.util.ArrayList;
import java.util.List;
/**
* Contains the List of TunnelEntries.
* There's two of these, one for client tunnels and
* one for server tunnels.
* Created by the TunnelListFragment.
*/
public class TunnelEntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context mCtx;
private boolean mClientTunnels;
private TunnelListFragment.OnTunnelSelectedListener mListener;
private FragmentUtils.TwoPaneProvider mTwoPane;
private final Context mCtx;
private final boolean mClientTunnels;
private final TunnelListFragment.OnTunnelSelectedListener mListener;
private final FragmentUtils.TwoPaneProvider mTwoPane;
private List<TunnelEntry> mTunnels;
/**
* The current activated item position. Only used on tablets.
@ -33,10 +46,10 @@ public class TunnelEntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
}
public static class TunnelViewHolder extends RecyclerView.ViewHolder {
public ImageView status;
public TextView name;
public TextView description;
public TextView interfacePort;
public final ImageView status;
public final TextView name;
public final TextView description;
public final TextView interfacePort;
public TunnelViewHolder(View itemView) {
super(itemView);
@ -64,8 +77,15 @@ public class TunnelEntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
}
public void addTunnel(TunnelEntry tunnel) {
if (mTunnels == null)
mTunnels = new ArrayList<TunnelEntry>();
boolean wasEmpty = mTunnels.isEmpty();
mTunnels.add(tunnel);
notifyItemInserted(mTunnels.size()-1);
if (wasEmpty) {
notifyDataSetChanged();
} else {
notifyItemInserted(mTunnels.size() - 1);
}
}
public TunnelEntry getTunnel(int position) {
@ -114,13 +134,24 @@ public class TunnelEntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
}
}
private void setClipboard(Context context, String text) {
if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB) {
android.text.ClipboardManager clipboard = (android.text.ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
clipboard.setText(text);
} else {
android.content.ClipboardManager clipboard = (android.content.ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
android.content.ClipData clip = android.content.ClipData.newPlainText("Copied Text", text);
clipboard.setPrimaryClip(clip);
}
}
// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
switch (holder.getItemViewType()) {
case R.string.router_not_running:
((TextView) holder.itemView).setText(
mCtx.getString(R.string.router_not_running));
mCtx.getString(R.string.i2ptunnel_not_initialized));
break;
case R.layout.listitem_empty:
@ -138,6 +169,8 @@ public class TunnelEntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
tvh.status.setBackgroundDrawable(tunnel.getStatusBackground());
else
tvh.status.setBackground(tunnel.getStatusBackground());
ViewCompat.setTransitionName(tvh.status,
"status" + tunnel.getId());
tvh.name.setText(tunnel.getName());
tvh.description.setText(tunnel.getDescription());
@ -147,13 +180,38 @@ public class TunnelEntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
tvh.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// TODO
// lint priority 8/10
// lint: Do not treat position as fixed; only use immediately and call holder.getAdapterPosition() to look it up later
// javadocs: Note that unlike ListView, RecyclerView will not call this method again
// if the position of the item changes in the data set unless the item itself is invalidated
// or the new position cannot be determined.
// For this reason, you should only use the position parameter while acquiring
// the related data item inside this method and should not keep a copy of it.
// If you need the position of an item later on (e.g. in a click listener),
// use RecyclerView.ViewHolder.getAdapterPosition() which will have the updated adapter position.
int oldPosition = mActivatedPosition;
mActivatedPosition = position;
notifyItemChanged(oldPosition);
notifyItemChanged(position);
mListener.onTunnelSelected(tunnel.getId(),
Pair.create((View)tvh.name, mCtx.getString(R.string.TUNNEL_NAME)),
Pair.create((View)tvh.description, mCtx.getString(R.string.TUNNEL_DESCRIPTION)));
Pair<View, String> statusPair = Pair.create(
(View)tvh.status,
ViewCompat.getTransitionName(tvh.status));
Pair<View, String>[] pairs = new Pair[]{ statusPair};
mListener.onTunnelSelected(tunnel.getId(), pairs);
view.invalidate();
}
});
tvh.itemView.setOnLongClickListener(new View.OnLongClickListener() {
//@Override
public boolean onLongClick(View view) {
setClipboard(mCtx, tunnel.getDestHashBase32());
Toast clipboardMessage = Toast.makeText(mCtx, R.string.copied_base32_system_notification_title, Toast. LENGTH_LONG);
clipboardMessage.setGravity(Gravity.TOP, 0, 0); //optional
clipboardMessage.show();
view.invalidate();
return true;
}
});
break;
@ -161,6 +219,7 @@ public class TunnelEntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
default:
break;
}
}
// Return the size of the dataset (invoked by the layout manager)

View File

@ -2,7 +2,8 @@ package net.i2p.android.i2ptunnel;
import android.content.Context;
import android.os.Handler;
import android.support.v4.content.AsyncTaskLoader;
//import android.support.v4.content.AsyncTaskLoader;
import androidx.loader.content.AsyncTaskLoader;
import net.i2p.android.router.util.Util;
import net.i2p.i2ptunnel.TunnelController;
@ -13,10 +14,10 @@ import java.util.ArrayList;
import java.util.List;
public class TunnelEntryLoader extends AsyncTaskLoader<List<TunnelEntry>> {
private TunnelControllerGroup mGroup;
private boolean mClientTunnels;
private final TunnelControllerGroup mGroup;
private final boolean mClientTunnels;
private List<TunnelEntry> mData;
private Handler mHandler;
private final Handler mHandler;
private TunnelControllerMonitor mMonitor;
public TunnelEntryLoader(Context context, TunnelControllerGroup tcg, boolean clientTunnels) {

View File

@ -6,13 +6,20 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.util.Pair;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
//import android.support.v4.app.Fragment;
import androidx.fragment.app.Fragment;
//import android.support.v4.app.LoaderManager;
import androidx.loader.app.LoaderManager;
//import android.support.v4.content.Loader;
import androidx.loader.content.Loader;
//import android.support.v4.content.LocalBroadcastManager;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
//import android.support.v4.util.Pair;
import androidx.core.util.Pair;
//import android.support.v7.widget.LinearLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
//import android.support.v7.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -31,6 +38,12 @@ import net.i2p.i2ptunnel.TunnelControllerGroup;
import java.util.ArrayList;
import java.util.List;
/**
* The list of tunnels.
* There's two of these, one for client tunnels and
* one for server tunnels.
* Creates the TunnelEntryAdapter.
*/
public class TunnelListFragment extends Fragment implements
LoaderManager.LoaderCallbacks<List<TunnelEntry>> {
public static final String SHOW_CLIENT_TUNNELS = "show_client_tunnels";
@ -51,10 +64,11 @@ public class TunnelListFragment extends Fragment implements
private TunnelEntryAdapter mAdapter;
private boolean mClientTunnels;
private static final String KEY_SELECTED_TUNNEL = "selected_tunnel";
// Container Activity must implement this interface
public interface OnTunnelSelectedListener {
void onTunnelSelected(int tunnelId, Pair<View, String> tunnelName,
Pair<View, String> tunnelDescription);
void onTunnelSelected(int tunnelId, Pair<View, String>[] pairs);
}
public static TunnelListFragment newInstance(boolean showClientTunnels) {
@ -95,6 +109,12 @@ public class TunnelListFragment extends Fragment implements
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (savedInstanceState != null) {
int tunnelId = savedInstanceState.getInt(KEY_SELECTED_TUNNEL, -1);
if (tunnelId != -1) {
mCallback.onTunnelSelected(tunnelId, null);
}
}
}
@Override
@ -151,14 +171,21 @@ public class TunnelListFragment extends Fragment implements
};
public void updateState(State state) {
if (state == State.STOPPING || state == State.STOPPED ||
state == State.MANUAL_STOPPING ||
state == State.MANUAL_STOPPED ||
state == State.MANUAL_QUITTING ||
state == State.MANUAL_QUITTED)
getLoaderManager().destroyLoader(mClientTunnels ? CLIENT_LOADER_ID : SERVER_LOADER_ID);
else
initTunnels();
try {
if (state == State.INIT ||
state == State.STARTING || // Wait until RouterContext is initialised
state == State.STOPPING ||
state == State.STOPPED ||
state == State.MANUAL_STOPPING ||
state == State.MANUAL_STOPPED ||
state == State.MANUAL_QUITTING ||
state == State.MANUAL_QUITTED)
getLoaderManager().destroyLoader(mClientTunnels ? CLIENT_LOADER_ID : SERVER_LOADER_ID);
else
initTunnels();
} catch (IllegalStateException ise) {
// Fragment isn't attached to any activity, so ignore state change
}
}
private void initTunnels() {

View File

@ -2,12 +2,15 @@ package net.i2p.android.i2ptunnel;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import androidx.annotation.NonNull;
//import android.support.v4.app.DialogFragment;
import androidx.fragment.app.DialogFragment;
//import android.support.v7.app.AlertDialog;
import androidx.appcompat.app.AlertDialog;
import net.i2p.android.router.R;
import net.i2p.android.wizard.model.AbstractWizardModel;
@ -21,26 +24,51 @@ public class TunnelWizardActivity extends AbstractWizardActivity {
@Override
protected DialogFragment onGetFinishWizardDialog() {
return new DialogFragment() {
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity())
.setMessage(R.string.i2ptunnel_wizard_submit_confirm_message)
.setPositiveButton(R.string.i2ptunnel_wizard_submit_confirm_button,
new DialogInterface.OnClickListener() {
return FinishWizardDialogFragment.newInstance();
}
public void onClick(DialogInterface dialog, int which) {
Intent result = new Intent();
result.putExtra(TunnelsContainer.TUNNEL_WIZARD_DATA, mWizardModel.save());
setResult(Activity.RESULT_OK, result);
dialog.dismiss();
finish();
}
})
.setNegativeButton(android.R.string.cancel, null)
.create();
public void onFinishWizard() {
Intent result = new Intent();
result.putExtra(TunnelsContainer.TUNNEL_WIZARD_DATA, mWizardModel.save());
setResult(Activity.RESULT_OK, result);
finish();
}
public static class FinishWizardDialogFragment extends DialogFragment {
TunnelWizardActivity mListener;
public static DialogFragment newInstance() {
return new FinishWizardDialogFragment();
}
public void onAttach(Context context) {
super.onAttach(context);
// Verify that the host fragment implements the callback interface
try {
// Instantiate the TunnelWizardActivity so we can send events to the host
mListener = (TunnelWizardActivity) context;
} catch (ClassCastException e) {
// The fragment doesn't implement the interface, throw exception
throw new ClassCastException(context.toString()
+ " must be TunnelWizardActivity");
}
};
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity())
.setMessage(R.string.i2ptunnel_wizard_submit_confirm_message)
.setPositiveButton(R.string.i2ptunnel_wizard_submit_confirm_button,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
mListener.onFinishWizard();
}
})
.setNegativeButton(android.R.string.cancel, null)
.create();
}
}
}

View File

@ -1,15 +1,25 @@
package net.i2p.android.i2ptunnel;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.ActivityOptionsCompat;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.util.Pair;
import android.support.v4.view.ViewPager;
//import android.support.v4.app.ActivityCompat;
import androidx.core.app.ActivityCompat;
//import android.support.v4.app.ActivityOptionsCompat;
import androidx.core.app.ActivityOptionsCompat;
//import android.support.v4.app.Fragment;
import androidx.fragment.app.Fragment;
//import android.support.v4.app.FragmentManager;
import androidx.fragment.app.FragmentManager;
//import android.support.v4.app.FragmentPagerAdapter;
import androidx.fragment.app.FragmentPagerAdapter;
//import android.support.v4.util.Pair;
import androidx.core.util.Pair;
//import android.support.v4.view.ViewPager;
import androidx.viewpager.widget.ViewPager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@ -19,7 +29,17 @@ import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.Toast;
import com.viewpagerindicator.TitlePageIndicator;
import com.google.android.material.tabs.TabLayout;
import net.lucode.hackware.magicindicator.MagicIndicator;
import net.lucode.hackware.magicindicator.ViewPagerHelper;
import net.lucode.hackware.magicindicator.buildins.commonnavigator.CommonNavigator;
import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.CommonNavigatorAdapter;
import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerIndicator;
import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerTitleView;
import net.lucode.hackware.magicindicator.buildins.commonnavigator.indicators.LinePagerIndicator;
import net.lucode.hackware.magicindicator.buildins.commonnavigator.titles.ColorTransitionPagerTitleView;
import net.lucode.hackware.magicindicator.buildins.commonnavigator.titles.SimplePagerTitleView;
import androidx.core.content.ContextCompat;
import net.i2p.android.i2ptunnel.preferences.EditTunnelContainerFragment;
import net.i2p.android.i2ptunnel.util.TunnelUtil;
@ -33,6 +53,11 @@ import net.i2p.router.RouterContext;
import java.util.List;
/**
* The top level Fragment of the tunnels tabs.
* Creates client and server TunnelListFragments,
* the options menu, and the new tunnel wizard button.
*/
public class TunnelsContainer extends Fragment implements
FragmentUtils.TwoPaneProvider,
TunnelListFragment.OnTunnelSelectedListener,
@ -47,7 +72,7 @@ public class TunnelsContainer extends Fragment implements
private boolean mTwoPane;
ViewPager mViewPager;
TitlePageIndicator mPageIndicator;
MagicIndicator mPageIndicator;
FragmentPagerAdapter mFragPagerAdapter;
private static final String FRAGMENT_CLIENT = "client_fragment";
@ -65,18 +90,28 @@ public class TunnelsContainer extends Fragment implements
setHasOptionsMenu(true);
}
private static boolean showActions() {
RouterContext rCtx = Util.getRouterContext();
TunnelControllerGroup tcg = TunnelControllerGroup.getInstance();
return rCtx != null && tcg != null &&
tcg.getState() == ClientAppState.RUNNING;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.container_tunnels, container, false);
mViewPager = (ViewPager) v.findViewById(R.id.pager);
mPageIndicator = (TitlePageIndicator) v.findViewById(R.id.page_indicator);
mNewTunnel = (ImageButton) v.findViewById(R.id.promoted_action);
mViewPager = v.findViewById(R.id.pager);
mPageIndicator = v.findViewById(R.id.magic_indicator);
mNewTunnel = v.findViewById(R.id.promoted_action);
mNewTunnel.setVisibility(showActions() ? View.VISIBLE : View.GONE);
// Initialize ViewPager adapter
mFragPagerAdapter = new TunnelsPagerAdapter(getChildFragmentManager());
mViewPager.setAdapter(mFragPagerAdapter);
if (v.findViewById(R.id.detail_fragment) != null) {
// The detail container view will be present only in the
// large-screen layouts (res/values-w720dp). If this view
// is present, then the activity should be in two-pane mode.
mTwoPane = true;
}
@ -87,19 +122,22 @@ public class TunnelsContainer extends Fragment implements
savedInstanceState, FRAGMENT_SERVER);
}
setupMagicIndicator();
return v;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Initialize ViewPager and adapter
mFragPagerAdapter = new TunnelsPagerAdapter(getChildFragmentManager());
mViewPager.setAdapter(mFragPagerAdapter);
// Bind the page indicator to the pager.
mPageIndicator.setViewPager(mViewPager);
setupMagicIndicator();
// Setup New Tunnel button
mNewTunnel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
@ -154,17 +192,16 @@ public class TunnelsContainer extends Fragment implements
@Override
public void onPrepareOptionsMenu(Menu menu) {
RouterContext rCtx = Util.getRouterContext();
TunnelControllerGroup tcg = TunnelControllerGroup.getInstance();
boolean showActions = rCtx != null && tcg != null &&
(tcg.getState() == ClientAppState.STARTING ||
tcg.getState() == ClientAppState.RUNNING);
boolean showActions = showActions();
menu.findItem(R.id.action_start_all_tunnels).setVisible(showActions);
menu.findItem(R.id.action_stop_all_tunnels).setVisible(showActions);
menu.findItem(R.id.action_restart_all_tunnels).setVisible(showActions);
mNewTunnel.setVisibility(showActions ? View.VISIBLE : View.GONE);
// Was causing a NPE in version 4745238 (0.9.31)
if (mNewTunnel != null) {
mNewTunnel.setVisibility(showActions ? View.VISIBLE : View.GONE);
}
}
@Override
@ -183,7 +220,10 @@ public class TunnelsContainer extends Fragment implements
msgs = tcg.stopAllControllers();
break;
case R.id.action_restart_all_tunnels:
msgs = tcg.restartAllControllers();
// Do a manual stop-start cycle, because tcg.restartAllControllers() happens in the
// foreground, whereas tcg.startAllControllers() fires off threads for starting.
msgs = tcg.stopAllControllers();
msgs.addAll(tcg.startAllControllers());
break;
default:
return super.onOptionsItemSelected(item);
@ -200,15 +240,26 @@ public class TunnelsContainer extends Fragment implements
if (requestCode == TUNNEL_WIZARD_REQUEST) {
if (resultCode == Activity.RESULT_OK) {
Bundle tunnelData = data.getExtras().getBundle(TUNNEL_WIZARD_DATA);
// ticket #2483
if (tunnelData == null)
return;
// TODO fetch earlier
TunnelControllerGroup tcg = TunnelControllerGroup.getInstance();
if (tcg == null) {
// router went away
Toast.makeText(getActivity().getApplicationContext(),
R.string.router_not_running, Toast.LENGTH_LONG).show();
return;
}
TunnelConfig cfg = TunnelUtil.createConfigFromWizard(getActivity(), tcg, tunnelData);
TunnelEntry tunnel = TunnelEntry.createNewTunnel(getActivity(), tcg, cfg);
if (tunnel.isClient() && mClientFrag != null)
mClientFrag.addTunnel(tunnel);
else if (mServerFrag != null)
mServerFrag.addTunnel(tunnel);
if (tunnel != null) {
if (tunnel.isClient() && mClientFrag != null)
mClientFrag.addTunnel(tunnel);
else if (mServerFrag != null)
mServerFrag.addTunnel(tunnel);
}
}
}
}
@ -239,15 +290,19 @@ public class TunnelsContainer extends Fragment implements
// TunnelListFragment.OnTunnelSelectedListener
public final void onTunnelSelected(int tunnelId, Pair<View, String> tunnelName,
Pair<View, String> tunnelDescription) {
public final void onTunnelSelected(int tunnelId, Pair<View, String>[] pairs) {
if (mTwoPane) {
// In two-pane mode, show the detail view in this activity by
// adding or replacing the detail fragment using a
// fragment transaction.
TunnelDetailFragment detailFrag = TunnelDetailFragment.newInstance(tunnelId);
getChildFragmentManager().beginTransaction()
.replace(R.id.detail_fragment, detailFrag).commit();
try {
TunnelDetailFragment detailFrag = TunnelDetailFragment.newInstance(tunnelId);
getChildFragmentManager().beginTransaction()
.replace(R.id.detail_fragment, detailFrag)
.commitNow(); // Use commitNow() to execute synchronously
} catch (Exception e) {
Log.e("TunnelsContainer", "Failed to update detail fragment", e);
}
} else {
// In single-pane mode, simply start the detail activity
// for the selected item ID.
@ -255,7 +310,7 @@ public class TunnelsContainer extends Fragment implements
detailIntent.putExtra(TunnelDetailFragment.TUNNEL_ID, tunnelId);
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(
getActivity(), tunnelName, tunnelDescription);
getActivity(), pairs);
ActivityCompat.startActivity(getActivity(), detailIntent, options.toBundle());
}
}
@ -286,4 +341,54 @@ public class TunnelsContainer extends Fragment implements
}
}
}
private void setupMagicIndicator() {
if (mPageIndicator == null || getContext() == null) {
return;
}
CommonNavigator commonNavigator = new CommonNavigator(getContext());
commonNavigator.setAdjustMode(true); // Add this line for better spacing
commonNavigator.setAdapter(new CommonNavigatorAdapter() {
@Override
public int getCount() {
return mFragPagerAdapter.getCount();
}
@Override
public IPagerTitleView getTitleView(Context context, final int index) {
SimplePagerTitleView simplePagerTitleView = new ColorTransitionPagerTitleView(context);
simplePagerTitleView.setText(mFragPagerAdapter.getPageTitle(index));
simplePagerTitleView.setTextSize(16); // Add this line to increase text size
simplePagerTitleView.setNormalColor(ContextCompat.getColor(context,
R.color.primary_text_disabled_material_dark));
simplePagerTitleView.setSelectedColor(ContextCompat.getColor(context,
R.color.primary_text_default_material_dark));
simplePagerTitleView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mViewPager.setCurrentItem(index);
}
});
return simplePagerTitleView;
}
@Override
public IPagerIndicator getIndicator(Context context) {
LinePagerIndicator indicator = new LinePagerIndicator(context);
indicator.setMode(LinePagerIndicator.MODE_WRAP_CONTENT);
indicator.setColors(ContextCompat.getColor(context, R.color.primary));
indicator.setLineHeight(dpToPx(context, 3));
return indicator;
}
});
mPageIndicator.setNavigator(commonNavigator);
ViewPagerHelper.bind(mPageIndicator, mViewPager);
}
private int dpToPx(Context context, int dp) {
float density = context.getResources().getDisplayMetrics().density;
return Math.round(dp * density);
}
}

View File

@ -3,11 +3,16 @@ package net.i2p.android.i2ptunnel.preferences;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.preference.PreferenceScreen;
import android.support.v7.app.AlertDialog;
//import android.support.v7.app.AlertDialog;
import androidx.appcompat.app.AlertDialog;
//import android.support.v7.preference.CheckBoxPreference;
import androidx.preference.CheckBoxPreference;
//import android.support.v7.preference.Preference;
import androidx.preference.Preference;
//import android.support.v7.preference.PreferenceCategory;
import androidx.preference.PreferenceCategory;
//import android.support.v7.preference.PreferenceScreen;
import androidx.preference.PreferenceScreen;
import net.i2p.android.i2ptunnel.util.TunnelLogic;
import net.i2p.android.i2ptunnel.util.TunnelUtil;

View File

@ -2,27 +2,35 @@ package net.i2p.android.i2ptunnel.preferences;
import android.os.Build;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import android.support.v4.preference.PreferenceFragment;
//import android.support.v7.preference.Preference;
import androidx.preference.Preference;
//import android.support.v7.preference.PreferenceGroup;
import androidx.preference.PreferenceGroup;
//import android.support.v7.preference.PreferenceScreen;
import androidx.preference.PreferenceScreen;
import android.widget.Toast;
import net.i2p.I2PAppContext;
import net.i2p.android.i2ptunnel.util.SaveTunnelTask;
import net.i2p.android.i2ptunnel.util.TunnelUtil;
import net.i2p.android.preferences.util.CustomPreferenceFragment;
import net.i2p.android.router.R;
import net.i2p.android.router.util.Util;
import net.i2p.i2ptunnel.TunnelControllerGroup;
import net.i2p.i2ptunnel.ui.TunnelConfig;
public abstract class BaseTunnelPreferenceFragment extends PreferenceFragment {
import java.util.concurrent.ExecutionException;
import java.util.concurrent.CancellationException;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.TimeUnit;
public abstract class BaseTunnelPreferenceFragment extends CustomPreferenceFragment {
protected static final String ARG_TUNNEL_ID = "tunnelId";
protected TunnelControllerGroup mGroup;
protected int mTunnelId;
@Override
public void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
public void onCreatePreferences(Bundle paramBundle, String s) {
String error;
try {
mGroup = TunnelControllerGroup.getInstance();
@ -33,13 +41,30 @@ public abstract class BaseTunnelPreferenceFragment extends PreferenceFragment {
}
if (mGroup == null) {
// TODO Show error
Toast.makeText(getActivity().getApplicationContext(),
error, Toast.LENGTH_LONG).show();
getActivity().finish();
} else if (getArguments().containsKey(ARG_TUNNEL_ID)) {
mTunnelId = getArguments().getInt(ARG_TUNNEL_ID, 0);
TunnelUtil.writeTunnelToPreferences(getActivity(), mGroup, mTunnelId);
try {
TunnelUtil.writeTunnelToPreferences(getActivity(), mGroup, mTunnelId);
} catch (IllegalArgumentException e) {
// Tunnel doesn't exist, or the tunnel config file could not be read
Util.e("Could not load tunnel details", e);
Toast.makeText(getActivity().getApplicationContext(),
R.string.i2ptunnel_no_tunnel_details, Toast.LENGTH_LONG).show();
getActivity().finish();
}
// https://stackoverflow.com/questions/17880437/which-settings-file-does-preferencefragment-read-write
getPreferenceManager().setSharedPreferencesName(TunnelUtil.getPreferencesFilename(mTunnelId));
loadPreferences();
try {
loadPreferences();
} catch (IllegalArgumentException iae) {
// mGroup couldn't load its config file
Toast.makeText(getActivity().getApplicationContext(),
iae.toString(), Toast.LENGTH_LONG).show();
getActivity().finish();
}
}
}
@ -64,7 +89,21 @@ public abstract class BaseTunnelPreferenceFragment extends PreferenceFragment {
private void saveTunnel() {
if (mGroup != null) {
TunnelConfig cfg = TunnelUtil.createConfigFromPreferences(getActivity(), mGroup, mTunnelId);
TunnelUtil.saveTunnel(I2PAppContext.getGlobalContext(), mGroup, mTunnelId, cfg);
SaveTunnelTask task = new SaveTunnelTask(mGroup, mTunnelId, cfg);
try {
// TODO: There used to be a possible ANR here, because the underlying I2P code
// checks if the session is open as part of updating its config. We may need to save
// completely asynchronously (and ensure we do actually save before the app closes).
task.execute().get(2, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Util.e("Interrupted while saving tunnel config", e);
} catch (ExecutionException e) {
Util.e("Error while saving tunnel config", e);
} catch (CancellationException e) {
Util.e("Cancelled while saving tunnel config", e);
} catch (TimeoutException e) {
Util.e("Timed out while savomg tunnel config", e);
}
}
}

View File

@ -2,10 +2,14 @@ package net.i2p.android.i2ptunnel.preferences;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
//import android.support.v4.app.Fragment;
import androidx.fragment.app.Fragment;
//import android.support.v4.app.FragmentManager;
import androidx.fragment.app.FragmentManager;
//import android.support.v7.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatActivity;
//import android.support.v7.widget.Toolbar;
import androidx.appcompat.widget.Toolbar;
import net.i2p.android.i2ptunnel.TunnelDetailActivity;
import net.i2p.android.i2ptunnel.TunnelDetailFragment;

View File

@ -1,9 +1,12 @@
package net.i2p.android.i2ptunnel.preferences;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.widget.Toolbar;
//import android.support.v4.app.Fragment;
import androidx.fragment.app.Fragment;
//import android.support.v4.app.FragmentManager;
import androidx.fragment.app.FragmentManager;
//import android.support.v7.widget.Toolbar;
import androidx.appcompat.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

View File

@ -1,16 +1,24 @@
package net.i2p.android.i2ptunnel.preferences;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.preference.PreferenceScreen;
import android.support.v4.app.Fragment;
import android.support.v7.app.AlertDialog;
//import android.support.v4.app.Fragment;
import androidx.fragment.app.Fragment;
//import android.support.v7.app.AlertDialog;
import androidx.appcompat.app.AlertDialog;
//import android.support.v7.preference.CheckBoxPreference;
import androidx.preference.CheckBoxPreference;
//import android.support.v7.preference.ListPreference;
import androidx.preference.ListPreference;
//import android.support.v7.preference.Preference;
import androidx.preference.Preference;
//import android.support.v7.preference.PreferenceCategory;
import androidx.preference.PreferenceCategory;
//import android.support.v7.preference.PreferenceScreen;
import androidx.preference.PreferenceScreen;
import net.i2p.android.i2ptunnel.util.TunnelLogic;
import net.i2p.android.i2ptunnel.util.TunnelUtil;
@ -110,7 +118,8 @@ public class GeneralTunnelPreferenceFragment extends BaseTunnelPreferenceFragmen
generalCategory.removePreference(generalCategory.findPreference(getString(R.string.TUNNEL_SHARED_CLIENT)));
addPreferencesFromResource(R.xml.tunnel_gen_server_port, portCategory);
portCategory.removePreference(portCategory.findPreference(getString(R.string.TUNNEL_TARGET_PORT)));
portCategory.removePreference(portCategory.findPreference(getString(R.string.TUNNEL_USE_SSL)));
// # TODO: See trac issue #2296
//portCategory.removePreference(portCategory.findPreference(getString(R.string.TUNNEL_USE_SSL)));
}
}
@ -134,10 +143,18 @@ public class GeneralTunnelPreferenceFragment extends BaseTunnelPreferenceFragmen
@Override
protected Void doInBackground(Void... voids) {
Set<String> interfaceSet = Addresses.getAllAddresses();
String[] interfaces = interfaceSet.toArray(new String[interfaceSet.size()]);
reachableBy.setEntries(interfaces);
reachableBy.setEntryValues(interfaces);
reachableBy.setEnabled(true);
final String[] interfaces = interfaceSet.toArray(new String[interfaceSet.size()]);
Activity activity = getActivity();
if (activity != null) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
reachableBy.setEntries(interfaces);
reachableBy.setEntryValues(interfaces);
reachableBy.setEnabled(true);
}
});
}
return null;
}
}.execute();
@ -159,8 +176,11 @@ public class GeneralTunnelPreferenceFragment extends BaseTunnelPreferenceFragmen
@Override
protected void generalClientStandardOrIrc(boolean isStandardOrIrc) {
/*
# TODO: See trac issue #2296
if (!isStandardOrIrc)
portCategory.removePreference(portCategory.findPreference(getString(R.string.TUNNEL_USE_SSL)));
*/
}
@Override
@ -176,7 +196,8 @@ public class GeneralTunnelPreferenceFragment extends BaseTunnelPreferenceFragmen
@Override
protected void generalServerHttpBidirOrStreamr(boolean isStreamr) {
addPreferencesFromResource(R.xml.tunnel_gen_client_port, portCategory);
portCategory.removePreference(portCategory.findPreference(getString(R.string.TUNNEL_USE_SSL)));
// # TODO: See trac issue #2296
//portCategory.removePreference(portCategory.findPreference(getString(R.string.TUNNEL_USE_SSL)));
if (isStreamr)
portCategory.removePreference(portCategory.findPreference(getString(R.string.TUNNEL_LISTEN_PORT)));
@ -192,7 +213,8 @@ public class GeneralTunnelPreferenceFragment extends BaseTunnelPreferenceFragmen
protected void generalServerPortStreamr(boolean isStreamr) {
if (isStreamr) {
portCategory.removePreference(portCategory.findPreference(getString(R.string.TUNNEL_TARGET_HOST)));
portCategory.removePreference(portCategory.findPreference(getString(R.string.TUNNEL_USE_SSL)));
// # TODO: See trac issue #2296
//portCategory.removePreference(portCategory.findPreference(getString(R.string.TUNNEL_USE_SSL)));
}
}

View File

@ -0,0 +1,32 @@
package net.i2p.android.i2ptunnel.util;
import android.os.AsyncTask;
import net.i2p.I2PAppContext;
import net.i2p.i2ptunnel.TunnelControllerGroup;
import net.i2p.i2ptunnel.ui.TunnelConfig;
import java.util.List;
/**
* Save a TunnelConfig.
*
* This must be performed in a background thread, because the underlying I2P code calls
* InetAddress.getByName(), which will trigger a NetworkOnMainThreadException otherwise.
*/
public class SaveTunnelTask extends AsyncTask<Void, Void, List<String>> {
final TunnelControllerGroup mGroup;
final int mTunnelId;
final TunnelConfig mCfg;
public SaveTunnelTask(TunnelControllerGroup group, int tunnelId, TunnelConfig cfg) {
mGroup = group;
mTunnelId = tunnelId;
mCfg = cfg;
}
@Override
protected List<String> doInBackground(Void... voids) {
return TunnelUtil.saveTunnel(I2PAppContext.getGlobalContext(), mGroup, mTunnelId, mCfg);
}
}

View File

@ -11,7 +11,7 @@ package net.i2p.android.i2ptunnel.util;
* tunnel properties always exist.
*/
public abstract class TunnelLogic {
protected String mType;
protected final String mType;
public TunnelLogic(String type) {
mType = type;

View File

@ -7,6 +7,7 @@ import android.os.Bundle;
import net.i2p.I2PAppContext;
import net.i2p.android.router.R;
import net.i2p.android.router.util.Util;
import net.i2p.android.wizard.model.Page;
import net.i2p.i2ptunnel.TunnelController;
import net.i2p.i2ptunnel.TunnelControllerGroup;
@ -99,6 +100,7 @@ public class TunnelUtil extends GeneralHelper {
public static void writeTunnelToPreferences(Context ctx, TunnelControllerGroup tcg, int tunnel) {
new TunnelUtil(tcg).writeTunnelToPreferences(ctx, tunnel);
}
public void writeTunnelToPreferences(Context ctx, int tunnel) {
Resources res = ctx.getResources();
@ -122,9 +124,9 @@ public class TunnelUtil extends GeneralHelper {
}
class TunnelToPreferences extends TunnelLogic {
SharedPreferences.Editor ed;
Resources res;
int tunnel;
final SharedPreferences.Editor ed;
final Resources res;
final int tunnel;
public TunnelToPreferences(SharedPreferences.Editor ed, Resources res, int tunnel, String type) {
super(type);
@ -182,8 +184,10 @@ public class TunnelUtil extends GeneralHelper {
@Override
protected void generalClientStandardOrIrc(boolean isStandardOrIrc) {
/* # TODO: See trac issue #2296
if (isStandardOrIrc)
ed.putBoolean(res.getString(R.string.TUNNEL_USE_SSL), isSSLEnabled(tunnel));
*/
}
@Override
@ -212,7 +216,8 @@ public class TunnelUtil extends GeneralHelper {
protected void generalServerPortStreamr(boolean isStreamr) {
if (!isStreamr) {
ed.putString(res.getString(R.string.TUNNEL_TARGET_HOST), getTargetHost(tunnel));
ed.putBoolean(res.getString(R.string.TUNNEL_USE_SSL), isSSLEnabled(tunnel));
// # TODO: See trac issue #2296
//ed.putBoolean(res.getString(R.string.TUNNEL_USE_SSL), isSSLEnabled(tunnel));
}
}
@ -225,7 +230,7 @@ public class TunnelUtil extends GeneralHelper {
ed.putInt(res.getString(R.string.TUNNEL_OPT_QUANTITY),
getTunnelQuantity(tunnel, res.getInteger(R.integer.DEFAULT_TUNNEL_QUANTITY)));
ed.putInt(res.getString(R.string.TUNNEL_OPT_BACKUP_QUANTITY),
getTunnelQuantity(tunnel, res.getInteger(R.integer.DEFAULT_TUNNEL_BACKUP_QUANTITY)));
getTunnelBackupQuantity(tunnel, res.getInteger(R.integer.DEFAULT_TUNNEL_BACKUP_QUANTITY)));
}
@Override
@ -325,6 +330,7 @@ public class TunnelUtil extends GeneralHelper {
public static TunnelConfig createConfigFromPreferences(Context ctx, TunnelControllerGroup tcg, int tunnel) {
return new TunnelUtil(tcg).createConfigFromPreferences(ctx, tunnel);
}
public TunnelConfig createConfigFromPreferences(Context ctx, int tunnel) {
Resources res = ctx.getResources();
@ -345,11 +351,11 @@ public class TunnelUtil extends GeneralHelper {
}
class TunnelConfigFromPreferences extends TunnelLogic {
TunnelConfig cfg;
SharedPreferences prefs;
Resources res;
TunnelControllerGroup tcg;
int tunnel;
final TunnelConfig cfg;
final SharedPreferences prefs;
final Resources res;
final TunnelControllerGroup tcg;
final int tunnel;
public TunnelConfigFromPreferences(TunnelConfig cfg, SharedPreferences prefs, Resources res,
TunnelControllerGroup tcg, int tunnel, String type) {
@ -414,8 +420,10 @@ public class TunnelUtil extends GeneralHelper {
@Override
protected void generalClientStandardOrIrc(boolean isStandardOrIrc) {
/* # TODO: See trac issue #2296
if (isStandardOrIrc)
cfg.setUseSSL(prefs.getBoolean(res.getString(R.string.TUNNEL_USE_SSL), false));
*/
}
@Override
@ -444,7 +452,8 @@ public class TunnelUtil extends GeneralHelper {
protected void generalServerPortStreamr(boolean isStreamr) {
if (!isStreamr) {
cfg.setTargetHost(prefs.getString(res.getString(R.string.TUNNEL_TARGET_HOST), "127.0.0.1"));
cfg.setUseSSL(prefs.getBoolean(res.getString(R.string.TUNNEL_USE_SSL), false));
// # TODO: See trac issue #2296
//cfg.setUseSSL(prefs.getBoolean(res.getString(R.string.TUNNEL_USE_SSL), false));
}
}
@ -560,10 +569,17 @@ public class TunnelUtil extends GeneralHelper {
}
}
/**
* @param data non-null
*/
public static TunnelConfig createConfigFromWizard(
Context ctx, TunnelControllerGroup tcg, Bundle data) {
return new TunnelUtil(tcg).createConfigFromWizard(ctx, data);
}
/**
* @param data non-null
*/
public TunnelConfig createConfigFromWizard(Context ctx, Bundle data) {
// Get the Bundle keys
Resources res = ctx.getResources();
@ -574,21 +590,26 @@ public class TunnelUtil extends GeneralHelper {
// Update the TunnelConfig from the tunnel wizard settings
String kClientServer = res.getString(R.string.i2ptunnel_wizard_k_client_server);
String kType = res.getString(R.string.i2ptunnel_wizard_k_type);
String clientServer = data.getBundle(kClientServer).getString(Page.SIMPLE_DATA_KEY);
String typeName = data.getBundle(clientServer + ":" + kType).getString(Page.SIMPLE_DATA_KEY);
String type = getTypeFromName(typeName, ctx);
cfg.setType(type);
try {
String clientServer = data.getBundle(kClientServer).getString(Page.SIMPLE_DATA_KEY);
String typeName = data.getBundle(clientServer + ":" + kType).getString(Page.SIMPLE_DATA_KEY);
String type = getTypeFromName(typeName, ctx);
cfg.setType(type);
new TunnelConfigFromWizard(cfg, data, res, _group, type).runLogic();
} catch (NullPointerException ex) {
Util.e("Exception while trying to create config from wizard: "+ex.getMessage());
}
new TunnelConfigFromWizard(cfg, data, res, _group, type).runLogic();
return cfg;
}
class TunnelConfigFromWizard extends TunnelLogic {
TunnelConfig cfg;
Bundle data;
Resources res;
TunnelControllerGroup tcg;
final TunnelConfig cfg;
final Bundle data;
final Resources res;
final TunnelControllerGroup tcg;
public TunnelConfigFromWizard(TunnelConfig cfg, Bundle data, Resources res,
TunnelControllerGroup tcg, String type) {

View File

@ -1,20 +1,20 @@
package net.i2p.android.preferences;
import android.os.Bundle;
import android.preference.Preference;
import android.support.v4.app.Fragment;
import android.support.v4.preference.PreferenceFragment;
//import android.support.v4.app.Fragment;
import androidx.fragment.app.Fragment;
//import android.support.v7.preference.Preference;
import androidx.preference.Preference;
import net.i2p.android.router.R;
import net.i2p.android.router.SettingsActivity;
public class AdvancedPreferenceFragment extends PreferenceFragment {
public class AdvancedPreferenceFragment extends I2PreferenceFragment {
private static final String PREFERENCE_CATEGORY_TRANSPORTS = "preference_category_transports";
private static final String PREFERENCE_CATEGORY_EXPL_TUNNELS = "preference_category_expl_tunnels";
@Override
public void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
public void onCreatePreferences(Bundle paramBundle, String s) {
addPreferencesFromResource(R.xml.settings_advanced);
findPreference(PREFERENCE_CATEGORY_TRANSPORTS)

View File

@ -7,8 +7,7 @@ import net.i2p.android.router.SettingsActivity;
public class AppearancePreferenceFragment extends I2PreferenceFragment {
@Override
public void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
public void onCreatePreferences(Bundle paramBundle, String s) {
addPreferencesFromResource(R.xml.settings_appearance);
}

View File

@ -7,8 +7,7 @@ import net.i2p.android.router.SettingsActivity;
public class ExploratoryPoolPreferenceFragment extends I2PreferenceFragment {
@Override
public void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
public void onCreatePreferences(Bundle paramBundle, String s) {
addPreferencesFromResource(R.xml.settings_expl_tunnels);
}

View File

@ -2,10 +2,14 @@ package net.i2p.android.preferences;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.PreferenceCategory;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
//import android.support.v7.preference.CheckBoxPreference;
import androidx.preference.CheckBoxPreference;
//import android.support.v7.preference.PreferenceCategory;
import androidx.preference.PreferenceCategory;
//import android.support.v7.preference.PreferenceManager;
import androidx.preference.PreferenceManager;
//import android.support.v7.preference.PreferenceScreen;
import androidx.preference.PreferenceScreen;
import net.i2p.android.router.R;
import net.i2p.android.router.SettingsActivity;
@ -24,8 +28,7 @@ public class GraphsPreferenceFragment extends I2PreferenceFragment {
public static final String GRAPH_PREFERENCES_SEEN = "graphPreferencesSeen";
@Override
public void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
public void onCreatePreferences(Bundle paramBundle, String s) {
addPreferencesFromResource(R.xml.settings_graphs);
setupGraphSettings();
}

View File

@ -1,9 +1,9 @@
package net.i2p.android.preferences;
import android.support.v4.preference.PreferenceFragment;
import android.widget.Toast;
import net.i2p.I2PAppContext;
import net.i2p.android.preferences.util.CustomPreferenceFragment;
import net.i2p.android.router.R;
import net.i2p.android.router.util.Util;
import net.i2p.router.RouterContext;
@ -15,7 +15,7 @@ import java.util.Set;
/**
* A PreferenceFragment that handles saving router settings.
*/
public class I2PreferenceFragment extends PreferenceFragment {
public abstract class I2PreferenceFragment extends CustomPreferenceFragment {
@Override
public void onPause() {
List<Properties> lProps = Util.getPropertiesFromPreferences(getActivity());

View File

@ -1,7 +1,8 @@
package net.i2p.android.preferences;
import android.os.Bundle;
import android.preference.PreferenceScreen;
//import android.support.v7.preference.PreferenceScreen;
import androidx.preference.PreferenceScreen;
import net.i2p.android.router.R;
import net.i2p.android.router.SettingsActivity;
@ -11,8 +12,7 @@ import net.i2p.util.LogManager;
public class LoggingPreferenceFragment extends I2PreferenceFragment {
@Override
public void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
public void onCreatePreferences(Bundle paramBundle, String s) {
addPreferencesFromResource(R.xml.settings_logging);
setupLoggingSettings();
}

View File

@ -7,8 +7,7 @@ import net.i2p.android.router.SettingsActivity;
public class NetworkPreferenceFragment extends I2PreferenceFragment {
@Override
public void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
public void onCreatePreferences(Bundle paramBundle, String s) {
addPreferencesFromResource(R.xml.settings_net);
}

View File

@ -1,24 +1,29 @@
package net.i2p.android.preferences;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
//import android.support.v7.preference.CheckBoxPreference;
//import android.support.v7.preference.Preference;
//import android.support.v7.preference.PreferenceManager;
//import android.support.v7.preference.PreferenceScreen;
import androidx.preference.CheckBoxPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import android.widget.Toast;
import net.i2p.android.router.R;
import net.i2p.android.router.SettingsActivity;
import net.i2p.android.router.util.PortPreference;
import net.i2p.android.preferences.util.PortPreference;
import net.i2p.android.router.util.Util;
import net.i2p.router.RouterContext;
public class TransportsPreferenceFragment extends I2PreferenceFragment {
@Override
public void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
public void onCreatePreferences(Bundle paramBundle, String s) {
// Load any properties that the router might have changed on us.
loadProperties();
addPreferencesFromResource(R.xml.settings_transports);
@ -31,6 +36,7 @@ public class TransportsPreferenceFragment extends I2PreferenceFragment {
((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_label_transports);
}
@SuppressLint("ApplySharedPref")
private void loadProperties() {
Context context= getActivity();
RouterContext ctx = Util.getRouterContext();

View File

@ -1,9 +1,9 @@
package net.i2p.android.router.util;
package net.i2p.android.preferences.util;
import android.content.Context;
import android.content.res.TypedArray;
import android.preference.EditTextPreference;
import android.text.InputType;
//import android.support.v7.preference.EditTextPreference;
import androidx.preference.EditTextPreference;
import android.util.AttributeSet;
import net.i2p.android.router.R;
@ -26,7 +26,6 @@ public class ConnectionLimitPreference extends EditTextPreference {
}
void init(Context context, AttributeSet attrs) {
getEditText().setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED);
TypedArray attr = context.obtainStyledAttributes(attrs, R.styleable.ConnectionLimitPreference, 0, 0);
mValueInTitle = attr.getBoolean(R.styleable.ConnectionLimitPreference_clp_valueInTitle, false);
attr.recycle();
@ -70,6 +69,10 @@ public class ConnectionLimitPreference extends EditTextPreference {
@Override
protected boolean persistString(String value) {
return value != null && persistInt(Integer.valueOf(value));
try {
return value != null && persistInt(Integer.valueOf(value));
} catch (NumberFormatException e) {
return false;
}
}
}

View File

@ -0,0 +1,25 @@
package net.i2p.android.preferences.util;
import android.os.Bundle;
//import android.support.v7.preference.EditTextPreferenceDialogFragmentCompat;
import androidx.preference.EditTextPreferenceDialogFragmentCompat;
import android.text.InputType;
import android.view.View;
import android.widget.EditText;
public class ConnectionLimitPreferenceDialog extends EditTextPreferenceDialogFragmentCompat {
public static ConnectionLimitPreferenceDialog newInstance(String key) {
final ConnectionLimitPreferenceDialog fragment = new ConnectionLimitPreferenceDialog();
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,37 @@
package net.i2p.android.preferences.util;
//import android.support.v4.app.DialogFragment;
import androidx.fragment.app.DialogFragment;
import androidx.preference.Preference;
import androidx.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 ConnectionLimitPreference) {
f = ConnectionLimitPreferenceDialog.newInstance(preference.getKey());
} else if (preference instanceof IntEditTextPreference) {
f = IntEditTextPreferenceDialog.newInstance(preference.getKey());
} else if (preference instanceof PortPreference) {
f = PortPreferenceDialog.newInstance(preference.getKey());
} else {
super.onDisplayPreferenceDialog(preference);
}
if (f != null) {
f.setTargetFragment(this, 0);
f.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
}
}
}

View File

@ -1,25 +1,22 @@
package net.i2p.android.router.util;
package net.i2p.android.preferences.util;
import android.content.Context;
import android.preference.EditTextPreference;
import android.text.InputType;
//import android.support.v7.preference.EditTextPreference;
import androidx.preference.EditTextPreference;
import android.util.AttributeSet;
public class IntEditTextPreference extends EditTextPreference {
public IntEditTextPreference(Context context) {
super(context);
getEditText().setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED);
}
public IntEditTextPreference(Context context, AttributeSet attrs) {
super(context, attrs);
getEditText().setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED);
}
public IntEditTextPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
getEditText().setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED);
}
@Override
@ -42,6 +39,10 @@ public class IntEditTextPreference extends EditTextPreference {
@Override
protected boolean persistString(String value) {
return value != null && persistInt(Integer.valueOf(value));
try {
return value != null && persistInt(Integer.valueOf(value));
} catch (NumberFormatException e) {
return false;
}
}
}

View File

@ -0,0 +1,25 @@
package net.i2p.android.preferences.util;
import android.os.Bundle;
//import android.support.v7.preference.EditTextPreferenceDialogFragmentCompat;
import androidx.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

@ -1,7 +1,8 @@
package net.i2p.android.router.util;
package net.i2p.android.preferences.util;
import android.content.Context;
import android.preference.ListPreference;
//import android.support.v7.preference.ListPreference;
import androidx.preference.ListPreference;
import android.util.AttributeSet;
public class IntListPreference extends ListPreference {
@ -20,7 +21,7 @@ public class IntListPreference extends ListPreference {
getPersistedInt(0);
} catch (ClassCastException e) {
// Fix for where this preference was previously stored in a ListPreference
getSharedPreferences().edit().remove(getKey()).commit();
getSharedPreferences().edit().remove(getKey()).apply();
}
}

View File

@ -1,8 +1,8 @@
package net.i2p.android.router.util;
package net.i2p.android.preferences.util;
import android.content.Context;
import android.preference.EditTextPreference;
import android.text.InputType;
//import android.support.v7.preference.EditTextPreference;
import androidx.preference.EditTextPreference;
import android.util.AttributeSet;
import net.i2p.android.router.R;
@ -10,17 +10,14 @@ import net.i2p.android.router.R;
public class PortPreference extends EditTextPreference {
public PortPreference(Context context) {
super(context);
getEditText().setInputType(InputType.TYPE_CLASS_NUMBER);
}
public PortPreference(Context context, AttributeSet attrs) {
super(context, attrs);
getEditText().setInputType(InputType.TYPE_CLASS_NUMBER);
}
public PortPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
getEditText().setInputType(InputType.TYPE_CLASS_NUMBER);
}
@Override

View File

@ -0,0 +1,24 @@
package net.i2p.android.preferences.util;
import android.os.Bundle;
//import android.support.v7.preference.EditTextPreferenceDialogFragmentCompat;
import androidx.preference.EditTextPreferenceDialogFragmentCompat;
import android.text.InputType;
import android.view.View;
import android.widget.EditText;
public class PortPreferenceDialog extends EditTextPreferenceDialogFragmentCompat {
public static PortPreferenceDialog newInstance(String key) {
final PortPreferenceDialog fragment = new PortPreferenceDialog();
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);
}
}

View File

@ -1,6 +1,7 @@
package net.i2p.android.router.util;
package net.i2p.android.preferences.util;
import android.content.Context;
//import android.support.v7.preference.EditTextPreference;
import android.preference.EditTextPreference;
import android.util.AttributeSet;

View File

@ -3,7 +3,8 @@ package net.i2p.android.router;
import android.content.Intent;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
//import android.support.v4.app.Fragment;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@ -12,6 +13,7 @@ import android.view.View;
import android.view.ViewGroup;
import net.i2p.android.ext.floatingactionbutton.FloatingActionsMenu;
import net.i2p.android.ext.floatingactionbutton.FloatingActionsMenu.OnFloatingActionsMenuUpdateListener;
import net.i2p.android.router.dialog.AboutDialog;
import net.i2p.android.router.dialog.TextResourceDialog;
import net.i2p.android.router.log.LogActivity;
@ -42,6 +44,17 @@ public class ConsoleContainer extends Fragment {
}
mConsoleMenu = (FloatingActionsMenu) v.findViewById(R.id.console_action_menu);
// update visibility based on router state
mConsoleMenu.setOnFloatingActionsMenuUpdateListener(new OnFloatingActionsMenuUpdateListener() {
public void onMenuExpanded() {
// this is called after the animation starts, sadly
setMenuVisibility();
}
public void onMenuCollapsed() {
// call it here too so the expand animation isn't glitchy as often
setMenuVisibility();
}
});
mConsoleMenu.findViewById(R.id.action_news).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
@ -63,13 +76,13 @@ public class ConsoleContainer extends Fragment {
startActivity(graphs);
}
});
mConsoleMenu.findViewById(R.id.action_peers).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent peers = new Intent(getActivity(), PeersActivity.class);
startActivity(peers);
}
});
// mConsoleMenu.findViewById(R.id.action_peers).setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View view) {
// Intent peers = new Intent(getActivity(), PeersActivity.class);
// startActivity(peers);
// }
// });
mConsoleMenu.findViewById(R.id.action_netdb).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
@ -87,20 +100,19 @@ public class ConsoleContainer extends Fragment {
inflater.inflate(R.menu.activity_main_actions, menu);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
setMenuVisibility();
}
private void setMenuVisibility() {
boolean advanced = PreferenceManager.getDefaultSharedPreferences(getActivity()).getBoolean(
"i2pandroid.main.showStats", false);
boolean routerRunning = Util.getRouterContext() != null;
mConsoleMenu.findViewById(R.id.action_logs).setVisibility(routerRunning ? View.VISIBLE : View.GONE);
mConsoleMenu.findViewById(R.id.action_graphs).setVisibility(routerRunning ? View.VISIBLE : View.GONE);
mConsoleMenu.findViewById(R.id.action_peers).setVisibility(advanced && routerRunning ? View.VISIBLE : View.GONE);
mConsoleMenu.findViewById(R.id.action_netdb).setVisibility(advanced && routerRunning ? View.VISIBLE : View.GONE);
if (getActivity() != null) {
boolean advanced = PreferenceManager.getDefaultSharedPreferences(getActivity())
.getBoolean("i2pandroid.main.showStats", false);
// mConsoleMenu.findViewById(R.id.action_peers).setVisibility(
// advanced && routerRunning ? View.VISIBLE : View.GONE);
mConsoleMenu.findViewById(R.id.action_netdb).setVisibility(
advanced && routerRunning ? View.VISIBLE : View.GONE);
}
}
@Override

View File

@ -1,7 +1,8 @@
package net.i2p.android.router;
import android.os.Bundle;
import android.support.v4.app.Fragment;
//import android.support.v4.app.Fragment;
import androidx.fragment.app.Fragment;
import net.i2p.android.router.util.Util;
import net.i2p.router.CommSystemFacade;

View File

@ -1,7 +1,8 @@
package net.i2p.android.router;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
//import android.support.v7.widget.Toolbar;
import androidx.appcompat.widget.Toolbar;
import net.i2p.android.I2PActivityBase;

View File

@ -1,7 +1,8 @@
package net.i2p.android.router;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
//import android.support.v4.app.ListFragment;
import androidx.fragment.app.ListFragment;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;

View File

@ -1,17 +1,24 @@
package net.i2p.android.router;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
import android.preference.PreferenceManager;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AlertDialog;
import android.provider.Settings;
//import android.support.v4.content.LocalBroadcastManager;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
//import android.support.v7.app.AlertDialog;
import androidx.appcompat.app.AlertDialog;
import android.util.AndroidRuntimeException;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@ -23,6 +30,7 @@ import android.widget.ScrollView;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;
import net.i2p.android.I2PActivityBase;
@ -69,6 +77,7 @@ public class MainFragment extends I2PFragmentBase {
private TextView vAdvStatusText;
private static final String PREF_CONFIGURE_BROWSER = "app.dialog.configureBrowser";
private static final String PREF_CONFIGURE_BATTERY = "app.dialog.configureBattery";
private static final String PREF_FIRST_START = "app.router.firstStart";
private static final String PREF_SHOW_STATS = "i2pandroid.main.showStats";
protected static final String PROP_NEW_INSTALL = "i2p.newInstall";
@ -320,7 +329,10 @@ public class MainFragment extends I2PFragmentBase {
}
}
public void updateState(State newState) {
/**
* Changes the logo based on the state.
*/
private void updateState(State newState) {
if (newState == State.INIT ||
newState == State.STOPPED ||
newState == State.MANUAL_STOPPED ||
@ -328,13 +340,13 @@ public class MainFragment extends I2PFragmentBase {
newState == State.NETWORK_STOPPED) {
mConsoleLights.setImageResource(R.drawable.routerlogo_0);
} else if (newState == State.STARTING ||
newState == State.GRACEFUL_SHUTDOWN ||
newState == State.STOPPING ||
newState == State.MANUAL_STOPPING ||
newState == State.MANUAL_QUITTING ||
newState == State.NETWORK_STOPPING) {
mConsoleLights.setImageResource(R.drawable.routerlogo_1);
} else if (newState == State.RUNNING) {
} else if (newState == State.RUNNING ||
newState == State.GRACEFUL_SHUTDOWN) {
mConsoleLights.setImageResource(R.drawable.routerlogo_2);
} else if (newState == State.ACTIVE) {
mConsoleLights.setImageResource(R.drawable.routerlogo_3);
@ -352,7 +364,10 @@ public class MainFragment extends I2PFragmentBase {
vNetStatusText.setText(R.string.no_internet);
vStatusContainer.setVisibility(View.VISIBLE);
vNonNetStatus.setVisibility(View.GONE);
} else if (ctx != null) {
} else if (lastRouterState != null &&
!Util.isStopping(lastRouterState) &&
!Util.isStopped(lastRouterState) &&
ctx != null) {
Util.NetStatus netStatus = Util.getNetStatus(getActivity(), ctx);
switch (netStatus.level) {
case ERROR:
@ -372,9 +387,9 @@ public class MainFragment extends I2PFragmentBase {
String uptime = DataHelper.formatDuration(ctx.router().getUptime());
int active = ctx.commSystem().countActivePeers();
int known = Math.max(ctx.netDb().getKnownRouters() - 1, 0);
vUptime.setText("" + uptime);
vActive.setText("" + active);
vKnown.setText("" + known);
vUptime.setText(uptime);
vActive.setText(Integer.toString(active));
vKnown.setText(Integer.toString(known));
// Load running tunnels
loadDestinations(ctx);
@ -393,20 +408,21 @@ public class MainFragment extends I2PFragmentBase {
//ctx.commSystem().getReachabilityStatus();
String status =
"Exploratory Tunnels in/out: " + inEx + " / " + outEx
+ "\nClient Tunnels in/out: " + inCl + " / " + outCl;
getString(R.string.notification_status_expl, inEx, outEx) + '\n' +
getString(R.string.notification_status_client, inCl, outCl);
// Need to see if we have the participation option set to on.
// I thought there was a router method for that? I guess not! WHY NOT?
// It would be easier if we had a number to test status.
String participate = "\nParticipation: " + tunnelStatus + " (" + part + ")";
String participate = '\n' + getString(R.string.settings_label_hiddenMode) + ": " + tunnelStatus + " (" + part + ")";
String details =
"\nMemory: " + DataHelper.formatSize(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())
+ "B / " + DataHelper.formatSize(Runtime.getRuntime().maxMemory()) + 'B'
+ "\nJob Lag: " + jobLag
+ "\nMsg Delay: " + msgDelay;
'\n' + getString(R.string.stats_memory) + ": " +
DataHelper.formatSize(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) +
"B / " + DataHelper.formatSize(Runtime.getRuntime().maxMemory()) + 'B' +
'\n' + getString(R.string.stats_lag) + ": " + jobLag +
'\n' + getString(R.string.stats_delay) + ": " + msgDelay;
_savedStatus = status + participate + details;
vAdvStatusText.setText(_savedStatus);
@ -425,15 +441,16 @@ public class MainFragment extends I2PFragmentBase {
double outData = ctx.bandwidthLimiter().getTotalAllocatedOutboundBytes();
((TextView) getActivity().findViewById(R.id.console_download_stats)).setText(
Util.formatSize(inBw) + "Bps / " + Util.formatSize(inData) + "B");
Util.formatSpeed(inBw) + "Bps / " + Util.formatSize(inData) + "B");
((TextView) getActivity().findViewById(R.id.console_upload_stats)).setText(
Util.formatSize(outBw) + "Bps / " + Util.formatSize(outData) + "B");
Util.formatSpeed(outBw) + "Bps / " + Util.formatSize(outData) + "B");
getActivity().findViewById(R.id.console_usage_stats).setVisibility(View.VISIBLE);
} else {
// network but no router context
vStatusContainer.setVisibility(View.GONE);
getActivity().findViewById(R.id.console_usage_stats).setVisibility(View.INVISIBLE);
updateState(State.STOPPED);
/**
* **
* RouterService svc = _routerService; String status = "connected? "
@ -523,12 +540,12 @@ public class MainFragment extends I2PFragmentBase {
* compare translated nicknames - put "shared clients" first in the sort
*/
private class AlphaComparator implements Comparator<Destination> {
private String xsc;
private RouterContext _ctx;
private final String xsc;
private final RouterContext _ctx;
public AlphaComparator(RouterContext ctx) {
_ctx = ctx;
xsc = _(ctx, SHARED_CLIENTS);
xsc = _t(ctx, SHARED_CLIENTS);
}
public int compare(Destination lhs, Destination rhs) {
@ -556,12 +573,12 @@ public class MainFragment extends I2PFragmentBase {
if (name == null)
name = d.calculateHash().toBase64().substring(0, 6);
else
name = _(ctx, name);
name = _t(ctx, name);
return name;
}
private String _(RouterContext ctx, String s) {
private String _t(RouterContext ctx, String s) {
if (SHARED_CLIENTS.equals(s))
return getString(R.string.shared_clients);
else
@ -575,16 +592,18 @@ public class MainFragment extends I2PFragmentBase {
);
if (language == null) {
AlertDialog.Builder b = new AlertDialog.Builder(getActivity());
// avoid ISE caused by fragment detachment ticket #2631
final String languages[] = getResources().getStringArray(R.array.languages);
b.setTitle(R.string.choose_language)
.setItems(R.array.language_names, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// Save the language choice
String language = getResources().getStringArray(R.array.languages)[which];
String language = languages[which];
PreferenceManager.getDefaultSharedPreferences(getActivity())
.edit()
.putString(getString(R.string.PREF_LANGUAGE), language)
.commit();
.apply();
// Close the dialog
dialog.dismiss();
// Broadcast the change to RouterService just in case the router is running
@ -611,16 +630,62 @@ public class MainFragment extends I2PFragmentBase {
ab.setPref(PREF_CONFIGURE_BROWSER, false);
Intent hi = new Intent(getActivity(), BrowserConfigActivity.class);
startActivity(hi);
checkDialog();
}
})
.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int i) {
dialog.cancel();
dialog.dismiss();
ab.setPref(PREF_CONFIGURE_BROWSER, false);
checkDialog();
}
})
.show();
} else if (ab.getPref(PREF_CONFIGURE_BATTERY, true)) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
// only for Marshmallow and newer versions
final Intent intent = new Intent();
final Context mContext = ab.getApplicationContext();
String packageName = mContext.getPackageName();
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
AlertDialog.Builder b = new AlertDialog.Builder(getActivity());
b.setTitle(R.string.configure_no_doze_title);
b.setMessage(R.string.configure_no_doze);
b.setCancelable(false);
b.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int i) {
String packageName = mContext.getPackageName();
ab.setPref(PREF_CONFIGURE_BATTERY, false);
dialog.dismiss();
// Simply do not re-attempt a battery optimization after the first time,
// even if an error occurs. http://trac.i2p2.i2p/ticket/2783
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + packageName));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
mContext.startActivity(intent);
} catch (ActivityNotFoundException activityNotFound) {
ab.setPref(PREF_CONFIGURE_BATTERY, false);
} catch (AndroidRuntimeException activityNotFound) {
ab.setPref(PREF_CONFIGURE_BATTERY, false);
}
}
});
b.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int i) {
dialog.cancel();
ab.setPref(PREF_CONFIGURE_BATTERY, false);
}
});
b.show();
}
} else {
ab.setPref(PREF_CONFIGURE_BATTERY, false);
}
}
/*VersionDialog dialog = new VersionDialog();
String oldVersion = ((I2PActivityBase) getActivity()).getPref(PREF_INSTALLED_VERSION, "??");
@ -645,9 +710,11 @@ public class MainFragment extends I2PFragmentBase {
private void checkFirstStart() {
I2PActivityBase ab = (I2PActivityBase) getActivity();
boolean firstStart = ab.getPref(PREF_FIRST_START, true);
if (firstStart) {
// Check ab.isFinishing() because DialogFragment.show() will throw IllegalStateException if
// called after ab.onSaveInstanceState().
if (firstStart && !ab.isFinishing()) {
FirstStartDialog dialog = new FirstStartDialog();
dialog.show(getActivity().getSupportFragmentManager(), "firststart");
dialog.show(ab.getSupportFragmentManager(), "firststart");
ab.setPref(PREF_FIRST_START, false);
}
}

View File

@ -1,7 +1,8 @@
package net.i2p.android.router;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
//import android.support.v7.widget.Toolbar;
import androidx.appcompat.widget.Toolbar;
import net.i2p.android.I2PActivityBase;

View File

@ -13,6 +13,7 @@ import android.widget.TextView;
import net.i2p.android.apps.NewsFetcher;
import net.i2p.android.router.util.Util;
import net.i2p.router.RouterContext;
import java.io.ByteArrayOutputStream;
import java.io.File;
@ -33,12 +34,16 @@ public class NewsFragment extends I2PFragmentBase {
@Override
public void onResume() {
super.onResume();
NewsFetcher nf = NewsFetcher.getInstance();
if (nf != null) {
// Always update the status
TextView tv = (TextView) getActivity().findViewById(R.id.news_status);
tv.setText(nf.status().replace("&nbsp;", " "));
tv.setVisibility(View.VISIBLE);
RouterContext ctx = getRouterContext();
if (ctx != null) {
NewsFetcher nf = (NewsFetcher) ctx.clientAppManager().getRegisteredApp(NewsFetcher.APP_NAME);
if (nf != null) {
// Always update the status
// This is the news last updated/checked text at the bottom
TextView tv = (TextView) getActivity().findViewById(R.id.news_status);
tv.setText(nf.status().replace("&nbsp;", " "));
tv.setVisibility(View.VISIBLE);
}
}
// Only update the content if we need to

View File

@ -3,13 +3,20 @@ package net.i2p.android.router;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.Preference;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.preference.PreferenceFragment;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
//import android.support.v4.app.Fragment;
import androidx.fragment.app.Fragment;
//import android.support.v4.app.FragmentManager;
import androidx.fragment.app.FragmentManager;
//import android.support.v4.content.LocalBroadcastManager;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
//import android.support.v7.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatActivity;
//import android.support.v7.preference.Preference;
import androidx.preference.Preference;
//import android.support.v7.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceFragmentCompat;
//import android.support.v7.widget.Toolbar;
import androidx.appcompat.widget.Toolbar;
import net.i2p.android.I2PActivity;
import net.i2p.android.preferences.AdvancedPreferenceFragment;
@ -84,10 +91,11 @@ public class SettingsActivity extends AppCompatActivity implements
}
}
public static class SettingsFragment extends PreferenceFragment {
public static class SettingsFragment extends PreferenceFragmentCompat {
@Override
public void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
public void onCreatePreferences(Bundle paramBundle, String s) {
migrateOldSettings();
addPreferencesFromResource(R.xml.settings);
this.findPreference(PREFERENCE_CATEGORY_NETWORK)
@ -104,6 +112,29 @@ public class SettingsActivity extends AppCompatActivity implements
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_ADVANCED));
}
private void migrateOldSettings() {
SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
try {
prefs.getInt("i2np.bandwidth.inboundKBytesPerSecond", 0);
} catch (ClassCastException e) {
// Migrate pre-0.9.25 settings
SharedPreferences.Editor editor = prefs.edit();
editor.remove("i2np.bandwidth.inboundKBytesPerSecond");
editor.putInt("i2np.bandwidth.inboundKBytesPerSecond", Integer.parseInt(
prefs.getString("i2np.bandwidth.inboundKBytesPerSecond", "100")));
editor.remove("i2np.bandwidth.outboundKBytesPerSecond");
editor.putInt("i2np.bandwidth.outboundKBytesPerSecond", Integer.parseInt(
prefs.getString("i2np.bandwidth.outboundKBytesPerSecond", "100")));
editor.remove("i2np.ntcp.maxConnections");
editor.putInt("i2np.ntcp.maxConnections", Integer.parseInt(
prefs.getString("i2np.ntcp.maxConnections", "32")));
editor.remove("i2np.udp.maxConnections");
editor.putInt("i2np.udp.maxConnections", Integer.parseInt(
prefs.getString("i2np.udp.maxConnections", "32")));
editor.apply();
}
}
@Override
public void onResume() {
super.onResume();

View File

@ -1,8 +1,9 @@
package net.i2p.android.router.addressbook;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import androidx.annotation.NonNull;
//import android.support.v7.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

View File

@ -1,7 +1,8 @@
package net.i2p.android.router.addressbook;
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
//import android.support.v4.content.AsyncTaskLoader;
import androidx.loader.content.AsyncTaskLoader;
import net.i2p.android.router.util.NamingServiceUtil;
import net.i2p.android.router.util.Util;

View File

@ -2,12 +2,15 @@ package net.i2p.android.router.addressbook;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import androidx.annotation.NonNull;
//import android.support.v4.app.DialogFragment;
import androidx.fragment.app.DialogFragment;
//import android.support.v7.app.AlertDialog;
import androidx.appcompat.app.AlertDialog;
import net.i2p.android.wizard.model.AbstractWizardModel;
import net.i2p.android.wizard.ui.AbstractWizardActivity;
@ -20,26 +23,51 @@ public class AddressbookAddWizardActivity extends AbstractWizardActivity {
@Override
protected DialogFragment onGetFinishWizardDialog() {
return new DialogFragment() {
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity())
.setMessage("Add to private addressbook?")
.setPositiveButton("Add",
new DialogInterface.OnClickListener() {
return FinishWizardDialogFragment.newInstance();
}
public void onClick(DialogInterface dialog, int which) {
Intent result = new Intent();
setResult(Activity.RESULT_OK, result);
result.putExtra(AddressbookContainer.ADD_WIZARD_DATA, mWizardModel.save());
dialog.dismiss();
finish();
}
})
.setNegativeButton(android.R.string.cancel, null)
.create();
public void onFinishWizard() {
Intent result = new Intent();
result.putExtra(AddressbookContainer.ADD_WIZARD_DATA, mWizardModel.save());
setResult(Activity.RESULT_OK, result);
finish();
}
public static class FinishWizardDialogFragment extends DialogFragment {
AddressbookAddWizardActivity mListener;
public static DialogFragment newInstance() {
return new FinishWizardDialogFragment();
}
public void onAttach(Context context) {
super.onAttach(context);
// Verify that the host fragment implements the callback interface
try {
// Instantiate the AddressbookAddWizardActivity so we can send events to the host
mListener = (AddressbookAddWizardActivity) context;
} catch (ClassCastException e) {
// The fragment doesn't implement the interface, throw exception
throw new ClassCastException(context.toString()
+ " must be AddressbookAddWizardActivity");
}
};
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity())
.setMessage("Add to private addressbook?")
.setPositiveButton("Add",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
mListener.onFinishWizard();
}
})
.setNegativeButton(android.R.string.cancel, null)
.create();
}
}
}

View File

@ -4,7 +4,7 @@ import android.content.Context;
import android.content.res.Resources;
import net.i2p.android.router.R;
import net.i2p.android.wizard.model.AbstractWizardModel;
import net.i2p.android.wizard.model.I2PB64DestinationPage;
import net.i2p.android.wizard.model.I2PDestinationPage;
import net.i2p.android.wizard.model.PageList;
import net.i2p.android.wizard.model.SingleTextFieldPage;
@ -22,7 +22,7 @@ public class AddressbookAddWizardModel extends AbstractWizardModel {
.setDescription(res.getString(R.string.addressbook_add_wizard_desc_name))
.setRequired(true),
new I2PB64DestinationPage(this, res.getString(R.string.addressbook_add_wizard_k_destination))
new I2PDestinationPage(this, res.getString(R.string.i2ptunnel_wizard_k_dest))
.setDescription(res.getString(R.string.addressbook_add_wizard_desc_destination))
.setRequired(true)
);

View File

@ -6,13 +6,21 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
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.app.FragmentTransaction;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.view.ViewPager;
import android.support.v7.widget.SearchView;
//import android.support.v4.app.Fragment;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
//import android.support.v4.app.FragmentManager;
import androidx.fragment.app.FragmentManager;
//import android.support.v4.app.FragmentPagerAdapter;
import androidx.fragment.app.FragmentPagerAdapter;
//import android.support.v4.app.FragmentTransaction;
import androidx.fragment.app.FragmentTransaction;
//import android.support.v4.view.MenuItemCompat;
import androidx.core.view.MenuItemCompat;
//import android.support.v4.view.ViewPager;
import androidx.viewpager.widget.ViewPager;
//import android.support.v7.widget.SearchView;
import androidx.appcompat.widget.SearchView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@ -20,18 +28,27 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import com.viewpagerindicator.TitlePageIndicator;
import net.lucode.hackware.magicindicator.MagicIndicator;
import net.lucode.hackware.magicindicator.ViewPagerHelper;
import net.lucode.hackware.magicindicator.buildins.commonnavigator.CommonNavigator;
import net.i2p.android.router.R;
import net.i2p.android.router.util.NamingServiceUtil;
import net.i2p.android.router.util.Util;
import net.i2p.client.naming.NamingService;
import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.CommonNavigatorAdapter;
import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerIndicator;
import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerTitleView;
import net.lucode.hackware.magicindicator.buildins.commonnavigator.indicators.LinePagerIndicator;
import net.lucode.hackware.magicindicator.buildins.commonnavigator.titles.ColorTransitionPagerTitleView;
import net.lucode.hackware.magicindicator.buildins.commonnavigator.titles.SimplePagerTitleView;
public class AddressbookContainer extends Fragment
implements AddressbookFragment.OnAddressSelectedListener,
SearchView.OnQueryTextListener {
public static final int ADD_WIZARD_REQUEST = 1;
public static final String ADD_WIZARD_DATA = "add_wizard_data";
private MagicIndicator mPageIndicator;
/**
* Whether or not the container is in two-pane mode, i.e. running on a tablet
@ -41,7 +58,6 @@ public class AddressbookContainer extends Fragment
ViewPager mViewPager;
FragmentPagerAdapter mFragPagerAdapter;
private static final String FRAGMENT_ROUTER = "router_fragment";
private static final String FRAGMENT_PRIVATE = "private_fragment";
private static final int FRAGMENT_ID_ROUTER = 0;
@ -66,6 +82,17 @@ public class AddressbookContainer extends Fragment
// activity should be in two-pane mode.
mTwoPane = true;
}
if (!mTwoPane) {
// Initialize ViewPager and adapter first
mViewPager = (ViewPager) v.findViewById(R.id.pager);
mFragPagerAdapter = new AddressbookPagerAdapter(getActivity(), getChildFragmentManager());
mViewPager.setAdapter(mFragPagerAdapter);
// Then set up MagicIndicator
mPageIndicator = v.findViewById(R.id.magic_indicator);
setupMagicIndicator();
}
if (savedInstanceState != null) {
mRouterFrag = (AddressbookFragment) getChildFragmentManager().getFragment(
@ -90,14 +117,6 @@ public class AddressbookContainer extends Fragment
ft.commit();
}
if (!mTwoPane) {
mViewPager = (ViewPager) v.findViewById(R.id.pager);
TitlePageIndicator pageIndicator = (TitlePageIndicator) v.findViewById(R.id.page_indicator);
mFragPagerAdapter = new AddressbookPagerAdapter(getActivity(), getChildFragmentManager());
mViewPager.setAdapter(mFragPagerAdapter);
pageIndicator.setViewPager(mViewPager);
}
return v;
}
@ -144,11 +163,14 @@ public class AddressbookContainer extends Fragment
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.container_addressbook_actions, menu);
SearchManager searchManager = (SearchManager) getActivity().getSystemService(Context.SEARCH_SERVICE);
MenuItem searchItem = menu.findItem(R.id.action_search_addressbook);
SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
searchView.setSearchableInfo(searchManager.getSearchableInfo(getActivity().getComponentName()));
searchView.setOnQueryTextListener(this);
Activity activity = getActivity();
if (activity != null) {
SearchManager searchManager = (SearchManager) activity.getSystemService(Context.SEARCH_SERVICE);
MenuItem searchItem = menu.findItem(R.id.action_search_addressbook);
SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
searchView.setSearchableInfo(searchManager.getSearchableInfo(activity.getComponentName()));
searchView.setOnQueryTextListener(this);
}
}
@Override
@ -247,4 +269,38 @@ public class AddressbookContainer extends Fragment
if (mPrivateFrag != null)
mPrivateFrag.filterAddresses(query);
}
private void setupMagicIndicator() {
if (mPageIndicator == null || mFragPagerAdapter == null || mViewPager == null) {
return;
}
CommonNavigator commonNavigator = new CommonNavigator(getContext());
commonNavigator.setAdapter(new CommonNavigatorAdapter() {
@Override
public int getCount() {
return mFragPagerAdapter.getCount();
}
@Override
public IPagerTitleView getTitleView(Context context, int index) {
SimplePagerTitleView titleView = new ColorTransitionPagerTitleView(context);
titleView.setText(mFragPagerAdapter.getPageTitle(index));
titleView.setNormalColor(ContextCompat.getColor(context, R.color.primary_text_disabled_material_dark));
titleView.setSelectedColor(ContextCompat.getColor(context, R.color.primary_text_default_material_dark));
titleView.setOnClickListener(v -> mViewPager.setCurrentItem(index));
return titleView;
}
@Override
public IPagerIndicator getIndicator(Context context) {
LinePagerIndicator indicator = new LinePagerIndicator(context);
indicator.setColors(ContextCompat.getColor(context, R.color.primary));
return indicator;
}
});
mPageIndicator.setNavigator(commonNavigator);
ViewPagerHelper.bind(mPageIndicator, mViewPager);
}
}

View File

@ -6,12 +6,18 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
//import android.support.v4.app.Fragment;
import androidx.fragment.app.Fragment;
//import android.support.v4.app.LoaderManager;
import androidx.loader.app.LoaderManager;
//import android.support.v4.content.Loader;
import androidx.loader.content.Loader;
//import android.support.v4.content.LocalBroadcastManager;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
//import android.support.v7.widget.LinearLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
//import android.support.v7.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.Menu;
@ -26,7 +32,6 @@ import com.eowise.recyclerview.stickyheaders.StickyHeadersBuilder;
import com.eowise.recyclerview.stickyheaders.StickyHeadersItemDecoration;
import com.pnikosis.materialishprogress.ProgressWheel;
import net.i2p.addressbook.Daemon;
import net.i2p.android.router.R;
import net.i2p.android.router.service.RouterService;
import net.i2p.android.router.service.State;
@ -167,15 +172,23 @@ public class AddressbookFragment extends Fragment implements
int loaderId = PRIVATE_BOOK.equals(mBook) ?
PRIVATE_LOADER_ID : ROUTER_LOADER_ID;
if (state == State.STOPPING || state == State.STOPPED ||
state == State.MANUAL_STOPPING ||
state == State.MANUAL_STOPPED ||
state == State.MANUAL_QUITTING ||
state == State.MANUAL_QUITTED)
getLoaderManager().destroyLoader(loaderId);
else {
mRecyclerView.setLoading(true);
getLoaderManager().initLoader(loaderId, null, this);
try {
LoaderManager manager = getLoaderManager();
if (state == State.INIT ||
state == State.STARTING || // Wait until RouterContext is initialised
state == State.STOPPING ||
state == State.STOPPED ||
state == State.MANUAL_STOPPING ||
state == State.MANUAL_STOPPED ||
state == State.MANUAL_QUITTING ||
state == State.MANUAL_QUITTED)
manager.destroyLoader(loaderId);
else {
mRecyclerView.setLoading(true);
manager.initLoader(loaderId, null, this);
}
} catch (IllegalStateException ise) {
// Fragment isn't attached to any activity, so ignore state change
}
}
@ -207,8 +220,11 @@ public class AddressbookFragment extends Fragment implements
mAddToAddressbook.setVisibility(rCtx == null ? View.GONE : View.VISIBLE);
// Only show "Reload subscriptions" for router addressbook
menu.findItem(R.id.action_reload_subscriptions).setVisible(
rCtx != null && !PRIVATE_BOOK.equals(mBook));
MenuItem reloadSubs = menu.findItem(R.id.action_reload_subscriptions);
if (reloadSubs != null) {
reloadSubs.setVisible(
rCtx != null && !PRIVATE_BOOK.equals(mBook));
}
// Only allow adding to private book
if (!PRIVATE_BOOK.equals(mBook) && mAddToAddressbook != null) {
@ -223,9 +239,12 @@ public class AddressbookFragment extends Fragment implements
switch (item.getItemId()) {
case R.id.action_reload_subscriptions:
Daemon.wakeup();
Toast.makeText(getActivity(), "Reloading subscriptions...",
Toast.LENGTH_SHORT).show();
RouterContext rCtx = Util.getRouterContext();
if (rCtx != null) {
rCtx.namingService().requestUpdate(null);
Toast.makeText(getActivity(), "Reloading subscriptions...",
Toast.LENGTH_SHORT).show();
}
return true;
default:
return super.onOptionsItemSelected(item);

View File

@ -2,8 +2,10 @@ package net.i2p.android.router.addressbook;
import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
//import android.support.v7.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatActivity;
//import android.support.v7.widget.Toolbar;
import androidx.appcompat.widget.Toolbar;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
@ -61,7 +63,7 @@ public class AddressbookSettingsActivity extends AppCompatActivity {
private boolean load() {
String res = FileUtil.readTextFile(i2pDir.getAbsolutePath(), -1, true);
if (res.length() > 0) {
if (res != null && res.length() > 0) {
text_content_subscriptions.setText(res);
return true;
}

View File

@ -4,9 +4,11 @@ import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import androidx.annotation.NonNull;
//import android.support.v4.app.DialogFragment;
import androidx.fragment.app.DialogFragment;
//import android.support.v7.app.AlertDialog;
import androidx.appcompat.app.AlertDialog;
import android.text.util.Linkify;
import android.view.LayoutInflater;
import android.view.View;
@ -25,16 +27,16 @@ public class AboutDialog extends DialogFragment {
View view = li.inflate(R.layout.fragment_dialog_about, null);
final String currentVersion = Util.getOurVersion(getActivity());
TextView tv = (TextView)view.findViewById(R.id.about_version);
TextView tv = (TextView) view.findViewById(R.id.about_version);
tv.setText(currentVersion);
tv = (TextView)view.findViewById(R.id.url_project);
tv = (TextView) view.findViewById(R.id.url_project);
Linkify.addLinks(tv, I2Patterns.I2P_WEB_URL, "http://");
tv = (TextView)view.findViewById(R.id.url_android_bugs);
tv = (TextView) view.findViewById(R.id.url_android_bugs);
Linkify.addLinks(tv, I2Patterns.I2P_WEB_URL, "http://");
tv = (TextView)view.findViewById(R.id.url_android_volunteer);
tv = (TextView) view.findViewById(R.id.url_android_volunteer);
Linkify.addLinks(tv, I2Patterns.I2P_WEB_URL, "http://");
tv = (TextView)view.findViewById(R.id.url_donate);
tv = (TextView) view.findViewById(R.id.url_gitlab);
Linkify.addLinks(tv, I2Patterns.I2P_WEB_URL, "http://");
AlertDialog.Builder b = new AlertDialog.Builder(getActivity());

View File

@ -2,10 +2,16 @@ package net.i2p.android.router.dialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import androidx.annotation.NonNull;
//import android.support.v4.app.DialogFragment;
import androidx.fragment.app.DialogFragment;
//import android.support.v7.app.AlertDialog;
import androidx.appcompat.app.AlertDialog;
import android.text.util.Linkify;
import android.view.LayoutInflater;
import android.view.View;
@ -14,6 +20,8 @@ import android.widget.TextView;
import net.i2p.android.router.R;
import net.i2p.android.router.util.I2Patterns;
import java.util.List;
public class FirstStartDialog extends DialogFragment {
@NonNull
@Override
@ -23,8 +31,20 @@ public class FirstStartDialog extends DialogFragment {
TextView tv = (TextView)view.findViewById(R.id.url_faq);
Linkify.addLinks(tv, I2Patterns.I2P_WEB_URL, "http://");
tv = (TextView)view.findViewById(R.id.url_irc_i2p);
Linkify.addLinks(tv, I2Patterns.IRC_URL, "irc://");
// Find all installed browsers that listen for "irc://"
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("irc://127.0.0.1:6668/i2p"));
final PackageManager pm = getActivity().getPackageManager();
List<ResolveInfo> installedIrcClients = pm.queryIntentActivities(intent, 0);
// Only linkify "irc://" if we have an app that can handle them.
// Otherwise, the app crashes with an un-catchable ActivityNotFoundException
// if the user clicks one of them.
if (installedIrcClients.size() > 0) {
tv = (TextView) view.findViewById(R.id.url_irc_i2p);
Linkify.addLinks(tv, I2Patterns.IRC_URL, "irc://");
}
AlertDialog.Builder b = new AlertDialog.Builder(getActivity());
b.setTitle(R.string.first_start_title)

View File

@ -4,9 +4,11 @@ import android.app.Dialog;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import androidx.annotation.NonNull;
//import android.support.v4.app.DialogFragment;
import androidx.fragment.app.DialogFragment;
//import android.support.v7.app.AlertDialog;
import androidx.appcompat.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;

View File

@ -3,9 +3,11 @@ package net.i2p.android.router.dialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import androidx.annotation.NonNull;
//import android.support.v4.app.DialogFragment;
import androidx.fragment.app.DialogFragment;
//import android.support.v7.app.AlertDialog;
import androidx.appcompat.app.AlertDialog;
import net.i2p.android.I2PActivityBase;
import net.i2p.android.router.MainFragment;

View File

@ -3,7 +3,8 @@ package net.i2p.android.router.log;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
//import android.support.v7.widget.Toolbar;
import androidx.appcompat.widget.Toolbar;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;

View File

@ -1,7 +1,8 @@
package net.i2p.android.router.log;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
//import android.support.v7.widget.Toolbar;
import androidx.appcompat.widget.Toolbar;
import net.i2p.android.I2PActivityBase;
import net.i2p.android.router.R;

View File

@ -4,9 +4,12 @@ import android.app.Activity;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
//import android.support.v4.app.ListFragment;
import androidx.fragment.app.ListFragment;
//import android.support.v4.app.LoaderManager;
import androidx.loader.app.LoaderManager;
//import android.support.v4.content.Loader;
import androidx.loader.content.Loader;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;

View File

@ -1,7 +1,8 @@
package net.i2p.android.router.log;
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
//import android.support.v4.content.AsyncTaskLoader;
import androidx.loader.content.AsyncTaskLoader;
import net.i2p.I2PAppContext;

View File

@ -2,8 +2,10 @@ package net.i2p.android.router.netdb;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.widget.Toolbar;
//import android.support.v4.app.Fragment;
import androidx.fragment.app.Fragment;
//import android.support.v7.widget.Toolbar;
import androidx.appcompat.widget.Toolbar;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;

View File

@ -2,7 +2,8 @@ package net.i2p.android.router.netdb;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
//import android.support.v7.widget.Toolbar;
import androidx.appcompat.widget.Toolbar;
import net.i2p.android.I2PActivityBase;
import net.i2p.android.router.R;

View File

@ -154,6 +154,8 @@ public class NetDbDetailFragment extends I2PFragmentBase {
String val = (String)e.getValue();
addTableRow(table, DataHelper.stripHTML(key), DataHelper.stripHTML(val));
}
// spacer
addTableRow(table, "", "");
addresses.addView(table);
}
@ -230,9 +232,11 @@ public class NetDbDetailFragment extends I2PFragmentBase {
TextView tl1, tl2;
row = new TableRow(getActivity());
// left top right bottom
row.setPadding(10, 0, 0, 0);
tl1 = new TextView(getActivity());
tl1.setPadding(0, 0, 20, 0);
tl2 = new TextView(getActivity());
tl1.setText(key);

View File

@ -1,10 +1,12 @@
package net.i2p.android.router.netdb;
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
//import android.support.v4.content.AsyncTaskLoader;
import androidx.loader.content.AsyncTaskLoader;
import net.i2p.android.router.util.Util;
import net.i2p.data.Destination;
import net.i2p.data.Hash;
import net.i2p.data.LeaseSet;
import net.i2p.data.router.RouterInfo;
import net.i2p.router.RouterContext;
@ -24,14 +26,27 @@ public class NetDbEntryLoader extends AsyncTaskLoader<List<NetDbEntry>> {
mRouters = routers;
}
/** put us on top */
private static class RouterInfoComparator implements Comparator<RouterInfo> {
private final Hash _us;
public RouterInfoComparator(Hash us) {
_us = us;
}
public int compare(RouterInfo l, RouterInfo r) {
return l.getIdentity().getHash().toBase64().compareTo(r.getIdentity().getHash().toBase64());
Hash lh = l.getIdentity().getHash();
Hash rh = r.getIdentity().getHash();
if (lh.equals(_us))
return -1;
if (rh.equals(_us))
return 1;
return lh.toBase64().compareTo(rh.toBase64());
}
}
private class LeaseSetComparator implements Comparator<LeaseSet> {
private RouterContext mRContext;
private final RouterContext mRContext;
public LeaseSetComparator(RouterContext rContext) {
super();
@ -53,9 +68,9 @@ public class NetDbEntryLoader extends AsyncTaskLoader<List<NetDbEntry>> {
public List<NetDbEntry> loadInBackground() {
List<NetDbEntry> ret = new ArrayList<>();
RouterContext routerContext = Util.getRouterContext();
if (routerContext != null && routerContext.netDb().isInitialized()) {
if (routerContext != null && routerContext.netDb().isInitialized() && routerContext.routerHash() != null) {
if (mRouters) {
Set<RouterInfo> routers = new TreeSet<>(new RouterInfoComparator());
Set<RouterInfo> routers = new TreeSet<>(new RouterInfoComparator(routerContext.routerHash()));
routers.addAll(routerContext.netDb().getRouters());
for (RouterInfo ri : routers) {
NetDbEntry entry = NetDbEntry.fromRouterInfo(routerContext, ri);

View File

@ -2,9 +2,12 @@ package net.i2p.android.router.netdb;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
//import android.support.v4.app.ListFragment;
import androidx.fragment.app.ListFragment;
//import android.support.v4.app.LoaderManager;
import androidx.loader.app.LoaderManager;
//import android.support.v4.content.Loader;
import androidx.loader.content.Loader;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;

View File

@ -1,7 +1,8 @@
package net.i2p.android.router.netdb;
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
//import android.support.v4.content.AsyncTaskLoader;
import androidx.loader.content.AsyncTaskLoader;
import net.i2p.android.router.R;
import net.i2p.data.Hash;
@ -17,7 +18,7 @@ import java.util.Set;
import java.util.TreeSet;
public class NetDbStatsLoader extends AsyncTaskLoader<List<ObjectCounter<String>>> {
private RouterContext mRContext;
private final RouterContext mRContext;
private List<ObjectCounter<String>> mData;
public NetDbStatsLoader(Context context, RouterContext rContext) {
@ -60,7 +61,7 @@ public class NetDbStatsLoader extends AsyncTaskLoader<List<ObjectCounter<String>
}
ret.add(versions);
ret.add(countries);
//ret.add(countries);
ret.add(transports);
return ret;
@ -95,7 +96,7 @@ public class NetDbStatsLoader extends AsyncTaskLoader<List<ObjectCounter<String>
int rv = 0;
for (RouterAddress addr : info.getAddresses()) {
String style = addr.getTransportStyle();
if (style.equals("NTCP")) {
if (style.equals("NTCP") || style.equals("NTCP2")) {
rv |= NTCP;
} else if (style.equals("SSU")) {
if (addr.getOption("iport0") != null)
@ -108,7 +109,12 @@ public class NetDbStatsLoader extends AsyncTaskLoader<List<ObjectCounter<String>
rv |= IPV6;
}
return getContext().getString(TNAMES[rv]);
int tname = TNAMES[rv];
// remap cases with no string to "Hidden or starting up"
// so we don't crash NotFoundException
if (tname == 0)
tname = TNAMES[0];
return getContext().getString(tname);
}
@Override

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