Compare commits
242 Commits
android-cl
...
android-0.
Author | SHA1 | Date | |
---|---|---|---|
6b585822f1 | |||
c81c57daa0 | |||
f28be9cb02 | |||
6582f67ed5 | |||
d138c482d9 | |||
fc2d962cad | |||
c541ae0347 | |||
d6e79ed0a7 | |||
7ec20fe60c | |||
f3464c5095 | |||
32512fecbc | |||
1d7fcd47ef | |||
f156c591ad | |||
fb6ca0d61e | |||
a4662984a7 | |||
6cccf1fb23 | |||
39f32acd5b | |||
3d60d10f8e | |||
8cbc11dff0 | |||
858f007e21 | |||
02f830e472 | |||
d32ef0bb9b | |||
6ae103373e | |||
b2a82b9809 | |||
73b6898d76 | |||
be9336228c | |||
62546a779a | |||
b4afc3b4b2 | |||
4415a5fc25 | |||
bddb02f6ba | |||
f2c3f30224 | |||
90ff16009c | |||
4cd97536c5 | |||
4f2a5fdc1f | |||
c35d13270a | |||
6c9bb31da7 | |||
d5f6be4428 | |||
77c6e8702d | |||
c4fc10f552 | |||
4b0f8eb571 | |||
f94d2c57db | |||
fc2d6f5f09 | |||
c6ab6f64e9 | |||
5315771118 | |||
39b8bff6a2 | |||
c2a7c8ebde | |||
32a8c71bf1 | |||
f35257fc65 | |||
3ab1fa7c97 | |||
551910abce | |||
170f8afafd | |||
20c08b1929 | |||
e943c31ef9 | |||
601b979d4e | |||
973808b392 | |||
25811b742f | |||
2d57d7cad7 | |||
687a62656b | |||
5a7c33d9b9 | |||
e66c98fbfe | |||
2897bfcb77 | |||
b8aca7badc | |||
edb2506083 | |||
eceb7f14a4 | |||
b165f41266 | |||
9dd15e550d | |||
e94984901d | |||
55d682fdfc | |||
c231a9f851 | |||
bbde471e99 | |||
51c5409f12 | |||
1ff96d5530 | |||
5e099d0e3b | |||
c27e8ff513 | |||
632ed5b4b3 | |||
407b5c1441 | |||
fd2050bc1f | |||
c615497a9e | |||
e48b2f1dff | |||
44cd0a6d55 | |||
196b6ffbbc | |||
6cf01e3db0 | |||
204f2d8adc | |||
93853bd6b6 | |||
4095b48a82 | |||
3ab1a68b94 | |||
7675d78d0d | |||
1b2fa9bda6 | |||
8819bbfa44 | |||
2e6ff0ac07 | |||
8d9a532424 | |||
6f5e3e2386 | |||
fb63f1eee7 | |||
8c65812fa0 | |||
adfc6415b3 | |||
bb2a1c7c62 | |||
3d63269286 | |||
6dde8d2a88 | |||
d0c6bcff7d | |||
b938bc9698 | |||
2cb877d61b | |||
77368e370b | |||
2e6589ea74 | |||
90367b0f9c | |||
97a350c482 | |||
57d7708ae6 | |||
913d39a9a2 | |||
fa83742386 | |||
94539b8ebb | |||
bccbe30074 | |||
4e954eef50 | |||
6eeafb64f5 | |||
d7184e9c99 | |||
be8016c02c | |||
11d4e05518 | |||
fdccadda04 | |||
ccb1c73cf3 | |||
f3168f0dc2 | |||
b4210cfb33 | |||
6e0a66292d | |||
71856397de | |||
568cdeca36 | |||
4e64fb3095 | |||
f33f26987b | |||
99fa7fbec6 | |||
315931a032 | |||
8c63fb0ce2 | |||
31b7e18aad | |||
b51ca8b27c | |||
13af3d7869 | |||
c91de6f7ab | |||
d961b5f8b1 | |||
7d8141f62b | |||
6ef5d793f4 | |||
c5c97366b8 | |||
124692db8e | |||
72461cb678 | |||
6f84f3ce06 | |||
cbe21d94c9 | |||
9c8c462089 | |||
cc7c67c494 | |||
0e14ed20e6 | |||
d620535246 | |||
350d8a1363 | |||
23e80ec72b | |||
fd052f1b38 | |||
865f5d271a | |||
d1293f5949 | |||
2a354288bf | |||
f20e82a25e | |||
4a7899ce59 | |||
2adc368307 | |||
2bb57d2c4d | |||
e649ac448b | |||
337c93bb6c | |||
f5624cf259 | |||
6afd9b950d | |||
94c46d4a43 | |||
dcf072905c | |||
c67aeddb3b | |||
f125079aa5 | |||
0ddd9e5f4c | |||
0074cf5a19 | |||
19082db9a6 | |||
109aeb7796 | |||
3e06a11017 | |||
dafd438982 | |||
063fd63fe2 | |||
5e27a93df9 | |||
c428195127 | |||
1fba23a144 | |||
3cc1e2b4bb | |||
77b05fa6e0 | |||
3553065ce2 | |||
167c06225f | |||
3420c04735 | |||
973282ab59 | |||
7c04a96639 | |||
b2db1f4a6c | |||
e3e0960ed8 | |||
78ad153c47 | |||
fa6ba6bdce | |||
8f302e6eeb | |||
93d2677b34 | |||
1d02535158 | |||
955e40b0fc | |||
b4367843cd | |||
76fd69544d | |||
99b582a005 | |||
8e0e5ed5c4 | |||
234bc6e5a0 | |||
b0131843ae | |||
c124cafe3c | |||
24f0d72aae | |||
8780d69be5 | |||
1853f5bcfd | |||
3f6caf18b4 | |||
97aa6e64bc | |||
f1bfd7d4aa | |||
70ede5a370 | |||
d1d06840fd | |||
5f62418513 | |||
5e6dab9bf1 | |||
ef65a94e4c | |||
c2e00ecf26 | |||
dac63d4401 | |||
8ed2ce3e3d | |||
c95f140fb4 | |||
4e21cc890f | |||
cf3594962d | |||
9fa38c2840 | |||
2da8bbf214 | |||
c984206f8e | |||
4a8b87d50c | |||
a5a08a7282 | |||
80f8469154 | |||
490137adc3 | |||
6556156486 | |||
0095777a63 | |||
d8fc177b2d | |||
fcd5b2a503 | |||
0a5312e81b | |||
7b67cf1581 | |||
2975b811d0 | |||
a213ac51cd | |||
db4a817ded | |||
3de78063f2 | |||
601d3c0ee3 | |||
3bcbd8c876 | |||
e33be52b97 | |||
f513580525 | |||
ca2fde0b57 | |||
b442552146 | |||
9aee319607 | |||
d112e1a415 | |||
9cd6ab51ac | |||
6477f7d43f | |||
65d1e6e089 | |||
6aff527456 | |||
f32b896bb1 | |||
58624ebf9d | |||
db8355c477 |
@ -42,6 +42,7 @@ build
|
|||||||
^app/src/main/res/drawable/i2plogo.png
|
^app/src/main/res/drawable/i2plogo.png
|
||||||
^app/src/main/res/raw/blocklist_txt
|
^app/src/main/res/raw/blocklist_txt
|
||||||
^app/src/main/res/raw/hosts_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/license_
|
||||||
^app/src/main/res/raw/certificates_zip
|
^app/src/main/res/raw/certificates_zip
|
||||||
^app/src/main/assets/themes/console/images
|
^app/src/main/assets/themes/console/images
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[main]
|
[main]
|
||||||
host = https://www.transifex.com
|
host = https://www.transifex.com
|
||||||
lang_map = pt_BR: pt-rBR, ru_RU: ru, sv_SE: sv, tr_TR: tr, uk_UA: uk, zh_CN: zh
|
lang_map = he: iw, id: in, pt_BR: pt-rBR, ru_RU: ru, sv_SE: sv, tr_TR: tr, uk_UA: uk, yi: ji, zh_CN: zh
|
||||||
|
|
||||||
[I2P.android]
|
[I2P.android]
|
||||||
file_filter = app/src/main/res/values-<lang>/strings.xml
|
file_filter = app/src/main/res/values-<lang>/strings.xml
|
||||||
|
16
CHANGELOG
16
CHANGELOG
@ -1,6 +1,20 @@
|
|||||||
0.9.19
|
0.9.20
|
||||||
|
* 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
|
||||||
|
* Fixed crash when opening advanced settings
|
||||||
|
|
||||||
|
0.9.19 / 2015-04-13 / 3cfb748946a5876dc06d5f81d811b142a88846f7
|
||||||
* Made internal state handling more stable
|
* Made internal state handling more stable
|
||||||
* Added graceful shutdown support
|
* Added graceful shutdown support
|
||||||
|
* Updated libjbigi to use GMP 6.0.0
|
||||||
|
* New libjbigi binary for armeabi-v7a to speed up newer devices
|
||||||
* Improved logging
|
* Improved logging
|
||||||
* Bug fixes and translation updates
|
* Bug fixes and translation updates
|
||||||
|
|
||||||
|
21
TODO
21
TODO
@ -1,23 +1,33 @@
|
|||||||
# Fixes
|
# Fixes
|
||||||
|
|
||||||
- Better twopane column widths
|
|
||||||
<zzz> on the i2ptunnel and addressbook pages on the tablet, the columns are too skinny, they aren't as wide as the tab
|
|
||||||
<zzz> only a few addressbook entries wrap but on i2ptunnel everything is wrapped and most of the screen is empty
|
|
||||||
- Create tunnel wizard
|
- Create tunnel wizard
|
||||||
<zzz> hmm would be nice if they could be shared-client or have an option
|
<zzz> hmm would be nice if they could be shared-client or have an option
|
||||||
<zzz> was setting up email tunnels
|
<zzz> was setting up email tunnels
|
||||||
- Browser
|
- Browser
|
||||||
<zzzccc> Bug report: i2p browser treats 302 as an error
|
<zzzccc> Bug report: i2p browser treats 302 as an error
|
||||||
<zzzccc> Bug 2: rotate screen in i2p browser seems to go back one page
|
<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?
|
||||||
|
|
||||||
# Short-term
|
# Short-term
|
||||||
|
|
||||||
- Disable uPnP when on cell networks
|
- 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
|
<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
|
||||||
- I2PTunnel
|
- I2PTunnel
|
||||||
|
- Improve tunnel list status indicators
|
||||||
- Show all messages somewhere
|
- Show all messages somewhere
|
||||||
- Improve detail page, expose advanced settings
|
- Icons/header images for tunnel types on details page
|
||||||
- Add edit page
|
|
||||||
- Progress feedback for addressbook subscriptions reload
|
- Progress feedback for addressbook subscriptions reload
|
||||||
- Display release notes directly on new router version
|
- Display release notes directly on new router version
|
||||||
- Fill out help pages
|
- Fill out help pages
|
||||||
@ -38,7 +48,6 @@
|
|||||||
- Also look at connection type: Connectivity.isConnectionFast()
|
- Also look at connection type: Connectivity.isConnectionFast()
|
||||||
- Expose log level overrides
|
- Expose log level overrides
|
||||||
- Improve graphs
|
- Improve graphs
|
||||||
- Show time on bottom axis
|
|
||||||
- Show fixed x range, not only available data
|
- Show fixed x range, not only available data
|
||||||
- Think about pan/zoom
|
- Think about pan/zoom
|
||||||
- How to persist data across restarts?
|
- How to persist data across restarts?
|
||||||
|
@ -2,13 +2,13 @@ apply plugin: 'com.android.application'
|
|||||||
apply plugin: 'witness'
|
apply plugin: 'witness'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION)
|
compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION as String)
|
||||||
buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION
|
buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION as String
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
versionCode 4745226
|
versionCode 4745230
|
||||||
versionName '0.9.18'
|
versionName '0.9.20'
|
||||||
minSdkVersion 9
|
minSdkVersion 9
|
||||||
targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION)
|
targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION as String)
|
||||||
|
|
||||||
// For Espresso
|
// For Espresso
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
@ -56,17 +56,21 @@ dependencies {
|
|||||||
compile project(':client')
|
compile project(':client')
|
||||||
|
|
||||||
// Android Support Repository dependencies
|
// Android Support Repository dependencies
|
||||||
compile 'com.android.support:support-v4:21.0.3'
|
compile 'com.android.support:support-v4:22.2.0'
|
||||||
compile 'com.android.support:appcompat-v7:21.0.3'
|
compile 'com.android.support:appcompat-v7:22.2.0'
|
||||||
compile 'com.android.support:recyclerview-v7:21.0.3'
|
compile 'com.android.support:recyclerview-v7:22.2.0'
|
||||||
|
|
||||||
// Remote dependencies
|
// Remote dependencies
|
||||||
compile 'net.i2p.android.ext:floatingactionbutton:1.8.0'
|
compile 'net.i2p.android.ext:floatingactionbutton:1.9.0'
|
||||||
compile files('libs/androidplot-core-0.6.1.jar')
|
compile files('libs/androidplot-core-0.6.1.jar')
|
||||||
|
|
||||||
compile ('com.android.support:support-v4-preferencefragment:1.0.0@aar'){
|
compile ('com.android.support:support-v4-preferencefragment:1.0.0@aar'){
|
||||||
exclude module: 'support-v4'
|
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'
|
||||||
|
}
|
||||||
|
|
||||||
// Testing-only dependencies
|
// Testing-only dependencies
|
||||||
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.0'
|
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.0'
|
||||||
@ -75,11 +79,14 @@ dependencies {
|
|||||||
|
|
||||||
dependencyVerification {
|
dependencyVerification {
|
||||||
verify = [
|
verify = [
|
||||||
'com.android.support:support-v4:703572d3015a088cc5604b7e38885af3d307c829d0c5ceaf8654ff41c71cd160',
|
'com.android.support:support-v4:7bb6e40a18774aa2595e4d8f9fe0ae14e61670f71a1279272fb0b79b8be71180',
|
||||||
'com.android.support:appcompat-v7:5dbeb5316d0a6027d646ae552804c3baa5e3bd53f7f33db50904d51505c8a0e5',
|
'com.android.support:appcompat-v7:2d5867698410b41f75140c91d6c1e58da74ae0f97baf6e0bdd1f7cc1017ceb2c',
|
||||||
'com.android.support:recyclerview-v7:e525ad3f33c84bb12b73d2dc975b55364a53f0f2d0697e043efba59ba73e22d2',
|
'com.android.support:recyclerview-v7:3a8da14585fa1c81f06e7cef4d93a7641f0323d8f984ff9a7bd7a6e416b46888',
|
||||||
'net.i2p.android.ext:floatingactionbutton:a20d1f0cae15f8965b81486ba31245937968ae6ee5fa6e8a3ea21d7f6c6243ab',
|
'net.i2p.android.ext:floatingactionbutton:b41eae5fe6be599e3fade00273521b0914f2e199d5f04c50fa34cfe935347f76',
|
||||||
'com.android.support:support-v4-preferencefragment:5470f5872514a6226fa1fc6f4e000991f38805691c534cf0bd2778911fc773ad',
|
'com.android.support:support-v4-preferencefragment:5470f5872514a6226fa1fc6f4e000991f38805691c534cf0bd2778911fc773ad',
|
||||||
|
'com.pnikosis:materialish-progress:d71d80e00717a096784482aee21001a9d299fec3833e4ebd87739ed36cf77c54',
|
||||||
|
'com.eowise:recyclerview-stickyheaders:7b236da49b33b840e9ba6e7e4182218d1a2d9047236fdbc3ca947352f9b0883b',
|
||||||
|
'com.mcxiaoke.viewpagerindicator:library:1e8aad664137f68abdfee94889f6da3dc98be652a235176a403965a07a25de62',
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,6 +121,32 @@ task copyI2PResources(type: Copy) {
|
|||||||
into('raw') {
|
into('raw') {
|
||||||
from(i2pbase + '/installer/resources/blocklist.txt') { rename { 'blocklist_txt' } }
|
from(i2pbase + '/installer/resources/blocklist.txt') { rename { 'blocklist_txt' } }
|
||||||
from(i2pbase + '/installer/resources/hosts.txt') { rename { 'hosts_txt' } }
|
from(i2pbase + '/installer/resources/hosts.txt') { rename { 'hosts_txt' } }
|
||||||
|
from(i2pbase + '/installer/resources/proxy') {
|
||||||
|
include { elem ->
|
||||||
|
elem.name.endsWith('.ht')
|
||||||
|
}
|
||||||
|
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('../LICENSE.txt') { rename { 'license_app_txt' } }
|
||||||
from('../licenses/LICENSE-Apache2.0.txt') { rename { 'license_apache20_txt' } }
|
from('../licenses/LICENSE-Apache2.0.txt') { rename { 'license_apache20_txt' } }
|
||||||
from(i2pbase + '/licenses') {
|
from(i2pbase + '/licenses') {
|
||||||
@ -134,9 +167,7 @@ task copyI2PResources(type: Copy) {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
rename { String name ->
|
rename { String name ->
|
||||||
String part = name.substring(8, name.lastIndexOf('.txt'))
|
name.toLowerCase(Locale.US).replace('-', '_').replace('.', '_')
|
||||||
String.format('license_%s_txt',
|
|
||||||
part.toLowerCase(Locale.US).replace('.', '_'))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
from certificatesZip
|
from certificatesZip
|
||||||
@ -168,6 +199,7 @@ task cleanI2PResources(type: Delete) {
|
|||||||
delete fileTree('src/main/res/raw') {
|
delete fileTree('src/main/res/raw') {
|
||||||
include 'blocklist_txt'
|
include 'blocklist_txt'
|
||||||
include 'hosts_txt'
|
include 'hosts_txt'
|
||||||
|
include '*_ht'
|
||||||
include 'license_*'
|
include 'license_*'
|
||||||
include 'certificates_zip'
|
include 'certificates_zip'
|
||||||
}
|
}
|
||||||
|
108
app/src/androidTest/java/net/i2p/android/I2PActivityTest.java
Normal file
108
app/src/androidTest/java/net/i2p/android/I2PActivityTest.java
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
package net.i2p.android;
|
||||||
|
|
||||||
|
import android.test.ActivityInstrumentationTestCase2;
|
||||||
|
|
||||||
|
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 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMainTabs() {
|
||||||
|
onView(withId(R.id.router_onoff_button)).check(matches(isDisplayed()));
|
||||||
|
|
||||||
|
// Press "Tunnels" tab
|
||||||
|
onView(allOf(withText(R.string.label_tunnels),
|
||||||
|
not(isDescendantOfA(withId(R.id.main_scrollview))))).perform(click());
|
||||||
|
onView(withId(R.id.router_onoff_button)).check(matches(not(isDisplayed())));
|
||||||
|
onView(withText(R.string.label_i2ptunnel_client)).check(matches(isDisplayed()));
|
||||||
|
|
||||||
|
// Press "Addresses" tab
|
||||||
|
onView(withText(R.string.label_addresses)).perform(click());
|
||||||
|
onView(withText(R.string.label_i2ptunnel_client)).check(matches(not(isDisplayed())));
|
||||||
|
onView(withText(R.string.label_router)).check(matches(isDisplayed()));
|
||||||
|
|
||||||
|
// Press "Console" tab
|
||||||
|
onView(withText(R.string.label_console)).perform(click());
|
||||||
|
// Addressbook fragment should have been destroyed
|
||||||
|
onView(withText(R.string.label_router)).check(doesNotExist());
|
||||||
|
onView(withId(R.id.router_onoff_button)).check(matches(isDisplayed()));
|
||||||
|
}
|
||||||
|
|
||||||
|
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(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());
|
||||||
|
// TODO: test tunnels ViewPager
|
||||||
|
onView(allOf(withId(R.id.pager), 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
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSettingsNavigation() {
|
||||||
|
// Open settings menu
|
||||||
|
openActionBarOverflowOrOptionsMenu(getActivity());
|
||||||
|
onView(withText(R.string.menu_settings)).perform(click());
|
||||||
|
|
||||||
|
// Open bandwidth page
|
||||||
|
onView(withText(R.string.settings_label_bandwidth_net)).perform(click());
|
||||||
|
onView(withText(R.string.settings_label_startOnBoot)).check(matches(isDisplayed()));
|
||||||
|
pressBack();
|
||||||
|
|
||||||
|
// Open graphs page
|
||||||
|
onView(withText(R.string.label_graphs)).perform(click());
|
||||||
|
onView(withText(R.string.router_not_running)).check(matches(isDisplayed()));
|
||||||
|
pressBack();
|
||||||
|
|
||||||
|
// Open logging page
|
||||||
|
onView(withText(R.string.settings_label_logging)).perform(click());
|
||||||
|
onView(withText(R.string.settings_label_default_log_level)).check(matches(isDisplayed()));
|
||||||
|
pressBack();
|
||||||
|
|
||||||
|
// Open addressbook page
|
||||||
|
onView(withText(R.string.label_addressbook)).perform(click());
|
||||||
|
onView(withText("Subscriptions")).check(matches(isDisplayed()));
|
||||||
|
closeSoftKeyboard();
|
||||||
|
pressBack();
|
||||||
|
|
||||||
|
// Open graphs page
|
||||||
|
onView(withText(R.string.settings_label_advanced)).perform(click());
|
||||||
|
onView(withText(R.string.settings_label_transports)).check(matches(isDisplayed()));
|
||||||
|
pressBack();
|
||||||
|
|
||||||
|
// Check back exits settings
|
||||||
|
onView(withText(R.string.settings_label_advanced)).check(matches(isDisplayed()));
|
||||||
|
pressBack();
|
||||||
|
onView(withText(R.string.settings_label_advanced)).check(doesNotExist());
|
||||||
|
}
|
||||||
|
}
|
@ -1,37 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<header
|
|
||||||
android:fragment="net.i2p.android.router.SettingsActivity$SettingsFragment"
|
|
||||||
android:title="@string/settings_label_bandwidth_net">
|
|
||||||
<extra
|
|
||||||
android:name="settings"
|
|
||||||
android:value="net" />
|
|
||||||
</header>
|
|
||||||
<header
|
|
||||||
android:fragment="net.i2p.android.router.SettingsActivity$SettingsFragment"
|
|
||||||
android:title="@string/label_graphs">
|
|
||||||
<extra
|
|
||||||
android:name="settings"
|
|
||||||
android:value="graphs" />
|
|
||||||
</header>
|
|
||||||
<header
|
|
||||||
android:fragment="net.i2p.android.router.SettingsActivity$SettingsFragment"
|
|
||||||
android:title="@string/settings_label_logging">
|
|
||||||
<extra
|
|
||||||
android:name="settings"
|
|
||||||
android:value="logging" />
|
|
||||||
</header>
|
|
||||||
<header
|
|
||||||
android:title="@string/label_addressbook">
|
|
||||||
<intent
|
|
||||||
android:targetPackage="net.i2p.android.debug"
|
|
||||||
android:targetClass="net.i2p.android.router.addressbook.AddressbookSettingsActivity" />
|
|
||||||
</header>
|
|
||||||
<header
|
|
||||||
android:fragment="net.i2p.android.router.SettingsActivity$SettingsFragment"
|
|
||||||
android:title="@string/settings_label_advanced">
|
|
||||||
<extra
|
|
||||||
android:name="settings"
|
|
||||||
android:value="advanced" />
|
|
||||||
</header>
|
|
||||||
</preference-headers>
|
|
@ -1,32 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
|
|
||||||
<Preference android:title="@string/settings_label_bandwidth_net">
|
|
||||||
<intent
|
|
||||||
android:targetPackage="net.i2p.android.debug"
|
|
||||||
android:targetClass="net.i2p.android.router.SettingsActivity"
|
|
||||||
android:action="net.i2p.android.router.PREFS_NET" />
|
|
||||||
</Preference>
|
|
||||||
<Preference android:title="@string/label_graphs">
|
|
||||||
<intent
|
|
||||||
android:targetPackage="net.i2p.android.debug"
|
|
||||||
android:targetClass="net.i2p.android.router.SettingsActivity"
|
|
||||||
android:action="net.i2p.android.router.PREFS_GRAPHS" />
|
|
||||||
</Preference>
|
|
||||||
<Preference android:title="@string/settings_label_logging">
|
|
||||||
<intent
|
|
||||||
android:targetPackage="net.i2p.android.debug"
|
|
||||||
android:targetClass="net.i2p.android.router.SettingsActivity"
|
|
||||||
android:action="net.i2p.android.router.PREFS_LOGGING" />
|
|
||||||
</Preference>
|
|
||||||
<Preference android:title="@string/label_addressbook">
|
|
||||||
<intent
|
|
||||||
android:targetPackage="net.i2p.android.debug"
|
|
||||||
android:targetClass="net.i2p.android.router.addressbook.AddressbookSettingsActivity" />
|
|
||||||
</Preference>
|
|
||||||
<Preference android:title="@string/settings_label_advanced">
|
|
||||||
<intent
|
|
||||||
android:targetPackage="net.i2p.android.debug"
|
|
||||||
android:targetClass="net.i2p.android.router.SettingsActivity"
|
|
||||||
android:action="net.i2p.android.router.PREFS_ADVANCED" />
|
|
||||||
</Preference>
|
|
||||||
</PreferenceScreen>
|
|
@ -29,10 +29,11 @@
|
|||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name="net.i2p.android.I2PActivity"
|
||||||
android:icon="@drawable/ic_launcher_itoopie"
|
android:icon="@drawable/ic_launcher_itoopie"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:launchMode="singleTop">
|
android:launchMode="singleTop">
|
||||||
|
<!-- Console filters -->
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
@ -41,31 +42,43 @@
|
|||||||
<action android:name="net.i2p.android.router.START_I2P" />
|
<action android:name="net.i2p.android.router.START_I2P" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
|
<!-- Addressbook filters -->
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.SEARCH" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<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" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".NewsActivity"
|
android:name=".NewsActivity"
|
||||||
android:configChanges="orientation|keyboardHidden"
|
android:configChanges="orientation|keyboardHidden"
|
||||||
android:label="@string/label_news"
|
android:label="@string/label_news"
|
||||||
android:parentActivityName=".MainActivity">
|
android:parentActivityName="net.i2p.android.I2PActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="net.i2p.android.router.MainActivity" />
|
android:value="net.i2p.android.I2PActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name="net.i2p.android.help.HelpActivity"
|
android:name="net.i2p.android.help.HelpActivity"
|
||||||
android:label="@string/menu_help"
|
android:label="@string/menu_help"
|
||||||
android:parentActivityName=".MainActivity">
|
android:parentActivityName="net.i2p.android.I2PActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="net.i2p.android.router.MainActivity" />
|
android:value="net.i2p.android.I2PActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name="net.i2p.android.help.BrowserConfigActivity"
|
android:name="net.i2p.android.help.BrowserConfigActivity"
|
||||||
android:label="@string/label_browser_configuration"
|
android:label="@string/label_browser_configuration"
|
||||||
android:parentActivityName=".MainActivity">
|
android:parentActivityName="net.i2p.android.I2PActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="net.i2p.android.router.MainActivity" />
|
android:value="net.i2p.android.I2PActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".LicenseActivity"
|
android:name=".LicenseActivity"
|
||||||
@ -95,63 +108,36 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".SettingsActivity"
|
android:name=".SettingsActivity"
|
||||||
android:label="@string/menu_settings"
|
android:label="@string/menu_settings"
|
||||||
android:parentActivityName=".MainActivity">
|
android:parentActivityName="net.i2p.android.I2PActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="net.i2p.android.router.MainActivity" />
|
android:value="net.i2p.android.I2PActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".addressbook.AddressbookSettingsActivity"
|
android:name=".addressbook.AddressbookSettingsActivity"
|
||||||
android:label="Addressbook Settings"
|
android:label="@string/label_addressbook"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:parentActivityName=".addressbook.AddressbookActivity">
|
android:parentActivityName="net.i2p.android.I2PActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="net.i2p.android.router.addressbook.AddressbookActivity" />
|
android:value="net.i2p.android.I2PActivity" />
|
||||||
</activity>
|
|
||||||
<activity
|
|
||||||
android:name=".addressbook.AddressbookActivity"
|
|
||||||
android:label="@string/label_addressbook"
|
|
||||||
android:launchMode="singleTop">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.SEARCH" />
|
|
||||||
</intent-filter>
|
|
||||||
<intent-filter>
|
|
||||||
<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" />
|
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".addressbook.AddressbookAddWizardActivity"
|
android:name=".addressbook.AddressbookAddWizardActivity"
|
||||||
android:label="Add new Destination"
|
android:parentActivityName="net.i2p.android.I2PActivity">
|
||||||
android:parentActivityName=".addressbook.AddressbookActivity">
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="net.i2p.android.router.addressbook.AddressbookActivity" />
|
android:value="net.i2p.android.I2PActivity" />
|
||||||
</activity>
|
|
||||||
<activity
|
|
||||||
android:name="net.i2p.android.i2ptunnel.TunnelListActivity"
|
|
||||||
android:label="@string/label_i2ptunnel"
|
|
||||||
android:launchMode="singleTop"
|
|
||||||
android:parentActivityName=".MainActivity">
|
|
||||||
<meta-data
|
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
|
||||||
android:value="net.i2p.android.router.MainActivity" />
|
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name="net.i2p.android.i2ptunnel.TunnelDetailActivity"
|
android:name="net.i2p.android.i2ptunnel.TunnelDetailActivity"
|
||||||
android:label="I2PTunnel"
|
android:parentActivityName="net.i2p.android.I2PActivity">
|
||||||
android:parentActivityName="net.i2p.android.i2ptunnel.TunnelListActivity">
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="net.i2p.android.i2ptunnel.TunnelListActivity" />
|
android:value="net.i2p.android.I2PActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name="net.i2p.android.i2ptunnel.EditTunnelActivity"
|
android:name="net.i2p.android.i2ptunnel.preferences.EditTunnelActivity"
|
||||||
android:label="@string/edit_tunnel"
|
android:label="@string/edit_tunnel"
|
||||||
android:parentActivityName="net.i2p.android.i2ptunnel.TunnelDetailActivity">
|
android:parentActivityName="net.i2p.android.i2ptunnel.TunnelDetailActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
@ -160,23 +146,22 @@
|
|||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name="net.i2p.android.i2ptunnel.TunnelWizardActivity"
|
android:name="net.i2p.android.i2ptunnel.TunnelWizardActivity"
|
||||||
android:label="Tunnel Creation Wizard"
|
android:parentActivityName="net.i2p.android.I2PActivity">
|
||||||
android:parentActivityName="net.i2p.android.i2ptunnel.TunnelListActivity">
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="net.i2p.android.router.i2ptunnel.TunnelListActivity" />
|
android:value="net.i2p.android.I2PActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".log.LogActivity"
|
android:name=".log.LogActivity"
|
||||||
android:label="I2P Logs"
|
android:label="@string/label_logs"
|
||||||
android:parentActivityName=".MainActivity">
|
android:parentActivityName="net.i2p.android.I2PActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="net.i2p.android.router.MainActivity" />
|
android:value="net.i2p.android.I2PActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".log.LogDetailActivity"
|
android:name=".log.LogDetailActivity"
|
||||||
android:label="Log Entry"
|
android:label="@string/log_entry"
|
||||||
android:parentActivityName=".log.LogActivity">
|
android:parentActivityName=".log.LogActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
@ -184,29 +169,29 @@
|
|||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".stats.RateGraphActivity"
|
android:name=".stats.RateGraphActivity"
|
||||||
android:label="Rate Graph"
|
android:label="@string/label_graphs"
|
||||||
android:parentActivityName=".MainActivity">
|
android:parentActivityName="net.i2p.android.I2PActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="net.i2p.android.router.MainActivity" />
|
android:value="net.i2p.android.I2PActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".stats.PeersActivity"
|
android:name=".stats.PeersActivity"
|
||||||
android:configChanges="orientation|keyboardHidden"
|
android:configChanges="orientation|keyboardHidden"
|
||||||
android:label="I2P Peers and Transport Status"
|
android:label="@string/label_peers_status"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:parentActivityName=".MainActivity">
|
android:parentActivityName="net.i2p.android.I2PActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="net.i2p.android.router.MainActivity" />
|
android:value="net.i2p.android.I2PActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".netdb.NetDbActivity"
|
android:name=".netdb.NetDbActivity"
|
||||||
android:label="NetDB"
|
android:label="NetDB"
|
||||||
android:parentActivityName=".MainActivity">
|
android:parentActivityName="net.i2p.android.I2PActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="net.i2p.android.router.MainActivity" />
|
android:value="net.i2p.android.I2PActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".netdb.NetDbDetailActivity"
|
android:name=".netdb.NetDbDetailActivity"
|
||||||
|
@ -132,7 +132,7 @@ public class SeekBarPreference extends DialogPreference implements SeekBar.OnSee
|
|||||||
if (shouldPersist()) {
|
if (shouldPersist()) {
|
||||||
persistString(t);
|
persistString(t);
|
||||||
}
|
}
|
||||||
callChangeListener(Integer.valueOf(value));
|
callChangeListener(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onStartTrackingTouch(SeekBar seek) {
|
public void onStartTrackingTouch(SeekBar seek) {
|
||||||
|
358
app/src/main/java/net/i2p/android/I2PActivity.java
Normal file
358
app/src/main/java/net/i2p/android/I2PActivity.java
Normal file
@ -0,0 +1,358 @@
|
|||||||
|
package net.i2p.android;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
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.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
|
||||||
|
import net.i2p.android.help.HelpActivity;
|
||||||
|
import net.i2p.android.i2ptunnel.TunnelsContainer;
|
||||||
|
import net.i2p.android.router.ConsoleContainer;
|
||||||
|
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.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 net.i2p.android.widget.SlidingTabLayout;
|
||||||
|
import net.i2p.router.RouterContext;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main activity of the app. Contains a ViewPager that holds the three main
|
||||||
|
* views:
|
||||||
|
* <ul>
|
||||||
|
* <li>The console</li>
|
||||||
|
* <li>The addressbook</li>
|
||||||
|
* <li>The tunnel manager</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public class I2PActivity extends I2PActivityBase implements
|
||||||
|
MainFragment.RouterControlListener {
|
||||||
|
CustomViewPager mViewPager;
|
||||||
|
ViewPagerAdapter mViewPagerAdapter;
|
||||||
|
SlidingTabLayout mSlidingTabLayout;
|
||||||
|
|
||||||
|
private boolean mAutoStartFromIntent = false;
|
||||||
|
private boolean _keep = true;
|
||||||
|
private boolean _startPressed = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_viewpager);
|
||||||
|
|
||||||
|
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
|
||||||
|
mViewPager = (CustomViewPager) findViewById(R.id.pager);
|
||||||
|
mViewPagerAdapter = new ViewPagerAdapter(this, getSupportFragmentManager());
|
||||||
|
mViewPager.setAdapter(mViewPagerAdapter);
|
||||||
|
|
||||||
|
mSlidingTabLayout = (SlidingTabLayout) findViewById(R.id.sliding_tabs);
|
||||||
|
// Center the tabs in the layout
|
||||||
|
mSlidingTabLayout.setDistributeEvenly(true);
|
||||||
|
// Customize tab color
|
||||||
|
mSlidingTabLayout.setCustomTabColorizer(new SlidingTabLayout.TabColorizer() {
|
||||||
|
@Override
|
||||||
|
public int getIndicatorColor(int position) {
|
||||||
|
return getResources().getColor(R.color.accent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Give the SlidingTabLayout the ViewPager
|
||||||
|
mSlidingTabLayout.setViewPager(mViewPager);
|
||||||
|
|
||||||
|
_keep = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ViewPagerAdapter extends MemoryFragmentPagerAdapter {
|
||||||
|
private static final int NUM_ITEMS = 3;
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
|
||||||
|
public ViewPagerAdapter(Context context, FragmentManager fm) {
|
||||||
|
super(fm);
|
||||||
|
mContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return NUM_ITEMS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Fragment getItem(int position) {
|
||||||
|
switch (position) {
|
||||||
|
case 0:
|
||||||
|
return new ConsoleContainer();
|
||||||
|
case 1:
|
||||||
|
return new TunnelsContainer();
|
||||||
|
case 2:
|
||||||
|
return new AddressbookContainer();
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence getPageTitle(int position) {
|
||||||
|
switch (position) {
|
||||||
|
case 0:
|
||||||
|
return mContext.getString(R.string.label_console);
|
||||||
|
case 1:
|
||||||
|
return mContext.getString(R.string.label_tunnels);
|
||||||
|
case 2:
|
||||||
|
return mContext.getString(R.string.label_addresses);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostCreate(Bundle savedInstanceState) {
|
||||||
|
Util.d("Initializing...");
|
||||||
|
InitActivities init = new InitActivities(this);
|
||||||
|
init.debugStuff();
|
||||||
|
init.initialize();
|
||||||
|
super.onPostCreate(savedInstanceState);
|
||||||
|
handleIntents();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onNewIntent(Intent intent) {
|
||||||
|
super.onNewIntent(intent);
|
||||||
|
handleIntents();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleIntents() {
|
||||||
|
if (getIntent() == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Intent intent = getIntent();
|
||||||
|
String action = intent.getAction();
|
||||||
|
|
||||||
|
if (action == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (action.equals("net.i2p.android.router.START_I2P")) {
|
||||||
|
if (mViewPager.getCurrentItem() != 0)
|
||||||
|
mViewPager.setCurrentItem(0, false);
|
||||||
|
autoStart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void autoStart() {
|
||||||
|
if (canStart()) {
|
||||||
|
if (Connectivity.isConnected(this)) {
|
||||||
|
mAutoStartFromIntent = true;
|
||||||
|
onStartRouterClicked();
|
||||||
|
} else {
|
||||||
|
// Not connected to a network
|
||||||
|
// TODO: Notify user
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// TODO: Notify user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
|
||||||
|
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this);
|
||||||
|
IntentFilter filter = new IntentFilter();
|
||||||
|
filter.addAction(RouterService.LOCAL_BROADCAST_STATE_NOTIFICATION);
|
||||||
|
filter.addAction(RouterService.LOCAL_BROADCAST_STATE_CHANGED);
|
||||||
|
lbm.registerReceiver(onStateChange, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BroadcastReceiver onStateChange = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
State state = intent.getParcelableExtra(RouterService.LOCAL_BROADCAST_EXTRA_STATE);
|
||||||
|
|
||||||
|
if (_startPressed && Util.getRouterContext() != null)
|
||||||
|
_startPressed = false;
|
||||||
|
|
||||||
|
// Update menus, FAMs etc.
|
||||||
|
supportInvalidateOptionsMenu();
|
||||||
|
|
||||||
|
// Update main paging state
|
||||||
|
mViewPager.setPagingEnabled(!(Util.isStopping(state) || Util.isStopped(state)));
|
||||||
|
|
||||||
|
// If I2P was started by another app and is running, return to that app
|
||||||
|
if (state == State.RUNNING && mAutoStartFromIntent) {
|
||||||
|
I2PActivity.this.setResult(RESULT_OK);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
|
||||||
|
// Handle edge cases after shutting down router
|
||||||
|
mViewPager.updatePagingState();
|
||||||
|
|
||||||
|
LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(RouterService.LOCAL_BROADCAST_REQUEST_STATE));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
getMenuInflater().inflate(R.menu.activity_base_actions, menu);
|
||||||
|
return super.onCreateOptionsMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.menu_settings:
|
||||||
|
Intent intent = new Intent(this, SettingsActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case R.id.menu_help:
|
||||||
|
Intent hi = new Intent(this, HelpActivity.class);
|
||||||
|
switch (mViewPager.getCurrentItem()) {
|
||||||
|
case 1:
|
||||||
|
hi.putExtra(HelpActivity.CATEGORY, HelpActivity.CAT_I2PTUNNEL);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
hi.putExtra(HelpActivity.CATEGORY, HelpActivity.CAT_ADDRESSBOOK);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
startActivity(hi);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onBackPressed() {
|
||||||
|
super.onBackPressed();
|
||||||
|
|
||||||
|
RouterContext ctx = Util.getRouterContext();
|
||||||
|
// RouterService svc = _routerService; Which is better to use?!
|
||||||
|
_keep = Connectivity.isConnected(this) && (ctx != null || _startPressed);
|
||||||
|
Util.d("*********************************************************");
|
||||||
|
Util.d("Back pressed, Keep? " + _keep);
|
||||||
|
Util.d("*********************************************************");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
|
||||||
|
LocalBroadcastManager.getInstance(this).unregisterReceiver(onStateChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
if (!_keep) {
|
||||||
|
Thread t = new Thread(new KillMe());
|
||||||
|
t.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class KillMe implements Runnable {
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
Util.d("*********************************************************");
|
||||||
|
Util.d("KillMe started!");
|
||||||
|
Util.d("*********************************************************");
|
||||||
|
try {
|
||||||
|
Thread.sleep(500); // is 500ms long enough?
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
}
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean canStart() {
|
||||||
|
RouterService svc = _routerService;
|
||||||
|
return (svc == null) || (!_isBound) || svc.canManualStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean canStop() {
|
||||||
|
RouterService svc = _routerService;
|
||||||
|
return svc != null && _isBound && svc.canManualStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// MainFragment.RouterControlListener
|
||||||
|
|
||||||
|
public boolean shouldShowOnOff() {
|
||||||
|
return (canStart() && Connectivity.isConnected(this)) || (canStop() && !isGracefulShutdownInProgress());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldBeOn() {
|
||||||
|
String action = getIntent().getAction();
|
||||||
|
return (canStop()) ||
|
||||||
|
(action != null && action.equals("net.i2p.android.router.START_I2P"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onStartRouterClicked() {
|
||||||
|
_startPressed = true;
|
||||||
|
RouterService svc = _routerService;
|
||||||
|
if (svc != null && _isBound) {
|
||||||
|
setPref(PREF_AUTO_START, true);
|
||||||
|
svc.manualStart();
|
||||||
|
} else {
|
||||||
|
(new File(Util.getFileDir(this), "wrapper.log")).delete();
|
||||||
|
startRouter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onStopRouterClicked() {
|
||||||
|
RouterService svc = _routerService;
|
||||||
|
if (svc != null && _isBound) {
|
||||||
|
setPref(PREF_AUTO_START, false);
|
||||||
|
svc.manualQuit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 0.9.19 */
|
||||||
|
public boolean isGracefulShutdownInProgress() {
|
||||||
|
RouterService svc = _routerService;
|
||||||
|
return svc != null && svc.isGracefulShutdownInProgress();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 0.9.19 */
|
||||||
|
public boolean onGracefulShutdownClicked() {
|
||||||
|
RouterService svc = _routerService;
|
||||||
|
if(svc != null && _isBound) {
|
||||||
|
setPref(PREF_AUTO_START, false);
|
||||||
|
svc.gracefulShutdown();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 0.9.19 */
|
||||||
|
public boolean onCancelGracefulShutdownClicked() {
|
||||||
|
RouterService svc = _routerService;
|
||||||
|
if(svc != null && _isBound) {
|
||||||
|
setPref(PREF_AUTO_START, false);
|
||||||
|
svc.cancelGracefulShutdown();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
207
app/src/main/java/net/i2p/android/I2PActivityBase.java
Normal file
207
app/src/main/java/net/i2p/android/I2PActivityBase.java
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
package net.i2p.android;
|
||||||
|
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.ServiceConnection;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
|
||||||
|
import net.i2p.android.router.service.RouterBinder;
|
||||||
|
import net.i2p.android.router.service.RouterService;
|
||||||
|
import net.i2p.android.router.util.Util;
|
||||||
|
import net.i2p.android.util.LocaleManager;
|
||||||
|
|
||||||
|
public abstract class I2PActivityBase extends AppCompatActivity {
|
||||||
|
/**
|
||||||
|
* Router variables
|
||||||
|
*/
|
||||||
|
protected boolean _isBound;
|
||||||
|
protected boolean _triedBind;
|
||||||
|
protected ServiceConnection _connection;
|
||||||
|
protected RouterService _routerService;
|
||||||
|
private SharedPreferences _sharedPrefs;
|
||||||
|
|
||||||
|
private static final String SHARED_PREFS = "net.i2p.android.router";
|
||||||
|
protected static final String PREF_AUTO_START = "autoStart";
|
||||||
|
/**
|
||||||
|
* true leads to a poor install experience, very slow to paint the screen
|
||||||
|
*/
|
||||||
|
protected static final boolean DEFAULT_AUTO_START = false;
|
||||||
|
|
||||||
|
private final LocaleManager localeManager = new LocaleManager();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the activity is first created.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
Util.d(this + " onCreate called");
|
||||||
|
localeManager.onCreate(this);
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
_sharedPrefs = getSharedPreferences(SHARED_PREFS, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRestart() {
|
||||||
|
Util.d(this + " onRestart called");
|
||||||
|
super.onRestart();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
Util.d(this + " onStart called");
|
||||||
|
super.onStart();
|
||||||
|
if (_sharedPrefs.getBoolean(PREF_AUTO_START, DEFAULT_AUTO_START))
|
||||||
|
startRouter();
|
||||||
|
else
|
||||||
|
bindRouter(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param def default
|
||||||
|
*/
|
||||||
|
public boolean getPref(String pref, boolean def) {
|
||||||
|
return _sharedPrefs.getBoolean(pref, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param def default
|
||||||
|
*/
|
||||||
|
public String getPref(String pref, String def) {
|
||||||
|
return _sharedPrefs.getString(pref, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return success
|
||||||
|
*/
|
||||||
|
public boolean setPref(String pref, boolean val) {
|
||||||
|
SharedPreferences.Editor edit = _sharedPrefs.edit();
|
||||||
|
edit.putBoolean(pref, val);
|
||||||
|
return edit.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return success
|
||||||
|
*/
|
||||||
|
public boolean setPref(String pref, String val) {
|
||||||
|
SharedPreferences.Editor edit = _sharedPrefs.edit();
|
||||||
|
edit.putString(pref, val);
|
||||||
|
return edit.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
Util.d(this + " onResume called");
|
||||||
|
super.onResume();
|
||||||
|
localeManager.onResume(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void notifyLocaleChanged() {
|
||||||
|
localeManager.onResume(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
Util.d(this + " onPause called");
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
|
Util.d(this + " onSaveInstanceState called");
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
Util.d(this + " onStop called");
|
||||||
|
unbindRouter();
|
||||||
|
super.onStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
Util.d(this + " onDestroy called");
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
////// Service stuff
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the service and bind to it
|
||||||
|
*/
|
||||||
|
protected boolean startRouter() {
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.setClassName(this, "net.i2p.android.router.service.RouterService");
|
||||||
|
Util.d(this + " calling startService");
|
||||||
|
ComponentName name = startService(intent);
|
||||||
|
if (name == null)
|
||||||
|
Util.d(this + " XXXXXXXXXXXXXXXXXXXX got null from startService!");
|
||||||
|
Util.d(this + " got from startService: " + name);
|
||||||
|
boolean success = bindRouter(true);
|
||||||
|
if (!success)
|
||||||
|
Util.d(this + " Bind router failed");
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind only
|
||||||
|
*/
|
||||||
|
protected boolean bindRouter(boolean autoCreate) {
|
||||||
|
Intent intent = new Intent(RouterBinder.class.getName());
|
||||||
|
intent.setClassName(this, "net.i2p.android.router.service.RouterService");
|
||||||
|
Util.d(this + " calling bindService");
|
||||||
|
_connection = new RouterConnection();
|
||||||
|
_triedBind = bindService(intent, _connection, autoCreate ? BIND_AUTO_CREATE : 0);
|
||||||
|
Util.d(this + " bindService: auto create? " + autoCreate + " success? " + _triedBind);
|
||||||
|
return _triedBind;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void unbindRouter() {
|
||||||
|
Util.d(this + " unbindRouter called with _isBound:" + _isBound + " _connection:" + _connection + " _triedBind:" + _triedBind);
|
||||||
|
if (_triedBind && _connection != null)
|
||||||
|
unbindService(_connection);
|
||||||
|
|
||||||
|
_triedBind = false;
|
||||||
|
_connection = null;
|
||||||
|
_routerService = null;
|
||||||
|
_isBound = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for interacting with the main interface of the RouterService.
|
||||||
|
*/
|
||||||
|
protected class RouterConnection implements ServiceConnection {
|
||||||
|
|
||||||
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||||
|
Util.d(this + " connected to router service");
|
||||||
|
RouterBinder binder = (RouterBinder) service;
|
||||||
|
RouterService svc = binder.getService();
|
||||||
|
_routerService = svc;
|
||||||
|
_isBound = true;
|
||||||
|
onRouterBind(svc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onServiceDisconnected(ComponentName name) {
|
||||||
|
Util.d(this + " disconnected from router service!!!!!!!");
|
||||||
|
// save memory
|
||||||
|
_routerService = null;
|
||||||
|
_isBound = false;
|
||||||
|
onRouterUnbind();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* callback from ServiceConnection, override as necessary
|
||||||
|
*/
|
||||||
|
protected void onRouterBind(RouterService svc) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* callback from ServiceConnection, override as necessary
|
||||||
|
*/
|
||||||
|
protected void onRouterUnbind() {
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,10 @@
|
|||||||
package net.i2p.android.router;
|
package net.i2p.android;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
|
||||||
|
import net.i2p.android.router.R;
|
||||||
import net.i2p.android.router.util.Util;
|
import net.i2p.android.router.util.Util;
|
||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
import net.i2p.util.FileUtil;
|
import net.i2p.util.FileUtil;
|
||||||
@ -93,15 +94,23 @@ class InitActivities {
|
|||||||
docsDir.mkdir();
|
docsDir.mkdir();
|
||||||
copyResourceToFile(R.raw.ahelper_conflict_header_ht, "docs/ahelper-conflict-header.ht");
|
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_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.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.denied_header_ht, "docs/denied-header.ht");
|
||||||
copyResourceToFile(R.raw.dnf_header_ht, "docs/dnf-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.dnfb_header_ht, "docs/dnfb-header.ht");
|
||||||
copyResourceToFile(R.raw.dnfh_header_ht, "docs/dnfh-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.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.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.noproxy_header_ht, "docs/noproxy-header.ht");
|
||||||
copyResourceToFile(R.raw.protocol_header_ht, "docs/protocol-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");
|
File cssDir = new File(docsDir, "themes/console/light");
|
||||||
cssDir.mkdirs();
|
cssDir.mkdirs();
|
@ -1,22 +1,40 @@
|
|||||||
package net.i2p.android.apps;
|
package net.i2p.android.apps;
|
||||||
|
|
||||||
import java.io.File;
|
import android.content.Context;
|
||||||
|
|
||||||
import net.i2p.android.router.NewsActivity;
|
import net.i2p.android.router.NewsActivity;
|
||||||
|
import net.i2p.android.router.R;
|
||||||
import net.i2p.android.router.util.Notifications;
|
import net.i2p.android.router.util.Notifications;
|
||||||
|
import net.i2p.crypto.SU3File;
|
||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
import net.i2p.router.RouterContext;
|
import net.i2p.router.RouterContext;
|
||||||
|
import net.i2p.router.news.NewsEntry;
|
||||||
|
import net.i2p.router.news.NewsMetadata;
|
||||||
|
import net.i2p.router.news.NewsXMLParser;
|
||||||
import net.i2p.router.util.RFC822Date;
|
import net.i2p.router.util.RFC822Date;
|
||||||
import net.i2p.util.EepGet;
|
import net.i2p.util.EepGet;
|
||||||
import net.i2p.util.FileUtil;
|
import net.i2p.util.FileUtil;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
import net.i2p.util.Translate;
|
import net.i2p.util.ReusableGZIPInputStream;
|
||||||
|
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.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* From router console, simplified since we don't deal with router versions
|
* From router console, simplified since we don't deal with router versions
|
||||||
* or updates.
|
* or updates.
|
||||||
*/
|
*/
|
||||||
public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
||||||
|
private final Context mCtx;
|
||||||
private final RouterContext _context;
|
private final RouterContext _context;
|
||||||
private final Notifications _notif;
|
private final Notifications _notif;
|
||||||
private final Log _log;
|
private final Log _log;
|
||||||
@ -35,10 +53,10 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static /* final */ synchronized NewsFetcher getInstance(
|
public static /* final */ synchronized NewsFetcher getInstance(
|
||||||
RouterContext ctx, Notifications notif) {
|
Context context, RouterContext ctx, Notifications notif) {
|
||||||
if (_instance != null)
|
if (_instance != null)
|
||||||
return _instance;
|
return _instance;
|
||||||
_instance = new NewsFetcher(ctx, notif);
|
_instance = new NewsFetcher(context, ctx, notif);
|
||||||
return _instance;
|
return _instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,20 +65,21 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
|||||||
private static final String TEMP_NEWS_FILE = "news.xml.temp";
|
private static final String TEMP_NEWS_FILE = "news.xml.temp";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changed in 0.9.11 to the b32 for psi.i2p, run by psi.
|
* 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
|
* We may be able to change it to psi.i2p in a future release after
|
||||||
* the hostname propagates.
|
* 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://avviiexdngd32ccoy4kuckvc3mkf53ycvzbz6vz75vzhv4tbpk5a.b32.i2p/news.su3";
|
||||||
private static final String PROP_LAST_CHECKED = "router.newsLastChecked";
|
private static final String PROP_LAST_CHECKED = "router.newsLastChecked";
|
||||||
private static final String PROP_REFRESH_FREQUENCY = "router.newsRefreshFrequency";
|
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 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://echelon.i2p/news/news.su3";
|
||||||
|
|
||||||
private NewsFetcher(RouterContext ctx, Notifications notif) {
|
private NewsFetcher(Context context, RouterContext ctx, Notifications notif) {
|
||||||
|
mCtx = context;
|
||||||
_context = ctx;
|
_context = ctx;
|
||||||
_notif = notif;
|
_notif = notif;
|
||||||
_context.addShutdownTask(new Shutdown());
|
_context.addShutdownTask(new Shutdown());
|
||||||
@ -69,7 +88,8 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
|||||||
String last = ctx.getProperty(PROP_LAST_CHECKED);
|
String last = ctx.getProperty(PROP_LAST_CHECKED);
|
||||||
if (last != null)
|
if (last != null)
|
||||||
_lastFetch = Long.parseLong(last);
|
_lastFetch = Long.parseLong(last);
|
||||||
} catch (NumberFormatException nfe) {}
|
} catch (NumberFormatException nfe) {
|
||||||
|
}
|
||||||
File newsDir = new File(_context.getRouterDir(), NEWS_DIR);
|
File newsDir = new File(_context.getRouterDir(), NEWS_DIR);
|
||||||
// isn't already there on android
|
// isn't already there on android
|
||||||
newsDir.mkdir();
|
newsDir.mkdir();
|
||||||
@ -94,24 +114,24 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String status() {
|
public String status() {
|
||||||
StringBuilder buf = new StringBuilder(128);
|
StringBuilder buf = new StringBuilder(128);
|
||||||
long now = _context.clock().now();
|
long now = _context.clock().now();
|
||||||
if (_lastUpdated > 0) {
|
if (_lastUpdated > 0) {
|
||||||
buf.append(Translate.getString("News last updated {0} ago.",
|
buf.append(mCtx.getString(R.string.news_last_updated,
|
||||||
DataHelper.formatDuration2(now - _lastUpdated),
|
DataHelper.formatDuration2(now - _lastUpdated)))
|
||||||
_context, "foo"))
|
.append('\n');
|
||||||
.append('\n');
|
}
|
||||||
}
|
if (_lastFetch > _lastUpdated) {
|
||||||
if (_lastFetch > _lastUpdated) {
|
buf.append(mCtx.getString(R.string.news_last_checked,
|
||||||
buf.append(Translate.getString("News last checked {0} ago.",
|
DataHelper.formatDuration2(now - _lastFetch)));
|
||||||
DataHelper.formatDuration2(now - _lastFetch),
|
}
|
||||||
_context, "foo"));
|
return buf.toString();
|
||||||
}
|
|
||||||
return buf.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final long INITIAL_DELAY = 5*60*1000;
|
// Runnable
|
||||||
private static final long RUN_DELAY = 30*60*1000;
|
|
||||||
|
private static final long INITIAL_DELAY = 5 * 60 * 1000;
|
||||||
|
private static final long RUN_DELAY = 30 * 60 * 1000;
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
_thread = Thread.currentThread();
|
_thread = Thread.currentThread();
|
||||||
@ -137,7 +157,7 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
|||||||
return true;
|
return true;
|
||||||
updateLastFetched();
|
updateLastFetched();
|
||||||
String freq = _context.getProperty(PROP_REFRESH_FREQUENCY,
|
String freq = _context.getProperty(PROP_REFRESH_FREQUENCY,
|
||||||
DEFAULT_REFRESH_FREQUENCY);
|
DEFAULT_REFRESH_FREQUENCY);
|
||||||
try {
|
try {
|
||||||
long ms = Long.parseLong(freq);
|
long ms = Long.parseLong(freq);
|
||||||
if (ms <= 0)
|
if (ms <= 0)
|
||||||
@ -158,8 +178,9 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call this when changing news URLs to force an update next time the timer fires.
|
* Call this when changing news URLs to force an update next time the timer fires.
|
||||||
* @since 0.8.7
|
*
|
||||||
|
* @since 0.8.7
|
||||||
*/
|
*/
|
||||||
void invalidateNews() {
|
void invalidateNews() {
|
||||||
_lastModified = null;
|
_lastModified = null;
|
||||||
@ -167,7 +188,7 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void fetchNews() {
|
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";
|
String proxyHost = "127.0.0.1";
|
||||||
int proxyPort = 4444;
|
int proxyPort = 4444;
|
||||||
if (_tempFile.exists())
|
if (_tempFile.exists())
|
||||||
@ -183,7 +204,7 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
|||||||
} else {
|
} else {
|
||||||
// backup news location - always proxied
|
// backup news location - always proxied
|
||||||
_tempFile.delete();
|
_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);
|
get.addStatusListener(this);
|
||||||
if (get.fetch())
|
if (get.fetch())
|
||||||
_lastModified = get.getLastModified();
|
_lastModified = get.getLastModified();
|
||||||
@ -193,25 +214,38 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
|
// EepGet.StatusListener
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {
|
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {
|
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {
|
||||||
if (_log.shouldLog(Log.INFO))
|
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();
|
long now = _context.clock().now();
|
||||||
if (_tempFile.exists()) {
|
if (_tempFile.exists() && _tempFile.length() > 0) {
|
||||||
boolean copied = FileUtil.copy(_tempFile.getAbsolutePath(), _newsFile.getAbsolutePath(), true);
|
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) {
|
if (copied) {
|
||||||
_lastUpdated = now;
|
_lastUpdated = now;
|
||||||
_tempFile.delete();
|
|
||||||
|
|
||||||
// Notify user
|
// Notify user
|
||||||
_notif.notify("News Updated", "Touch to view latest I2P news",
|
_notif.notify(mCtx.getString(R.string.news_updated),
|
||||||
|
mCtx.getString(R.string.view_news),
|
||||||
NewsActivity.class);
|
NewsActivity.class);
|
||||||
} else {
|
} else {
|
||||||
if (_log.shouldLog(Log.ERROR))
|
if (_log.shouldLog(Log.ERROR))
|
||||||
@ -226,13 +260,21 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
|||||||
_context.router().saveConfig();
|
_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) {
|
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn("Failed to fetch the news from " + url);
|
_log.warn("Failed to fetch the news from " + url);
|
||||||
_tempFile.delete();
|
_tempFile.delete();
|
||||||
}
|
}
|
||||||
public void headerReceived(String url, int attemptNum, String key, String val) {}
|
|
||||||
public void attempting(String url) {}
|
public void headerReceived(String url, int attemptNum, String key, String val) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void attempting(String url) {
|
||||||
|
}
|
||||||
|
|
||||||
private class Shutdown implements Runnable {
|
private class Shutdown implements Runnable {
|
||||||
public void run() {
|
public void run() {
|
||||||
@ -241,4 +283,138 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
|||||||
_thread.interrupt();
|
_thread.interrupt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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();
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,8 +35,8 @@ public class BrowserAdapter extends RecyclerView.Adapter<BrowserAdapter.ViewHold
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static interface OnBrowserSelectedListener {
|
public interface OnBrowserSelectedListener {
|
||||||
public void onBrowserSelected(Browser browser);
|
void onBrowserSelected(Browser browser);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BrowserAdapter(Context ctx, OnBrowserSelectedListener listener) {
|
public BrowserAdapter(Context ctx, OnBrowserSelectedListener listener) {
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
package net.i2p.android.help;
|
package net.i2p.android.help;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v7.app.ActionBarActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
|
||||||
import net.i2p.android.router.R;
|
import net.i2p.android.router.R;
|
||||||
|
import net.i2p.android.util.LocaleManager;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
public class BrowserConfigActivity extends ActionBarActivity implements
|
public class BrowserConfigActivity extends AppCompatActivity implements
|
||||||
BrowserAdapter.OnBrowserSelectedListener {
|
BrowserAdapter.OnBrowserSelectedListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -18,8 +19,11 @@ public class BrowserConfigActivity extends ActionBarActivity implements
|
|||||||
*/
|
*/
|
||||||
private boolean mTwoPane;
|
private boolean mTwoPane;
|
||||||
|
|
||||||
|
private final LocaleManager localeManager = new LocaleManager();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
localeManager.onCreate(this);
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_help);
|
setContentView(R.layout.activity_help);
|
||||||
|
|
||||||
@ -43,6 +47,12 @@ public class BrowserConfigActivity extends ActionBarActivity implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
localeManager.onResume(this);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
|
@ -19,6 +19,7 @@ import android.view.ViewGroup;
|
|||||||
|
|
||||||
import net.i2p.android.router.R;
|
import net.i2p.android.router.R;
|
||||||
import net.i2p.android.router.util.BetterAsyncTaskLoader;
|
import net.i2p.android.router.util.BetterAsyncTaskLoader;
|
||||||
|
import net.i2p.android.widget.DividerItemDecoration;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -55,6 +56,9 @@ public class BrowserListFragment extends Fragment implements
|
|||||||
View v = inflater.inflate(R.layout.fragment_help_browsers, container, false);
|
View v = inflater.inflate(R.layout.fragment_help_browsers, container, false);
|
||||||
RecyclerView mRecyclerView = (RecyclerView) v.findViewById(R.id.browser_list);
|
RecyclerView mRecyclerView = (RecyclerView) v.findViewById(R.id.browser_list);
|
||||||
|
|
||||||
|
mRecyclerView.setHasFixedSize(true);
|
||||||
|
mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST));
|
||||||
|
|
||||||
// use a linear layout manager
|
// use a linear layout manager
|
||||||
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
|
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
|
||||||
mRecyclerView.setLayoutManager(mLayoutManager);
|
mRecyclerView.setLayoutManager(mLayoutManager);
|
||||||
|
@ -4,7 +4,7 @@ import android.content.Intent;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.NavUtils;
|
import android.support.v4.app.NavUtils;
|
||||||
import android.support.v4.app.TaskStackBuilder;
|
import android.support.v4.app.TaskStackBuilder;
|
||||||
import android.support.v7.app.ActionBarActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
@ -12,8 +12,9 @@ import android.view.MenuItem;
|
|||||||
import net.i2p.android.router.LicenseActivity;
|
import net.i2p.android.router.LicenseActivity;
|
||||||
import net.i2p.android.router.R;
|
import net.i2p.android.router.R;
|
||||||
import net.i2p.android.router.dialog.TextResourceDialog;
|
import net.i2p.android.router.dialog.TextResourceDialog;
|
||||||
|
import net.i2p.android.util.LocaleManager;
|
||||||
|
|
||||||
public class HelpActivity extends ActionBarActivity implements
|
public class HelpActivity extends AppCompatActivity implements
|
||||||
HelpListFragment.OnEntrySelectedListener {
|
HelpListFragment.OnEntrySelectedListener {
|
||||||
public static final String CATEGORY = "help_category";
|
public static final String CATEGORY = "help_category";
|
||||||
public static final int CAT_MAIN = 0;
|
public static final int CAT_MAIN = 0;
|
||||||
@ -28,8 +29,11 @@ public class HelpActivity extends ActionBarActivity implements
|
|||||||
private boolean mTwoPane;
|
private boolean mTwoPane;
|
||||||
private int mCategory;
|
private int mCategory;
|
||||||
|
|
||||||
|
private final LocaleManager localeManager = new LocaleManager();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
localeManager.onCreate(this);
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_help);
|
setContentView(R.layout.activity_help);
|
||||||
|
|
||||||
@ -58,6 +62,12 @@ public class HelpActivity extends ActionBarActivity implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
localeManager.onResume(this);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
getMenuInflater().inflate(R.menu.activity_help_actions, menu);
|
getMenuInflater().inflate(R.menu.activity_help_actions, menu);
|
||||||
|
@ -14,7 +14,7 @@ public class HelpListFragment extends ListFragment {
|
|||||||
|
|
||||||
// Container Activity must implement this interface
|
// Container Activity must implement this interface
|
||||||
public interface OnEntrySelectedListener {
|
public interface OnEntrySelectedListener {
|
||||||
public void onEntrySelected(int entry);
|
void onEntrySelected(int entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
package net.i2p.android.i2ptunnel;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.v7.app.ActionBarActivity;
|
|
||||||
import android.support.v7.widget.Toolbar;
|
|
||||||
|
|
||||||
import net.i2p.android.router.R;
|
|
||||||
|
|
||||||
public class EditTunnelActivity extends ActionBarActivity {
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setContentView(R.layout.activity_help_onepane);
|
|
||||||
|
|
||||||
// Set the action bar
|
|
||||||
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
|
|
||||||
setSupportActionBar(toolbar);
|
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
|
||||||
|
|
||||||
if (savedInstanceState == null) {
|
|
||||||
int tunnelId = getIntent().getIntExtra(TunnelDetailFragment.TUNNEL_ID, 0);
|
|
||||||
EditTunnelFragment editFrag = EditTunnelFragment.newInstance(tunnelId);
|
|
||||||
getSupportFragmentManager().beginTransaction()
|
|
||||||
.add(R.id.main_fragment, editFrag).commit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,24 +2,51 @@ package net.i2p.android.i2ptunnel;
|
|||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.support.v4.app.ActivityCompat;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
import net.i2p.android.router.I2PActivityBase;
|
import net.i2p.android.I2PActivityBase;
|
||||||
|
import net.i2p.android.i2ptunnel.preferences.EditTunnelActivity;
|
||||||
import net.i2p.android.router.R;
|
import net.i2p.android.router.R;
|
||||||
|
|
||||||
public class TunnelDetailActivity extends I2PActivityBase implements
|
public class TunnelDetailActivity extends I2PActivityBase implements
|
||||||
TunnelDetailFragment.TunnelDetailListener {
|
TunnelDetailFragment.TunnelDetailListener {
|
||||||
|
private boolean transitionReversed;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
mDrawerToggle.setDrawerIndicatorEnabled(false);
|
transitionReversed = false;
|
||||||
|
|
||||||
getSupportActionBar().setDisplayShowTitleEnabled(false);
|
|
||||||
|
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
int tunnelId = getIntent().getIntExtra(TunnelDetailFragment.TUNNEL_ID, 0);
|
int tunnelId = getIntent().getIntExtra(TunnelDetailFragment.TUNNEL_ID, 0);
|
||||||
TunnelDetailFragment detailFrag = TunnelDetailFragment.newInstance(tunnelId);
|
TunnelDetailFragment detailFrag = TunnelDetailFragment.newInstance(tunnelId);
|
||||||
getSupportFragmentManager().beginTransaction()
|
getSupportFragmentManager().beginTransaction()
|
||||||
.add(R.id.main_fragment, detailFrag).commit();
|
.add(android.R.id.content, detailFrag).commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
Toolbar toolbar = (Toolbar) findViewById(R.id.detail_toolbar);
|
||||||
|
toolbar.setNavigationIcon(getResources().getDrawable(R.drawable.ic_arrow_back_white_24dp));
|
||||||
|
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
onBackPressed();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finish() {
|
||||||
|
if (transitionReversed)
|
||||||
|
super.finish();
|
||||||
|
else {
|
||||||
|
transitionReversed = true;
|
||||||
|
ActivityCompat.finishAfterTransition(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
package net.i2p.android.i2ptunnel;
|
package net.i2p.android.i2ptunnel;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.app.DialogFragment;
|
import android.support.v4.app.DialogFragment;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@ -21,6 +24,8 @@ import android.widget.Toast;
|
|||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.android.i2ptunnel.util.TunnelUtil;
|
import net.i2p.android.i2ptunnel.util.TunnelUtil;
|
||||||
import net.i2p.android.router.R;
|
import net.i2p.android.router.R;
|
||||||
|
import net.i2p.android.util.FragmentUtils;
|
||||||
|
import net.i2p.app.ClientAppState;
|
||||||
import net.i2p.i2ptunnel.TunnelControllerGroup;
|
import net.i2p.i2ptunnel.TunnelControllerGroup;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -31,6 +36,7 @@ public class TunnelDetailFragment extends Fragment {
|
|||||||
TunnelDetailListener mCallback;
|
TunnelDetailListener mCallback;
|
||||||
private TunnelControllerGroup mGroup;
|
private TunnelControllerGroup mGroup;
|
||||||
private TunnelEntry mTunnel;
|
private TunnelEntry mTunnel;
|
||||||
|
private Toolbar mToolbar;
|
||||||
|
|
||||||
public static TunnelDetailFragment newInstance(int tunnelId) {
|
public static TunnelDetailFragment newInstance(int tunnelId) {
|
||||||
TunnelDetailFragment f = new TunnelDetailFragment();
|
TunnelDetailFragment f = new TunnelDetailFragment();
|
||||||
@ -42,8 +48,8 @@ public class TunnelDetailFragment extends Fragment {
|
|||||||
|
|
||||||
// Container Activity must implement this interface
|
// Container Activity must implement this interface
|
||||||
public interface TunnelDetailListener {
|
public interface TunnelDetailListener {
|
||||||
public void onEditTunnel(int tunnelId);
|
void onEditTunnel(int tunnelId);
|
||||||
public void onTunnelDeleted(int tunnelId, int numTunnelsLeft);
|
void onTunnelDeleted(int tunnelId, int numTunnelsLeft);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -52,19 +58,15 @@ public class TunnelDetailFragment extends Fragment {
|
|||||||
|
|
||||||
// This makes sure that the container activity has implemented
|
// This makes sure that the container activity has implemented
|
||||||
// the callback interface. If not, it throws an exception
|
// the callback interface. If not, it throws an exception
|
||||||
try {
|
mCallback = FragmentUtils.getParent(this, TunnelDetailListener.class);
|
||||||
mCallback = (TunnelDetailListener) activity;
|
if (mCallback == null)
|
||||||
} catch (ClassCastException e) {
|
throw new ClassCastException("Parent must implement TunnelDetailListener");
|
||||||
throw new ClassCastException(activity.toString()
|
|
||||||
+ " must implement OnTunnelDeletedListener");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setHasOptionsMenu(true);
|
|
||||||
|
|
||||||
String error;
|
String error;
|
||||||
try {
|
try {
|
||||||
@ -90,6 +92,16 @@ public class TunnelDetailFragment extends Fragment {
|
|||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
View v = inflater.inflate(R.layout.fragment_i2ptunnel_detail, container, false);
|
View v = inflater.inflate(R.layout.fragment_i2ptunnel_detail, container, false);
|
||||||
|
|
||||||
|
mToolbar = (Toolbar) v.findViewById(R.id.detail_toolbar);
|
||||||
|
mToolbar.inflateMenu(R.menu.fragment_i2ptunnel_detail_actions);
|
||||||
|
mToolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||||
|
return onToolbarItemSelected(menuItem);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
updateToolbar();
|
||||||
|
|
||||||
if (mTunnel != null) {
|
if (mTunnel != null) {
|
||||||
TextView name = (TextView) v.findViewById(R.id.tunnel_name);
|
TextView name = (TextView) v.findViewById(R.id.tunnel_name);
|
||||||
name.setText(mTunnel.getName());
|
name.setText(mTunnel.getName());
|
||||||
@ -103,34 +115,39 @@ public class TunnelDetailFragment extends Fragment {
|
|||||||
TextView details = (TextView) v.findViewById(R.id.tunnel_details);
|
TextView details = (TextView) v.findViewById(R.id.tunnel_details);
|
||||||
details.setText(mTunnel.getDetails());
|
details.setText(mTunnel.getDetails());
|
||||||
|
|
||||||
View accessIfacePortLabel = v.findViewById(R.id.tunnel_access_interface_port_label);
|
View accessIfacePortItem = v.findViewById(R.id.tunnel_access_interface_port_item);
|
||||||
TextView accessIfacePort = (TextView) v.findViewById(R.id.tunnel_access_interface_port);
|
TextView accessIfacePort = (TextView) v.findViewById(R.id.tunnel_access_interface_port);
|
||||||
View targetIfacePortLabel = v.findViewById(R.id.tunnel_target_interface_port_label);
|
View accessIfaceOpen = v.findViewById(R.id.tunnel_access_open);
|
||||||
|
View targetIfacePortItem = v.findViewById(R.id.tunnel_target_interface_port_item);
|
||||||
TextView targetIfacePort = (TextView) v.findViewById(R.id.tunnel_target_interface_port);
|
TextView targetIfacePort = (TextView) v.findViewById(R.id.tunnel_target_interface_port);
|
||||||
|
View targetIfaceOpen = v.findViewById(R.id.tunnel_target_open);
|
||||||
switch (mTunnel.getInternalType()) {
|
switch (mTunnel.getInternalType()) {
|
||||||
case "httpbidirserver":
|
case "httpbidirserver":
|
||||||
accessIfacePort.setText(mTunnel.getClientLink(false));
|
accessIfacePort.setText(mTunnel.getClientLink(false));
|
||||||
|
setupOpen(accessIfaceOpen, true);
|
||||||
|
v.findViewById(R.id.icon_link_access).setVisibility(View.GONE);
|
||||||
targetIfacePort.setText(mTunnel.getServerLink(false));
|
targetIfacePort.setText(mTunnel.getServerLink(false));
|
||||||
|
setupOpen(targetIfaceOpen, false);
|
||||||
break;
|
break;
|
||||||
case "streamrserver":
|
case "streamrserver":
|
||||||
accessIfacePort.setText(mTunnel.getServerLink(false));
|
accessIfacePort.setText(mTunnel.getServerLink(false));
|
||||||
targetIfacePortLabel.setVisibility(View.GONE);
|
setupOpen(accessIfaceOpen, true);
|
||||||
targetIfacePort.setVisibility(View.GONE);
|
targetIfacePortItem.setVisibility(View.GONE);
|
||||||
break;
|
break;
|
||||||
case "streamrclient":
|
case "streamrclient":
|
||||||
accessIfacePortLabel.setVisibility(View.GONE);
|
accessIfacePortItem.setVisibility(View.GONE);
|
||||||
accessIfacePort.setVisibility(View.GONE);
|
|
||||||
targetIfacePort.setText(mTunnel.getClientLink(false));
|
targetIfacePort.setText(mTunnel.getClientLink(false));
|
||||||
|
setupOpen(targetIfaceOpen, false);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (mTunnel.isClient()) {
|
if (mTunnel.isClient()) {
|
||||||
accessIfacePort.setText(mTunnel.getClientLink(false));
|
accessIfacePort.setText(mTunnel.getClientLink(false));
|
||||||
targetIfacePortLabel.setVisibility(View.GONE);
|
setupOpen(accessIfaceOpen, true);
|
||||||
targetIfacePort.setVisibility(View.GONE);
|
targetIfacePortItem.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
accessIfacePortLabel.setVisibility(View.GONE);
|
accessIfacePortItem.setVisibility(View.GONE);
|
||||||
accessIfacePort.setVisibility(View.GONE);
|
|
||||||
targetIfacePort.setText(mTunnel.getServerLink(false));
|
targetIfacePort.setText(mTunnel.getServerLink(false));
|
||||||
|
setupOpen(targetIfaceOpen, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,19 +158,50 @@ public class TunnelDetailFragment extends Fragment {
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void setupOpen(View open, final boolean client) {
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
if (mTunnel.isRunning() &&
|
||||||
inflater.inflate(R.menu.fragment_i2ptunnel_detail_actions, menu);
|
(client ? mTunnel.isClientLinkValid() : mTunnel.isServerLinkValid())) {
|
||||||
// Disable until ticket #815 is closed
|
open.setVisibility(View.VISIBLE);
|
||||||
menu.findItem(R.id.action_edit_tunnel).setVisible(false);
|
open.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||||
|
i.setData(Uri.parse(client ? mTunnel.getClientLink(true) : mTunnel.getServerLink(true)));
|
||||||
|
try {
|
||||||
|
startActivity(i);
|
||||||
|
} catch (ActivityNotFoundException e) {
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||||
|
builder.setTitle(R.string.install_recommended_app)
|
||||||
|
.setMessage(R.string.app_needed_for_this_tunnel_type)
|
||||||
|
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialogInterface, int i) {
|
||||||
|
Uri uri = mTunnel.getRecommendedAppForTunnel();
|
||||||
|
if (uri != null) {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setNegativeButton(net.i2p.android.lib.client.R.string.no, new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialogInterface, int i) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else
|
||||||
|
open.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void updateToolbar() {
|
||||||
public void onPrepareOptionsMenu(Menu menu) {
|
Menu menu = mToolbar.getMenu();
|
||||||
MenuItem start = menu.findItem(R.id.action_start_tunnel);
|
MenuItem start = menu.findItem(R.id.action_start_tunnel);
|
||||||
MenuItem stop = menu.findItem(R.id.action_stop_tunnel);
|
MenuItem stop = menu.findItem(R.id.action_stop_tunnel);
|
||||||
|
|
||||||
if (mTunnel != null) {
|
if (mTunnel != null && mGroup != null &&
|
||||||
|
(mGroup.getState() == ClientAppState.STARTING ||
|
||||||
|
mGroup.getState() == ClientAppState.RUNNING)) {
|
||||||
boolean isStopped = mTunnel.getStatus() == TunnelEntry.NOT_RUNNING;
|
boolean isStopped = mTunnel.getStatus() == TunnelEntry.NOT_RUNNING;
|
||||||
|
|
||||||
start.setVisible(isStopped);
|
start.setVisible(isStopped);
|
||||||
@ -170,8 +218,7 @@ public class TunnelDetailFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private boolean onToolbarItemSelected(MenuItem item) {
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
if (mTunnel == null)
|
if (mTunnel == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -182,16 +229,16 @@ public class TunnelDetailFragment extends Fragment {
|
|||||||
Toast.makeText(getActivity().getApplicationContext(),
|
Toast.makeText(getActivity().getApplicationContext(),
|
||||||
getResources().getString(R.string.i2ptunnel_msg_tunnel_starting)
|
getResources().getString(R.string.i2ptunnel_msg_tunnel_starting)
|
||||||
+ ' ' + mTunnel.getName(), Toast.LENGTH_LONG).show();
|
+ ' ' + mTunnel.getName(), Toast.LENGTH_LONG).show();
|
||||||
// Reload the action bar to change the start/stop action
|
// Reload the toolbar to change the start/stop action
|
||||||
getActivity().supportInvalidateOptionsMenu();
|
updateToolbar();
|
||||||
return true;
|
return true;
|
||||||
case R.id.action_stop_tunnel:
|
case R.id.action_stop_tunnel:
|
||||||
mTunnel.getController().stopTunnel();
|
mTunnel.getController().stopTunnel();
|
||||||
Toast.makeText(getActivity().getApplicationContext(),
|
Toast.makeText(getActivity().getApplicationContext(),
|
||||||
getResources().getString(R.string.i2ptunnel_msg_tunnel_stopping)
|
getResources().getString(R.string.i2ptunnel_msg_tunnel_stopping)
|
||||||
+ ' ' + mTunnel.getName(), Toast.LENGTH_LONG).show();
|
+ ' ' + mTunnel.getName(), Toast.LENGTH_LONG).show();
|
||||||
// Reload the action bar to change the start/stop action
|
// Reload the toolbar to change the start/stop action
|
||||||
getActivity().supportInvalidateOptionsMenu();
|
updateToolbar();
|
||||||
return true;
|
return true;
|
||||||
case R.id.action_edit_tunnel:
|
case R.id.action_edit_tunnel:
|
||||||
mCallback.onEditTunnel(mTunnel.getId());
|
mCallback.onEditTunnel(mTunnel.getId());
|
||||||
|
@ -1,94 +1,174 @@
|
|||||||
package net.i2p.android.i2ptunnel;
|
package net.i2p.android.i2ptunnel;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.content.ActivityNotFoundException;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.support.v4.util.Pair;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import net.i2p.android.router.R;
|
import net.i2p.android.router.R;
|
||||||
|
import net.i2p.android.util.FragmentUtils;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class TunnelEntryAdapter extends ArrayAdapter<TunnelEntry> {
|
public class TunnelEntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||||
private final LayoutInflater mInflater;
|
private Context mCtx;
|
||||||
|
private boolean mClientTunnels;
|
||||||
|
private TunnelListFragment.OnTunnelSelectedListener mListener;
|
||||||
|
private FragmentUtils.TwoPaneProvider mTwoPane;
|
||||||
|
private List<TunnelEntry> mTunnels;
|
||||||
|
/**
|
||||||
|
* The current activated item position. Only used on tablets.
|
||||||
|
*/
|
||||||
|
private int mActivatedPosition = -1;
|
||||||
|
|
||||||
public TunnelEntryAdapter(Context context) {
|
public static class SimpleViewHolder extends RecyclerView.ViewHolder {
|
||||||
super(context, android.R.layout.simple_list_item_2);
|
public SimpleViewHolder(View itemView) {
|
||||||
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
super(itemView);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setData(List<TunnelEntry> tunnels) {
|
public static class TunnelViewHolder extends RecyclerView.ViewHolder {
|
||||||
clear();
|
public ImageView status;
|
||||||
if (tunnels != null) {
|
public TextView name;
|
||||||
for (TunnelEntry tunnel : tunnels) {
|
public TextView description;
|
||||||
add(tunnel);
|
public TextView interfacePort;
|
||||||
}
|
|
||||||
|
public TunnelViewHolder(View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
|
||||||
|
status = (ImageView) itemView.findViewById(R.id.tunnel_status);
|
||||||
|
name = (TextView) itemView.findViewById(R.id.tunnel_name);
|
||||||
|
description = (TextView) itemView.findViewById(R.id.tunnel_description);
|
||||||
|
interfacePort = (TextView) itemView.findViewById(R.id.tunnel_interface_port);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TunnelEntryAdapter(Context context, boolean clientTunnels,
|
||||||
|
TunnelListFragment.OnTunnelSelectedListener listener,
|
||||||
|
FragmentUtils.TwoPaneProvider twoPane) {
|
||||||
|
super();
|
||||||
|
mCtx = context;
|
||||||
|
mClientTunnels = clientTunnels;
|
||||||
|
mListener = listener;
|
||||||
|
mTwoPane = twoPane;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTunnels(List<TunnelEntry> tunnels) {
|
||||||
|
mTunnels = tunnels;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addTunnel(TunnelEntry tunnel) {
|
||||||
|
mTunnels.add(tunnel);
|
||||||
|
notifyItemInserted(mTunnels.size()-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TunnelEntry getTunnel(int position) {
|
||||||
|
if (position < 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return mTunnels.get(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActivatedPosition(int position) {
|
||||||
|
mActivatedPosition = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getActivatedPosition() {
|
||||||
|
return mActivatedPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearActivatedPosition() {
|
||||||
|
mActivatedPosition = -1;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View getView(int position, View convertView, ViewGroup parent) {
|
public int getItemViewType(int position) {
|
||||||
View v = mInflater.inflate(R.layout.listitem_i2ptunnel, parent, false);
|
if (mTunnels == null)
|
||||||
final TunnelEntry tunnel = getItem(position);
|
return R.string.router_not_running;
|
||||||
|
else if (mTunnels.isEmpty())
|
||||||
ImageView status = (ImageView) v.findViewById(R.id.tunnel_status);
|
return R.layout.listitem_empty;
|
||||||
status.setImageDrawable(tunnel.getStatusIcon());
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
|
|
||||||
status.setBackgroundDrawable(tunnel.getStatusBackground());
|
|
||||||
else
|
else
|
||||||
status.setBackground(tunnel.getStatusBackground());
|
return R.layout.listitem_i2ptunnel;
|
||||||
|
}
|
||||||
|
|
||||||
TextView name = (TextView) v.findViewById(R.id.tunnel_name);
|
// Create new views (invoked by the layout manager)
|
||||||
name.setText(tunnel.getName());
|
@Override
|
||||||
|
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
|
int vt = viewType;
|
||||||
|
if (viewType == R.string.router_not_running)
|
||||||
|
vt = R.layout.listitem_empty;
|
||||||
|
|
||||||
TextView type = (TextView) v.findViewById(R.id.tunnel_description);
|
View v = LayoutInflater.from(parent.getContext())
|
||||||
type.setText(tunnel.getDescription());
|
.inflate(vt, parent, false);
|
||||||
|
switch (viewType) {
|
||||||
TextView ifacePort = (TextView) v.findViewById(R.id.tunnel_interface_port);
|
case R.layout.listitem_i2ptunnel:
|
||||||
ifacePort.setText(tunnel.getTunnelLink(false));
|
return new TunnelViewHolder(v);
|
||||||
|
default:
|
||||||
if (tunnel.isRunning() && tunnel.isTunnelLinkValid()) {
|
return new SimpleViewHolder(v);
|
||||||
View open = v.findViewById(R.id.tunnel_open);
|
|
||||||
open.setVisibility(View.VISIBLE);
|
|
||||||
open.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
|
||||||
i.setData(Uri.parse(tunnel.getTunnelLink(true)));
|
|
||||||
try {
|
|
||||||
getContext().startActivity(i);
|
|
||||||
} catch (ActivityNotFoundException e) {
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
|
|
||||||
builder.setTitle(R.string.install_recommended_app)
|
|
||||||
.setMessage(R.string.app_needed_for_this_tunnel_type)
|
|
||||||
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialogInterface, int i) {
|
|
||||||
Uri uri = tunnel.getRecommendedAppForTunnel();
|
|
||||||
if (uri != null) {
|
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
|
||||||
getContext().startActivity(intent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.setNegativeButton(net.i2p.android.lib.client.R.string.no, new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialogInterface, int i) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
builder.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return v;
|
// 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));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case R.layout.listitem_empty:
|
||||||
|
((TextView) holder.itemView).setText(mClientTunnels ?
|
||||||
|
R.string.no_configured_client_tunnels :
|
||||||
|
R.string.no_configured_server_tunnels);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case R.layout.listitem_i2ptunnel:
|
||||||
|
final TunnelViewHolder tvh = (TunnelViewHolder) holder;
|
||||||
|
final TunnelEntry tunnel = getTunnel(position);
|
||||||
|
|
||||||
|
tvh.status.setImageDrawable(tunnel.getStatusIcon());
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
|
||||||
|
tvh.status.setBackgroundDrawable(tunnel.getStatusBackground());
|
||||||
|
else
|
||||||
|
tvh.status.setBackground(tunnel.getStatusBackground());
|
||||||
|
|
||||||
|
tvh.name.setText(tunnel.getName());
|
||||||
|
tvh.description.setText(tunnel.getDescription());
|
||||||
|
tvh.interfacePort.setText(tunnel.getTunnelLink(false));
|
||||||
|
|
||||||
|
tvh.itemView.setSelected(mTwoPane.isTwoPane() && position == mActivatedPosition);
|
||||||
|
tvh.itemView.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
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)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the size of the dataset (invoked by the layout manager)
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
if (mTunnels == null || mTunnels.isEmpty())
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return mTunnels.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,10 @@ import android.content.Context;
|
|||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.support.v4.content.AsyncTaskLoader;
|
import android.support.v4.content.AsyncTaskLoader;
|
||||||
|
|
||||||
|
import net.i2p.android.router.util.Util;
|
||||||
import net.i2p.i2ptunnel.TunnelController;
|
import net.i2p.i2ptunnel.TunnelController;
|
||||||
import net.i2p.i2ptunnel.TunnelControllerGroup;
|
import net.i2p.i2ptunnel.TunnelControllerGroup;
|
||||||
|
import net.i2p.router.RouterContext;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -26,6 +28,12 @@ public class TunnelEntryLoader extends AsyncTaskLoader<List<TunnelEntry>> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<TunnelEntry> loadInBackground() {
|
public List<TunnelEntry> loadInBackground() {
|
||||||
|
// Don't load tunnels if the router is not running
|
||||||
|
// TODO: in future we might be able to view and edit tunnels while router is not running
|
||||||
|
RouterContext routerContext = Util.getRouterContext();
|
||||||
|
if (routerContext == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
List<TunnelEntry> ret = new ArrayList<>();
|
List<TunnelEntry> ret = new ArrayList<>();
|
||||||
List<TunnelController> controllers = mGroup.getControllers();
|
List<TunnelController> controllers = mGroup.getControllers();
|
||||||
for (int i = 0; i < controllers.size(); i++) {
|
for (int i = 0; i < controllers.size(); i++) {
|
||||||
|
@ -1,134 +0,0 @@
|
|||||||
package net.i2p.android.i2ptunnel;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.Spinner;
|
|
||||||
|
|
||||||
import net.i2p.android.router.I2PActivityBase;
|
|
||||||
import net.i2p.android.router.R;
|
|
||||||
|
|
||||||
public class TunnelListActivity extends I2PActivityBase implements
|
|
||||||
TunnelListFragment.OnTunnelSelectedListener,
|
|
||||||
TunnelDetailFragment.TunnelDetailListener {
|
|
||||||
/**
|
|
||||||
* Whether or not the activity is in two-pane mode, i.e. running on a tablet
|
|
||||||
* device.
|
|
||||||
*/
|
|
||||||
private boolean mTwoPane;
|
|
||||||
|
|
||||||
private static final String SELECTED_PAGE = "selected_page";
|
|
||||||
private static final int PAGE_CLIENT = 0;
|
|
||||||
|
|
||||||
private Spinner mSpinner;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean canUseTwoPanes() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
mSpinner = (Spinner) findViewById(R.id.main_spinner);
|
|
||||||
mSpinner.setVisibility(View.VISIBLE);
|
|
||||||
|
|
||||||
mSpinner.setAdapter(ArrayAdapter.createFromResource(this,
|
|
||||||
R.array.i2ptunnel_pages, android.R.layout.simple_spinner_dropdown_item));
|
|
||||||
|
|
||||||
mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
|
||||||
@Override
|
|
||||||
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
|
||||||
selectPage(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNothingSelected(AdapterView<?> adapterView) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (findViewById(R.id.detail_fragment) != null) {
|
|
||||||
// The detail container view will be present only in the
|
|
||||||
// large-screen layouts (res/values-large and
|
|
||||||
// res/values-sw600dp). If this view is present, then the
|
|
||||||
// activity should be in two-pane mode.
|
|
||||||
mTwoPane = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
|
||||||
int selected = savedInstanceState.getInt(SELECTED_PAGE);
|
|
||||||
mSpinner.setSelection(selected);
|
|
||||||
} else
|
|
||||||
selectPage(PAGE_CLIENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
outState.putInt(SELECTED_PAGE, mSpinner.getSelectedItemPosition());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void selectPage(int page) {
|
|
||||||
TunnelListFragment f = new TunnelListFragment();
|
|
||||||
Bundle args = new Bundle();
|
|
||||||
args.putBoolean(TunnelListFragment.SHOW_CLIENT_TUNNELS, page == PAGE_CLIENT);
|
|
||||||
f.setArguments(args);
|
|
||||||
|
|
||||||
// In two-pane mode, list items should be given the
|
|
||||||
// 'activated' state when touched.
|
|
||||||
if (mTwoPane)
|
|
||||||
f.setActivateOnItemClick(true);
|
|
||||||
|
|
||||||
getSupportFragmentManager().beginTransaction()
|
|
||||||
.replace(R.id.main_fragment, f).commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TunnelListFragment.OnTunnelSelectedListener
|
|
||||||
|
|
||||||
public void onTunnelSelected(int tunnelId) {
|
|
||||||
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);
|
|
||||||
getSupportFragmentManager().beginTransaction()
|
|
||||||
.replace(R.id.detail_fragment, detailFrag).commit();
|
|
||||||
} else {
|
|
||||||
// In single-pane mode, simply start the detail activity
|
|
||||||
// for the selected item ID.
|
|
||||||
Intent detailIntent = new Intent(this, TunnelDetailActivity.class);
|
|
||||||
detailIntent.putExtra(TunnelDetailFragment.TUNNEL_ID, tunnelId);
|
|
||||||
startActivity(detailIntent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TunnelDetailFragment.TunnelDetailListener
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEditTunnel(int tunnelId) {
|
|
||||||
EditTunnelFragment editFrag = EditTunnelFragment.newInstance(tunnelId);
|
|
||||||
getSupportFragmentManager().beginTransaction()
|
|
||||||
.replace(R.id.detail_fragment, editFrag)
|
|
||||||
.addToBackStack("")
|
|
||||||
.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onTunnelDeleted(int tunnelId, int numTunnelsLeft) {
|
|
||||||
// Should only get here in two-pane mode, but just to be safe:
|
|
||||||
if (mTwoPane) {
|
|
||||||
if (numTunnelsLeft > 0) {
|
|
||||||
TunnelDetailFragment detailFrag = TunnelDetailFragment.newInstance(
|
|
||||||
(tunnelId > 0 ? tunnelId - 1 : 0));
|
|
||||||
getSupportFragmentManager().beginTransaction()
|
|
||||||
.replace(R.id.detail_fragment, detailFrag).commit();
|
|
||||||
} else {
|
|
||||||
TunnelDetailFragment detailFrag = (TunnelDetailFragment) getSupportFragmentManager().findFragmentById(R.id.detail_fragment);
|
|
||||||
getSupportFragmentManager().beginTransaction()
|
|
||||||
.remove(detailFrag).commit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +1,39 @@
|
|||||||
package net.i2p.android.i2ptunnel;
|
package net.i2p.android.i2ptunnel;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.ListFragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.app.LoaderManager;
|
import android.support.v4.app.LoaderManager;
|
||||||
import android.support.v4.content.Loader;
|
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.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuInflater;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.FrameLayout;
|
|
||||||
import android.widget.ImageButton;
|
|
||||||
import android.widget.ListView;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import net.i2p.android.help.HelpActivity;
|
import com.pnikosis.materialishprogress.ProgressWheel;
|
||||||
import net.i2p.android.i2ptunnel.util.TunnelUtil;
|
|
||||||
import net.i2p.android.router.I2PFragmentBase;
|
|
||||||
import net.i2p.android.router.I2PFragmentBase.RouterContextProvider;
|
|
||||||
import net.i2p.android.router.R;
|
import net.i2p.android.router.R;
|
||||||
|
import net.i2p.android.router.service.RouterService;
|
||||||
|
import net.i2p.android.router.service.State;
|
||||||
|
import net.i2p.android.router.util.Util;
|
||||||
|
import net.i2p.android.util.FragmentUtils;
|
||||||
|
import net.i2p.android.widget.DividerItemDecoration;
|
||||||
|
import net.i2p.android.widget.LoadingRecyclerView;
|
||||||
import net.i2p.i2ptunnel.TunnelControllerGroup;
|
import net.i2p.i2ptunnel.TunnelControllerGroup;
|
||||||
import net.i2p.i2ptunnel.ui.TunnelConfig;
|
|
||||||
import net.i2p.router.RouterContext;
|
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class TunnelListFragment extends ListFragment implements
|
public class TunnelListFragment extends Fragment implements
|
||||||
I2PFragmentBase.RouterContextUser,
|
|
||||||
LoaderManager.LoaderCallbacks<List<TunnelEntry>> {
|
LoaderManager.LoaderCallbacks<List<TunnelEntry>> {
|
||||||
public static final String SHOW_CLIENT_TUNNELS = "show_client_tunnels";
|
public static final String SHOW_CLIENT_TUNNELS = "show_client_tunnels";
|
||||||
public static final String TUNNEL_WIZARD_DATA = "tunnel_wizard_data";
|
|
||||||
|
|
||||||
static final int TUNNEL_WIZARD_REQUEST = 1;
|
|
||||||
|
|
||||||
private static final int CLIENT_LOADER_ID = 1;
|
private static final int CLIENT_LOADER_ID = 1;
|
||||||
private static final int SERVER_LOADER_ID = 2;
|
private static final int SERVER_LOADER_ID = 2;
|
||||||
@ -44,23 +43,26 @@ public class TunnelListFragment extends ListFragment implements
|
|||||||
*/
|
*/
|
||||||
private static final String STATE_ACTIVATED_POSITION = "activated_position";
|
private static final String STATE_ACTIVATED_POSITION = "activated_position";
|
||||||
|
|
||||||
private boolean mOnActivityCreated;
|
|
||||||
RouterContextProvider mRouterContextProvider;
|
|
||||||
OnTunnelSelectedListener mCallback;
|
OnTunnelSelectedListener mCallback;
|
||||||
|
FragmentUtils.TwoPaneProvider mTwoPane;
|
||||||
private TunnelControllerGroup mGroup;
|
private TunnelControllerGroup mGroup;
|
||||||
|
|
||||||
|
private LoadingRecyclerView mRecyclerView;
|
||||||
private TunnelEntryAdapter mAdapter;
|
private TunnelEntryAdapter mAdapter;
|
||||||
private boolean mClientTunnels;
|
private boolean mClientTunnels;
|
||||||
/**
|
|
||||||
* The current activated item position. Only used on tablets.
|
|
||||||
*/
|
|
||||||
private int mActivatedPosition = ListView.INVALID_POSITION;
|
|
||||||
private boolean mActivateOnItemClick = false;
|
|
||||||
|
|
||||||
private ImageButton mNewTunnel;
|
|
||||||
|
|
||||||
// Container Activity must implement this interface
|
// Container Activity must implement this interface
|
||||||
public interface OnTunnelSelectedListener {
|
public interface OnTunnelSelectedListener {
|
||||||
public void onTunnelSelected(int tunnelId);
|
void onTunnelSelected(int tunnelId, Pair<View, String> tunnelName,
|
||||||
|
Pair<View, String> tunnelDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TunnelListFragment newInstance(boolean showClientTunnels) {
|
||||||
|
TunnelListFragment f = new TunnelListFragment();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putBoolean(TunnelListFragment.SHOW_CLIENT_TUNNELS, showClientTunnels);
|
||||||
|
f.setArguments(args);
|
||||||
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -69,47 +71,23 @@ public class TunnelListFragment extends ListFragment implements
|
|||||||
|
|
||||||
// This makes sure that the container activity has implemented
|
// This makes sure that the container activity has implemented
|
||||||
// the callback interface. If not, it throws an exception
|
// the callback interface. If not, it throws an exception
|
||||||
try {
|
mCallback = FragmentUtils.getParent(this, OnTunnelSelectedListener.class);
|
||||||
mRouterContextProvider = (RouterContextProvider) activity;
|
if (mCallback == null)
|
||||||
} catch (ClassCastException e) {
|
throw new ClassCastException("Parent must implement OnTunnelSelectedListener");
|
||||||
throw new ClassCastException(activity.toString()
|
mTwoPane = FragmentUtils.getParent(this, FragmentUtils.TwoPaneProvider.class);
|
||||||
+ " must implement RouterContextProvider");
|
if (mTwoPane == null)
|
||||||
}
|
throw new ClassCastException("Parent must implement TwoPaneProvider");
|
||||||
|
|
||||||
// This makes sure that the container activity has implemented
|
|
||||||
// the callback interface. If not, it throws an exception
|
|
||||||
try {
|
|
||||||
mCallback = (OnTunnelSelectedListener) activity;
|
|
||||||
} catch (ClassCastException e) {
|
|
||||||
throw new ClassCastException(activity.toString()
|
|
||||||
+ " must implement OnTunnelSelectedListener");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setHasOptionsMenu(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
// Create the list fragment's content view by calling the super method
|
View v = inflater.inflate(R.layout.fragment_list, container, false);
|
||||||
final View listFragmentView = super.onCreateView(inflater, container, savedInstanceState);
|
|
||||||
|
|
||||||
View v = inflater.inflate(R.layout.fragment_list_with_add, container, false);
|
mRecyclerView = (LoadingRecyclerView) v.findViewById(R.id.list);
|
||||||
FrameLayout listContainer = (FrameLayout) v.findViewById(R.id.list_container);
|
View empty = v.findViewById(R.id.empty);
|
||||||
listContainer.addView(listFragmentView);
|
ProgressWheel loading = (ProgressWheel) v.findViewById(R.id.loading);
|
||||||
|
mRecyclerView.setLoadingView(empty, loading);
|
||||||
mNewTunnel = (ImageButton) v.findViewById(R.id.promoted_action);
|
|
||||||
mNewTunnel.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
Intent wi = new Intent(getActivity(), TunnelWizardActivity.class);
|
|
||||||
startActivityForResult(wi, TUNNEL_WIZARD_REQUEST);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
@ -117,156 +95,116 @@ public class TunnelListFragment extends ListFragment implements
|
|||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
// Restore the previously serialized activated item position.
|
|
||||||
if (savedInstanceState != null
|
|
||||||
&& savedInstanceState.containsKey(STATE_ACTIVATED_POSITION)) {
|
|
||||||
setActivatedPosition(savedInstanceState
|
|
||||||
.getInt(STATE_ACTIVATED_POSITION));
|
|
||||||
}
|
|
||||||
|
|
||||||
// When setting CHOICE_MODE_SINGLE, ListView will automatically
|
|
||||||
// give items the 'activated' state when touched.
|
|
||||||
getListView().setChoiceMode(
|
|
||||||
mActivateOnItemClick ? ListView.CHOICE_MODE_SINGLE
|
|
||||||
: ListView.CHOICE_MODE_NONE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivityCreated(Bundle savedInstanceState) {
|
public void onActivityCreated(Bundle savedInstanceState) {
|
||||||
super.onActivityCreated(savedInstanceState);
|
super.onActivityCreated(savedInstanceState);
|
||||||
mAdapter = new TunnelEntryAdapter(getActivity());
|
|
||||||
mClientTunnels = getArguments().getBoolean(SHOW_CLIENT_TUNNELS);
|
mClientTunnels = getArguments().getBoolean(SHOW_CLIENT_TUNNELS);
|
||||||
|
|
||||||
setListAdapter(mAdapter);
|
mRecyclerView.setHasFixedSize(true);
|
||||||
|
mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST));
|
||||||
|
|
||||||
mOnActivityCreated = true;
|
// use a linear layout manager
|
||||||
if (getRouterContext() != null)
|
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
|
||||||
onRouterConnectionReady();
|
mRecyclerView.setLayoutManager(mLayoutManager);
|
||||||
|
|
||||||
|
// Set the adapter for the list view
|
||||||
|
mAdapter = new TunnelEntryAdapter(getActivity(), mClientTunnels, mCallback, mTwoPane);
|
||||||
|
mRecyclerView.setAdapter(mAdapter);
|
||||||
|
|
||||||
|
// Restore the previously serialized activated item position.
|
||||||
|
if (savedInstanceState != null
|
||||||
|
&& savedInstanceState.containsKey(STATE_ACTIVATED_POSITION))
|
||||||
|
mAdapter.setActivatedPosition(savedInstanceState
|
||||||
|
.getInt(STATE_ACTIVATED_POSITION));
|
||||||
else
|
else
|
||||||
setEmptyText(getResources().getString(
|
mAdapter.clearActivatedPosition();
|
||||||
R.string.router_not_running));
|
|
||||||
|
// Initialize the adapter in case the RouterService has not been created
|
||||||
|
if (Util.getRouterContext() == null)
|
||||||
|
mAdapter.setTunnels(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onRouterConnectionReady() {
|
@Override
|
||||||
String error;
|
public void onStart() {
|
||||||
try {
|
super.onStart();
|
||||||
mGroup = TunnelControllerGroup.getInstance();
|
|
||||||
error = mGroup == null ? getResources().getString(R.string.i2ptunnel_not_initialized) : null;
|
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getActivity());
|
||||||
} catch (IllegalArgumentException iae) {
|
|
||||||
mGroup = null;
|
IntentFilter filter = new IntentFilter();
|
||||||
error = iae.toString();
|
filter.addAction(RouterService.LOCAL_BROADCAST_STATE_NOTIFICATION);
|
||||||
|
filter.addAction(RouterService.LOCAL_BROADCAST_STATE_CHANGED);
|
||||||
|
lbm.registerReceiver(onStateChange, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private State lastRouterState = null;
|
||||||
|
private BroadcastReceiver onStateChange = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
State state = intent.getParcelableExtra(RouterService.LOCAL_BROADCAST_EXTRA_STATE);
|
||||||
|
if (lastRouterState == null || lastRouterState != state) {
|
||||||
|
updateState(state);
|
||||||
|
lastRouterState = state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initTunnels() {
|
||||||
|
if (mGroup == null) {
|
||||||
|
try {
|
||||||
|
mGroup = TunnelControllerGroup.getInstance();
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
Util.e("Could not load tunnels", iae);
|
||||||
|
mGroup = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mGroup == null) {
|
if (mGroup != null && isAdded()) {
|
||||||
setEmptyText(error);
|
mRecyclerView.setLoading(true);
|
||||||
} else {
|
|
||||||
if (mClientTunnels)
|
|
||||||
setEmptyText("No configured client tunnels.");
|
|
||||||
else
|
|
||||||
setEmptyText("No configured server tunnels.");
|
|
||||||
|
|
||||||
setListShown(false);
|
|
||||||
getLoaderManager().initLoader(mClientTunnels ? CLIENT_LOADER_ID
|
getLoaderManager().initLoader(mClientTunnels ? CLIENT_LOADER_ID
|
||||||
: SERVER_LOADER_ID, null, this);
|
: SERVER_LOADER_ID, null, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onListItemClick(ListView parent, View view, int pos, long id) {
|
public void onResume() {
|
||||||
super.onListItemClick(parent, view, pos, id);
|
super.onResume();
|
||||||
mCallback.onTunnelSelected(mAdapter.getItem(pos).getId());
|
|
||||||
|
// Triggers loader init via updateState() if the router is running
|
||||||
|
LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(new Intent(RouterService.LOCAL_BROADCAST_REQUEST_STATE));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
if (mActivatedPosition != ListView.INVALID_POSITION) {
|
int activatedPosition = mAdapter.getActivatedPosition();
|
||||||
|
if (activatedPosition >= 0) {
|
||||||
// Serialize and persist the activated item position.
|
// Serialize and persist the activated item position.
|
||||||
outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition);
|
outState.putInt(STATE_ACTIVATED_POSITION, activatedPosition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public void onStop() {
|
||||||
inflater.inflate(R.menu.fragment_i2ptunnel_list_actions, menu);
|
super.onStop();
|
||||||
if (getRouterContext() == null) {
|
|
||||||
mNewTunnel.setVisibility(View.GONE);
|
LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(onStateChange);
|
||||||
menu.findItem(R.id.action_start_all_tunnels).setVisible(false);
|
|
||||||
menu.findItem(R.id.action_stop_all_tunnels).setVisible(false);
|
|
||||||
menu.findItem(R.id.action_restart_all_tunnels).setVisible(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void addTunnel(TunnelEntry tunnelEntry) {
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
mAdapter.addTunnel(tunnelEntry);
|
||||||
// Handle presses on the action bar items
|
|
||||||
List<String> msgs;
|
|
||||||
switch (item.getItemId()) {
|
|
||||||
case R.id.action_start_all_tunnels:
|
|
||||||
msgs = mGroup.startAllControllers();
|
|
||||||
break;
|
|
||||||
case R.id.action_stop_all_tunnels:
|
|
||||||
msgs = mGroup.stopAllControllers();
|
|
||||||
break;
|
|
||||||
case R.id.action_restart_all_tunnels:
|
|
||||||
msgs = mGroup.restartAllControllers();
|
|
||||||
break;
|
|
||||||
case R.id.action_i2ptunnel_help:
|
|
||||||
Intent hi = new Intent(getActivity(), HelpActivity.class);
|
|
||||||
hi.putExtra(HelpActivity.CATEGORY, HelpActivity.CAT_I2PTUNNEL);
|
|
||||||
startActivity(hi);
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
// TODO: Do something with the other messages
|
|
||||||
if (msgs.size() > 0)
|
|
||||||
Toast.makeText(getActivity().getApplicationContext(),
|
|
||||||
msgs.get(0), Toast.LENGTH_LONG).show();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
||||||
if (requestCode == TUNNEL_WIZARD_REQUEST) {
|
|
||||||
if (resultCode == Activity.RESULT_OK) {
|
|
||||||
Bundle tunnelData = data.getExtras().getBundle(TUNNEL_WIZARD_DATA);
|
|
||||||
TunnelConfig cfg = TunnelUtil.createConfigFromWizard(getActivity(), mGroup, tunnelData);
|
|
||||||
TunnelEntry tunnel = TunnelEntry.createNewTunnel(getActivity(), mGroup, cfg);
|
|
||||||
mAdapter.add(tunnel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Turns on activate-on-click mode. When this mode is on, list items will be
|
|
||||||
* given the 'activated' state when touched.
|
|
||||||
*/
|
|
||||||
public void setActivateOnItemClick(boolean activateOnItemClick) {
|
|
||||||
mActivateOnItemClick = activateOnItemClick;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setActivatedPosition(int position) {
|
|
||||||
if (position == ListView.INVALID_POSITION) {
|
|
||||||
getListView().setItemChecked(mActivatedPosition, false);
|
|
||||||
} else {
|
|
||||||
getListView().setItemChecked(position, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
mActivatedPosition = position;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Duplicated from I2PFragmentBase because this extends ListFragment
|
|
||||||
private RouterContext getRouterContext() {
|
|
||||||
return mRouterContextProvider.getRouterContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
// I2PFragmentBase.RouterContextUser
|
|
||||||
|
|
||||||
public void onRouterBind() {
|
|
||||||
if (mOnActivityCreated)
|
|
||||||
onRouterConnectionReady();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoaderManager.LoaderCallbacks<List<TunnelEntry>>
|
// LoaderManager.LoaderCallbacks<List<TunnelEntry>>
|
||||||
@ -279,20 +217,17 @@ public class TunnelListFragment extends ListFragment implements
|
|||||||
List<TunnelEntry> data) {
|
List<TunnelEntry> data) {
|
||||||
if (loader.getId() == (mClientTunnels ?
|
if (loader.getId() == (mClientTunnels ?
|
||||||
CLIENT_LOADER_ID : SERVER_LOADER_ID)) {
|
CLIENT_LOADER_ID : SERVER_LOADER_ID)) {
|
||||||
mAdapter.setData(data);
|
mAdapter.setTunnels(data);
|
||||||
|
|
||||||
if (isResumed()) {
|
|
||||||
setListShown(true);
|
|
||||||
} else {
|
|
||||||
setListShownNoAnimation(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onLoaderReset(Loader<List<TunnelEntry>> loader) {
|
public void onLoaderReset(Loader<List<TunnelEntry>> loader) {
|
||||||
if (loader.getId() == (mClientTunnels ?
|
if (loader.getId() == (mClientTunnels ?
|
||||||
CLIENT_LOADER_ID : SERVER_LOADER_ID)) {
|
CLIENT_LOADER_ID : SERVER_LOADER_ID)) {
|
||||||
mAdapter.setData(null);
|
if (Util.getRouterContext() == null)
|
||||||
|
mAdapter.setTunnels(null);
|
||||||
|
else
|
||||||
|
mAdapter.setTunnels(new ArrayList<TunnelEntry>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package net.i2p.android.i2ptunnel;
|
package net.i2p.android.i2ptunnel;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.app.DialogFragment;
|
import android.support.v4.app.DialogFragment;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
|
||||||
import net.i2p.android.router.R;
|
import net.i2p.android.router.R;
|
||||||
import net.i2p.android.wizard.model.AbstractWizardModel;
|
import net.i2p.android.wizard.model.AbstractWizardModel;
|
||||||
@ -32,7 +32,7 @@ public class TunnelWizardActivity extends AbstractWizardActivity {
|
|||||||
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
Intent result = new Intent();
|
Intent result = new Intent();
|
||||||
result.putExtra(TunnelListFragment.TUNNEL_WIZARD_DATA, mWizardModel.save());
|
result.putExtra(TunnelsContainer.TUNNEL_WIZARD_DATA, mWizardModel.save());
|
||||||
setResult(Activity.RESULT_OK, result);
|
setResult(Activity.RESULT_OK, result);
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
finish();
|
finish();
|
||||||
|
@ -0,0 +1,289 @@
|
|||||||
|
package net.i2p.android.i2ptunnel;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
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.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.viewpagerindicator.TitlePageIndicator;
|
||||||
|
|
||||||
|
import net.i2p.android.i2ptunnel.preferences.EditTunnelContainerFragment;
|
||||||
|
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.TunnelControllerGroup;
|
||||||
|
import net.i2p.i2ptunnel.ui.TunnelConfig;
|
||||||
|
import net.i2p.router.RouterContext;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TunnelsContainer extends Fragment implements
|
||||||
|
FragmentUtils.TwoPaneProvider,
|
||||||
|
TunnelListFragment.OnTunnelSelectedListener,
|
||||||
|
TunnelDetailFragment.TunnelDetailListener {
|
||||||
|
static final int TUNNEL_WIZARD_REQUEST = 1;
|
||||||
|
public static final String TUNNEL_WIZARD_DATA = "tunnel_wizard_data";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the activity is in two-pane mode, i.e. running on a tablet
|
||||||
|
* device.
|
||||||
|
*/
|
||||||
|
private boolean mTwoPane;
|
||||||
|
|
||||||
|
ViewPager mViewPager;
|
||||||
|
TitlePageIndicator mPageIndicator;
|
||||||
|
FragmentPagerAdapter mFragPagerAdapter;
|
||||||
|
|
||||||
|
private static final String FRAGMENT_CLIENT = "client_fragment";
|
||||||
|
private static final String FRAGMENT_SERVER = "server_fragment";
|
||||||
|
private static final int FRAGMENT_ID_CLIENT = 0;
|
||||||
|
private static final int FRAGMENT_ID_SERVER = 1;
|
||||||
|
TunnelListFragment mClientFrag;
|
||||||
|
TunnelListFragment mServerFrag;
|
||||||
|
|
||||||
|
private ImageButton mNewTunnel;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
mClientFrag = (TunnelListFragment) getChildFragmentManager().getFragment(
|
||||||
|
savedInstanceState, FRAGMENT_CLIENT);
|
||||||
|
mServerFrag = (TunnelListFragment) getChildFragmentManager().getFragment(
|
||||||
|
savedInstanceState, FRAGMENT_SERVER);
|
||||||
|
}
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityCreated(Bundle savedInstanceState) {
|
||||||
|
super.onActivityCreated(savedInstanceState);
|
||||||
|
|
||||||
|
mFragPagerAdapter = new TunnelsPagerAdapter(getChildFragmentManager());
|
||||||
|
mViewPager.setAdapter(mFragPagerAdapter);
|
||||||
|
|
||||||
|
// Bind the page indicator to the pager.
|
||||||
|
mPageIndicator.setViewPager(mViewPager);
|
||||||
|
|
||||||
|
mNewTunnel.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
Intent wi = new Intent(getActivity(), TunnelWizardActivity.class);
|
||||||
|
startActivityForResult(wi, TUNNEL_WIZARD_REQUEST);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TunnelsPagerAdapter extends FragmentPagerAdapter {
|
||||||
|
private static final int NUM_ITEMS = 2;
|
||||||
|
|
||||||
|
public TunnelsPagerAdapter(FragmentManager fm) {
|
||||||
|
super(fm);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return NUM_ITEMS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Fragment getItem(int position) {
|
||||||
|
switch (position) {
|
||||||
|
case FRAGMENT_ID_CLIENT:
|
||||||
|
return (mClientFrag = TunnelListFragment.newInstance(true));
|
||||||
|
case FRAGMENT_ID_SERVER:
|
||||||
|
return (mServerFrag = TunnelListFragment.newInstance(false));
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence getPageTitle(int position) {
|
||||||
|
switch (position) {
|
||||||
|
case FRAGMENT_ID_CLIENT:
|
||||||
|
return getActivity().getString(R.string.label_i2ptunnel_client);
|
||||||
|
case FRAGMENT_ID_SERVER:
|
||||||
|
return getActivity().getString(R.string.label_i2ptunnel_server);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
inflater.inflate(R.menu.fragment_i2ptunnel_list_actions, menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
TunnelControllerGroup tcg = TunnelControllerGroup.getInstance();
|
||||||
|
if (tcg == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Handle presses on the action bar items
|
||||||
|
List<String> msgs;
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.action_start_all_tunnels:
|
||||||
|
msgs = tcg.startAllControllers();
|
||||||
|
break;
|
||||||
|
case R.id.action_stop_all_tunnels:
|
||||||
|
msgs = tcg.stopAllControllers();
|
||||||
|
break;
|
||||||
|
case R.id.action_restart_all_tunnels:
|
||||||
|
msgs = tcg.restartAllControllers();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
// TODO: Do something with the other messages
|
||||||
|
if (msgs.size() > 0)
|
||||||
|
Toast.makeText(getActivity().getApplicationContext(),
|
||||||
|
msgs.get(0), Toast.LENGTH_LONG).show();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
if (requestCode == TUNNEL_WIZARD_REQUEST) {
|
||||||
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
|
Bundle tunnelData = data.getExtras().getBundle(TUNNEL_WIZARD_DATA);
|
||||||
|
// TODO fetch earlier
|
||||||
|
TunnelControllerGroup tcg = TunnelControllerGroup.getInstance();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
|
||||||
|
// Since the pager fragments don't have known tags or IDs, the only way to persist the
|
||||||
|
// reference is to use putFragment/getFragment. Remember, we're not persisting the exact
|
||||||
|
// Fragment instance. This mechanism simply gives us a way to persist access to the
|
||||||
|
// 'current' fragment instance for the given fragment (which changes across orientation
|
||||||
|
// changes).
|
||||||
|
//
|
||||||
|
// The outcome of all this is that the "Refresh" menu button refreshes the stream across
|
||||||
|
// orientation changes.
|
||||||
|
if (mClientFrag != null)
|
||||||
|
getChildFragmentManager().putFragment(outState, FRAGMENT_CLIENT, mClientFrag);
|
||||||
|
if (mServerFrag != null)
|
||||||
|
getChildFragmentManager().putFragment(outState, FRAGMENT_SERVER, mServerFrag);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FragmentUtils.TwoPaneProvider
|
||||||
|
|
||||||
|
public boolean isTwoPane() {
|
||||||
|
return mTwoPane;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TunnelListFragment.OnTunnelSelectedListener
|
||||||
|
|
||||||
|
public final void onTunnelSelected(int tunnelId, Pair<View, String> tunnelName,
|
||||||
|
Pair<View, String> tunnelDescription) {
|
||||||
|
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();
|
||||||
|
} else {
|
||||||
|
// In single-pane mode, simply start the detail activity
|
||||||
|
// for the selected item ID.
|
||||||
|
Intent detailIntent = new Intent(getActivity(), TunnelDetailActivity.class);
|
||||||
|
detailIntent.putExtra(TunnelDetailFragment.TUNNEL_ID, tunnelId);
|
||||||
|
|
||||||
|
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(
|
||||||
|
getActivity(), tunnelName, tunnelDescription);
|
||||||
|
ActivityCompat.startActivity(getActivity(), detailIntent, options.toBundle());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TunnelDetailFragment.TunnelDetailListener
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEditTunnel(int tunnelId) {
|
||||||
|
Fragment editFrag = EditTunnelContainerFragment.newInstance(tunnelId);
|
||||||
|
getChildFragmentManager().beginTransaction()
|
||||||
|
.replace(R.id.detail_fragment, editFrag)
|
||||||
|
.addToBackStack("")
|
||||||
|
.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onTunnelDeleted(int tunnelId, int numTunnelsLeft) {
|
||||||
|
// Should only get here in two-pane mode, but just to be safe:
|
||||||
|
if (mTwoPane) {
|
||||||
|
if (numTunnelsLeft > 0) {
|
||||||
|
TunnelDetailFragment detailFrag = TunnelDetailFragment.newInstance(
|
||||||
|
(tunnelId > 0 ? tunnelId - 1 : 0));
|
||||||
|
getChildFragmentManager().beginTransaction()
|
||||||
|
.replace(R.id.detail_fragment, detailFrag).commit();
|
||||||
|
} else {
|
||||||
|
TunnelDetailFragment detailFrag = (TunnelDetailFragment) getChildFragmentManager().findFragmentById(R.id.detail_fragment);
|
||||||
|
getChildFragmentManager().beginTransaction()
|
||||||
|
.remove(detailFrag).commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,193 @@
|
|||||||
|
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 net.i2p.android.i2ptunnel.util.TunnelLogic;
|
||||||
|
import net.i2p.android.i2ptunnel.util.TunnelUtil;
|
||||||
|
import net.i2p.android.router.R;
|
||||||
|
|
||||||
|
public class AdvancedTunnelPreferenceFragment extends BaseTunnelPreferenceFragment {
|
||||||
|
public static AdvancedTunnelPreferenceFragment newInstance(int tunnelId) {
|
||||||
|
AdvancedTunnelPreferenceFragment f = new AdvancedTunnelPreferenceFragment();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putInt(ARG_TUNNEL_ID, tunnelId);
|
||||||
|
f.setArguments(args);
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void loadPreferences() {
|
||||||
|
String type = TunnelUtil.getController(mGroup, mTunnelId).getType();
|
||||||
|
new TunnelPreferences(type).runLogic();
|
||||||
|
}
|
||||||
|
|
||||||
|
class TunnelPreferences extends TunnelLogic {
|
||||||
|
PreferenceScreen ps;
|
||||||
|
PreferenceCategory tunParamCategory;
|
||||||
|
|
||||||
|
public TunnelPreferences(String type) {
|
||||||
|
super(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void general() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void generalClient() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void generalClientStreamr(boolean isStreamr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void generalClientPort() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void generalClientPortStreamr(boolean isStreamr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void generalClientProxy(boolean isProxy) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void generalClientProxyHttp(boolean isHttp) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void generalClientStandardOrIrc(boolean isStandardOrIrc) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void generalClientIrc() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void generalServerHttp() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void generalServerHttpBidirOrStreamr(boolean isStreamr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void generalServerPort() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void generalServerPortStreamr(boolean isStreamr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void advanced() {
|
||||||
|
addPreferencesFromResource(R.xml.tunnel_adv);
|
||||||
|
ps = getPreferenceScreen();
|
||||||
|
tunParamCategory = (PreferenceCategory) ps.findPreference(
|
||||||
|
getString(R.string.TUNNEL_CAT_TUNNEL_PARAMS));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void advancedStreamr(boolean isStreamr) {
|
||||||
|
if (isStreamr)
|
||||||
|
tunParamCategory.removePreference(tunParamCategory.findPreference(getString(R.string.TUNNEL_OPT_PROFILE)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void advancedServerOrStreamrClient(boolean isServerOrStreamrClient) {
|
||||||
|
if (isServerOrStreamrClient)
|
||||||
|
tunParamCategory.removePreference(tunParamCategory.findPreference(getString(R.string.TUNNEL_OPT_DELAY_CONNECT)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void advancedServer() {
|
||||||
|
addPreferencesFromResource(R.xml.tunnel_adv_server);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void advancedServerHttp(boolean isHttp) {
|
||||||
|
if (isHttp)
|
||||||
|
addPreferencesFromResource(R.xml.tunnel_adv_server_http);
|
||||||
|
else {
|
||||||
|
PreferenceCategory accessCtlCategory = (PreferenceCategory) ps.findPreference(
|
||||||
|
getString(R.string.TUNNEL_CAT_ACCESS_CONTROL));
|
||||||
|
accessCtlCategory.removePreference(accessCtlCategory.findPreference(getString(R.string.TUNNEL_OPT_REJECT_INPROXY)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void advancedIdle() {
|
||||||
|
addPreferencesFromResource(R.xml.tunnel_adv_idle);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void advancedIdleServerOrStreamrClient(boolean isServerOrStreamrClient) {
|
||||||
|
if (isServerOrStreamrClient)
|
||||||
|
ps.removePreference(ps.findPreference(getString(R.string.TUNNEL_OPT_DELAY_OPEN)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void advancedClient() {
|
||||||
|
PreferenceCategory idleCategory = (PreferenceCategory) ps.findPreference(
|
||||||
|
getString(R.string.TUNNEL_CAT_IDLE)
|
||||||
|
);
|
||||||
|
addPreferencesFromResource(R.xml.tunnel_adv_idle_client, idleCategory);
|
||||||
|
|
||||||
|
// PERSISTENT_KEY and NEW_KEYS can't be set simultaneously
|
||||||
|
final CheckBoxPreference nk = (CheckBoxPreference) findPreference(getString(R.string.TUNNEL_OTP_NEW_KEYS));
|
||||||
|
nk.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceChange(Preference preference, Object o) {
|
||||||
|
final SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
|
||||||
|
if ((Boolean) o && prefs.getBoolean(getString(R.string.TUNNEL_OPT_PERSISTENT_KEY),
|
||||||
|
getResources().getBoolean(R.bool.DEFAULT_PERSISTENT_KEY))) {
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||||
|
builder.setTitle(R.string.new_keys_on_reopen_conflict_title)
|
||||||
|
.setMessage(R.string.new_keys_on_reopen_conflict_msg)
|
||||||
|
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialogInterface, int i) {
|
||||||
|
SharedPreferences.Editor editor = prefs.edit();
|
||||||
|
editor.putBoolean(getString(R.string.TUNNEL_OPT_PERSISTENT_KEY), false);
|
||||||
|
editor.apply();
|
||||||
|
nk.setChecked(true);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialogInterface, int i) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.show();
|
||||||
|
return false;
|
||||||
|
} else
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void advancedClientHttp() {
|
||||||
|
addPreferencesFromResource(R.xml.tunnel_adv_client_http);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void advancedClientProxy() {
|
||||||
|
addPreferencesFromResource(R.xml.tunnel_adv_client_proxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void advancedOther() {
|
||||||
|
addPreferencesFromResource(R.xml.tunnel_adv_other);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
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 net.i2p.I2PAppContext;
|
||||||
|
import net.i2p.android.i2ptunnel.util.TunnelUtil;
|
||||||
|
import net.i2p.android.router.R;
|
||||||
|
import net.i2p.i2ptunnel.TunnelControllerGroup;
|
||||||
|
import net.i2p.i2ptunnel.ui.TunnelConfig;
|
||||||
|
|
||||||
|
public abstract class BaseTunnelPreferenceFragment extends PreferenceFragment {
|
||||||
|
protected static final String ARG_TUNNEL_ID = "tunnelId";
|
||||||
|
|
||||||
|
protected TunnelControllerGroup mGroup;
|
||||||
|
protected int mTunnelId;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle paramBundle) {
|
||||||
|
super.onCreate(paramBundle);
|
||||||
|
|
||||||
|
String error;
|
||||||
|
try {
|
||||||
|
mGroup = TunnelControllerGroup.getInstance();
|
||||||
|
error = mGroup == null ? getResources().getString(R.string.i2ptunnel_not_initialized) : null;
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
mGroup = null;
|
||||||
|
error = iae.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mGroup == null) {
|
||||||
|
// TODO Show error
|
||||||
|
} else if (getArguments().containsKey(ARG_TUNNEL_ID)) {
|
||||||
|
mTunnelId = getArguments().getInt(ARG_TUNNEL_ID, 0);
|
||||||
|
TunnelUtil.writeTunnelToPreferences(getActivity(), mGroup, mTunnelId);
|
||||||
|
// https://stackoverflow.com/questions/17880437/which-settings-file-does-preferencefragment-read-write
|
||||||
|
getPreferenceManager().setSharedPreferencesName(TunnelUtil.getPreferencesFilename(mTunnelId));
|
||||||
|
loadPreferences();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
|
||||||
|
// Pre-Honeycomb: onPause() is the last method guaranteed to be called.
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB)
|
||||||
|
saveTunnel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
|
||||||
|
// Honeycomb and above: onStop() is the last method guaranteed to be called.
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
|
||||||
|
saveTunnel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveTunnel() {
|
||||||
|
if (mGroup != null) {
|
||||||
|
TunnelConfig cfg = TunnelUtil.createConfigFromPreferences(getActivity(), mGroup, mTunnelId);
|
||||||
|
TunnelUtil.saveTunnel(I2PAppContext.getGlobalContext(), mGroup, mTunnelId, cfg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void loadPreferences();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* http://stackoverflow.com/a/20806812
|
||||||
|
*
|
||||||
|
* @param id the Preferences XML to load
|
||||||
|
* @param newParent the parent PreferenceGroup to add the new Preferences to.
|
||||||
|
*/
|
||||||
|
protected void addPreferencesFromResource(int id, PreferenceGroup newParent) {
|
||||||
|
PreferenceScreen screen = getPreferenceScreen();
|
||||||
|
int last = screen.getPreferenceCount();
|
||||||
|
addPreferencesFromResource(id);
|
||||||
|
while (screen.getPreferenceCount() > last) {
|
||||||
|
Preference p = screen.getPreference(last);
|
||||||
|
screen.removePreference(p); // decreases the preference count
|
||||||
|
newParent.addPreference(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
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 net.i2p.android.i2ptunnel.TunnelDetailActivity;
|
||||||
|
import net.i2p.android.i2ptunnel.TunnelDetailFragment;
|
||||||
|
import net.i2p.android.router.R;
|
||||||
|
import net.i2p.android.util.LocaleManager;
|
||||||
|
|
||||||
|
public class EditTunnelActivity extends AppCompatActivity {
|
||||||
|
private int mTunnelId;
|
||||||
|
|
||||||
|
private final LocaleManager localeManager = new LocaleManager();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
localeManager.onCreate(this);
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_single_fragment);
|
||||||
|
|
||||||
|
// Set the action bar
|
||||||
|
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
mTunnelId = getIntent().getIntExtra(TunnelDetailFragment.TUNNEL_ID, 0);
|
||||||
|
Fragment editFrag = GeneralTunnelPreferenceFragment.newInstance(mTunnelId);
|
||||||
|
getSupportFragmentManager().beginTransaction()
|
||||||
|
.add(R.id.fragment, editFrag).commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
localeManager.onResume(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onSupportNavigateUp() {
|
||||||
|
FragmentManager fragmentManager = getSupportFragmentManager();
|
||||||
|
if (fragmentManager.getBackStackEntryCount() > 0) {
|
||||||
|
fragmentManager.popBackStack();
|
||||||
|
} else {
|
||||||
|
Intent intent = new Intent(this, TunnelDetailActivity.class);
|
||||||
|
intent.putExtra(TunnelDetailFragment.TUNNEL_ID, mTunnelId);
|
||||||
|
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
|
startActivity(intent);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
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.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import net.i2p.android.router.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A shim that emulates EditTunnelActivity to provide a Toolbar with navigation
|
||||||
|
* in two-pane mode.
|
||||||
|
*/
|
||||||
|
public class EditTunnelContainerFragment extends Fragment {
|
||||||
|
private static final String ARG_TUNNEL_ID = "tunnelId";
|
||||||
|
|
||||||
|
public static EditTunnelContainerFragment newInstance(int tunnelId) {
|
||||||
|
EditTunnelContainerFragment f = new EditTunnelContainerFragment();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putInt(ARG_TUNNEL_ID, tunnelId);
|
||||||
|
f.setArguments(args);
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
View v = inflater.inflate(R.layout.activity_single_fragment, container, false);
|
||||||
|
|
||||||
|
// Set the action bar
|
||||||
|
Toolbar toolbar = (Toolbar) v.findViewById(R.id.main_toolbar);
|
||||||
|
toolbar.setTitle(R.string.edit_tunnel);
|
||||||
|
toolbar.setNavigationIcon(getResources().getDrawable(R.drawable.ic_arrow_back_white_24dp));
|
||||||
|
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
// Try and navigate back through the edit tunnel fragments.
|
||||||
|
// Otherwise, pop us back off.
|
||||||
|
FragmentManager fragmentManager = getChildFragmentManager();
|
||||||
|
if (fragmentManager.getBackStackEntryCount() > 0)
|
||||||
|
fragmentManager.popBackStack();
|
||||||
|
else
|
||||||
|
getFragmentManager().popBackStack();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
int tunnelId = getArguments().getInt(ARG_TUNNEL_ID);
|
||||||
|
BaseTunnelPreferenceFragment editFrag = GeneralTunnelPreferenceFragment.newInstance(tunnelId);
|
||||||
|
getChildFragmentManager().beginTransaction()
|
||||||
|
.add(R.id.fragment, editFrag).commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,28 +1,29 @@
|
|||||||
package net.i2p.android.i2ptunnel;
|
package net.i2p.android.i2ptunnel.preferences;
|
||||||
|
|
||||||
import android.os.Build;
|
import android.content.DialogInterface;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.preference.CheckBoxPreference;
|
||||||
|
import android.preference.ListPreference;
|
||||||
import android.preference.Preference;
|
import android.preference.Preference;
|
||||||
import android.preference.PreferenceCategory;
|
import android.preference.PreferenceCategory;
|
||||||
import android.preference.PreferenceGroup;
|
|
||||||
import android.preference.PreferenceScreen;
|
import android.preference.PreferenceScreen;
|
||||||
import android.support.v4.preference.PreferenceFragment;
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
|
||||||
import net.i2p.android.i2ptunnel.util.TunnelLogic;
|
import net.i2p.android.i2ptunnel.util.TunnelLogic;
|
||||||
import net.i2p.android.i2ptunnel.util.TunnelUtil;
|
import net.i2p.android.i2ptunnel.util.TunnelUtil;
|
||||||
import net.i2p.android.router.R;
|
import net.i2p.android.router.R;
|
||||||
import net.i2p.i2ptunnel.TunnelControllerGroup;
|
import net.i2p.util.Addresses;
|
||||||
import net.i2p.i2ptunnel.ui.TunnelConfig;
|
|
||||||
|
|
||||||
public class EditTunnelFragment extends PreferenceFragment {
|
import java.util.Set;
|
||||||
private static final String ARG_TUNNEL_ID = "tunnelId";
|
|
||||||
|
|
||||||
private TunnelControllerGroup mGroup;
|
public class GeneralTunnelPreferenceFragment extends BaseTunnelPreferenceFragment {
|
||||||
private int mTunnelId;
|
private CheckBoxPreference persistentKeys;
|
||||||
|
|
||||||
public static EditTunnelFragment newInstance(int tunnelId) {
|
public static GeneralTunnelPreferenceFragment newInstance(int tunnelId) {
|
||||||
EditTunnelFragment f = new EditTunnelFragment();
|
GeneralTunnelPreferenceFragment f = new GeneralTunnelPreferenceFragment();
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
args.putInt(ARG_TUNNEL_ID, tunnelId);
|
args.putInt(ARG_TUNNEL_ID, tunnelId);
|
||||||
f.setArguments(args);
|
f.setArguments(args);
|
||||||
@ -30,65 +31,27 @@ public class EditTunnelFragment extends PreferenceFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle paramBundle) {
|
protected void loadPreferences() {
|
||||||
super.onCreate(paramBundle);
|
|
||||||
|
|
||||||
String error;
|
|
||||||
try {
|
|
||||||
mGroup = TunnelControllerGroup.getInstance();
|
|
||||||
error = mGroup == null ? getResources().getString(R.string.i2ptunnel_not_initialized) : null;
|
|
||||||
} catch (IllegalArgumentException iae) {
|
|
||||||
mGroup = null;
|
|
||||||
error = iae.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mGroup == null) {
|
|
||||||
// TODO Show error
|
|
||||||
} else if (getArguments().containsKey(ARG_TUNNEL_ID)) {
|
|
||||||
mTunnelId = getArguments().getInt(ARG_TUNNEL_ID, 0);
|
|
||||||
TunnelUtil.writeTunnelToPreferences(getActivity(), mGroup, mTunnelId);
|
|
||||||
// https://stackoverflow.com/questions/17880437/which-settings-file-does-preferencefragment-read-write
|
|
||||||
getPreferenceManager().setSharedPreferencesName(TunnelUtil.getPreferencesFilename(mTunnelId));
|
|
||||||
loadPreferences();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
|
|
||||||
// Pre-Honeycomb: onPause() is the last method guaranteed to be called.
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB)
|
|
||||||
saveTunnel();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStop() {
|
|
||||||
super.onStop();
|
|
||||||
|
|
||||||
// Honeycomb and above: onStop() is the last method guaranteed to be called.
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
|
|
||||||
saveTunnel();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveTunnel() {
|
|
||||||
if (mGroup != null) {
|
|
||||||
TunnelConfig cfg = TunnelUtil.createConfigFromPreferences(getActivity(), mGroup, mTunnelId);
|
|
||||||
TunnelUtil.saveTunnel(I2PAppContext.getGlobalContext(), mGroup, mTunnelId, cfg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadPreferences() {
|
|
||||||
String type = TunnelUtil.getController(mGroup, mTunnelId).getType();
|
String type = TunnelUtil.getController(mGroup, mTunnelId).getType();
|
||||||
new TunnelPreferences(type).runLogic();
|
new TunnelPreferences(type).runLogic();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
|
||||||
|
// In case this was changed when toggling NEW_KEYS and then we navigated back
|
||||||
|
if (persistentKeys != null)
|
||||||
|
persistentKeys.setChecked(getPreferenceManager().getSharedPreferences().getBoolean(
|
||||||
|
getString(R.string.TUNNEL_OPT_PERSISTENT_KEY),
|
||||||
|
getResources().getBoolean(R.bool.DEFAULT_PERSISTENT_KEY)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
class TunnelPreferences extends TunnelLogic {
|
class TunnelPreferences extends TunnelLogic {
|
||||||
PreferenceScreen ps;
|
PreferenceScreen ps;
|
||||||
PreferenceCategory generalCategory;
|
PreferenceCategory generalCategory;
|
||||||
PreferenceCategory portCategory;
|
PreferenceCategory portCategory;
|
||||||
PreferenceScreen advanced;
|
|
||||||
PreferenceCategory tunParamCategory;
|
|
||||||
|
|
||||||
public TunnelPreferences(String type) {
|
public TunnelPreferences(String type) {
|
||||||
super(type);
|
super(type);
|
||||||
@ -107,6 +70,38 @@ public class EditTunnelFragment extends PreferenceFragment {
|
|||||||
@Override
|
@Override
|
||||||
protected void generalClient() {
|
protected void generalClient() {
|
||||||
addPreferencesFromResource(R.xml.tunnel_gen_client, generalCategory);
|
addPreferencesFromResource(R.xml.tunnel_gen_client, generalCategory);
|
||||||
|
|
||||||
|
// PERSISTENT_KEY and NEW_KEYS can't be set simultaneously
|
||||||
|
persistentKeys = (CheckBoxPreference) findPreference(getString(R.string.TUNNEL_OPT_PERSISTENT_KEY));
|
||||||
|
persistentKeys.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceChange(Preference preference, Object o) {
|
||||||
|
final SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
|
||||||
|
if ((Boolean) o && prefs.getBoolean(getString(R.string.TUNNEL_OTP_NEW_KEYS),
|
||||||
|
getResources().getBoolean(R.bool.DEFAULT_NEW_KEYS))) {
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||||
|
builder.setTitle(R.string.persistent_key_conflict_title)
|
||||||
|
.setMessage(R.string.persistent_key_conflict_msg)
|
||||||
|
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialogInterface, int i) {
|
||||||
|
SharedPreferences.Editor editor = prefs.edit();
|
||||||
|
editor.putBoolean(getString(R.string.TUNNEL_OTP_NEW_KEYS), false);
|
||||||
|
editor.apply();
|
||||||
|
persistentKeys.setChecked(true);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialogInterface, int i) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.show();
|
||||||
|
return false;
|
||||||
|
} else
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -126,8 +121,26 @@ public class EditTunnelFragment extends PreferenceFragment {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void generalClientPortStreamr(boolean isStreamr) {
|
protected void generalClientPortStreamr(boolean isStreamr) {
|
||||||
|
ListPreference reachableBy = (ListPreference) portCategory.findPreference(getString(R.string.TUNNEL_INTERFACE));
|
||||||
if (isStreamr)
|
if (isStreamr)
|
||||||
portCategory.removePreference(portCategory.findPreference(getString(R.string.TUNNEL_INTERFACE)));
|
portCategory.removePreference(reachableBy);
|
||||||
|
else
|
||||||
|
setupReachableBy(reachableBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupReachableBy(final ListPreference reachableBy) {
|
||||||
|
reachableBy.setEnabled(false);
|
||||||
|
new AsyncTask<Void, Void, Void>() {
|
||||||
|
@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);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -166,6 +179,8 @@ public class EditTunnelFragment extends PreferenceFragment {
|
|||||||
portCategory.removePreference(portCategory.findPreference(getString(R.string.TUNNEL_USE_SSL)));
|
portCategory.removePreference(portCategory.findPreference(getString(R.string.TUNNEL_USE_SSL)));
|
||||||
if (isStreamr)
|
if (isStreamr)
|
||||||
portCategory.removePreference(portCategory.findPreference(getString(R.string.TUNNEL_LISTEN_PORT)));
|
portCategory.removePreference(portCategory.findPreference(getString(R.string.TUNNEL_LISTEN_PORT)));
|
||||||
|
|
||||||
|
setupReachableBy((ListPreference) portCategory.findPreference(getString(R.string.TUNNEL_INTERFACE)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -183,90 +198,61 @@ public class EditTunnelFragment extends PreferenceFragment {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void advanced() {
|
protected void advanced() {
|
||||||
addPreferencesFromResource(R.xml.tunnel_adv);
|
Preference advanced = new Preference(getActivity());
|
||||||
advanced = (PreferenceScreen) ps.findPreference(
|
advanced.setKey(getString(R.string.TUNNEL_CAT_ADVANCED));
|
||||||
getString(R.string.TUNNEL_CAT_ADVANCED));
|
advanced.setTitle(R.string.settings_label_advanced);
|
||||||
tunParamCategory = (PreferenceCategory) ps.findPreference(
|
advanced.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||||
getString(R.string.TUNNEL_CAT_TUNNEL_PARAMS));
|
@Override
|
||||||
|
public boolean onPreferenceClick(Preference preference) {
|
||||||
|
Fragment fragment = AdvancedTunnelPreferenceFragment.newInstance(mTunnelId);
|
||||||
|
getFragmentManager().beginTransaction()
|
||||||
|
.replace(R.id.fragment, fragment)
|
||||||
|
.addToBackStack(null)
|
||||||
|
.commit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ps.addPreference(advanced);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void advancedStreamr(boolean isStreamr) {
|
protected void advancedStreamr(boolean isStreamr) {
|
||||||
if (isStreamr)
|
|
||||||
tunParamCategory.removePreference(tunParamCategory.findPreference(getString(R.string.TUNNEL_OPT_PROFILE)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void advancedServerOrStreamrClient(boolean isServerOrStreamrClient) {
|
protected void advancedServerOrStreamrClient(boolean isServerOrStreamrClient) {
|
||||||
if (isServerOrStreamrClient)
|
|
||||||
tunParamCategory.removePreference(tunParamCategory.findPreference(getString(R.string.TUNNEL_OPT_DELAY_CONNECT)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void advancedServer() {
|
protected void advancedServer() {
|
||||||
addPreferencesFromResource(R.xml.tunnel_adv_server, advanced);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void advancedServerHttp(boolean isHttp) {
|
protected void advancedServerHttp(boolean isHttp) {
|
||||||
if (isHttp)
|
|
||||||
addPreferencesFromResource(R.xml.tunnel_adv_server_http, advanced);
|
|
||||||
else {
|
|
||||||
PreferenceCategory accessCtlCategory = (PreferenceCategory) ps.findPreference(
|
|
||||||
getString(R.string.TUNNEL_CAT_ACCESS_CONTROL));
|
|
||||||
accessCtlCategory.removePreference(accessCtlCategory.findPreference(getString(R.string.TUNNEL_OPT_REJECT_INPROXY)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void advancedIdle() {
|
protected void advancedIdle() {
|
||||||
addPreferencesFromResource(R.xml.tunnel_adv_idle, advanced);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void advancedIdleServerOrStreamrClient(boolean isServerOrStreamrClient) {
|
protected void advancedIdleServerOrStreamrClient(boolean isServerOrStreamrClient) {
|
||||||
if (isServerOrStreamrClient)
|
|
||||||
advanced.removePreference(advanced.findPreference(getString(R.string.TUNNEL_OPT_DELAY_OPEN)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void advancedClient() {
|
protected void advancedClient() {
|
||||||
PreferenceCategory idleCategory = (PreferenceCategory) ps.findPreference(
|
|
||||||
getString(R.string.TUNNEL_CAT_IDLE)
|
|
||||||
);
|
|
||||||
addPreferencesFromResource(R.xml.tunnel_adv_idle_client, idleCategory);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void advancedClientHttp() {
|
protected void advancedClientHttp() {
|
||||||
addPreferencesFromResource(R.xml.tunnel_adv_client_http, advanced);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void advancedClientProxy() {
|
protected void advancedClientProxy() {
|
||||||
addPreferencesFromResource(R.xml.tunnel_adv_client_proxy, advanced);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void advancedOther() {
|
protected void advancedOther() {
|
||||||
addPreferencesFromResource(R.xml.tunnel_adv_other, advanced);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* http://stackoverflow.com/a/20806812
|
|
||||||
*
|
|
||||||
* @param id the Preferences XML to load
|
|
||||||
* @param newParent the parent PreferenceGroup to add the new Preferences to.
|
|
||||||
*/
|
|
||||||
private void addPreferencesFromResource (int id, PreferenceGroup newParent) {
|
|
||||||
PreferenceScreen screen = getPreferenceScreen();
|
|
||||||
int last = screen.getPreferenceCount();
|
|
||||||
addPreferencesFromResource(id);
|
|
||||||
while (screen.getPreferenceCount () > last) {
|
|
||||||
Preference p = screen.getPreference (last);
|
|
||||||
screen.removePreference(p); // decreases the preference count
|
|
||||||
newParent.addPreference(p);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -225,7 +225,7 @@ public class TunnelUtil extends GeneralHelper {
|
|||||||
ed.putInt(res.getString(R.string.TUNNEL_OPT_QUANTITY),
|
ed.putInt(res.getString(R.string.TUNNEL_OPT_QUANTITY),
|
||||||
getTunnelQuantity(tunnel, res.getInteger(R.integer.DEFAULT_TUNNEL_QUANTITY)));
|
getTunnelQuantity(tunnel, res.getInteger(R.integer.DEFAULT_TUNNEL_QUANTITY)));
|
||||||
ed.putInt(res.getString(R.string.TUNNEL_OPT_BACKUP_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
|
@Override
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
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 net.i2p.android.router.R;
|
||||||
|
import net.i2p.android.router.SettingsActivity;
|
||||||
|
|
||||||
|
public class AdvancedPreferenceFragment extends PreferenceFragment {
|
||||||
|
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);
|
||||||
|
addPreferencesFromResource(R.xml.settings_advanced);
|
||||||
|
|
||||||
|
findPreference(PREFERENCE_CATEGORY_TRANSPORTS)
|
||||||
|
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_TRANSPORTS));
|
||||||
|
findPreference(PREFERENCE_CATEGORY_EXPL_TUNNELS)
|
||||||
|
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_EXPL_TUNNELS));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_label_advanced);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CategoryClickListener implements Preference.OnPreferenceClickListener {
|
||||||
|
private String category;
|
||||||
|
|
||||||
|
public CategoryClickListener(String category) {
|
||||||
|
this.category = category;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceClick(Preference preference) {
|
||||||
|
Fragment fragment;
|
||||||
|
switch (category) {
|
||||||
|
case PREFERENCE_CATEGORY_TRANSPORTS:
|
||||||
|
fragment = new TransportsPreferenceFragment();
|
||||||
|
break;
|
||||||
|
case PREFERENCE_CATEGORY_EXPL_TUNNELS:
|
||||||
|
fragment = new ExploratoryPoolPreferenceFragment();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
getActivity().getSupportFragmentManager().beginTransaction()
|
||||||
|
.replace(R.id.fragment, fragment)
|
||||||
|
.addToBackStack(null)
|
||||||
|
.commit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package net.i2p.android.preferences;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import net.i2p.android.router.R;
|
||||||
|
import net.i2p.android.router.SettingsActivity;
|
||||||
|
|
||||||
|
public class AppearancePreferenceFragment extends I2PreferenceFragment {
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle paramBundle) {
|
||||||
|
super.onCreate(paramBundle);
|
||||||
|
addPreferencesFromResource(R.xml.settings_appearance);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(
|
||||||
|
(SettingsActivity) getActivity()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_label_appearance);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(
|
||||||
|
(SettingsActivity) getActivity()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package net.i2p.android.preferences;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import net.i2p.android.router.R;
|
||||||
|
import net.i2p.android.router.SettingsActivity;
|
||||||
|
|
||||||
|
public class ExploratoryPoolPreferenceFragment extends I2PreferenceFragment {
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle paramBundle) {
|
||||||
|
super.onCreate(paramBundle);
|
||||||
|
addPreferencesFromResource(R.xml.settings_expl_tunnels);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_label_exploratory_pool);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,108 @@
|
|||||||
|
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 net.i2p.android.router.R;
|
||||||
|
import net.i2p.android.router.SettingsActivity;
|
||||||
|
import net.i2p.android.router.service.StatSummarizer;
|
||||||
|
import net.i2p.android.router.util.Util;
|
||||||
|
import net.i2p.router.RouterContext;
|
||||||
|
import net.i2p.stat.FrequencyStat;
|
||||||
|
import net.i2p.stat.Rate;
|
||||||
|
import net.i2p.stat.RateStat;
|
||||||
|
import net.i2p.stat.StatManager;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
|
||||||
|
public class GraphsPreferenceFragment extends I2PreferenceFragment {
|
||||||
|
public static final String GRAPH_PREFERENCES_SEEN = "graphPreferencesSeen";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle paramBundle) {
|
||||||
|
super.onCreate(paramBundle);
|
||||||
|
addPreferencesFromResource(R.xml.settings_graphs);
|
||||||
|
setupGraphSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.label_graphs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupGraphSettings() {
|
||||||
|
PreferenceScreen ps = getPreferenceScreen();
|
||||||
|
RouterContext ctx = Util.getRouterContext();
|
||||||
|
if (ctx == null) {
|
||||||
|
PreferenceCategory noRouter = new PreferenceCategory(getActivity());
|
||||||
|
noRouter.setTitle(R.string.router_not_running);
|
||||||
|
ps.addPreference(noRouter);
|
||||||
|
} else if (StatSummarizer.instance() == null) {
|
||||||
|
PreferenceCategory noStats = new PreferenceCategory(getActivity());
|
||||||
|
noStats.setTitle(R.string.stats_not_ready);
|
||||||
|
ps.addPreference(noStats);
|
||||||
|
} else {
|
||||||
|
StatManager mgr = ctx.statManager();
|
||||||
|
Map<String, SortedSet<String>> all = mgr.getStatsByGroup();
|
||||||
|
for (String group : all.keySet()) {
|
||||||
|
SortedSet<String> stats = all.get(group);
|
||||||
|
if (stats.size() == 0) continue;
|
||||||
|
PreferenceCategory groupPrefs = new PreferenceCategory(getActivity());
|
||||||
|
groupPrefs.setKey("stat.groups." + group);
|
||||||
|
groupPrefs.setTitle(group);
|
||||||
|
ps.addPreference(groupPrefs);
|
||||||
|
for (String stat : stats) {
|
||||||
|
String key;
|
||||||
|
String description;
|
||||||
|
boolean canBeGraphed = false;
|
||||||
|
boolean currentIsGraphed = false;
|
||||||
|
RateStat rs = mgr.getRate(stat);
|
||||||
|
if (rs != null) {
|
||||||
|
description = rs.getDescription();
|
||||||
|
long period = rs.getPeriods()[0]; // should be the minimum
|
||||||
|
key = stat + "." + period;
|
||||||
|
if (period <= 10*60*1000) {
|
||||||
|
Rate r = rs.getRate(period);
|
||||||
|
canBeGraphed = r != null;
|
||||||
|
if (canBeGraphed) {
|
||||||
|
currentIsGraphed = r.getSummaryListener() != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FrequencyStat fs = mgr.getFrequency(stat);
|
||||||
|
if (fs != null) {
|
||||||
|
key = stat;
|
||||||
|
description = fs.getDescription();
|
||||||
|
// FrequencyStats cannot be graphed, but can be logged.
|
||||||
|
// XXX: Should log settings be here as well, or in a
|
||||||
|
// separate settings menu?
|
||||||
|
} else {
|
||||||
|
Util.e("Stat does not exist?! [" + stat + "]");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CheckBoxPreference statPref = new CheckBoxPreference(getActivity());
|
||||||
|
statPref.setKey("stat.summaries." + key);
|
||||||
|
statPref.setTitle(stat);
|
||||||
|
statPref.setSummary(description);
|
||||||
|
statPref.setEnabled(canBeGraphed);
|
||||||
|
statPref.setChecked(currentIsGraphed);
|
||||||
|
groupPrefs.addPreference(statPref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The user has now seen the current (possibly default) configuration
|
||||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||||
|
if (!prefs.getBoolean(GRAPH_PREFERENCES_SEEN, false))
|
||||||
|
prefs.edit()
|
||||||
|
.putBoolean(GRAPH_PREFERENCES_SEEN, true)
|
||||||
|
.apply();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
package net.i2p.android.preferences;
|
||||||
|
|
||||||
|
import android.support.v4.preference.PreferenceFragment;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import net.i2p.I2PAppContext;
|
||||||
|
import net.i2p.android.router.R;
|
||||||
|
import net.i2p.android.router.util.Util;
|
||||||
|
import net.i2p.router.RouterContext;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A PreferenceFragment that handles saving router settings.
|
||||||
|
*/
|
||||||
|
public class I2PreferenceFragment extends PreferenceFragment {
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
List<Properties> lProps = Util.getPropertiesFromPreferences(getActivity());
|
||||||
|
Properties props = lProps.get(0);
|
||||||
|
Properties propsToRemove = lProps.get(1);
|
||||||
|
Properties logSettings = lProps.get(2);
|
||||||
|
|
||||||
|
Set toRemove = propsToRemove.keySet();
|
||||||
|
|
||||||
|
boolean restartRequired = Util.checkAndCorrectRouterConfig(getActivity(), props, toRemove);
|
||||||
|
|
||||||
|
// Apply new config if we are running.
|
||||||
|
RouterContext rCtx = Util.getRouterContext();
|
||||||
|
if (rCtx != null) {
|
||||||
|
rCtx.router().saveConfig(props, toRemove);
|
||||||
|
|
||||||
|
// Merge in new log settings
|
||||||
|
saveLoggingChanges(rCtx, logSettings);
|
||||||
|
} else {
|
||||||
|
// Merge in new config settings, write the file.
|
||||||
|
Util.mergeResourceToFile(getActivity(),
|
||||||
|
Util.getFileDir(getActivity()),
|
||||||
|
"router.config", R.raw.router_config, props, toRemove);
|
||||||
|
|
||||||
|
// Merge in new log settings
|
||||||
|
saveLoggingChanges(I2PAppContext.getGlobalContext(), logSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the settings in Android
|
||||||
|
super.onPause();
|
||||||
|
|
||||||
|
if (restartRequired)
|
||||||
|
Toast.makeText(getActivity(), R.string.settings_router_restart_required, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveLoggingChanges(I2PAppContext ctx, Properties logSettings) {
|
||||||
|
boolean shouldSave = false;
|
||||||
|
|
||||||
|
for (Object key : logSettings.keySet()) {
|
||||||
|
if ("logger.defaultLevel".equals(key)) {
|
||||||
|
String defaultLevel = (String) logSettings.get(key);
|
||||||
|
String oldDefault = ctx.logManager().getDefaultLimit();
|
||||||
|
if (!defaultLevel.equals(oldDefault)) {
|
||||||
|
shouldSave = true;
|
||||||
|
ctx.logManager().setDefaultLimit(defaultLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldSave) {
|
||||||
|
ctx.logManager().saveConfig();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package net.i2p.android.preferences;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.preference.PreferenceScreen;
|
||||||
|
|
||||||
|
import net.i2p.android.router.R;
|
||||||
|
import net.i2p.android.router.SettingsActivity;
|
||||||
|
import net.i2p.android.router.util.Util;
|
||||||
|
import net.i2p.router.RouterContext;
|
||||||
|
import net.i2p.util.LogManager;
|
||||||
|
|
||||||
|
public class LoggingPreferenceFragment extends I2PreferenceFragment {
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle paramBundle) {
|
||||||
|
super.onCreate(paramBundle);
|
||||||
|
addPreferencesFromResource(R.xml.settings_logging);
|
||||||
|
setupLoggingSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_label_logging);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupLoggingSettings() {
|
||||||
|
PreferenceScreen ps = getPreferenceScreen();
|
||||||
|
RouterContext ctx = Util.getRouterContext();
|
||||||
|
if (ctx != null) {
|
||||||
|
LogManager mgr = ctx.logManager();
|
||||||
|
// Log level overrides
|
||||||
|
/*
|
||||||
|
StringBuilder buf = new StringBuilder(32*1024);
|
||||||
|
Properties limits = mgr.getLimits();
|
||||||
|
TreeSet<String> sortedLogs = new TreeSet<String>();
|
||||||
|
for (Iterator iter = limits.keySet().iterator(); iter.hasNext(); ) {
|
||||||
|
String prefix = (String)iter.next();
|
||||||
|
sortedLogs.add(prefix);
|
||||||
|
}
|
||||||
|
for (Iterator iter = sortedLogs.iterator(); iter.hasNext(); ) {
|
||||||
|
String prefix = (String)iter.next();
|
||||||
|
String level = limits.getProperty(prefix);
|
||||||
|
buf.append(prefix).append('=').append(level).append('\n');
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
/* Don't show, there are no settings that require the router
|
||||||
|
} else {
|
||||||
|
PreferenceCategory noRouter = new PreferenceCategory(getActivity());
|
||||||
|
noRouter.setTitle(R.string.router_not_running);
|
||||||
|
ps.addPreference(noRouter);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package net.i2p.android.preferences;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import net.i2p.android.router.R;
|
||||||
|
import net.i2p.android.router.SettingsActivity;
|
||||||
|
|
||||||
|
public class NetworkPreferenceFragment extends I2PreferenceFragment {
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle paramBundle) {
|
||||||
|
super.onCreate(paramBundle);
|
||||||
|
addPreferencesFromResource(R.xml.settings_net);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_label_bandwidth_net);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,122 @@
|
|||||||
|
package net.i2p.android.preferences;
|
||||||
|
|
||||||
|
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.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.router.util.Util;
|
||||||
|
import net.i2p.router.RouterContext;
|
||||||
|
|
||||||
|
public class TransportsPreferenceFragment extends I2PreferenceFragment {
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle paramBundle) {
|
||||||
|
super.onCreate(paramBundle);
|
||||||
|
// Load any properties that the router might have changed on us.
|
||||||
|
loadProperties();
|
||||||
|
addPreferencesFromResource(R.xml.settings_transports);
|
||||||
|
setupTransportSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_label_transports);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadProperties() {
|
||||||
|
Context context= getActivity();
|
||||||
|
RouterContext ctx = Util.getRouterContext();
|
||||||
|
if (ctx != null) {
|
||||||
|
final String udpPortKey = context.getString(R.string.PROP_UDP_INTERNAL_PORT);
|
||||||
|
final String ntcpPortKey = context.getString(R.string.PROP_I2NP_NTCP_PORT);
|
||||||
|
final String ntcpAutoPortKey = context.getString(R.string.PROP_I2NP_NTCP_AUTO_PORT);
|
||||||
|
|
||||||
|
int udpPort = ctx.getProperty(udpPortKey, -1);
|
||||||
|
int ntcpPort = ctx.getProperty(ntcpPortKey, -1);
|
||||||
|
boolean ntcpAutoPort = ctx.getBooleanPropertyDefaultTrue(ntcpAutoPortKey);
|
||||||
|
if (ntcpPort < 0 && ntcpAutoPort)
|
||||||
|
ntcpPort = udpPort;
|
||||||
|
|
||||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
if (prefs.getInt(udpPortKey, -1) != udpPort ||
|
||||||
|
prefs.getInt(ntcpPortKey, -1) != ntcpPort) {
|
||||||
|
SharedPreferences.Editor editor = prefs.edit();
|
||||||
|
editor.putInt(udpPortKey, udpPort);
|
||||||
|
editor.putInt(ntcpPortKey, ntcpPort);
|
||||||
|
// commit() instead of apply() because this needs to happen
|
||||||
|
// before AdvancedPreferenceFragment loads its Preferences.
|
||||||
|
editor.commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupTransportSettings() {
|
||||||
|
final Context context= getActivity();
|
||||||
|
PreferenceScreen ps = getPreferenceScreen();
|
||||||
|
|
||||||
|
final String udpEnableKey = context.getString(R.string.PROP_ENABLE_UDP);
|
||||||
|
final String ntcpEnableKey = context.getString(R.string.PROP_ENABLE_NTCP);
|
||||||
|
final String udpPortKey = context.getString(R.string.PROP_UDP_INTERNAL_PORT);
|
||||||
|
final String ntcpPortKey = context.getString(R.string.PROP_I2NP_NTCP_PORT);
|
||||||
|
final String ntcpAutoPortKey = context.getString(R.string.PROP_I2NP_NTCP_AUTO_PORT);
|
||||||
|
|
||||||
|
final CheckBoxPreference udpEnable = (CheckBoxPreference) ps.findPreference(udpEnableKey);
|
||||||
|
final CheckBoxPreference ntcpEnable = (CheckBoxPreference) ps.findPreference(ntcpEnableKey);
|
||||||
|
final PortPreference udpPort = (PortPreference) ps.findPreference(udpPortKey);
|
||||||
|
final PortPreference ntcpPort = (PortPreference) ps.findPreference(ntcpPortKey);
|
||||||
|
final CheckBoxPreference ntcpAutoPort = (CheckBoxPreference) ps.findPreference(ntcpAutoPortKey);
|
||||||
|
|
||||||
|
udpEnable.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||||
|
final Boolean checked = (Boolean) newValue;
|
||||||
|
if (checked || ntcpEnable.isChecked())
|
||||||
|
return true;
|
||||||
|
else {
|
||||||
|
Toast.makeText(context, R.string.settings_need_transport_enabled, Toast.LENGTH_LONG).show();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ntcpEnable.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||||
|
final Boolean checked = (Boolean) newValue;
|
||||||
|
if (checked || udpEnable.isChecked())
|
||||||
|
return true;
|
||||||
|
else {
|
||||||
|
Toast.makeText(context, R.string.settings_need_transport_enabled, Toast.LENGTH_LONG).show();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
udpPort.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||||
|
if (ntcpAutoPort.isChecked())
|
||||||
|
ntcpPort.setText((String) newValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ntcpAutoPort.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||||
|
final Boolean checked = (Boolean) newValue;
|
||||||
|
if (checked)
|
||||||
|
ntcpPort.setText(udpPort.getText());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
128
app/src/main/java/net/i2p/android/router/ConsoleContainer.java
Normal file
128
app/src/main/java/net/i2p/android/router/ConsoleContainer.java
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
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.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import net.i2p.android.ext.floatingactionbutton.FloatingActionsMenu;
|
||||||
|
import net.i2p.android.router.dialog.AboutDialog;
|
||||||
|
import net.i2p.android.router.dialog.TextResourceDialog;
|
||||||
|
import net.i2p.android.router.log.LogActivity;
|
||||||
|
import net.i2p.android.router.netdb.NetDbActivity;
|
||||||
|
import net.i2p.android.router.stats.PeersActivity;
|
||||||
|
import net.i2p.android.router.stats.RateGraphActivity;
|
||||||
|
import net.i2p.android.router.util.Util;
|
||||||
|
|
||||||
|
public class ConsoleContainer extends Fragment {
|
||||||
|
MainFragment mMainFragment = null;
|
||||||
|
FloatingActionsMenu mConsoleMenu;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
View v = inflater.inflate(R.layout.container_console, container, false);
|
||||||
|
// Start with the home view
|
||||||
|
if (savedInstanceState == null && getChildFragmentManager().findFragmentById(R.id.main_fragment) == null) {
|
||||||
|
mMainFragment = new MainFragment();
|
||||||
|
mMainFragment.setArguments(getActivity().getIntent().getExtras());
|
||||||
|
getChildFragmentManager().beginTransaction()
|
||||||
|
.add(R.id.main_fragment, mMainFragment).commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
mConsoleMenu = (FloatingActionsMenu) v.findViewById(R.id.console_action_menu);
|
||||||
|
mConsoleMenu.findViewById(R.id.action_news).setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
Intent news = new Intent(getActivity(), NewsActivity.class);
|
||||||
|
startActivity(news);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mConsoleMenu.findViewById(R.id.action_logs).setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
Intent log = new Intent(getActivity(), LogActivity.class);
|
||||||
|
startActivity(log);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mConsoleMenu.findViewById(R.id.action_graphs).setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
Intent graphs = new Intent(getActivity(), RateGraphActivity.class);
|
||||||
|
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_netdb).setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
Intent netdb = new Intent(getActivity(), NetDbActivity.class);
|
||||||
|
startActivity(netdb);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setMenuVisibility();
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.menu_about:
|
||||||
|
AboutDialog dialog = new AboutDialog();
|
||||||
|
dialog.show(getFragmentManager(), "about");
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case R.id.menu_help_release_notes:
|
||||||
|
TextResourceDialog rDdialog = new TextResourceDialog();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putString(TextResourceDialog.TEXT_DIALOG_TITLE,
|
||||||
|
getResources().getString(R.string.label_release_notes));
|
||||||
|
args.putInt(TextResourceDialog.TEXT_RESOURCE_ID, R.raw.releasenotes_txt);
|
||||||
|
rDdialog.setArguments(args);
|
||||||
|
rDdialog.show(getFragmentManager(), "release_notes");
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,477 +0,0 @@
|
|||||||
package net.i2p.android.router;
|
|
||||||
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.ServiceConnection;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.content.res.Configuration;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.support.v4.app.FragmentTransaction;
|
|
||||||
import android.support.v4.view.GravityCompat;
|
|
||||||
import android.support.v4.widget.DrawerLayout;
|
|
||||||
import android.support.v7.app.ActionBar;
|
|
||||||
import android.support.v7.app.ActionBar.Tab;
|
|
||||||
import android.support.v7.app.ActionBarActivity;
|
|
||||||
import android.support.v7.app.ActionBarDrawerToggle;
|
|
||||||
import android.support.v7.widget.Toolbar;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.ListView;
|
|
||||||
|
|
||||||
import net.i2p.android.i2ptunnel.TunnelListActivity;
|
|
||||||
import net.i2p.android.router.addressbook.AddressbookActivity;
|
|
||||||
import net.i2p.android.router.log.LogActivity;
|
|
||||||
import net.i2p.android.router.netdb.NetDbActivity;
|
|
||||||
import net.i2p.android.router.service.RouterBinder;
|
|
||||||
import net.i2p.android.router.service.RouterService;
|
|
||||||
import net.i2p.android.router.stats.PeersActivity;
|
|
||||||
import net.i2p.android.router.stats.RateGraphActivity;
|
|
||||||
import net.i2p.android.router.util.Util;
|
|
||||||
import net.i2p.android.router.web.WebActivity;
|
|
||||||
import net.i2p.android.router.web.WebFragment;
|
|
||||||
import net.i2p.router.RouterContext;
|
|
||||||
|
|
||||||
public abstract class I2PActivityBase extends ActionBarActivity implements
|
|
||||||
I2PFragmentBase.RouterContextProvider {
|
|
||||||
/**
|
|
||||||
* Navigation drawer variables
|
|
||||||
*/
|
|
||||||
protected DrawerLayout mDrawerLayout;
|
|
||||||
protected ListView mDrawerList;
|
|
||||||
protected ActionBarDrawerToggle mDrawerToggle;
|
|
||||||
|
|
||||||
private CharSequence mDrawerTitle;
|
|
||||||
private CharSequence mTitle;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Router variables
|
|
||||||
*/
|
|
||||||
protected boolean _isBound;
|
|
||||||
protected boolean _triedBind;
|
|
||||||
protected ServiceConnection _connection;
|
|
||||||
protected RouterService _routerService;
|
|
||||||
private SharedPreferences _sharedPrefs;
|
|
||||||
|
|
||||||
private static final String SHARED_PREFS = "net.i2p.android.router";
|
|
||||||
protected static final String PREF_AUTO_START = "autoStart";
|
|
||||||
/**
|
|
||||||
* true leads to a poor install experience, very slow to paint the screen
|
|
||||||
*/
|
|
||||||
protected static final boolean DEFAULT_AUTO_START = false;
|
|
||||||
protected static final String PREF_NAV_DRAWER_OPENED = "navDrawerOpened";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Override this in subclasses that need a ViewPager, such as a
|
|
||||||
* category view.
|
|
||||||
*
|
|
||||||
* @return whether this Activity needs a ViewPager.
|
|
||||||
*/
|
|
||||||
protected boolean useViewPager() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Override this in subclasses that can use two panes, such as a
|
|
||||||
* list/detail class.
|
|
||||||
*
|
|
||||||
* @return whether this Activity can use a two-pane layout.
|
|
||||||
*/
|
|
||||||
protected boolean canUseTwoPanes() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the activity is first created.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
Util.d(this + " onCreate called");
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
_sharedPrefs = getSharedPreferences(SHARED_PREFS, 0);
|
|
||||||
|
|
||||||
// If the Activity wants to use a ViewPager, provide it.
|
|
||||||
// If the Activity can make use of two panes (if available),
|
|
||||||
// load the layout that will enable them. Otherwise, load the
|
|
||||||
// layout that will only ever have a single pane.
|
|
||||||
if (useViewPager())
|
|
||||||
setContentView(R.layout.activity_navdrawer_viewpager);
|
|
||||||
else if (canUseTwoPanes())
|
|
||||||
setContentView(R.layout.activity_navdrawer);
|
|
||||||
else
|
|
||||||
setContentView(R.layout.activity_navdrawer_onepane);
|
|
||||||
|
|
||||||
// Set the action bar
|
|
||||||
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
|
|
||||||
setSupportActionBar(toolbar);
|
|
||||||
|
|
||||||
mTitle = mDrawerTitle = getTitle();
|
|
||||||
String[] activityTitles = getResources().getStringArray(R.array.navdrawer_activity_titles);
|
|
||||||
if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean("i2pandroid.main.showStats", false)) {
|
|
||||||
String[] advActivityTitles = getResources().getStringArray(R.array.navdrawer_activity_titles_advanced);
|
|
||||||
String[] allTitles = new String[activityTitles.length + advActivityTitles.length];
|
|
||||||
System.arraycopy(activityTitles, 0, allTitles, 0, activityTitles.length);
|
|
||||||
System.arraycopy(advActivityTitles, 0, allTitles, activityTitles.length, advActivityTitles.length);
|
|
||||||
activityTitles = allTitles;
|
|
||||||
}
|
|
||||||
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
|
|
||||||
mDrawerList = (ListView) findViewById(R.id.drawer);
|
|
||||||
|
|
||||||
// Set a custom shadow that overlays the main content when the drawer opens
|
|
||||||
mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
|
|
||||||
mDrawerList.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
|
|
||||||
// Set the adapter for the list view
|
|
||||||
mDrawerList.setAdapter(new ArrayAdapter<>(this,
|
|
||||||
R.layout.listitem_navdrawer, activityTitles));
|
|
||||||
// Set the list's click listener
|
|
||||||
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
|
|
||||||
|
|
||||||
mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
|
|
||||||
R.string.drawer_open, R.string.drawer_close) {
|
|
||||||
private boolean wasDragged = false;
|
|
||||||
|
|
||||||
/** Called when a drawer has settled in a completely closed state. */
|
|
||||||
public void onDrawerClosed(View view) {
|
|
||||||
// Don't mark as opened if the user closed by dragging
|
|
||||||
// but uses the action bar icon to open
|
|
||||||
wasDragged = false;
|
|
||||||
getSupportActionBar().setTitle(mTitle);
|
|
||||||
supportInvalidateOptionsMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Called when a drawer has settled in a completely open state. */
|
|
||||||
public void onDrawerOpened(View view) {
|
|
||||||
if (wasDragged && !getPref(PREF_NAV_DRAWER_OPENED, false))
|
|
||||||
setPref(PREF_NAV_DRAWER_OPENED, true);
|
|
||||||
getSupportActionBar().setTitle(mDrawerTitle);
|
|
||||||
supportInvalidateOptionsMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Called when the drawer motion state changes. */
|
|
||||||
public void onDrawerStateChanged(int newState) {
|
|
||||||
if (newState == DrawerLayout.STATE_DRAGGING)
|
|
||||||
wasDragged = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Set the drawer toggle as the DrawerListener
|
|
||||||
mDrawerLayout.setDrawerListener(mDrawerToggle);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DrawerItemClickListener implements ListView.OnItemClickListener {
|
|
||||||
public void onItemClick(AdapterView<?> parent, View view, int pos, long id) {
|
|
||||||
selectItem(pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void selectItem(int pos) {
|
|
||||||
switch (pos) {
|
|
||||||
case 1:
|
|
||||||
if (!(this instanceof NewsActivity)) {
|
|
||||||
Intent news = new Intent(I2PActivityBase.this, NewsActivity.class);
|
|
||||||
startActivity(news);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
Intent ab = new Intent(I2PActivityBase.this, AddressbookActivity.class);
|
|
||||||
startActivity(ab);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
Intent itb = new Intent(I2PActivityBase.this, TunnelListActivity.class);
|
|
||||||
startActivity(itb);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
if (!(this instanceof LogActivity)) {
|
|
||||||
Intent log = new Intent(I2PActivityBase.this, LogActivity.class);
|
|
||||||
startActivity(log);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
Intent wp = new Intent(I2PActivityBase.this, WebActivity.class);
|
|
||||||
wp.putExtra(WebFragment.HTML_RESOURCE_ID, R.raw.welcome_html);
|
|
||||||
startActivity(wp);
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
if (!(this instanceof RateGraphActivity)) {
|
|
||||||
Intent active = new Intent(I2PActivityBase.this, RateGraphActivity.class);
|
|
||||||
startActivity(active);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
Intent peers = new Intent(I2PActivityBase.this, PeersActivity.class);
|
|
||||||
startActivity(peers);
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
if (!(this instanceof NetDbActivity)) {
|
|
||||||
Intent netdb = new Intent(I2PActivityBase.this, NetDbActivity.class);
|
|
||||||
startActivity(netdb);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Intent main = new Intent(I2PActivityBase.this, MainActivity.class);
|
|
||||||
startActivity(main);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
mDrawerLayout.closeDrawer(mDrawerList);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRestart() {
|
|
||||||
Util.d(this + " onRestart called");
|
|
||||||
super.onRestart();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStart() {
|
|
||||||
Util.d(this + " onStart called");
|
|
||||||
super.onStart();
|
|
||||||
if (_sharedPrefs.getBoolean(PREF_AUTO_START, DEFAULT_AUTO_START))
|
|
||||||
startRouter();
|
|
||||||
else
|
|
||||||
bindRouter(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param def default
|
|
||||||
*/
|
|
||||||
public boolean getPref(String pref, boolean def) {
|
|
||||||
return _sharedPrefs.getBoolean(pref, def);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param def default
|
|
||||||
*/
|
|
||||||
public String getPref(String pref, String def) {
|
|
||||||
return _sharedPrefs.getString(pref, def);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return success
|
|
||||||
*/
|
|
||||||
public boolean setPref(String pref, boolean val) {
|
|
||||||
SharedPreferences.Editor edit = _sharedPrefs.edit();
|
|
||||||
edit.putBoolean(pref, val);
|
|
||||||
return edit.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return success
|
|
||||||
*/
|
|
||||||
public boolean setPref(String pref, String val) {
|
|
||||||
SharedPreferences.Editor edit = _sharedPrefs.edit();
|
|
||||||
edit.putString(pref, val);
|
|
||||||
return edit.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
Util.d(this + " onResume called");
|
|
||||||
super.onResume();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPause() {
|
|
||||||
Util.d(this + " onPause called");
|
|
||||||
super.onPause();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
|
||||||
Util.d(this + " onSaveInstanceState called");
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStop() {
|
|
||||||
Util.d(this + " onStop called");
|
|
||||||
unbindRouter();
|
|
||||||
super.onStop();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
Util.d(this + " onDestroy called");
|
|
||||||
super.onDestroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called whenever we call invalidateOptionsMenu()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
|
||||||
// If the nav drawer is open, hide action items related to the content view
|
|
||||||
boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
|
|
||||||
onDrawerChange(drawerOpen);
|
|
||||||
|
|
||||||
return super.onPrepareOptionsMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Override in subclass with e.g.
|
|
||||||
* menu.findItem(R.id.action_add_to_addressbook).setVisible(!drawerOpen);
|
|
||||||
*
|
|
||||||
* @param drawerOpen true if the drawer is open
|
|
||||||
*/
|
|
||||||
protected void onDrawerChange(boolean drawerOpen) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
// The action bar home/up action should open or close the drawer.
|
|
||||||
// ActionBarDrawerToggle will take care of this.
|
|
||||||
if (mDrawerToggle.onOptionsItemSelected(item))
|
|
||||||
return true;
|
|
||||||
else if (item.getItemId() == android.R.id.home) {
|
|
||||||
// This happens when mDrawerToggle.setDrawerIndicatorEnabled(false)
|
|
||||||
onBackPressed();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle action buttons and overflow
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setTitle(CharSequence title) {
|
|
||||||
mTitle = title;
|
|
||||||
getSupportActionBar().setTitle(mTitle);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostCreate(Bundle savedInstanceState) {
|
|
||||||
super.onPostCreate(savedInstanceState);
|
|
||||||
// Sync the toggle state after onRestoreInstanceState has occurred.
|
|
||||||
mDrawerToggle.syncState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onConfigurationChanged(Configuration newConfig) {
|
|
||||||
super.onConfigurationChanged(newConfig);
|
|
||||||
// Pass any configuration change to the drawer toggle
|
|
||||||
mDrawerToggle.onConfigurationChanged(newConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class TabListener implements ActionBar.TabListener {
|
|
||||||
protected Fragment mFragment;
|
|
||||||
|
|
||||||
public TabListener(Fragment fragment) {
|
|
||||||
mFragment = fragment;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onTabSelected(Tab tab, FragmentTransaction ft) {
|
|
||||||
ft.replace(R.id.main_fragment, mFragment);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
|
|
||||||
ft.remove(mFragment);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onTabReselected(Tab tab, FragmentTransaction ft) {
|
|
||||||
// User selected the already selected tab.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////// Service stuff
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start the service and bind to it
|
|
||||||
*/
|
|
||||||
protected boolean startRouter() {
|
|
||||||
Intent intent = new Intent();
|
|
||||||
intent.setClassName(this, "net.i2p.android.router.service.RouterService");
|
|
||||||
Util.d(this + " calling startService");
|
|
||||||
ComponentName name = startService(intent);
|
|
||||||
if (name == null)
|
|
||||||
Util.d(this + " XXXXXXXXXXXXXXXXXXXX got null from startService!");
|
|
||||||
Util.d(this + " got from startService: " + name);
|
|
||||||
boolean success = bindRouter(true);
|
|
||||||
if (!success)
|
|
||||||
Util.d(this + " Bind router failed");
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bind only
|
|
||||||
*/
|
|
||||||
protected boolean bindRouter(boolean autoCreate) {
|
|
||||||
Intent intent = new Intent(RouterBinder.class.getName());
|
|
||||||
intent.setClassName(this, "net.i2p.android.router.service.RouterService");
|
|
||||||
Util.d(this + " calling bindService");
|
|
||||||
_connection = new RouterConnection();
|
|
||||||
_triedBind = bindService(intent, _connection, autoCreate ? BIND_AUTO_CREATE : 0);
|
|
||||||
Util.d(this + " bindService: auto create? " + autoCreate + " success? " + _triedBind);
|
|
||||||
return _triedBind;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void unbindRouter() {
|
|
||||||
Util.d(this + " unbindRouter called with _isBound:" + _isBound + " _connection:" + _connection + " _triedBind:" + _triedBind);
|
|
||||||
if (_triedBind && _connection != null)
|
|
||||||
unbindService(_connection);
|
|
||||||
|
|
||||||
_triedBind = false;
|
|
||||||
_connection = null;
|
|
||||||
_routerService = null;
|
|
||||||
_isBound = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class for interacting with the main interface of the RouterService.
|
|
||||||
*/
|
|
||||||
protected class RouterConnection implements ServiceConnection {
|
|
||||||
|
|
||||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
|
||||||
Util.d(this + " connected to router service");
|
|
||||||
RouterBinder binder = (RouterBinder) service;
|
|
||||||
RouterService svc = binder.getService();
|
|
||||||
_routerService = svc;
|
|
||||||
_isBound = true;
|
|
||||||
onRouterBind(svc);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onServiceDisconnected(ComponentName name) {
|
|
||||||
Util.d(this + " disconnected from router service!!!!!!!");
|
|
||||||
// save memory
|
|
||||||
_routerService = null;
|
|
||||||
_isBound = false;
|
|
||||||
onRouterUnbind();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* callback from ServiceConnection, override as necessary
|
|
||||||
*/
|
|
||||||
protected void onRouterBind(RouterService svc) {
|
|
||||||
Fragment f = getSupportFragmentManager().findFragmentById(R.id.main_fragment);
|
|
||||||
if (f instanceof I2PFragmentBase)
|
|
||||||
((I2PFragmentBase) f).onRouterBind();
|
|
||||||
else if (f instanceof I2PFragmentBase.RouterContextUser)
|
|
||||||
((I2PFragmentBase.RouterContextUser) f).onRouterBind();
|
|
||||||
|
|
||||||
if (canUseTwoPanes()) {
|
|
||||||
f = getSupportFragmentManager().findFragmentById(R.id.detail_fragment);
|
|
||||||
if (f instanceof I2PFragmentBase)
|
|
||||||
((I2PFragmentBase) f).onRouterBind();
|
|
||||||
else if (f instanceof I2PFragmentBase.RouterContextUser)
|
|
||||||
((I2PFragmentBase.RouterContextUser) f).onRouterBind();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* callback from ServiceConnection, override as necessary
|
|
||||||
*/
|
|
||||||
protected void onRouterUnbind() {
|
|
||||||
}
|
|
||||||
|
|
||||||
// I2PFragmentBase.RouterContextProvider
|
|
||||||
|
|
||||||
public RouterContext getRouterContext() {
|
|
||||||
RouterService svc = _routerService;
|
|
||||||
if (svc == null || !_isBound)
|
|
||||||
return null;
|
|
||||||
return svc.getRouterContext();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
package net.i2p.android.router;
|
package net.i2p.android.router;
|
||||||
|
|
||||||
public interface I2PConstants {
|
public interface I2PConstants {
|
||||||
public static final String ANDROID_PREF_PREFIX = "i2pandroid.";
|
String ANDROID_PREF_PREFIX = "i2pandroid.";
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
package net.i2p.android.router;
|
package net.i2p.android.router;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
|
||||||
|
import net.i2p.android.router.util.Util;
|
||||||
import net.i2p.router.CommSystemFacade;
|
import net.i2p.router.CommSystemFacade;
|
||||||
import net.i2p.router.NetworkDatabaseFacade;
|
import net.i2p.router.NetworkDatabaseFacade;
|
||||||
import net.i2p.router.Router;
|
import net.i2p.router.Router;
|
||||||
@ -8,40 +12,12 @@ import net.i2p.router.TunnelManagerFacade;
|
|||||||
import net.i2p.router.peermanager.ProfileOrganizer;
|
import net.i2p.router.peermanager.ProfileOrganizer;
|
||||||
import net.i2p.router.transport.FIFOBandwidthLimiter;
|
import net.i2p.router.transport.FIFOBandwidthLimiter;
|
||||||
import net.i2p.stat.StatManager;
|
import net.i2p.stat.StatManager;
|
||||||
import android.app.Activity;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
|
|
||||||
public class I2PFragmentBase extends Fragment {
|
public class I2PFragmentBase extends Fragment {
|
||||||
private boolean mOnActivityCreated;
|
private boolean mOnActivityCreated;
|
||||||
RouterContextProvider mCallback;
|
|
||||||
|
|
||||||
public static final String PREF_INSTALLED_VERSION = "app.version";
|
public static final String PREF_INSTALLED_VERSION = "app.version";
|
||||||
|
|
||||||
public interface RouterContextUser {
|
|
||||||
public void onRouterBind();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Container Activity must implement this interface
|
|
||||||
public interface RouterContextProvider {
|
|
||||||
public RouterContext getRouterContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAttach(Activity activity) {
|
|
||||||
super.onAttach(activity);
|
|
||||||
|
|
||||||
// This makes sure that the container activity has implemented
|
|
||||||
// the callback interface. If not, it throws an exception
|
|
||||||
try {
|
|
||||||
mCallback = (RouterContextProvider) activity;
|
|
||||||
} catch (ClassCastException e) {
|
|
||||||
throw new ClassCastException(activity.toString()
|
|
||||||
+ " must implement RouterContextProvider");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivityCreated(Bundle savedInstanceState) {
|
public void onActivityCreated(Bundle savedInstanceState) {
|
||||||
super.onActivityCreated(savedInstanceState);
|
super.onActivityCreated(savedInstanceState);
|
||||||
@ -64,7 +40,7 @@ public class I2PFragmentBase extends Fragment {
|
|||||||
public void onRouterConnectionNotReady() {}
|
public void onRouterConnectionNotReady() {}
|
||||||
|
|
||||||
protected RouterContext getRouterContext() {
|
protected RouterContext getRouterContext() {
|
||||||
return mCallback.getRouterContext();
|
return Util.getRouterContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Router getRouter() {
|
protected Router getRouter() {
|
||||||
|
@ -1,12 +1,18 @@
|
|||||||
package net.i2p.android.router;
|
package net.i2p.android.router;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
|
|
||||||
|
import net.i2p.android.I2PActivityBase;
|
||||||
|
|
||||||
public class LicenseActivity extends I2PActivityBase {
|
public class LicenseActivity extends I2PActivityBase {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
mDrawerToggle.setDrawerIndicatorEnabled(false);
|
setContentView(R.layout.activity_onepane);
|
||||||
|
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
// Start with the base view
|
// Start with the base view
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
LicenseFragment f = new LicenseFragment();
|
LicenseFragment f = new LicenseFragment();
|
||||||
|
@ -1,243 +0,0 @@
|
|||||||
package net.i2p.android.router;
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.IntentFilter;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.v4.content.LocalBroadcastManager;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuInflater;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
|
|
||||||
import net.i2p.android.help.HelpActivity;
|
|
||||||
import net.i2p.android.router.dialog.AboutDialog;
|
|
||||||
import net.i2p.android.router.dialog.TextResourceDialog;
|
|
||||||
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 java.io.File;
|
|
||||||
|
|
||||||
public class MainActivity extends I2PActivityBase implements
|
|
||||||
MainFragment.RouterControlListener {
|
|
||||||
MainFragment mMainFragment = null;
|
|
||||||
private boolean mAutoStartFromIntent = false;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
// Start with the home view
|
|
||||||
if (savedInstanceState == null) {
|
|
||||||
mMainFragment = new MainFragment();
|
|
||||||
mMainFragment.setArguments(getIntent().getExtras());
|
|
||||||
getSupportFragmentManager().beginTransaction()
|
|
||||||
.add(R.id.main_fragment, mMainFragment).commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open nav drawer if the user has never opened it themselves
|
|
||||||
if (!getPref(PREF_NAV_DRAWER_OPENED, false))
|
|
||||||
mDrawerLayout.openDrawer(mDrawerList);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostCreate(Bundle savedInstanceState) {
|
|
||||||
Util.d("Initializing...");
|
|
||||||
InitActivities init = new InitActivities(this);
|
|
||||||
init.debugStuff();
|
|
||||||
init.initialize();
|
|
||||||
super.onPostCreate(savedInstanceState);
|
|
||||||
handleIntents();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onNewIntent(Intent intent) {
|
|
||||||
super.onNewIntent(intent);
|
|
||||||
handleIntents();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleIntents() {
|
|
||||||
if (getIntent() == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Intent intent = getIntent();
|
|
||||||
String action = intent.getAction();
|
|
||||||
|
|
||||||
if (action == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (action.equals("net.i2p.android.router.START_I2P")) {
|
|
||||||
autoStart();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void autoStart() {
|
|
||||||
if (canStart()) {
|
|
||||||
if (Connectivity.isConnected(this)) {
|
|
||||||
mAutoStartFromIntent = true;
|
|
||||||
onStartRouterClicked();
|
|
||||||
} else {
|
|
||||||
// Not connected to a network
|
|
||||||
// TODO: Notify user
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// TODO: Notify user
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStart() {
|
|
||||||
super.onStart();
|
|
||||||
|
|
||||||
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this);
|
|
||||||
|
|
||||||
IntentFilter filter = new IntentFilter();
|
|
||||||
filter.addAction(RouterService.LOCAL_BROADCAST_STATE_NOTIFICATION);
|
|
||||||
filter.addAction(RouterService.LOCAL_BROADCAST_STATE_CHANGED);
|
|
||||||
lbm.registerReceiver(onStateChange, filter);
|
|
||||||
|
|
||||||
lbm.sendBroadcast(new Intent(RouterService.LOCAL_BROADCAST_REQUEST_STATE));
|
|
||||||
}
|
|
||||||
|
|
||||||
private State lastRouterState = null;
|
|
||||||
private BroadcastReceiver onStateChange = new BroadcastReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
State state = intent.getParcelableExtra(RouterService.LOCAL_BROADCAST_EXTRA_STATE);
|
|
||||||
if (lastRouterState == null || lastRouterState != state) {
|
|
||||||
if (mMainFragment == null)
|
|
||||||
mMainFragment = (MainFragment) getSupportFragmentManager().findFragmentById(R.id.main_fragment);
|
|
||||||
if (mMainFragment != null) {
|
|
||||||
mMainFragment.updateState(state);
|
|
||||||
lastRouterState = state;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state == State.RUNNING && mAutoStartFromIntent) {
|
|
||||||
MainActivity.this.setResult(RESULT_OK);
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
|
||||||
MenuInflater inflater = getMenuInflater();
|
|
||||||
inflater.inflate(R.menu.activity_main_actions, menu);
|
|
||||||
inflater.inflate(R.menu.activity_base_actions, menu);
|
|
||||||
return super.onCreateOptionsMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
switch (item.getItemId()) {
|
|
||||||
case R.id.menu_settings:
|
|
||||||
Intent intent = new Intent(MainActivity.this, SettingsActivity.class);
|
|
||||||
startActivity(intent);
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case R.id.menu_about:
|
|
||||||
AboutDialog dialog = new AboutDialog();
|
|
||||||
dialog.show(getSupportFragmentManager(), "about");
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case R.id.menu_help:
|
|
||||||
Intent hi = new Intent(MainActivity.this, HelpActivity.class);
|
|
||||||
startActivity(hi);
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case R.id.menu_help_release_notes:
|
|
||||||
TextResourceDialog rDdialog = new TextResourceDialog();
|
|
||||||
Bundle args = new Bundle();
|
|
||||||
args.putString(TextResourceDialog.TEXT_DIALOG_TITLE,
|
|
||||||
getResources().getString(R.string.label_release_notes));
|
|
||||||
args.putInt(TextResourceDialog.TEXT_RESOURCE_ID, R.raw.releasenotes_txt);
|
|
||||||
rDdialog.setArguments(args);
|
|
||||||
rDdialog.show(getSupportFragmentManager(), "release_notes");
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStop() {
|
|
||||||
super.onStop();
|
|
||||||
|
|
||||||
LocalBroadcastManager.getInstance(this).unregisterReceiver(onStateChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean canStart() {
|
|
||||||
RouterService svc = _routerService;
|
|
||||||
return (svc == null) || (!_isBound) || svc.canManualStart();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean canStop() {
|
|
||||||
RouterService svc = _routerService;
|
|
||||||
return svc != null && _isBound && svc.canManualStop();
|
|
||||||
}
|
|
||||||
|
|
||||||
// MainFragment.RouterControlListener
|
|
||||||
|
|
||||||
public boolean shouldShowOnOff() {
|
|
||||||
return (canStart() && Connectivity.isConnected(this)) || (canStop() && !isGracefulShutdownInProgress());
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean shouldBeOn() {
|
|
||||||
String action = getIntent().getAction();
|
|
||||||
return (canStop()) ||
|
|
||||||
(action != null && action.equals("net.i2p.android.router.START_I2P"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onStartRouterClicked() {
|
|
||||||
RouterService svc = _routerService;
|
|
||||||
if(svc != null && _isBound) {
|
|
||||||
setPref(PREF_AUTO_START, true);
|
|
||||||
svc.manualStart();
|
|
||||||
} else {
|
|
||||||
(new File(Util.getFileDir(this), "wrapper.log")).delete();
|
|
||||||
startRouter();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean onStopRouterClicked() {
|
|
||||||
RouterService svc = _routerService;
|
|
||||||
if(svc != null && _isBound) {
|
|
||||||
setPref(PREF_AUTO_START, false);
|
|
||||||
svc.manualQuit();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @since 0.9.19 */
|
|
||||||
public boolean isGracefulShutdownInProgress() {
|
|
||||||
RouterService svc = _routerService;
|
|
||||||
return svc != null && svc.isGracefulShutdownInProgress();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @since 0.9.19 */
|
|
||||||
public boolean onGracefulShutdownClicked() {
|
|
||||||
RouterService svc = _routerService;
|
|
||||||
if(svc != null && _isBound) {
|
|
||||||
setPref(PREF_AUTO_START, false);
|
|
||||||
svc.gracefulShutdown();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @since 0.9.19 */
|
|
||||||
public boolean onCancelGracefulShutdownClicked() {
|
|
||||||
RouterService svc = _routerService;
|
|
||||||
if(svc != null && _isBound) {
|
|
||||||
setPref(PREF_AUTO_START, false);
|
|
||||||
svc.cancelGracefulShutdown();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +1,17 @@
|
|||||||
package net.i2p.android.router;
|
package net.i2p.android.router;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
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.graphics.Typeface;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@ -18,8 +25,10 @@ import android.widget.TableRow;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.ToggleButton;
|
import android.widget.ToggleButton;
|
||||||
|
|
||||||
import net.i2p.android.router.dialog.ConfigureBrowserDialog;
|
import net.i2p.android.I2PActivityBase;
|
||||||
|
import net.i2p.android.help.BrowserConfigActivity;
|
||||||
import net.i2p.android.router.dialog.FirstStartDialog;
|
import net.i2p.android.router.dialog.FirstStartDialog;
|
||||||
|
import net.i2p.android.router.service.RouterService;
|
||||||
import net.i2p.android.router.service.State;
|
import net.i2p.android.router.service.State;
|
||||||
import net.i2p.android.router.util.Connectivity;
|
import net.i2p.android.router.util.Connectivity;
|
||||||
import net.i2p.android.router.util.LongToggleButton;
|
import net.i2p.android.router.util.LongToggleButton;
|
||||||
@ -30,10 +39,8 @@ import net.i2p.data.Hash;
|
|||||||
import net.i2p.data.LeaseSet;
|
import net.i2p.data.LeaseSet;
|
||||||
import net.i2p.router.RouterContext;
|
import net.i2p.router.RouterContext;
|
||||||
import net.i2p.router.TunnelPoolSettings;
|
import net.i2p.router.TunnelPoolSettings;
|
||||||
import net.i2p.util.Translate;
|
|
||||||
|
|
||||||
import java.text.Collator;
|
import java.text.Collator;
|
||||||
import java.text.DecimalFormat;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
@ -45,8 +52,22 @@ public class MainFragment extends I2PFragmentBase {
|
|||||||
private Runnable _updater;
|
private Runnable _updater;
|
||||||
private Runnable _oneShotUpdate;
|
private Runnable _oneShotUpdate;
|
||||||
private String _savedStatus;
|
private String _savedStatus;
|
||||||
private boolean _keep = true;
|
|
||||||
private boolean _startPressed = false;
|
private ImageView mConsoleLights;
|
||||||
|
private LongToggleButton mOnOffButton;
|
||||||
|
private LinearLayout vGracefulButtons;
|
||||||
|
private ScrollView mScrollView;
|
||||||
|
private View vStatusContainer;
|
||||||
|
private ImageView vNetStatusLevel;
|
||||||
|
private TextView vNetStatusText;
|
||||||
|
private View vNonNetStatus;
|
||||||
|
private TextView vUptime;
|
||||||
|
private TextView vActive;
|
||||||
|
private TextView vKnown;
|
||||||
|
private TableLayout vTunnels;
|
||||||
|
private LinearLayout vAdvStatus;
|
||||||
|
private TextView vAdvStatusText;
|
||||||
|
|
||||||
private static final String PREF_CONFIGURE_BROWSER = "app.dialog.configureBrowser";
|
private static final String PREF_CONFIGURE_BROWSER = "app.dialog.configureBrowser";
|
||||||
private static final String PREF_FIRST_START = "app.router.firstStart";
|
private static final String PREF_FIRST_START = "app.router.firstStart";
|
||||||
private static final String PREF_SHOW_STATS = "i2pandroid.main.showStats";
|
private static final String PREF_SHOW_STATS = "i2pandroid.main.showStats";
|
||||||
@ -56,16 +77,28 @@ public class MainFragment extends I2PFragmentBase {
|
|||||||
|
|
||||||
// Container Activity must implement this interface
|
// Container Activity must implement this interface
|
||||||
public interface RouterControlListener {
|
public interface RouterControlListener {
|
||||||
public boolean shouldShowOnOff();
|
boolean shouldShowOnOff();
|
||||||
public boolean shouldBeOn();
|
|
||||||
public void onStartRouterClicked();
|
boolean shouldBeOn();
|
||||||
public boolean onStopRouterClicked();
|
|
||||||
/** @since 0.9.19 */
|
void onStartRouterClicked();
|
||||||
public boolean isGracefulShutdownInProgress();
|
|
||||||
/** @since 0.9.19 */
|
boolean onStopRouterClicked();
|
||||||
public boolean onGracefulShutdownClicked();
|
|
||||||
/** @since 0.9.19 */
|
/**
|
||||||
public boolean onCancelGracefulShutdownClicked();
|
* @since 0.9.19
|
||||||
|
*/
|
||||||
|
boolean isGracefulShutdownInProgress();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 0.9.19
|
||||||
|
*/
|
||||||
|
boolean onGracefulShutdownClicked();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 0.9.19
|
||||||
|
*/
|
||||||
|
boolean onCancelGracefulShutdownClicked();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -90,15 +123,14 @@ public class MainFragment extends I2PFragmentBase {
|
|||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
// Init stuff here so settings work.
|
// Init stuff here so settings work.
|
||||||
if(savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
|
lastRouterState = savedInstanceState.getParcelable("lastState");
|
||||||
String saved = savedInstanceState.getString("status");
|
String saved = savedInstanceState.getString("status");
|
||||||
if(saved != null) {
|
if (saved != null) {
|
||||||
_savedStatus = saved;
|
_savedStatus = saved;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_keep = true;
|
|
||||||
|
|
||||||
_handler = new Handler();
|
_handler = new Handler();
|
||||||
_updater = new Updater();
|
_updater = new Updater();
|
||||||
_oneShotUpdate = new OneShotUpdate();
|
_oneShotUpdate = new OneShotUpdate();
|
||||||
@ -109,20 +141,32 @@ public class MainFragment extends I2PFragmentBase {
|
|||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
View v = inflater.inflate(R.layout.fragment_main, container, false);
|
View v = inflater.inflate(R.layout.fragment_main, container, false);
|
||||||
|
|
||||||
final ImageView lightImage = (ImageView) v.findViewById(R.id.main_lights);
|
mConsoleLights = (ImageView) v.findViewById(R.id.console_lights);
|
||||||
lightImage.setImageResource(R.drawable.routerlogo_0);
|
mOnOffButton = (LongToggleButton) v.findViewById(R.id.router_onoff_button);
|
||||||
|
vGracefulButtons = (LinearLayout) v.findViewById(R.id.router_graceful_buttons);
|
||||||
|
mScrollView = (ScrollView) v.findViewById(R.id.main_scrollview);
|
||||||
|
vStatusContainer = v.findViewById(R.id.status_container);
|
||||||
|
vNetStatusLevel = (ImageView) v.findViewById(R.id.console_net_status_level);
|
||||||
|
vNetStatusText = (TextView) v.findViewById(R.id.console_net_status_text);
|
||||||
|
vNonNetStatus = v.findViewById(R.id.console_non_net_status_container);
|
||||||
|
vUptime = (TextView) v.findViewById(R.id.console_uptime);
|
||||||
|
vActive = (TextView) v.findViewById(R.id.console_active);
|
||||||
|
vKnown = (TextView) v.findViewById(R.id.console_known);
|
||||||
|
vTunnels = (TableLayout) v.findViewById(R.id.main_tunnels);
|
||||||
|
vAdvStatus = (LinearLayout) v.findViewById(R.id.console_advanced_status);
|
||||||
|
vAdvStatusText = (TextView) v.findViewById(R.id.console_advanced_status_text);
|
||||||
|
|
||||||
LongToggleButton b = (LongToggleButton) v.findViewById(R.id.router_onoff_button);
|
updateState(lastRouterState);
|
||||||
b.setOnLongClickListener(new View.OnLongClickListener() {
|
|
||||||
|
mOnOffButton.setOnLongClickListener(new View.OnLongClickListener() {
|
||||||
|
|
||||||
public boolean onLongClick(View view) {
|
public boolean onLongClick(View view) {
|
||||||
boolean on = ((ToggleButton) view).isChecked();
|
boolean on = ((ToggleButton) view).isChecked();
|
||||||
if (on) {
|
if (on) {
|
||||||
_startPressed = true;
|
|
||||||
mCallback.onStartRouterClicked();
|
mCallback.onStartRouterClicked();
|
||||||
updateOneShot();
|
updateOneShot();
|
||||||
checkFirstStart();
|
checkFirstStart();
|
||||||
} else if(mCallback.onGracefulShutdownClicked())
|
} else if (mCallback.onGracefulShutdownClicked())
|
||||||
updateOneShot();
|
updateOneShot();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -133,7 +177,7 @@ public class MainFragment extends I2PFragmentBase {
|
|||||||
@Override
|
@Override
|
||||||
public boolean onLongClick(View view) {
|
public boolean onLongClick(View view) {
|
||||||
if (mCallback.isGracefulShutdownInProgress())
|
if (mCallback.isGracefulShutdownInProgress())
|
||||||
if(mCallback.onStopRouterClicked())
|
if (mCallback.onStopRouterClicked())
|
||||||
updateOneShot();
|
updateOneShot();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -143,7 +187,7 @@ public class MainFragment extends I2PFragmentBase {
|
|||||||
@Override
|
@Override
|
||||||
public boolean onLongClick(View view) {
|
public boolean onLongClick(View view) {
|
||||||
if (mCallback.isGracefulShutdownInProgress())
|
if (mCallback.isGracefulShutdownInProgress())
|
||||||
if(mCallback.onCancelGracefulShutdownClicked())
|
if (mCallback.onCancelGracefulShutdownClicked())
|
||||||
updateOneShot();
|
updateOneShot();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -157,19 +201,46 @@ public class MainFragment extends I2PFragmentBase {
|
|||||||
super.onStart();
|
super.onStart();
|
||||||
_handler.removeCallbacks(_updater);
|
_handler.removeCallbacks(_updater);
|
||||||
_handler.removeCallbacks(_oneShotUpdate);
|
_handler.removeCallbacks(_oneShotUpdate);
|
||||||
if(_savedStatus != null) {
|
if (_savedStatus != null) {
|
||||||
TextView tv = (TextView) getActivity().findViewById(R.id.main_status_text);
|
TextView tv = (TextView) getActivity().findViewById(R.id.console_advanced_status_text);
|
||||||
tv.setText(_savedStatus);
|
tv.setText(_savedStatus);
|
||||||
}
|
}
|
||||||
checkDialog();
|
checkDialog();
|
||||||
_handler.postDelayed(_updater, 100);
|
_handler.postDelayed(_updater, 100);
|
||||||
|
|
||||||
|
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getActivity());
|
||||||
|
|
||||||
|
IntentFilter filter = new IntentFilter();
|
||||||
|
filter.addAction(RouterService.LOCAL_BROADCAST_STATE_NOTIFICATION);
|
||||||
|
filter.addAction(RouterService.LOCAL_BROADCAST_STATE_CHANGED);
|
||||||
|
lbm.registerReceiver(onStateChange, filter);
|
||||||
|
|
||||||
|
lbm.sendBroadcast(new Intent(RouterService.LOCAL_BROADCAST_REQUEST_STATE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private State lastRouterState;
|
||||||
|
private BroadcastReceiver onStateChange = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
State state = intent.getParcelableExtra(RouterService.LOCAL_BROADCAST_EXTRA_STATE);
|
||||||
|
if (lastRouterState == null || lastRouterState != state) {
|
||||||
|
updateState(state);
|
||||||
|
// If we have stopped, clear the status info immediately
|
||||||
|
if (Util.isStopped(state)) {
|
||||||
|
updateOneShot();
|
||||||
|
}
|
||||||
|
lastRouterState = state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStop() {
|
public void onStop() {
|
||||||
super.onStop();
|
super.onStop();
|
||||||
_handler.removeCallbacks(_updater);
|
_handler.removeCallbacks(_updater);
|
||||||
_handler.removeCallbacks(_oneShotUpdate);
|
_handler.removeCallbacks(_oneShotUpdate);
|
||||||
|
|
||||||
|
LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(onStateChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -180,9 +251,10 @@ public class MainFragment extends I2PFragmentBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
if(_savedStatus != null) {
|
if (lastRouterState != null)
|
||||||
|
outState.putParcelable("lastState", lastRouterState);
|
||||||
|
if (_savedStatus != null)
|
||||||
outState.putString("status", _savedStatus);
|
outState.putString("status", _savedStatus);
|
||||||
}
|
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,7 +266,12 @@ public class MainFragment extends I2PFragmentBase {
|
|||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
updateVisibility();
|
updateVisibility();
|
||||||
updateStatus();
|
try {
|
||||||
|
updateStatus();
|
||||||
|
} catch (NullPointerException npe) {
|
||||||
|
// RouterContext wasn't quite ready
|
||||||
|
Util.w("Status was updated before RouterContext was ready", npe);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,10 +280,16 @@ public class MainFragment extends I2PFragmentBase {
|
|||||||
private int counter;
|
private int counter;
|
||||||
private final int delay = 1000;
|
private final int delay = 1000;
|
||||||
private final int toloop = delay / 500;
|
private final int toloop = delay / 500;
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
updateVisibility();
|
updateVisibility();
|
||||||
if(counter++ % toloop == 0) {
|
if (counter++ % toloop == 0) {
|
||||||
updateStatus();
|
try {
|
||||||
|
updateStatus();
|
||||||
|
} catch (NullPointerException npe) {
|
||||||
|
// RouterContext wasn't quite ready
|
||||||
|
Util.w("Status was updated before RouterContext was ready", npe);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//_handler.postDelayed(this, 2500);
|
//_handler.postDelayed(this, 2500);
|
||||||
_handler.postDelayed(this, delay);
|
_handler.postDelayed(this, delay);
|
||||||
@ -215,119 +298,91 @@ public class MainFragment extends I2PFragmentBase {
|
|||||||
|
|
||||||
private void updateVisibility() {
|
private void updateVisibility() {
|
||||||
boolean showOnOff = mCallback.shouldShowOnOff();
|
boolean showOnOff = mCallback.shouldShowOnOff();
|
||||||
ToggleButton b = (ToggleButton) getActivity().findViewById(R.id.router_onoff_button);
|
mOnOffButton.setVisibility(showOnOff ? View.VISIBLE : View.GONE);
|
||||||
b.setVisibility(showOnOff ? View.VISIBLE : View.GONE);
|
|
||||||
|
|
||||||
boolean isOn = mCallback.shouldBeOn();
|
boolean isOn = mCallback.shouldBeOn();
|
||||||
b.setChecked(isOn);
|
mOnOffButton.setChecked(isOn);
|
||||||
|
|
||||||
boolean isGraceful = mCallback.isGracefulShutdownInProgress();
|
boolean isGraceful = mCallback.isGracefulShutdownInProgress();
|
||||||
LinearLayout gv = (LinearLayout) getActivity().findViewById(R.id.router_graceful_buttons);
|
vGracefulButtons.setVisibility(isGraceful ? View.VISIBLE : View.GONE);
|
||||||
gv.setVisibility(isGraceful ? View.VISIBLE : View.GONE);
|
|
||||||
if (isOn && isGraceful) {
|
if (isOn && isGraceful) {
|
||||||
RouterContext ctx = getRouterContext();
|
RouterContext ctx = getRouterContext();
|
||||||
if (ctx != null) {
|
if (ctx != null) {
|
||||||
TextView tv = (TextView) gv.findViewById(R.id.router_graceful_status);
|
TextView tv = (TextView) vGracefulButtons.findViewById(R.id.router_graceful_status);
|
||||||
long ms = ctx.router().getShutdownTimeRemaining();
|
long ms = ctx.router().getShutdownTimeRemaining();
|
||||||
if (ms > 1000) {
|
if (ms > 1000) {
|
||||||
tv.setText(getActivity().getResources().getString(R.string.button_router_graceful,
|
tv.setText(getActivity().getResources().getString(R.string.button_router_graceful,
|
||||||
DataHelper.formatDuration(ms)));
|
DataHelper.formatDuration(ms)));
|
||||||
} else {
|
} else {
|
||||||
tv.setText("Stopping I2P");
|
tv.setText(getActivity().getString(R.string.notification_status_stopping));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showOnOff && !isOn) {
|
|
||||||
// Sometimes the final state message from the RouterService
|
|
||||||
// is not received. Ensure that the state image is correct.
|
|
||||||
// TODO: Fix the race between RouterService shutdown and
|
|
||||||
// IRouterState unbinding.
|
|
||||||
updateState(State.INIT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean onBackPressed() {
|
|
||||||
RouterContext ctx = getRouterContext();
|
|
||||||
// RouterService svc = _routerService; Which is better to use?!
|
|
||||||
_keep = Connectivity.isConnected(getActivity()) && (ctx != null || _startPressed);
|
|
||||||
Util.d("*********************************************************");
|
|
||||||
Util.d("Back pressed, Keep? " + _keep);
|
|
||||||
Util.d("*********************************************************");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
if(!_keep) {
|
|
||||||
Thread t = new Thread(new KillMe());
|
|
||||||
t.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class KillMe implements Runnable {
|
|
||||||
|
|
||||||
public void run() {
|
|
||||||
Util.d("*********************************************************");
|
|
||||||
Util.d("KillMe started!");
|
|
||||||
Util.d("*********************************************************");
|
|
||||||
try {
|
|
||||||
Thread.sleep(500); // is 500ms long enough?
|
|
||||||
} catch(InterruptedException ex) {
|
|
||||||
}
|
|
||||||
System.exit(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateState(State newState) {
|
public void updateState(State newState) {
|
||||||
final ImageView lightImage = (ImageView) getView().findViewById(R.id.main_lights);
|
|
||||||
if (newState == State.INIT ||
|
if (newState == State.INIT ||
|
||||||
newState == State.STOPPED ||
|
newState == State.STOPPED ||
|
||||||
newState == State.MANUAL_STOPPED ||
|
newState == State.MANUAL_STOPPED ||
|
||||||
newState == State.MANUAL_QUITTED ||
|
newState == State.MANUAL_QUITTED ||
|
||||||
newState == State.NETWORK_STOPPED) {
|
newState == State.NETWORK_STOPPED) {
|
||||||
lightImage.setImageResource(R.drawable.routerlogo_0);
|
mConsoleLights.setImageResource(R.drawable.routerlogo_0);
|
||||||
} else if (newState == State.STARTING ||
|
} else if (newState == State.STARTING ||
|
||||||
newState == State.GRACEFUL_SHUTDOWN ||
|
//newState == State.GRACEFUL_SHUTDOWN || // Don't change lights for graceful
|
||||||
newState == State.STOPPING ||
|
newState == State.STOPPING ||
|
||||||
newState == State.MANUAL_STOPPING ||
|
newState == State.MANUAL_STOPPING ||
|
||||||
newState == State.MANUAL_QUITTING ||
|
newState == State.MANUAL_QUITTING ||
|
||||||
newState == State.NETWORK_STOPPING) {
|
newState == State.NETWORK_STOPPING) {
|
||||||
lightImage.setImageResource(R.drawable.routerlogo_1);
|
mConsoleLights.setImageResource(R.drawable.routerlogo_1);
|
||||||
} else if (newState == State.RUNNING) {
|
} else if (newState == State.RUNNING) {
|
||||||
lightImage.setImageResource(R.drawable.routerlogo_2);
|
mConsoleLights.setImageResource(R.drawable.routerlogo_2);
|
||||||
} else if (newState == State.ACTIVE) {
|
} else if (newState == State.ACTIVE) {
|
||||||
lightImage.setImageResource(R.drawable.routerlogo_3);
|
mConsoleLights.setImageResource(R.drawable.routerlogo_3);
|
||||||
} else if (newState == State.WAITING) {
|
} else if (newState == State.WAITING) {
|
||||||
lightImage.setImageResource(R.drawable.routerlogo_4);
|
mConsoleLights.setImageResource(R.drawable.routerlogo_4);
|
||||||
} // Ignore unknown states.
|
} // Ignore unknown states.
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateStatus() {
|
private void updateStatus() {
|
||||||
RouterContext ctx = getRouterContext();
|
RouterContext ctx = getRouterContext();
|
||||||
ScrollView sv = (ScrollView) getActivity().findViewById(R.id.main_scrollview);
|
|
||||||
LinearLayout vStatus = (LinearLayout) getActivity().findViewById(R.id.main_status);
|
|
||||||
TextView vStatusText = (TextView) getActivity().findViewById(R.id.main_status_text);
|
|
||||||
|
|
||||||
if(!Connectivity.isConnected(getActivity())) {
|
if (!Connectivity.isConnected(getActivity())) {
|
||||||
// Manually set state, RouterService won't be running
|
// Manually set state, RouterService won't be running
|
||||||
updateState(State.WAITING);
|
updateState(State.WAITING);
|
||||||
vStatusText.setText("No Internet connection is available");
|
vNetStatusText.setText(R.string.no_internet);
|
||||||
vStatus.setVisibility(View.VISIBLE);
|
vStatusContainer.setVisibility(View.VISIBLE);
|
||||||
sv.setVisibility(View.VISIBLE);
|
vNonNetStatus.setVisibility(View.GONE);
|
||||||
} else if(ctx != null) {
|
} else if (lastRouterState != null &&
|
||||||
if(_startPressed) {
|
!Util.isStopping(lastRouterState) &&
|
||||||
_startPressed = false;
|
!Util.isStopped(lastRouterState) &&
|
||||||
|
ctx != null) {
|
||||||
|
Util.NetStatus netStatus = Util.getNetStatus(getActivity(), ctx);
|
||||||
|
switch (netStatus.level) {
|
||||||
|
case ERROR:
|
||||||
|
vNetStatusLevel.setImageDrawable(getResources().getDrawable(R.drawable.ic_error_red_24dp));
|
||||||
|
vNetStatusLevel.setVisibility(View.VISIBLE);
|
||||||
|
break;
|
||||||
|
case WARN:
|
||||||
|
vNetStatusLevel.setImageDrawable(getResources().getDrawable(R.drawable.ic_warning_amber_24dp));
|
||||||
|
vNetStatusLevel.setVisibility(View.VISIBLE);
|
||||||
|
break;
|
||||||
|
case INFO:
|
||||||
|
default:
|
||||||
|
vNetStatusLevel.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
vNetStatusText.setText(getString(R.string.settings_label_network) + ": " + netStatus.status);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
// Load running tunnels
|
// Load running tunnels
|
||||||
loadDestinations(ctx);
|
loadDestinations(ctx);
|
||||||
|
|
||||||
if (PreferenceManager.getDefaultSharedPreferences(getActivity()).getBoolean(PREF_SHOW_STATS, false)) {
|
if (PreferenceManager.getDefaultSharedPreferences(getActivity()).getBoolean(PREF_SHOW_STATS, false)) {
|
||||||
short reach = ctx.commSystem().getReachabilityStatus();
|
|
||||||
int active = ctx.commSystem().countActivePeers();
|
|
||||||
int known = Math.max(ctx.netDb().getKnownRouters() - 1, 0);
|
|
||||||
int inEx = ctx.tunnelManager().getFreeTunnelCount();
|
int inEx = ctx.tunnelManager().getFreeTunnelCount();
|
||||||
int outEx = ctx.tunnelManager().getOutboundTunnelCount();
|
int outEx = ctx.tunnelManager().getOutboundTunnelCount();
|
||||||
int inCl = ctx.tunnelManager().getInboundClientTunnelCount();
|
int inCl = ctx.tunnelManager().getInboundClientTunnelCount();
|
||||||
@ -336,80 +391,52 @@ public class MainFragment extends I2PFragmentBase {
|
|||||||
double dLag = ctx.statManager().getRate("jobQueue.jobLag").getRate(60000).getAverageValue();
|
double dLag = ctx.statManager().getRate("jobQueue.jobLag").getRate(60000).getAverageValue();
|
||||||
String jobLag = DataHelper.formatDuration((long) dLag);
|
String jobLag = DataHelper.formatDuration((long) dLag);
|
||||||
String msgDelay = DataHelper.formatDuration(ctx.throttle().getMessageDelay());
|
String msgDelay = DataHelper.formatDuration(ctx.throttle().getMessageDelay());
|
||||||
String uptime = DataHelper.formatDuration(ctx.router().getUptime());
|
|
||||||
|
|
||||||
String netstatus;
|
|
||||||
if (reach == net.i2p.router.CommSystemFacade.STATUS_DIFFERENT) {
|
|
||||||
netstatus = "Symmetric NAT";
|
|
||||||
} else if (reach == net.i2p.router.CommSystemFacade.STATUS_HOSED) {
|
|
||||||
netstatus = "Port Failure";
|
|
||||||
} else if (reach == net.i2p.router.CommSystemFacade.STATUS_OK) {
|
|
||||||
netstatus = "OK";
|
|
||||||
} else if (reach == net.i2p.router.CommSystemFacade.STATUS_REJECT_UNSOLICITED) {
|
|
||||||
netstatus = "Firewalled";
|
|
||||||
} else {
|
|
||||||
netstatus = "Unknown";
|
|
||||||
}
|
|
||||||
String tunnelStatus = ctx.throttle().getTunnelStatus();
|
String tunnelStatus = ctx.throttle().getTunnelStatus();
|
||||||
//ctx.commSystem().getReachabilityStatus();
|
//ctx.commSystem().getReachabilityStatus();
|
||||||
double inBW = ctx.bandwidthLimiter().getReceiveBps() / 1024;
|
|
||||||
double outBW = ctx.bandwidthLimiter().getSendBps() / 1024;
|
|
||||||
|
|
||||||
// control total width
|
|
||||||
DecimalFormat fmt;
|
|
||||||
if(inBW >= 1000 || outBW >= 1000) {
|
|
||||||
fmt = new DecimalFormat("#0");
|
|
||||||
} else if(inBW >= 100 || outBW >= 100) {
|
|
||||||
fmt = new DecimalFormat("#0.0");
|
|
||||||
} else {
|
|
||||||
fmt = new DecimalFormat("#0.00");
|
|
||||||
}
|
|
||||||
|
|
||||||
double kBytesIn = ctx.bandwidthLimiter().getTotalAllocatedInboundBytes() / 1024;
|
|
||||||
double kBytesOut = ctx.bandwidthLimiter().getTotalAllocatedOutboundBytes() / 1024;
|
|
||||||
|
|
||||||
// control total width
|
|
||||||
DecimalFormat kBfmt;
|
|
||||||
if(kBytesIn >= 1000 || kBytesOut >= 1000) {
|
|
||||||
kBfmt = new DecimalFormat("#0");
|
|
||||||
} else if(kBytesIn >= 100 || kBytesOut >= 100) {
|
|
||||||
kBfmt = new DecimalFormat("#0.0");
|
|
||||||
} else {
|
|
||||||
kBfmt = new DecimalFormat("#0.00");
|
|
||||||
}
|
|
||||||
|
|
||||||
String status =
|
String status =
|
||||||
"Network: " + netstatus
|
"Exploratory Tunnels in/out: " + inEx + " / " + outEx
|
||||||
+ "\nPeers active/known: " + active + " / " + known
|
+ "\nClient Tunnels in/out: " + inCl + " / " + outCl;
|
||||||
+ "\nExploratory Tunnels in/out: " + inEx + " / " + outEx
|
|
||||||
+ "\nClient Tunnels in/out: " + inCl + " / " + outCl;
|
|
||||||
|
|
||||||
|
|
||||||
// Need to see if we have the participation option set to on.
|
// 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?
|
// 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.
|
// It would be easier if we had a number to test status.
|
||||||
String participate = "\nParticipation: " + tunnelStatus +" (" + part + ")";
|
String participate = "\nParticipation: " + tunnelStatus + " (" + part + ")";
|
||||||
|
|
||||||
String details =
|
String details =
|
||||||
"\nBandwidth in/out: " + fmt.format(inBW) + " / " + fmt.format(outBW) + " KBps"
|
"\nMemory: " + DataHelper.formatSize(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())
|
||||||
+ "\nData usage in/out: " + kBfmt.format(kBytesIn) + " / " + kBfmt.format(kBytesOut) + " KB"
|
|
||||||
+ "\nMemory: " + DataHelper.formatSize(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())
|
|
||||||
+ "B / " + DataHelper.formatSize(Runtime.getRuntime().maxMemory()) + 'B'
|
+ "B / " + DataHelper.formatSize(Runtime.getRuntime().maxMemory()) + 'B'
|
||||||
+ "\nJob Lag: " + jobLag
|
+ "\nJob Lag: " + jobLag
|
||||||
+ "\nMsg Delay: " + msgDelay
|
+ "\nMsg Delay: " + msgDelay;
|
||||||
+ "\nUptime: " + uptime;
|
|
||||||
|
|
||||||
_savedStatus = status + participate + details;
|
_savedStatus = status + participate + details;
|
||||||
vStatusText.setText(_savedStatus);
|
vAdvStatusText.setText(_savedStatus);
|
||||||
vStatus.setVisibility(View.VISIBLE);
|
vAdvStatus.setVisibility(View.VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
vStatus.setVisibility(View.GONE);
|
vAdvStatus.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
sv.setVisibility(View.VISIBLE);
|
vStatusContainer.setVisibility(View.VISIBLE);
|
||||||
|
vNonNetStatus.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
// Usage stats in bottom toolbar
|
||||||
|
|
||||||
|
double inBw = ctx.bandwidthLimiter().getReceiveBps();
|
||||||
|
double outBw = ctx.bandwidthLimiter().getSendBps();
|
||||||
|
double inData = ctx.bandwidthLimiter().getTotalAllocatedInboundBytes();
|
||||||
|
double outData = ctx.bandwidthLimiter().getTotalAllocatedOutboundBytes();
|
||||||
|
|
||||||
|
((TextView) getActivity().findViewById(R.id.console_download_stats)).setText(
|
||||||
|
Util.formatSize(inBw) + "Bps / " + Util.formatSize(inData) + "B");
|
||||||
|
((TextView) getActivity().findViewById(R.id.console_upload_stats)).setText(
|
||||||
|
Util.formatSize(outBw) + "Bps / " + Util.formatSize(outData) + "B");
|
||||||
|
|
||||||
|
getActivity().findViewById(R.id.console_usage_stats).setVisibility(View.VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
// network but no router context
|
// network but no router context
|
||||||
vStatusText.setText("Not running");
|
vStatusContainer.setVisibility(View.GONE);
|
||||||
sv.setVisibility(View.INVISIBLE);
|
getActivity().findViewById(R.id.console_usage_stats).setVisibility(View.INVISIBLE);
|
||||||
/**
|
/**
|
||||||
* **
|
* **
|
||||||
* RouterService svc = _routerService; String status = "connected? "
|
* RouterService svc = _routerService; String status = "connected? "
|
||||||
@ -423,21 +450,24 @@ public class MainFragment extends I2PFragmentBase {
|
|||||||
* "null" : svc.canManualStart()) + "\ncan stop? " + (svc == null ?
|
* "null" : svc.canManualStart()) + "\ncan stop? " + (svc == null ?
|
||||||
* "null" : svc.canManualStop()); tv.setText(status);
|
* "null" : svc.canManualStop()); tv.setText(status);
|
||||||
* tv.setVisibility(View.VISIBLE);
|
* tv.setVisibility(View.VISIBLE);
|
||||||
***
|
***
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Based on net.i2p.router.web.SummaryHelper.getDestinations()
|
* Based on net.i2p.router.web.SummaryHelper.getDestinations()
|
||||||
|
*
|
||||||
* @param ctx The RouterContext
|
* @param ctx The RouterContext
|
||||||
*/
|
*/
|
||||||
private void loadDestinations(RouterContext ctx) {
|
private void loadDestinations(RouterContext ctx) {
|
||||||
TableLayout dests = (TableLayout) getView().findViewById(R.id.main_tunnels);
|
vTunnels.removeAllViews();
|
||||||
dests.removeAllViews();
|
|
||||||
|
|
||||||
List<Destination> clients = new ArrayList<Destination>(ctx.clientManager().listClients());
|
List<Destination> clients = null;
|
||||||
if (!clients.isEmpty()) {
|
if (ctx.clientManager() != null)
|
||||||
|
clients = new ArrayList<Destination>(ctx.clientManager().listClients());
|
||||||
|
|
||||||
|
if (clients != null && !clients.isEmpty()) {
|
||||||
Collections.sort(clients, new AlphaComparator(ctx));
|
Collections.sort(clients, new AlphaComparator(ctx));
|
||||||
for (Destination client : clients) {
|
for (Destination client : clients) {
|
||||||
String name = getName(ctx, client);
|
String name = getName(ctx, client);
|
||||||
@ -479,25 +509,29 @@ public class MainFragment extends I2PFragmentBase {
|
|||||||
type.setBackgroundResource(R.drawable.tunnel_yellow);
|
type.setBackgroundResource(R.drawable.tunnel_yellow);
|
||||||
}
|
}
|
||||||
|
|
||||||
dests.addView(dest);
|
vTunnels.addView(dest);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
TableRow empty = new TableRow(getActivity());
|
TableRow empty = new TableRow(getActivity());
|
||||||
TextView emptyText = new TextView(getActivity());
|
TextView emptyText = new TextView(getActivity());
|
||||||
emptyText.setText(R.string.no_client_tunnels_running);
|
emptyText.setText(R.string.no_tunnels_running);
|
||||||
empty.addView(emptyText);
|
empty.addView(emptyText);
|
||||||
dests.addView(empty);
|
vTunnels.addView(empty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** compare translated nicknames - put "shared clients" first in the sort */
|
private static final String SHARED_CLIENTS = "shared clients";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* compare translated nicknames - put "shared clients" first in the sort
|
||||||
|
*/
|
||||||
private class AlphaComparator implements Comparator<Destination> {
|
private class AlphaComparator implements Comparator<Destination> {
|
||||||
private String xsc;
|
private String xsc;
|
||||||
private RouterContext _ctx;
|
private RouterContext _ctx;
|
||||||
|
|
||||||
public AlphaComparator(RouterContext ctx) {
|
public AlphaComparator(RouterContext ctx) {
|
||||||
_ctx = ctx;
|
_ctx = ctx;
|
||||||
xsc = _(ctx, "shared clients");
|
xsc = _(ctx, SHARED_CLIENTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int compare(Destination lhs, Destination rhs) {
|
public int compare(Destination lhs, Destination rhs) {
|
||||||
@ -511,34 +545,85 @@ public class MainFragment extends I2PFragmentBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** translate here so collation works above */
|
/**
|
||||||
|
* translate here so collation works above
|
||||||
|
*/
|
||||||
private String getName(RouterContext ctx, Destination d) {
|
private String getName(RouterContext ctx, Destination d) {
|
||||||
TunnelPoolSettings in = ctx.tunnelManager().getInboundSettings(d.calculateHash());
|
TunnelPoolSettings in = ctx.tunnelManager().getInboundSettings(d.calculateHash());
|
||||||
String name = (in != null ? in.getDestinationNickname() : null);
|
String name = (in != null ? in.getDestinationNickname() : null);
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
TunnelPoolSettings out = ctx.tunnelManager().getOutboundSettings(d.calculateHash());
|
TunnelPoolSettings out = ctx.tunnelManager().getOutboundSettings(d.calculateHash());
|
||||||
name = (out != null ? out.getDestinationNickname() : null);
|
name = (out != null ? out.getDestinationNickname() : null);
|
||||||
if (name == null)
|
|
||||||
name = d.calculateHash().toBase64().substring(0,6);
|
|
||||||
else
|
|
||||||
name = _(ctx, name);
|
|
||||||
} else {
|
|
||||||
name = _(ctx, name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (name == null)
|
||||||
|
name = d.calculateHash().toBase64().substring(0, 6);
|
||||||
|
else
|
||||||
|
name = _(ctx, name);
|
||||||
|
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String _(RouterContext ctx, String s) {
|
private String _(RouterContext ctx, String s) {
|
||||||
return Translate.getString(s, ctx, "net.i2p.router.web.messages");
|
if (SHARED_CLIENTS.equals(s))
|
||||||
|
return getString(R.string.shared_clients);
|
||||||
|
else
|
||||||
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkDialog() {
|
private void checkDialog() {
|
||||||
I2PActivityBase ab = (I2PActivityBase) getActivity();
|
final I2PActivityBase ab = (I2PActivityBase) getActivity();
|
||||||
boolean configureBrowser = ab.getPref(PREF_CONFIGURE_BROWSER, true);
|
String language = PreferenceManager.getDefaultSharedPreferences(ab).getString(
|
||||||
if (configureBrowser) {
|
getString(R.string.PREF_LANGUAGE), null
|
||||||
ConfigureBrowserDialog dialog = new ConfigureBrowserDialog();
|
);
|
||||||
dialog.show(getActivity().getSupportFragmentManager(), "configurebrowser");
|
if (language == null) {
|
||||||
ab.setPref(PREF_CONFIGURE_BROWSER, false);
|
AlertDialog.Builder b = new AlertDialog.Builder(getActivity());
|
||||||
|
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];
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(getActivity())
|
||||||
|
.edit()
|
||||||
|
.putString(getString(R.string.PREF_LANGUAGE), language)
|
||||||
|
.commit();
|
||||||
|
// Close the dialog
|
||||||
|
dialog.dismiss();
|
||||||
|
// Broadcast the change to RouterService just in case the router is running
|
||||||
|
Intent intent = new Intent(RouterService.LOCAL_BROADCAST_LOCALE_CHANGED);
|
||||||
|
LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(intent);
|
||||||
|
// Update the parent
|
||||||
|
ab.notifyLocaleChanged();
|
||||||
|
// Run checkDialog() again to show the next dialog
|
||||||
|
// (if the change doesn't restart the Activity)
|
||||||
|
checkDialog();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setCancelable(false)
|
||||||
|
.show();
|
||||||
|
} else if (ab.getPref(PREF_CONFIGURE_BROWSER, true)) {
|
||||||
|
AlertDialog.Builder b = new AlertDialog.Builder(getActivity());
|
||||||
|
b.setTitle(R.string.configure_browser_title)
|
||||||
|
.setMessage(R.string.configure_browser_for_i2p)
|
||||||
|
.setCancelable(false)
|
||||||
|
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int i) {
|
||||||
|
dialog.dismiss();
|
||||||
|
ab.setPref(PREF_CONFIGURE_BROWSER, false);
|
||||||
|
Intent hi = new Intent(getActivity(), BrowserConfigActivity.class);
|
||||||
|
startActivity(hi);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int i) {
|
||||||
|
dialog.cancel();
|
||||||
|
ab.setPref(PREF_CONFIGURE_BROWSER, false);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.show();
|
||||||
}
|
}
|
||||||
/*VersionDialog dialog = new VersionDialog();
|
/*VersionDialog dialog = new VersionDialog();
|
||||||
String oldVersion = ((I2PActivityBase) getActivity()).getPref(PREF_INSTALLED_VERSION, "??");
|
String oldVersion = ((I2PActivityBase) getActivity()).getPref(PREF_INSTALLED_VERSION, "??");
|
||||||
|
@ -1,12 +1,20 @@
|
|||||||
package net.i2p.android.router;
|
package net.i2p.android.router;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
|
|
||||||
|
import net.i2p.android.I2PActivityBase;
|
||||||
|
|
||||||
public class NewsActivity extends I2PActivityBase {
|
public class NewsActivity extends I2PActivityBase {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
mDrawerToggle.setDrawerIndicatorEnabled(false);
|
setContentView(R.layout.activity_onepane);
|
||||||
|
|
||||||
|
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
|
||||||
// Start with the base view
|
// Start with the base view
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
NewsFragment f = new NewsFragment();
|
NewsFragment f = new NewsFragment();
|
||||||
|
@ -1,370 +1,154 @@
|
|||||||
package net.i2p.android.router;
|
package net.i2p.android.router;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.content.Intent;
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.CheckBoxPreference;
|
|
||||||
import android.preference.Preference;
|
import android.preference.Preference;
|
||||||
import android.preference.PreferenceActivity;
|
import android.support.v4.app.Fragment;
|
||||||
import android.preference.PreferenceCategory;
|
import android.support.v4.app.FragmentManager;
|
||||||
import android.preference.PreferenceFragment;
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
import android.preference.PreferenceManager;
|
import android.support.v4.preference.PreferenceFragment;
|
||||||
import android.preference.PreferenceScreen;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.android.I2PActivity;
|
||||||
import net.i2p.android.router.service.StatSummarizer;
|
import net.i2p.android.preferences.AdvancedPreferenceFragment;
|
||||||
import net.i2p.android.router.util.PortPreference;
|
import net.i2p.android.preferences.AppearancePreferenceFragment;
|
||||||
import net.i2p.android.router.util.Util;
|
import net.i2p.android.preferences.GraphsPreferenceFragment;
|
||||||
import net.i2p.router.RouterContext;
|
import net.i2p.android.preferences.LoggingPreferenceFragment;
|
||||||
import net.i2p.stat.FrequencyStat;
|
import net.i2p.android.preferences.NetworkPreferenceFragment;
|
||||||
import net.i2p.stat.Rate;
|
import net.i2p.android.router.addressbook.AddressbookSettingsActivity;
|
||||||
import net.i2p.stat.RateStat;
|
import net.i2p.android.router.service.RouterService;
|
||||||
import net.i2p.stat.StatManager;
|
import net.i2p.android.util.LocaleManager;
|
||||||
import net.i2p.util.LogManager;
|
|
||||||
|
|
||||||
import java.util.List;
|
public class SettingsActivity extends AppCompatActivity implements
|
||||||
import java.util.Map;
|
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
import java.util.Properties;
|
public static final String PREFERENCE_CATEGORY = "preference_category";
|
||||||
import java.util.Set;
|
public static final String PREFERENCE_CATEGORY_NETWORK = "preference_category_network";
|
||||||
import java.util.SortedSet;
|
public static final String PREFERENCE_CATEGORY_GRAPHS = "preference_category_graphs";
|
||||||
|
public static final String PREFERENCE_CATEGORY_LOGGING = "preference_category_logging";
|
||||||
|
public static final String PREFERENCE_CATEGORY_ADDRESSBOOK = "preference_category_addressbook";
|
||||||
|
public static final String PREFERENCE_CATEGORY_APPEARANCE = "preference_category_appearance";
|
||||||
|
public static final String PREFERENCE_CATEGORY_ADVANCED = "preference_category_advanced";
|
||||||
|
|
||||||
public class SettingsActivity extends PreferenceActivity {
|
private final LocaleManager localeManager = new LocaleManager();
|
||||||
// Actions for legacy settings
|
|
||||||
private static final String ACTION_PREFS_NET = "net.i2p.android.router.PREFS_NET";
|
|
||||||
public static final String ACTION_PREFS_GRAPHS = "net.i2p.android.router.PREFS_GRAPHS";
|
|
||||||
private static final String ACTION_PREFS_LOGGING = "net.i2p.android.router.PREFS_LOGGING";
|
|
||||||
private static final String ACTION_PREFS_ADVANCED = "net.i2p.android.router.PREFS_ADVANCED";
|
|
||||||
|
|
||||||
private Toolbar mToolbar;
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
localeManager.onCreate(this);
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_single_fragment);
|
||||||
|
|
||||||
String action = getIntent().getAction();
|
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
|
||||||
if (action != null) {
|
setSupportActionBar(toolbar);
|
||||||
switch (action) {
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
case ACTION_PREFS_NET:
|
|
||||||
addPreferencesFromResource(R.xml.settings_net);
|
|
||||||
break;
|
|
||||||
case ACTION_PREFS_GRAPHS:
|
|
||||||
addPreferencesFromResource(R.xml.settings_graphs);
|
|
||||||
setupGraphSettings(this, getPreferenceScreen(), Util.getRouterContext());
|
|
||||||
break;
|
|
||||||
case ACTION_PREFS_LOGGING:
|
|
||||||
addPreferencesFromResource(R.xml.settings_logging);
|
|
||||||
setupLoggingSettings(this, getPreferenceScreen(), Util.getRouterContext());
|
|
||||||
break;
|
|
||||||
case ACTION_PREFS_ADVANCED:
|
|
||||||
addPreferencesFromResource(R.xml.settings_advanced);
|
|
||||||
setupAdvancedSettings(this, getPreferenceScreen());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Load any properties that the router might have changed on us.
|
|
||||||
setupPreferences(this, Util.getRouterContext());
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
|
Fragment fragment;
|
||||||
// Load the legacy preferences headers
|
String category = getIntent().getStringExtra(PREFERENCE_CATEGORY);
|
||||||
addPreferencesFromResource(R.xml.settings_headers_legacy);
|
if (category != null)
|
||||||
}
|
fragment = getFragmentForCategory(category);
|
||||||
}
|
else
|
||||||
|
fragment = new SettingsFragment();
|
||||||
|
|
||||||
mToolbar.setTitle(getTitle());
|
getSupportFragmentManager().beginTransaction()
|
||||||
}
|
.replace(R.id.fragment, fragment)
|
||||||
|
.commit();
|
||||||
protected static void setupPreferences(Context context, RouterContext ctx) {
|
|
||||||
if (ctx != null) {
|
|
||||||
final String udpPortKey = context.getString(R.string.PROP_UDP_INTERNAL_PORT);
|
|
||||||
final String ntcpPortKey = context.getString(R.string.PROP_I2NP_NTCP_PORT);
|
|
||||||
final String ntcpAutoPortKey = context.getString(R.string.PROP_I2NP_NTCP_AUTO_PORT);
|
|
||||||
|
|
||||||
int udpPort = ctx.getProperty(udpPortKey, -1);
|
|
||||||
int ntcpPort = ctx.getProperty(ntcpPortKey, -1);
|
|
||||||
boolean ntcpAutoPort = ctx.getBooleanPropertyDefaultTrue(ntcpAutoPortKey);
|
|
||||||
if (ntcpPort < 0 && ntcpAutoPort)
|
|
||||||
ntcpPort = udpPort;
|
|
||||||
|
|
||||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
|
||||||
if (prefs.getInt(udpPortKey, -1) != udpPort ||
|
|
||||||
prefs.getInt(ntcpPortKey, -1) != ntcpPort) {
|
|
||||||
SharedPreferences.Editor editor = prefs.edit();
|
|
||||||
editor.putInt(udpPortKey, udpPort);
|
|
||||||
editor.putInt(ntcpPortKey, ntcpPort);
|
|
||||||
// commit() instead of apply() because this needs to happen
|
|
||||||
// before SettingsActivity loads its Preferences.
|
|
||||||
editor.commit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static void setupGraphSettings(Context context, PreferenceScreen ps, RouterContext ctx) {
|
|
||||||
if (ctx == null) {
|
|
||||||
PreferenceCategory noRouter = new PreferenceCategory(context);
|
|
||||||
noRouter.setTitle(R.string.router_not_running);
|
|
||||||
ps.addPreference(noRouter);
|
|
||||||
} else if (StatSummarizer.instance() == null) {
|
|
||||||
PreferenceCategory noStats = new PreferenceCategory(context);
|
|
||||||
noStats.setTitle(R.string.stats_not_ready);
|
|
||||||
ps.addPreference(noStats);
|
|
||||||
} else {
|
|
||||||
StatManager mgr = ctx.statManager();
|
|
||||||
Map<String, SortedSet<String>> all = mgr.getStatsByGroup();
|
|
||||||
for (String group : all.keySet()) {
|
|
||||||
SortedSet<String> stats = all.get(group);
|
|
||||||
if (stats.size() == 0) continue;
|
|
||||||
PreferenceCategory groupPrefs = new PreferenceCategory(context);
|
|
||||||
groupPrefs.setKey("stat.groups." + group);
|
|
||||||
groupPrefs.setTitle(group);
|
|
||||||
ps.addPreference(groupPrefs);
|
|
||||||
for (String stat : stats) {
|
|
||||||
String key;
|
|
||||||
String description;
|
|
||||||
boolean canBeGraphed = false;
|
|
||||||
boolean currentIsGraphed = false;
|
|
||||||
RateStat rs = mgr.getRate(stat);
|
|
||||||
if (rs != null) {
|
|
||||||
description = rs.getDescription();
|
|
||||||
long period = rs.getPeriods()[0]; // should be the minimum
|
|
||||||
key = stat + "." + period;
|
|
||||||
if (period <= 10*60*1000) {
|
|
||||||
Rate r = rs.getRate(period);
|
|
||||||
canBeGraphed = r != null;
|
|
||||||
if (canBeGraphed) {
|
|
||||||
currentIsGraphed = r.getSummaryListener() != null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
FrequencyStat fs = mgr.getFrequency(stat);
|
|
||||||
if (fs != null) {
|
|
||||||
key = stat;
|
|
||||||
description = fs.getDescription();
|
|
||||||
// FrequencyStats cannot be graphed, but can be logged.
|
|
||||||
// XXX: Should log settings be here as well, or in a
|
|
||||||
// separate settings menu?
|
|
||||||
} else {
|
|
||||||
Util.e("Stat does not exist?! [" + stat + "]");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CheckBoxPreference statPref = new CheckBoxPreference(context);
|
|
||||||
statPref.setKey("stat.summaries." + key);
|
|
||||||
statPref.setTitle(stat);
|
|
||||||
statPref.setSummary(description);
|
|
||||||
statPref.setEnabled(canBeGraphed);
|
|
||||||
statPref.setChecked(currentIsGraphed);
|
|
||||||
groupPrefs.addPreference(statPref);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static void setupLoggingSettings(Context context, PreferenceScreen ps, RouterContext ctx) {
|
|
||||||
if (ctx != null) {
|
|
||||||
LogManager mgr = ctx.logManager();
|
|
||||||
// Log level overrides
|
|
||||||
/*
|
|
||||||
StringBuilder buf = new StringBuilder(32*1024);
|
|
||||||
Properties limits = mgr.getLimits();
|
|
||||||
TreeSet<String> sortedLogs = new TreeSet<String>();
|
|
||||||
for (Iterator iter = limits.keySet().iterator(); iter.hasNext(); ) {
|
|
||||||
String prefix = (String)iter.next();
|
|
||||||
sortedLogs.add(prefix);
|
|
||||||
}
|
|
||||||
for (Iterator iter = sortedLogs.iterator(); iter.hasNext(); ) {
|
|
||||||
String prefix = (String)iter.next();
|
|
||||||
String level = limits.getProperty(prefix);
|
|
||||||
buf.append(prefix).append('=').append(level).append('\n');
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
/* Don't show, there are no settings that require the router
|
|
||||||
} else {
|
|
||||||
PreferenceCategory noRouter = new PreferenceCategory(context);
|
|
||||||
noRouter.setTitle(R.string.router_not_running);
|
|
||||||
ps.addPreference(noRouter);
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static void setupAdvancedSettings(final Context context, PreferenceScreen ps) {
|
|
||||||
final String udpEnableKey = context.getString(R.string.PROP_ENABLE_UDP);
|
|
||||||
final String ntcpEnableKey = context.getString(R.string.PROP_ENABLE_NTCP);
|
|
||||||
final String udpPortKey = context.getString(R.string.PROP_UDP_INTERNAL_PORT);
|
|
||||||
final String ntcpPortKey = context.getString(R.string.PROP_I2NP_NTCP_PORT);
|
|
||||||
final String ntcpAutoPortKey = context.getString(R.string.PROP_I2NP_NTCP_AUTO_PORT);
|
|
||||||
|
|
||||||
final CheckBoxPreference udpEnable = (CheckBoxPreference) ps.findPreference(udpEnableKey);
|
|
||||||
final CheckBoxPreference ntcpEnable = (CheckBoxPreference) ps.findPreference(ntcpEnableKey);
|
|
||||||
final PortPreference udpPort = (PortPreference) ps.findPreference(udpPortKey);
|
|
||||||
final PortPreference ntcpPort = (PortPreference) ps.findPreference(ntcpPortKey);
|
|
||||||
final CheckBoxPreference ntcpAutoPort = (CheckBoxPreference) ps.findPreference(ntcpAutoPortKey);
|
|
||||||
|
|
||||||
udpEnable.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
|
||||||
final Boolean checked = (Boolean) newValue;
|
|
||||||
if (checked || ntcpEnable.isChecked())
|
|
||||||
return true;
|
|
||||||
else {
|
|
||||||
Toast.makeText(context, R.string.settings_need_transport_enabled, Toast.LENGTH_LONG).show();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ntcpEnable.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
|
||||||
final Boolean checked = (Boolean) newValue;
|
|
||||||
if (checked || udpEnable.isChecked())
|
|
||||||
return true;
|
|
||||||
else {
|
|
||||||
Toast.makeText(context, R.string.settings_need_transport_enabled, Toast.LENGTH_LONG).show();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
udpPort.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
|
||||||
if (ntcpAutoPort.isChecked())
|
|
||||||
ntcpPort.setText((String) newValue);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ntcpAutoPort.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
|
||||||
final Boolean checked = (Boolean) newValue;
|
|
||||||
if (checked)
|
|
||||||
ntcpPort.setText(udpPort.getText());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
|
||||||
@Override
|
|
||||||
public void onBuildHeaders(List<Header> target) {
|
|
||||||
// The resource com.android.internal.R.bool.preferences_prefer_dual_pane
|
|
||||||
// has different definitions based upon screen size. At present, it will
|
|
||||||
// be true for -sw720dp devices, false otherwise. For your curiosity, in
|
|
||||||
// Nexus 7 it is false.
|
|
||||||
loadHeadersFromResource(R.xml.settings_headers, target);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setContentView(int layoutResID) {
|
public void onResume() {
|
||||||
ViewGroup contentView = (ViewGroup) LayoutInflater.from(this).inflate(
|
super.onResume();
|
||||||
R.layout.activity_settings,
|
localeManager.onResume(this);
|
||||||
(ViewGroup) getWindow().getDecorView().getRootView(), false);
|
|
||||||
|
|
||||||
mToolbar = (Toolbar) contentView.findViewById(R.id.main_toolbar);
|
|
||||||
mToolbar.setNavigationIcon(getResources().getDrawable(R.drawable.ic_arrow_back_white_24dp));
|
|
||||||
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
onBackPressed();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ViewGroup contentWrapper = (ViewGroup) contentView.findViewById(R.id.content_wrapper);
|
|
||||||
LayoutInflater.from(this).inflate(layoutResID, contentWrapper, true);
|
|
||||||
|
|
||||||
getWindow().setContentView(contentView);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPause() {
|
public boolean onSupportNavigateUp() {
|
||||||
List<Properties> lProps = Util.getPropertiesFromPreferences(this);
|
FragmentManager fragmentManager = getSupportFragmentManager();
|
||||||
Properties props = lProps.get(0);
|
if (fragmentManager.getBackStackEntryCount() > 0) {
|
||||||
Properties propsToRemove = lProps.get(1);
|
fragmentManager.popBackStack();
|
||||||
Properties logSettings = lProps.get(2);
|
|
||||||
|
|
||||||
Set toRemove = propsToRemove.keySet();
|
|
||||||
|
|
||||||
boolean restartRequired = Util.checkAndCorrectRouterConfig(this, props, toRemove);
|
|
||||||
|
|
||||||
// Apply new config if we are running.
|
|
||||||
RouterContext rCtx = Util.getRouterContext();
|
|
||||||
if (rCtx != null) {
|
|
||||||
rCtx.router().saveConfig(props, toRemove);
|
|
||||||
|
|
||||||
// Merge in new log settings
|
|
||||||
saveLoggingChanges(rCtx, logSettings);
|
|
||||||
} else {
|
} else {
|
||||||
// Merge in new config settings, write the file.
|
Intent intent = new Intent(this, I2PActivity.class);
|
||||||
Util.mergeResourceToFile(this, Util.getFileDir(this), "router.config", R.raw.router_config, props, toRemove);
|
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
|
startActivity(intent);
|
||||||
// Merge in new log settings
|
finish();
|
||||||
saveLoggingChanges(I2PAppContext.getGlobalContext(), logSettings);
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
// Store the settings in Android
|
|
||||||
super.onPause();
|
|
||||||
|
|
||||||
if (restartRequired)
|
|
||||||
Toast.makeText(this, R.string.settings_router_restart_required, Toast.LENGTH_LONG).show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveLoggingChanges(I2PAppContext ctx, Properties logSettings) {
|
@Override
|
||||||
boolean shouldSave = false;
|
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||||
|
if (key.equals(getResources().getString(R.string.PREF_LANGUAGE))) {
|
||||||
for (Object key : logSettings.keySet()) {
|
localeManager.onResume(this);
|
||||||
if ("logger.defaultLevel".equals(key)) {
|
Intent intent = new Intent(RouterService.LOCAL_BROADCAST_LOCALE_CHANGED);
|
||||||
String defaultLevel = (String) logSettings.get(key);
|
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
|
||||||
String oldDefault = ctx.logManager().getDefaultLimit();
|
|
||||||
if (!defaultLevel.equals(oldDefault)) {
|
|
||||||
shouldSave = true;
|
|
||||||
ctx.logManager().setDefaultLimit(defaultLevel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldSave) {
|
|
||||||
ctx.logManager().saveConfig();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
|
||||||
public static class SettingsFragment extends PreferenceFragment {
|
public static class SettingsFragment extends PreferenceFragment {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle paramBundle) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(paramBundle);
|
||||||
|
addPreferencesFromResource(R.xml.settings);
|
||||||
|
|
||||||
String settings = getArguments().getString("settings");
|
this.findPreference(PREFERENCE_CATEGORY_NETWORK)
|
||||||
switch (settings) {
|
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_NETWORK));
|
||||||
case "net":
|
this.findPreference(PREFERENCE_CATEGORY_GRAPHS)
|
||||||
addPreferencesFromResource(R.xml.settings_net);
|
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_GRAPHS));
|
||||||
break;
|
this.findPreference(PREFERENCE_CATEGORY_LOGGING)
|
||||||
case "graphs":
|
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_LOGGING));
|
||||||
addPreferencesFromResource(R.xml.settings_graphs);
|
this.findPreference(PREFERENCE_CATEGORY_ADDRESSBOOK)
|
||||||
setupGraphSettings(getActivity(), getPreferenceScreen(), Util.getRouterContext());
|
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_ADDRESSBOOK));
|
||||||
break;
|
this.findPreference(PREFERENCE_CATEGORY_APPEARANCE)
|
||||||
case "logging":
|
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_APPEARANCE));
|
||||||
addPreferencesFromResource(R.xml.settings_logging);
|
this.findPreference(PREFERENCE_CATEGORY_ADVANCED)
|
||||||
setupLoggingSettings(getActivity(), getPreferenceScreen(), Util.getRouterContext());
|
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_ADVANCED));
|
||||||
break;
|
}
|
||||||
case "advanced":
|
|
||||||
addPreferencesFromResource(R.xml.settings_advanced);
|
@Override
|
||||||
setupAdvancedSettings(getActivity(), getPreferenceScreen());
|
public void onResume() {
|
||||||
break;
|
super.onResume();
|
||||||
|
((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.menu_settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CategoryClickListener implements Preference.OnPreferenceClickListener {
|
||||||
|
private String category;
|
||||||
|
|
||||||
|
public CategoryClickListener(String category) {
|
||||||
|
this.category = category;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceClick(Preference preference) {
|
||||||
|
if (PREFERENCE_CATEGORY_ADDRESSBOOK.equals(category)) {
|
||||||
|
Intent i = new Intent(getActivity(), AddressbookSettingsActivity.class);
|
||||||
|
startActivity(i);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Fragment fragment = getFragmentForCategory(category);
|
||||||
|
getActivity().getSupportFragmentManager().beginTransaction()
|
||||||
|
.replace(R.id.fragment, fragment)
|
||||||
|
.addToBackStack(null)
|
||||||
|
.commit();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private static Fragment getFragmentForCategory(String category) {
|
||||||
protected boolean isValidFragment(String fragmentName) {
|
switch (category) {
|
||||||
return SettingsFragment.class.getName().equals(fragmentName);
|
case PREFERENCE_CATEGORY_NETWORK:
|
||||||
|
return new NetworkPreferenceFragment();
|
||||||
|
case PREFERENCE_CATEGORY_GRAPHS:
|
||||||
|
return new GraphsPreferenceFragment();
|
||||||
|
case PREFERENCE_CATEGORY_LOGGING:
|
||||||
|
return new LoggingPreferenceFragment();
|
||||||
|
case PREFERENCE_CATEGORY_APPEARANCE:
|
||||||
|
return new AppearancePreferenceFragment();
|
||||||
|
case PREFERENCE_CATEGORY_ADVANCED:
|
||||||
|
return new AdvancedPreferenceFragment();
|
||||||
|
default:
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,4 +18,14 @@ public class AddressEntry {
|
|||||||
public Destination getDestination() {
|
public Destination getDestination() {
|
||||||
return mDest;
|
return mDest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See item 8 from Josh Bloch's "Effective Java".
|
||||||
|
*
|
||||||
|
* @return the hashcode of this AddressEntry
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 37 * mHostName.hashCode() + mDest.hashCode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,40 +1,144 @@
|
|||||||
package net.i2p.android.router.addressbook;
|
package net.i2p.android.router.addressbook;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import net.i2p.android.router.R;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
public class AddressEntryAdapter extends ArrayAdapter<AddressEntry> {
|
import net.i2p.android.router.R;
|
||||||
private final LayoutInflater mInflater;
|
import net.i2p.android.util.AlphanumericHeaderAdapter;
|
||||||
|
|
||||||
public AddressEntryAdapter(Context context) {
|
import java.util.List;
|
||||||
super(context, R.layout.listitem_text);
|
|
||||||
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setData(List<AddressEntry> addresses) {
|
public class AddressEntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements
|
||||||
clear();
|
AlphanumericHeaderAdapter.SortedAdapter {
|
||||||
if (addresses != null) {
|
private Context mCtx;
|
||||||
for (AddressEntry address : addresses) {
|
private AddressbookFragment.OnAddressSelectedListener mListener;
|
||||||
add(address);
|
private List<AddressEntry> mAddresses;
|
||||||
}
|
|
||||||
|
public static class SimpleViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
public SimpleViewHolder(View itemView) {
|
||||||
|
super(itemView);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class AddressViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
public TextView hostName;
|
||||||
|
|
||||||
|
public AddressViewHolder(View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
|
||||||
|
hostName = (TextView) itemView.findViewById(R.id.host_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AddressEntryAdapter(Context context,
|
||||||
|
AddressbookFragment.OnAddressSelectedListener listener) {
|
||||||
|
super();
|
||||||
|
mCtx = context;
|
||||||
|
mListener = listener;
|
||||||
|
setHasStableIds(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAddresses(List<AddressEntry> addresses) {
|
||||||
|
mAddresses = addresses;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AddressEntry getAddress(int position) {
|
||||||
|
if (mAddresses == null || mAddresses.isEmpty() ||
|
||||||
|
position < 0 || position >= mAddresses.size())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return mAddresses.get(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public View getView(int position, View convertView, ViewGroup parent) {
|
public String getSortString(int position) {
|
||||||
View v = mInflater.inflate(R.layout.listitem_text, parent, false);
|
AddressEntry address = getAddress(position);
|
||||||
AddressEntry address = getItem(position);
|
if (address == null)
|
||||||
|
return "";
|
||||||
|
|
||||||
TextView text = (TextView) v.findViewById(R.id.text);
|
return address.getHostName();
|
||||||
text.setText(address.getHostName());
|
}
|
||||||
|
|
||||||
return v;
|
@Override
|
||||||
|
public int getItemViewType(int position) {
|
||||||
|
if (mAddresses == null)
|
||||||
|
return R.string.router_not_running;
|
||||||
|
else if (mAddresses.isEmpty())
|
||||||
|
return R.layout.listitem_empty;
|
||||||
|
else
|
||||||
|
return R.layout.listitem_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new views (invoked by the layout manager)
|
||||||
|
@Override
|
||||||
|
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
|
int vt = viewType;
|
||||||
|
if (viewType == R.string.router_not_running)
|
||||||
|
vt = R.layout.listitem_empty;
|
||||||
|
|
||||||
|
View v = LayoutInflater.from(parent.getContext())
|
||||||
|
.inflate(vt, parent, false);
|
||||||
|
switch (viewType) {
|
||||||
|
case R.layout.listitem_address:
|
||||||
|
return new AddressViewHolder(v);
|
||||||
|
default:
|
||||||
|
return new SimpleViewHolder(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace the contents of a view (invoked by the layout manager)
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
|
||||||
|
switch (holder.getItemViewType()) {
|
||||||
|
case R.string.router_not_running:
|
||||||
|
((TextView) holder.itemView).setText(
|
||||||
|
mCtx.getString(R.string.router_not_running));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case R.layout.listitem_empty:
|
||||||
|
((TextView) holder.itemView).setText(
|
||||||
|
mCtx.getString(R.string.addressbook_is_empty));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case R.layout.listitem_address:
|
||||||
|
final AddressEntry address = getAddress(position);
|
||||||
|
AddressViewHolder avh = (AddressViewHolder) holder;
|
||||||
|
avh.hostName.setText(address.getHostName());
|
||||||
|
|
||||||
|
avh.itemView.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
mListener.onAddressSelected(address.getHostName());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the size of the dataset (invoked by the layout manager)
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
if (mAddresses == null || mAddresses.isEmpty())
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return mAddresses.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getItemId(int position) {
|
||||||
|
AddressEntry address = getAddress(position);
|
||||||
|
if (address == null)
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
return address.hashCode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,10 @@ package net.i2p.android.router.addressbook;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.v4.content.AsyncTaskLoader;
|
import android.support.v4.content.AsyncTaskLoader;
|
||||||
|
|
||||||
import net.i2p.android.router.I2PFragmentBase;
|
|
||||||
import net.i2p.android.router.util.NamingServiceUtil;
|
import net.i2p.android.router.util.NamingServiceUtil;
|
||||||
import net.i2p.android.router.util.Util;
|
import net.i2p.android.router.util.Util;
|
||||||
import net.i2p.client.naming.NamingService;
|
import net.i2p.client.naming.NamingService;
|
||||||
|
import net.i2p.client.naming.NamingServiceListener;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
import net.i2p.router.RouterContext;
|
import net.i2p.router.RouterContext;
|
||||||
|
|
||||||
@ -16,23 +16,21 @@ import java.util.Map;
|
|||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
public class AddressEntryLoader extends AsyncTaskLoader<List<AddressEntry>> {
|
public class AddressEntryLoader extends AsyncTaskLoader<List<AddressEntry>> implements
|
||||||
private I2PFragmentBase.RouterContextProvider mRContextProvider;
|
NamingServiceListener {
|
||||||
private String mBook;
|
private String mBook;
|
||||||
private String mFilter;
|
private String mFilter;
|
||||||
private List<AddressEntry> mData;
|
private List<AddressEntry> mData;
|
||||||
|
|
||||||
public AddressEntryLoader(Context context, I2PFragmentBase.RouterContextProvider rContextProvider,
|
public AddressEntryLoader(Context context, String book, String filter) {
|
||||||
String book, String filter) {
|
|
||||||
super(context);
|
super(context);
|
||||||
mRContextProvider = rContextProvider;
|
|
||||||
mBook = book;
|
mBook = book;
|
||||||
mFilter = filter;
|
mFilter = filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<AddressEntry> loadInBackground() {
|
public List<AddressEntry> loadInBackground() {
|
||||||
RouterContext routerContext = mRContextProvider.getRouterContext();
|
RouterContext routerContext = Util.getRouterContext();
|
||||||
if (routerContext == null)
|
if (routerContext == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@ -89,6 +87,13 @@ public class AddressEntryLoader extends AsyncTaskLoader<List<AddressEntry>> {
|
|||||||
deliverResult(mData);
|
deliverResult(mData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Begin monitoring the underlying data source.
|
||||||
|
RouterContext routerContext = Util.getRouterContext();
|
||||||
|
if (routerContext != null) {
|
||||||
|
NamingService ns = NamingServiceUtil.getNamingService(routerContext, mBook);
|
||||||
|
ns.registerListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
if (takeContentChanged() || mData == null) {
|
if (takeContentChanged() || mData == null) {
|
||||||
// When the observer detects a change, it should call onContentChanged()
|
// When the observer detects a change, it should call onContentChanged()
|
||||||
// on the Loader, which will cause the next call to takeContentChanged()
|
// on the Loader, which will cause the next call to takeContentChanged()
|
||||||
@ -119,6 +124,13 @@ public class AddressEntryLoader extends AsyncTaskLoader<List<AddressEntry>> {
|
|||||||
releaseResources(mData);
|
releaseResources(mData);
|
||||||
mData = null;
|
mData = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The Loader is being reset, so we should stop monitoring for changes.
|
||||||
|
RouterContext routerContext = Util.getRouterContext();
|
||||||
|
if (routerContext != null) {
|
||||||
|
NamingService ns = NamingServiceUtil.getNamingService(routerContext, mBook);
|
||||||
|
ns.unregisterListener(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -136,4 +148,26 @@ public class AddressEntryLoader extends AsyncTaskLoader<List<AddressEntry>> {
|
|||||||
// would close it in this method. All resources associated with the Loader
|
// would close it in this method. All resources associated with the Loader
|
||||||
// should be released here.
|
// should be released here.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NamingServiceListener
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configurationChanged(NamingService ns) {
|
||||||
|
onContentChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void entryAdded(NamingService ns, String hostname, Destination dest, Properties options) {
|
||||||
|
onContentChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void entryChanged(NamingService ns, String hostname, Destination dest, Properties options) {
|
||||||
|
onContentChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void entryRemoved(NamingService ns, String hostname) {
|
||||||
|
onContentChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,140 +0,0 @@
|
|||||||
package net.i2p.android.router.addressbook;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.SearchManager;
|
|
||||||
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.view.MenuItemCompat;
|
|
||||||
import android.support.v7.widget.SearchView;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.Spinner;
|
|
||||||
|
|
||||||
import net.i2p.android.router.I2PActivityBase;
|
|
||||||
import net.i2p.android.router.R;
|
|
||||||
|
|
||||||
public class AddressbookActivity extends I2PActivityBase
|
|
||||||
implements AddressbookFragment.OnAddressSelectedListener,
|
|
||||||
SearchView.OnQueryTextListener {
|
|
||||||
/**
|
|
||||||
* Whether or not the activity is in two-pane mode, i.e. running on a tablet
|
|
||||||
* device.
|
|
||||||
*/
|
|
||||||
private boolean mTwoPane;
|
|
||||||
|
|
||||||
private static final String SELECTED_PAGE = "selected_page";
|
|
||||||
private static final int PAGE_ROUTER = 0;
|
|
||||||
|
|
||||||
private Spinner mSpinner;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean canUseTwoPanes() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
mSpinner = (Spinner) findViewById(R.id.main_spinner);
|
|
||||||
mSpinner.setVisibility(View.VISIBLE);
|
|
||||||
|
|
||||||
mSpinner.setAdapter(ArrayAdapter.createFromResource(this,
|
|
||||||
R.array.addressbook_pages, android.R.layout.simple_spinner_dropdown_item));
|
|
||||||
|
|
||||||
mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
|
||||||
@Override
|
|
||||||
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
|
||||||
selectPage(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNothingSelected(AdapterView<?> adapterView) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (findViewById(R.id.detail_fragment) != null) {
|
|
||||||
// The detail container view will be present only in the
|
|
||||||
// large-screen layouts (res/values-large and
|
|
||||||
// res/values-sw600dp). If this view is present, then the
|
|
||||||
// activity should be in two-pane mode.
|
|
||||||
mTwoPane = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
|
||||||
int selected = savedInstanceState.getInt(SELECTED_PAGE);
|
|
||||||
mSpinner.setSelection(selected);
|
|
||||||
} else
|
|
||||||
selectPage(PAGE_ROUTER);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
outState.putInt(SELECTED_PAGE, mSpinner.getSelectedItemPosition());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void selectPage(int page) {
|
|
||||||
AddressbookFragment f = new AddressbookFragment();
|
|
||||||
Bundle args = new Bundle();
|
|
||||||
args.putString(AddressbookFragment.BOOK_NAME,
|
|
||||||
page == PAGE_ROUTER ?
|
|
||||||
AddressbookFragment.ROUTER_BOOK :
|
|
||||||
AddressbookFragment.PRIVATE_BOOK);
|
|
||||||
f.setArguments(args);
|
|
||||||
getSupportFragmentManager().beginTransaction()
|
|
||||||
.replace(R.id.main_fragment, f).commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
|
||||||
getMenuInflater().inflate(R.menu.activity_addressbook_actions, menu);
|
|
||||||
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
|
|
||||||
MenuItem searchItem = menu.findItem(R.id.action_search_addressbook);
|
|
||||||
SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
|
|
||||||
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
|
|
||||||
searchView.setOnQueryTextListener(this);
|
|
||||||
return super.onCreateOptionsMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddressbookFragment.OnAddressSelectedListener
|
|
||||||
|
|
||||||
public void onAddressSelected(CharSequence host) {
|
|
||||||
if (Intent.ACTION_PICK.equals(getIntent().getAction())) {
|
|
||||||
Intent result = new Intent();
|
|
||||||
result.setData(Uri.parse("http://" + host));
|
|
||||||
setResult(Activity.RESULT_OK, result);
|
|
||||||
finish();
|
|
||||||
} else {
|
|
||||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
|
||||||
i.setData(Uri.parse("http://" + host));
|
|
||||||
startActivity(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SearchView.OnQueryTextListener
|
|
||||||
|
|
||||||
public boolean onQueryTextChange(String newText) {
|
|
||||||
filterAddresses(newText);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean onQueryTextSubmit(String query) {
|
|
||||||
filterAddresses(query);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void filterAddresses(String query) {
|
|
||||||
Fragment f = getSupportFragmentManager().findFragmentById(R.id.main_fragment);
|
|
||||||
if (f instanceof AddressbookFragment) {
|
|
||||||
AddressbookFragment af = (AddressbookFragment) f;
|
|
||||||
af.filterAddresses(query);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +1,14 @@
|
|||||||
package net.i2p.android.router.addressbook;
|
package net.i2p.android.router.addressbook;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.app.DialogFragment;
|
import android.support.v4.app.DialogFragment;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
|
||||||
import net.i2p.android.wizard.model.AbstractWizardModel;
|
import net.i2p.android.wizard.model.AbstractWizardModel;
|
||||||
import net.i2p.android.wizard.ui.AbstractWizardActivity;
|
import net.i2p.android.wizard.ui.AbstractWizardActivity;
|
||||||
|
|
||||||
@ -31,7 +32,7 @@ public class AddressbookAddWizardActivity extends AbstractWizardActivity {
|
|||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
Intent result = new Intent();
|
Intent result = new Intent();
|
||||||
setResult(Activity.RESULT_OK, result);
|
setResult(Activity.RESULT_OK, result);
|
||||||
result.putExtra(AddressbookFragment.ADD_WIZARD_DATA, mWizardModel.save());
|
result.putExtra(AddressbookContainer.ADD_WIZARD_DATA, mWizardModel.save());
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,250 @@
|
|||||||
|
package net.i2p.android.router.addressbook;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.SearchManager;
|
||||||
|
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.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import com.viewpagerindicator.TitlePageIndicator;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the container is in two-pane mode, i.e. running on a tablet
|
||||||
|
* device.
|
||||||
|
*/
|
||||||
|
private boolean mTwoPane;
|
||||||
|
|
||||||
|
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;
|
||||||
|
private static final int FRAGMENT_ID_PRIVATE = 1;
|
||||||
|
AddressbookFragment mRouterFrag;
|
||||||
|
AddressbookFragment mPrivateFrag;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
View v = inflater.inflate(R.layout.container_addressbook, container, false);
|
||||||
|
|
||||||
|
if (v.findViewById(R.id.right_fragment) != null) {
|
||||||
|
// The detail container view will be present only in the
|
||||||
|
// large-screen layouts (res/values-large and
|
||||||
|
// res/values-sw600dp). If this view is present, then the
|
||||||
|
// activity should be in two-pane mode.
|
||||||
|
mTwoPane = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
mRouterFrag = (AddressbookFragment) getChildFragmentManager().getFragment(
|
||||||
|
savedInstanceState, FRAGMENT_ROUTER);
|
||||||
|
mPrivateFrag = (AddressbookFragment) getChildFragmentManager().getFragment(
|
||||||
|
savedInstanceState, FRAGMENT_PRIVATE);
|
||||||
|
} else if (mTwoPane) {
|
||||||
|
// TODO if these were instantiated in the background, wouldn't savedInstanceState != null?
|
||||||
|
mRouterFrag = (AddressbookFragment) getChildFragmentManager().findFragmentById(R.id.left_fragment);
|
||||||
|
mPrivateFrag = (AddressbookFragment) getChildFragmentManager().findFragmentById(R.id.right_fragment);
|
||||||
|
|
||||||
|
// Set up the two pages
|
||||||
|
FragmentTransaction ft = getChildFragmentManager().beginTransaction();
|
||||||
|
if (mRouterFrag == null) {
|
||||||
|
mRouterFrag = AddressbookFragment.newInstance(AddressbookFragment.ROUTER_BOOK);
|
||||||
|
ft.add(R.id.left_fragment, mRouterFrag);
|
||||||
|
}
|
||||||
|
if (mPrivateFrag == null) {
|
||||||
|
mPrivateFrag = AddressbookFragment.newInstance(AddressbookFragment.PRIVATE_BOOK);
|
||||||
|
ft.add(R.id.right_fragment, mPrivateFrag);
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AddressbookPagerAdapter extends FragmentPagerAdapter {
|
||||||
|
private static final int NUM_ITEMS = 2;
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
|
||||||
|
public AddressbookPagerAdapter(Context context, FragmentManager fm) {
|
||||||
|
super(fm);
|
||||||
|
mContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return NUM_ITEMS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Fragment getItem(int position) {
|
||||||
|
switch (position) {
|
||||||
|
case FRAGMENT_ID_ROUTER:
|
||||||
|
return (mRouterFrag = AddressbookFragment.newInstance(AddressbookFragment.ROUTER_BOOK));
|
||||||
|
case FRAGMENT_ID_PRIVATE:
|
||||||
|
return (mPrivateFrag = AddressbookFragment.newInstance(AddressbookFragment.PRIVATE_BOOK));
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence getPageTitle(int position) {
|
||||||
|
switch (position) {
|
||||||
|
case FRAGMENT_ID_ROUTER:
|
||||||
|
return mContext.getString(R.string.label_router);
|
||||||
|
case FRAGMENT_ID_PRIVATE:
|
||||||
|
return mContext.getString(R.string.label_private);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setMenuVisibility(boolean menuVisible) {
|
||||||
|
super.setMenuVisibility(menuVisible);
|
||||||
|
|
||||||
|
setChildMenuVisibility(mRouterFrag, FRAGMENT_ID_ROUTER, menuVisible);
|
||||||
|
setChildMenuVisibility(mPrivateFrag, FRAGMENT_ID_PRIVATE, menuVisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setChildMenuVisibility(Fragment fragment, int itemNumber, boolean menuVisible) {
|
||||||
|
if (fragment != null) {
|
||||||
|
if (mViewPager != null)
|
||||||
|
menuVisible = menuVisible && mViewPager.getCurrentItem() == itemNumber;
|
||||||
|
fragment.setMenuVisibility(menuVisible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUserVisibleHint(boolean isVisibleToUser) {
|
||||||
|
super.setUserVisibleHint(isVisibleToUser);
|
||||||
|
|
||||||
|
setChildUserVisibleHint(mRouterFrag, FRAGMENT_ID_ROUTER, isVisibleToUser);
|
||||||
|
setChildUserVisibleHint(mPrivateFrag, FRAGMENT_ID_PRIVATE, isVisibleToUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setChildUserVisibleHint(Fragment fragment, int itemNumber, boolean isVisibleToUser) {
|
||||||
|
if (fragment != null) {
|
||||||
|
if (mViewPager != null)
|
||||||
|
isVisibleToUser = isVisibleToUser && mViewPager.getCurrentItem() == itemNumber;
|
||||||
|
fragment.setUserVisibleHint(isVisibleToUser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
|
||||||
|
// Since the pager fragments don't have known tags or IDs, the only way to persist the
|
||||||
|
// reference is to use putFragment/getFragment. Remember, we're not persisting the exact
|
||||||
|
// Fragment instance. This mechanism simply gives us a way to persist access to the
|
||||||
|
// 'current' fragment instance for the given fragment (which changes across orientation
|
||||||
|
// changes).
|
||||||
|
//
|
||||||
|
// The outcome of all this is that the "Refresh" menu button refreshes the stream across
|
||||||
|
// orientation changes.
|
||||||
|
if (mRouterFrag != null)
|
||||||
|
getChildFragmentManager().putFragment(outState, FRAGMENT_ROUTER, mRouterFrag);
|
||||||
|
if (mPrivateFrag != null)
|
||||||
|
getChildFragmentManager().putFragment(outState, FRAGMENT_PRIVATE, mPrivateFrag);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
if (requestCode == ADD_WIZARD_REQUEST &&
|
||||||
|
resultCode == Activity.RESULT_OK) {
|
||||||
|
// Save the new entry
|
||||||
|
Bundle entryData = data.getExtras().getBundle(ADD_WIZARD_DATA);
|
||||||
|
NamingService ns = NamingServiceUtil.getNamingService(Util.getRouterContext(),
|
||||||
|
AddressbookFragment.PRIVATE_BOOK);
|
||||||
|
NamingServiceUtil.addFromWizard(getActivity(), ns, entryData, false);
|
||||||
|
// The loader will be notified by the NamingService
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddressbookFragment.OnAddressSelectedListener
|
||||||
|
|
||||||
|
public void onAddressSelected(CharSequence host) {
|
||||||
|
if (Intent.ACTION_PICK.equals(getActivity().getIntent().getAction())) {
|
||||||
|
Intent result = new Intent();
|
||||||
|
result.setData(Uri.parse("http://" + host));
|
||||||
|
getActivity().setResult(Activity.RESULT_OK, result);
|
||||||
|
getActivity().finish();
|
||||||
|
} else {
|
||||||
|
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||||
|
i.setData(Uri.parse("http://" + host));
|
||||||
|
startActivity(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchView.OnQueryTextListener
|
||||||
|
|
||||||
|
public boolean onQueryTextChange(String newText) {
|
||||||
|
filterAddresses(newText);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onQueryTextSubmit(String query) {
|
||||||
|
filterAddresses(query);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void filterAddresses(String query) {
|
||||||
|
if (mRouterFrag != null)
|
||||||
|
mRouterFrag.filterAddresses(query);
|
||||||
|
if (mPrivateFrag != null)
|
||||||
|
mPrivateFrag.filterAddresses(query);
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,17 @@
|
|||||||
package net.i2p.android.router.addressbook;
|
package net.i2p.android.router.addressbook;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.ListFragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.app.LoaderManager;
|
import android.support.v4.app.LoaderManager;
|
||||||
import android.support.v4.content.Loader;
|
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.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
@ -13,51 +19,56 @@ import android.view.MenuInflater;
|
|||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.FrameLayout;
|
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
import android.widget.ListView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
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.addressbook.Daemon;
|
||||||
import net.i2p.android.help.HelpActivity;
|
|
||||||
import net.i2p.android.router.I2PFragmentBase;
|
|
||||||
import net.i2p.android.router.I2PFragmentBase.RouterContextProvider;
|
|
||||||
import net.i2p.android.router.R;
|
import net.i2p.android.router.R;
|
||||||
import net.i2p.android.router.util.NamingServiceUtil;
|
import net.i2p.android.router.service.RouterService;
|
||||||
import net.i2p.client.naming.NamingService;
|
import net.i2p.android.router.service.State;
|
||||||
|
import net.i2p.android.router.util.Util;
|
||||||
|
import net.i2p.android.util.AlphanumericHeaderAdapter;
|
||||||
|
import net.i2p.android.util.FragmentUtils;
|
||||||
|
import net.i2p.android.widget.DividerItemDecoration;
|
||||||
|
import net.i2p.android.widget.LoadingRecyclerView;
|
||||||
import net.i2p.router.RouterContext;
|
import net.i2p.router.RouterContext;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class AddressbookFragment extends ListFragment implements
|
public class AddressbookFragment extends Fragment implements
|
||||||
I2PFragmentBase.RouterContextUser,
|
|
||||||
LoaderManager.LoaderCallbacks<List<AddressEntry>> {
|
LoaderManager.LoaderCallbacks<List<AddressEntry>> {
|
||||||
public static final String BOOK_NAME = "book_name";
|
public static final String BOOK_NAME = "book_name";
|
||||||
public static final String ROUTER_BOOK = "hosts.txt";
|
public static final String ROUTER_BOOK = "hosts.txt";
|
||||||
public static final String PRIVATE_BOOK = "privatehosts.txt";
|
public static final String PRIVATE_BOOK = "privatehosts.txt";
|
||||||
public static final String ADD_WIZARD_DATA = "add_wizard_data";
|
|
||||||
|
|
||||||
private static final int ADD_WIZARD_REQUEST = 1;
|
|
||||||
|
|
||||||
private static final int ROUTER_LOADER_ID = 1;
|
private static final int ROUTER_LOADER_ID = 1;
|
||||||
private static final int PRIVATE_LOADER_ID = 2;
|
private static final int PRIVATE_LOADER_ID = 2;
|
||||||
|
|
||||||
private boolean mOnActivityCreated;
|
|
||||||
private RouterContextProvider mRouterContextProvider;
|
|
||||||
private OnAddressSelectedListener mCallback;
|
private OnAddressSelectedListener mCallback;
|
||||||
|
|
||||||
|
private LoadingRecyclerView mRecyclerView;
|
||||||
private AddressEntryAdapter mAdapter;
|
private AddressEntryAdapter mAdapter;
|
||||||
private String mBook;
|
private String mBook;
|
||||||
private String mCurFilter;
|
private String mCurFilter;
|
||||||
|
|
||||||
private ImageButton mAddToAddressbook;
|
private ImageButton mAddToAddressbook;
|
||||||
|
|
||||||
// Set in onActivityResult()
|
|
||||||
private Intent mAddWizardData;
|
|
||||||
|
|
||||||
// Container Activity must implement this interface
|
// Container Activity must implement this interface
|
||||||
public interface OnAddressSelectedListener {
|
public interface OnAddressSelectedListener {
|
||||||
public void onAddressSelected(CharSequence host);
|
void onAddressSelected(CharSequence host);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AddressbookFragment newInstance(String book) {
|
||||||
|
AddressbookFragment f = new AddressbookFragment();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putString(AddressbookFragment.BOOK_NAME, book);
|
||||||
|
f.setArguments(args);
|
||||||
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -66,21 +77,9 @@ public class AddressbookFragment extends ListFragment implements
|
|||||||
|
|
||||||
// This makes sure that the container activity has implemented
|
// This makes sure that the container activity has implemented
|
||||||
// the callback interface. If not, it throws an exception
|
// the callback interface. If not, it throws an exception
|
||||||
try {
|
mCallback = FragmentUtils.getParent(this, OnAddressSelectedListener.class);
|
||||||
mRouterContextProvider = (RouterContextProvider) activity;
|
if (mCallback == null)
|
||||||
} catch (ClassCastException e) {
|
throw new ClassCastException("Parent must implement OnAddressSelectedListener");
|
||||||
throw new ClassCastException(activity.toString()
|
|
||||||
+ " must implement RouterContextProvider");
|
|
||||||
}
|
|
||||||
|
|
||||||
// This makes sure that the container activity has implemented
|
|
||||||
// the callback interface. If not, it throws an exception
|
|
||||||
try {
|
|
||||||
mCallback = (OnAddressSelectedListener) activity;
|
|
||||||
} catch (ClassCastException e) {
|
|
||||||
throw new ClassCastException(activity.toString()
|
|
||||||
+ " must implement OnAddressSelectedListener");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,19 +91,19 @@ public class AddressbookFragment extends ListFragment implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
// Create the list fragment's content view by calling the super method
|
|
||||||
final View listFragmentView = super.onCreateView(inflater, container, savedInstanceState);
|
|
||||||
|
|
||||||
View v = inflater.inflate(R.layout.fragment_list_with_add, container, false);
|
View v = inflater.inflate(R.layout.fragment_list_with_add, container, false);
|
||||||
FrameLayout listContainer = (FrameLayout) v.findViewById(R.id.list_container);
|
|
||||||
listContainer.addView(listFragmentView);
|
mRecyclerView = (LoadingRecyclerView) v.findViewById(R.id.list);
|
||||||
|
View empty = v.findViewById(R.id.empty);
|
||||||
|
ProgressWheel loading = (ProgressWheel) v.findViewById(R.id.loading);
|
||||||
|
mRecyclerView.setLoadingView(empty, loading);
|
||||||
|
|
||||||
mAddToAddressbook = (ImageButton) v.findViewById(R.id.promoted_action);
|
mAddToAddressbook = (ImageButton) v.findViewById(R.id.promoted_action);
|
||||||
mAddToAddressbook.setOnClickListener(new View.OnClickListener() {
|
mAddToAddressbook.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
Intent wi = new Intent(getActivity(), AddressbookAddWizardActivity.class);
|
Intent wi = new Intent(getActivity(), AddressbookAddWizardActivity.class);
|
||||||
startActivityForResult(wi, ADD_WIZARD_REQUEST);
|
getParentFragment().startActivityForResult(wi, AddressbookContainer.ADD_WIZARD_REQUEST);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -114,75 +113,102 @@ public class AddressbookFragment extends ListFragment implements
|
|||||||
@Override
|
@Override
|
||||||
public void onActivityCreated(Bundle savedInstanceState) {
|
public void onActivityCreated(Bundle savedInstanceState) {
|
||||||
super.onActivityCreated(savedInstanceState);
|
super.onActivityCreated(savedInstanceState);
|
||||||
mAdapter = new AddressEntryAdapter(getActivity());
|
|
||||||
mBook = getArguments().getString(BOOK_NAME);
|
mBook = getArguments().getString(BOOK_NAME);
|
||||||
|
|
||||||
// Set adapter to null before setting the header
|
mRecyclerView.setHasFixedSize(true);
|
||||||
setListAdapter(null);
|
mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST));
|
||||||
|
|
||||||
TextView v = new TextView(getActivity());
|
// use a linear layout manager
|
||||||
v.setTag("addressbook_header");
|
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
|
||||||
getListView().addHeaderView(v);
|
mRecyclerView.setLayoutManager(mLayoutManager);
|
||||||
|
|
||||||
setListAdapter(mAdapter);
|
// Set the adapter for the list view
|
||||||
|
mAdapter = new AddressEntryAdapter(getActivity(), mCallback);
|
||||||
|
mRecyclerView.setAdapter(mAdapter);
|
||||||
|
|
||||||
mOnActivityCreated = true;
|
// Build item decoration and add it to the RecyclerView
|
||||||
if (getRouterContext() != null)
|
StickyHeadersItemDecoration decoration = new StickyHeadersBuilder()
|
||||||
onRouterConnectionReady();
|
.setAdapter(mAdapter)
|
||||||
else
|
.setRecyclerView(mRecyclerView)
|
||||||
setEmptyText(getResources().getString(
|
.setStickyHeadersAdapter(new AlphanumericHeaderAdapter(mAdapter))
|
||||||
R.string.router_not_running));
|
.build();
|
||||||
|
mRecyclerView.addItemDecoration(decoration);
|
||||||
|
|
||||||
|
// Initialize the adapter in case the RouterService has not been created
|
||||||
|
if (Util.getRouterContext() == null)
|
||||||
|
mAdapter.setAddresses(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onRouterConnectionReady() {
|
@Override
|
||||||
// Show actions
|
public void onStart() {
|
||||||
if (mSearchAddressbook != null)
|
super.onStart();
|
||||||
mSearchAddressbook.setVisible(true);
|
|
||||||
if (mAddToAddressbook != null && mAddToAddressbook.getVisibility() != View.VISIBLE)
|
|
||||||
mAddToAddressbook.setVisibility(View.VISIBLE);
|
|
||||||
|
|
||||||
if (mAddWizardData != null) {
|
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getActivity());
|
||||||
// Save the new entry
|
|
||||||
Bundle entryData = mAddWizardData.getExtras().getBundle(ADD_WIZARD_DATA);
|
IntentFilter filter = new IntentFilter();
|
||||||
NamingService ns = NamingServiceUtil.getNamingService(getRouterContext(), mBook);
|
filter.addAction(RouterService.LOCAL_BROADCAST_STATE_NOTIFICATION);
|
||||||
boolean success = NamingServiceUtil.addFromWizard(getActivity(), ns, entryData, false);
|
filter.addAction(RouterService.LOCAL_BROADCAST_STATE_CHANGED);
|
||||||
if (success) {
|
lbm.registerReceiver(onStateChange, filter);
|
||||||
// Reload the list
|
}
|
||||||
setListShown(false);
|
|
||||||
getLoaderManager().restartLoader(PRIVATE_LOADER_ID, null, this);
|
private State lastRouterState = null;
|
||||||
|
private BroadcastReceiver onStateChange = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
State state = intent.getParcelableExtra(RouterService.LOCAL_BROADCAST_EXTRA_STATE);
|
||||||
|
if (lastRouterState == null || lastRouterState != state) {
|
||||||
|
updateState(state);
|
||||||
|
lastRouterState = state;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
setEmptyText("No hosts in address book " + mBook);
|
};
|
||||||
|
|
||||||
setListShown(false);
|
public void updateState(State state) {
|
||||||
getLoaderManager().initLoader(PRIVATE_BOOK.equals(mBook) ?
|
int loaderId = PRIVATE_BOOK.equals(mBook) ?
|
||||||
PRIVATE_LOADER_ID : ROUTER_LOADER_ID, null, this);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onListItemClick(ListView parent, View view, int pos, long id) {
|
public void onResume() {
|
||||||
CharSequence host = ((TextView) view).getText();
|
super.onResume();
|
||||||
mCallback.onAddressSelected(host);
|
|
||||||
|
// Triggers loader init via updateState() if the router is running
|
||||||
|
LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(new Intent(RouterService.LOCAL_BROADCAST_REQUEST_STATE));
|
||||||
}
|
}
|
||||||
|
|
||||||
private MenuItem mSearchAddressbook;
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
|
||||||
|
LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(onStateChange);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
inflater.inflate(R.menu.fragment_addressbook_actions, menu);
|
inflater.inflate(R.menu.fragment_addressbook_actions, menu);
|
||||||
|
|
||||||
mSearchAddressbook = menu.findItem(R.id.action_search_addressbook);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPrepareOptionsMenu(Menu menu) {
|
public void onPrepareOptionsMenu(Menu menu) {
|
||||||
// Hide until needed
|
RouterContext rCtx = Util.getRouterContext();
|
||||||
if (getRouterContext() == null) {
|
|
||||||
mSearchAddressbook.setVisible(false);
|
if (mAddToAddressbook != null)
|
||||||
if (mAddToAddressbook != null)
|
mAddToAddressbook.setVisibility(rCtx == null ? View.GONE : View.VISIBLE);
|
||||||
mAddToAddressbook.setVisibility(View.GONE);
|
|
||||||
}
|
// Only show "Reload subscriptions" for router addressbook
|
||||||
|
menu.findItem(R.id.action_reload_subscriptions).setVisible(
|
||||||
|
rCtx != null && !PRIVATE_BOOK.equals(mBook));
|
||||||
|
|
||||||
// Only allow adding to private book
|
// Only allow adding to private book
|
||||||
if (!PRIVATE_BOOK.equals(mBook) && mAddToAddressbook != null) {
|
if (!PRIVATE_BOOK.equals(mBook) && mAddToAddressbook != null) {
|
||||||
@ -201,88 +227,41 @@ public class AddressbookFragment extends ListFragment implements
|
|||||||
Toast.makeText(getActivity(), "Reloading subscriptions...",
|
Toast.makeText(getActivity(), "Reloading subscriptions...",
|
||||||
Toast.LENGTH_SHORT).show();
|
Toast.LENGTH_SHORT).show();
|
||||||
return true;
|
return true;
|
||||||
case R.id.action_addressbook_settings:
|
|
||||||
Intent si = new Intent(getActivity(), AddressbookSettingsActivity.class);
|
|
||||||
startActivity(si);
|
|
||||||
return true;
|
|
||||||
case R.id.action_addressbook_help:
|
|
||||||
Intent hi = new Intent(getActivity(), HelpActivity.class);
|
|
||||||
hi.putExtra(HelpActivity.CATEGORY, HelpActivity.CAT_ADDRESSBOOK);
|
|
||||||
startActivity(hi);
|
|
||||||
return true;
|
|
||||||
default:
|
default:
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
||||||
if (requestCode == ADD_WIZARD_REQUEST &&
|
|
||||||
resultCode == Activity.RESULT_OK &&
|
|
||||||
PRIVATE_BOOK.equals(mBook)) {
|
|
||||||
mAddWizardData = data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void filterAddresses(String query) {
|
public void filterAddresses(String query) {
|
||||||
mCurFilter = !TextUtils.isEmpty(query) ? query : null;
|
mCurFilter = !TextUtils.isEmpty(query) ? query : null;
|
||||||
if (getRouterContext() != null && mAdapter != null) {
|
if (Util.getRouterContext() != null && mAdapter != null) {
|
||||||
setListShown(false);
|
mRecyclerView.setLoading(true);
|
||||||
getLoaderManager().restartLoader(PRIVATE_BOOK.equals(mBook) ?
|
getLoaderManager().restartLoader(PRIVATE_BOOK.equals(mBook) ?
|
||||||
PRIVATE_LOADER_ID : ROUTER_LOADER_ID, null, this);
|
PRIVATE_LOADER_ID : ROUTER_LOADER_ID, null, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Duplicated from I2PFragmentBase because this extends ListFragment
|
|
||||||
private RouterContext getRouterContext() {
|
|
||||||
return mRouterContextProvider.getRouterContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
// I2PFragmentBase.RouterContextUser
|
|
||||||
|
|
||||||
public void onRouterBind() {
|
|
||||||
if (mOnActivityCreated)
|
|
||||||
onRouterConnectionReady();
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoaderManager.LoaderCallbacks<List<AddressEntry>>
|
// LoaderManager.LoaderCallbacks<List<AddressEntry>>
|
||||||
|
|
||||||
public Loader<List<AddressEntry>> onCreateLoader(int id, Bundle args) {
|
public Loader<List<AddressEntry>> onCreateLoader(int id, Bundle args) {
|
||||||
return new AddressEntryLoader(getActivity(),
|
return new AddressEntryLoader(getActivity(), mBook, mCurFilter);
|
||||||
mRouterContextProvider, mBook, mCurFilter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onLoadFinished(Loader<List<AddressEntry>> loader,
|
public void onLoadFinished(Loader<List<AddressEntry>> loader,
|
||||||
List<AddressEntry> data) {
|
List<AddressEntry> data) {
|
||||||
if (loader.getId() == (PRIVATE_BOOK.equals(mBook) ?
|
if (loader.getId() == (PRIVATE_BOOK.equals(mBook) ?
|
||||||
PRIVATE_LOADER_ID : ROUTER_LOADER_ID)) {
|
PRIVATE_LOADER_ID : ROUTER_LOADER_ID)) {
|
||||||
if (data == null)
|
mAdapter.setAddresses(data);
|
||||||
setEmptyText(getResources().getString(
|
|
||||||
R.string.router_not_running));
|
|
||||||
else {
|
|
||||||
mAdapter.setData(data);
|
|
||||||
|
|
||||||
TextView v = (TextView) getListView().findViewWithTag("addressbook_header");
|
|
||||||
if (mCurFilter != null)
|
|
||||||
v.setText(getActivity().getResources().getString(
|
|
||||||
R.string.addressbook_search_header,
|
|
||||||
data.size()));
|
|
||||||
else
|
|
||||||
v.setText("");
|
|
||||||
|
|
||||||
if (isResumed()) {
|
|
||||||
setListShown(true);
|
|
||||||
} else {
|
|
||||||
setListShownNoAnimation(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onLoaderReset(Loader<List<AddressEntry>> loader) {
|
public void onLoaderReset(Loader<List<AddressEntry>> loader) {
|
||||||
if (loader.getId() == (PRIVATE_BOOK.equals(mBook) ?
|
if (loader.getId() == (PRIVATE_BOOK.equals(mBook) ?
|
||||||
PRIVATE_LOADER_ID : ROUTER_LOADER_ID)) {
|
PRIVATE_LOADER_ID : ROUTER_LOADER_ID)) {
|
||||||
mAdapter.setData(null);
|
if (Util.getRouterContext() == null)
|
||||||
|
mAdapter.setAddresses(null);
|
||||||
|
else
|
||||||
|
mAdapter.setAddresses(new ArrayList<AddressEntry>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ package net.i2p.android.router.addressbook;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v7.app.ActionBarActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
@ -10,21 +10,25 @@ import android.widget.EditText;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import net.i2p.android.router.R;
|
import net.i2p.android.router.R;
|
||||||
|
import net.i2p.android.util.LocaleManager;
|
||||||
import net.i2p.util.FileUtil;
|
import net.i2p.util.FileUtil;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class AddressbookSettingsActivity extends ActionBarActivity {
|
public class AddressbookSettingsActivity extends AppCompatActivity {
|
||||||
|
|
||||||
private EditText text_content_subscriptions;
|
private EditText text_content_subscriptions;
|
||||||
private Button btn_save_subscriptions;
|
private Button btn_save_subscriptions;
|
||||||
private String filename = "/addressbook/subscriptions.txt";
|
private String filename = "/addressbook/subscriptions.txt";
|
||||||
private File i2pDir;
|
private File i2pDir;
|
||||||
|
|
||||||
|
private final LocaleManager localeManager = new LocaleManager();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
localeManager.onCreate(this);
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_addressbook_settings);
|
setContentView(R.layout.activity_addressbook_settings);
|
||||||
|
|
||||||
@ -84,4 +88,10 @@ public class AddressbookSettingsActivity extends ActionBarActivity {
|
|||||||
if (out != null) try {out.close(); } catch (IOException ioe) {}
|
if (out != null) try {out.close(); } catch (IOException ioe) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
localeManager.onResume(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
package net.i2p.android.router.dialog;
|
package net.i2p.android.router.dialog;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.app.DialogFragment;
|
import android.support.v4.app.DialogFragment;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.text.util.Linkify;
|
import android.text.util.Linkify;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@ -40,6 +40,12 @@ public class AboutDialog extends DialogFragment {
|
|||||||
AlertDialog.Builder b = new AlertDialog.Builder(getActivity());
|
AlertDialog.Builder b = new AlertDialog.Builder(getActivity());
|
||||||
b.setTitle(R.string.menu_about)
|
b.setTitle(R.string.menu_about)
|
||||||
.setView(view)
|
.setView(view)
|
||||||
|
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int i) {
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
})
|
||||||
.setNeutralButton(R.string.label_licenses, new DialogInterface.OnClickListener() {
|
.setNeutralButton(R.string.label_licenses, new DialogInterface.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialogInterface, int i) {
|
public void onClick(DialogInterface dialogInterface, int i) {
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
package net.i2p.android.router.dialog;
|
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
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 net.i2p.android.help.BrowserConfigActivity;
|
|
||||||
import net.i2p.android.router.R;
|
|
||||||
|
|
||||||
public class ConfigureBrowserDialog extends DialogFragment {
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
|
||||||
AlertDialog.Builder b = new AlertDialog.Builder(getActivity());
|
|
||||||
b.setTitle(R.string.configure_browser_title)
|
|
||||||
.setMessage(R.string.configure_browser_for_i2p)
|
|
||||||
.setCancelable(false)
|
|
||||||
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialogInterface, int i) {
|
|
||||||
dialogInterface.dismiss();
|
|
||||||
Intent hi = new Intent(getActivity(), BrowserConfigActivity.class);
|
|
||||||
startActivity(hi);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialogInterface, int i) {
|
|
||||||
dialogInterface.cancel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return b.create();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +1,11 @@
|
|||||||
package net.i2p.android.router.dialog;
|
package net.i2p.android.router.dialog;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.app.DialogFragment;
|
import android.support.v4.app.DialogFragment;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.text.util.Linkify;
|
import android.text.util.Linkify;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@ -27,7 +28,13 @@ public class FirstStartDialog extends DialogFragment {
|
|||||||
|
|
||||||
AlertDialog.Builder b = new AlertDialog.Builder(getActivity());
|
AlertDialog.Builder b = new AlertDialog.Builder(getActivity());
|
||||||
b.setTitle(R.string.first_start_title)
|
b.setTitle(R.string.first_start_title)
|
||||||
.setView(view);
|
.setView(view)
|
||||||
|
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int i) {
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
return b.create();
|
return b.create();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
package net.i2p.android.router.dialog;
|
package net.i2p.android.router.dialog;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.app.DialogFragment;
|
import android.support.v4.app.DialogFragment;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import net.i2p.android.router.R;
|
import net.i2p.android.router.R;
|
||||||
@ -17,30 +20,38 @@ import java.io.InputStream;
|
|||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display a raw text resource.
|
* Display a raw text resource.
|
||||||
* The resource ID must be passed as an extra in the intent.
|
* The resource ID must be passed as an extra in the intent.
|
||||||
*/
|
*/
|
||||||
public class TextResourceDialog extends DialogFragment {
|
public class TextResourceDialog extends DialogFragment {
|
||||||
|
|
||||||
public static final String TEXT_DIALOG_TITLE = "text_title";
|
public static final String TEXT_DIALOG_TITLE = "text_title";
|
||||||
public final static String TEXT_RESOURCE_ID = "text_resource_id";
|
public final static String TEXT_RESOURCE_ID = "text_resource_id";
|
||||||
|
|
||||||
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
Bundle savedInstanceState)
|
AlertDialog.Builder b = new AlertDialog.Builder(getActivity());
|
||||||
{
|
LayoutInflater inflater = LayoutInflater.from(getActivity());
|
||||||
View v = inflater.inflate(R.layout.fragment_dialog_text_resource, container, false);
|
View v = inflater.inflate(R.layout.fragment_dialog_text_resource, null, false);
|
||||||
TextView tv = (TextView) v.findViewById(R.id.text_resource_text);
|
TextView tv = (TextView) v.findViewById(R.id.text_resource_text);
|
||||||
String title = getArguments().getString(TEXT_DIALOG_TITLE);
|
String title = getArguments().getString(TEXT_DIALOG_TITLE);
|
||||||
if (title != null)
|
if (title != null)
|
||||||
getDialog().setTitle(title);
|
b.setTitle(title);
|
||||||
int id = getArguments().getInt(TEXT_RESOURCE_ID, R.raw.releasenotes_txt);
|
int id = getArguments().getInt(TEXT_RESOURCE_ID, R.raw.releasenotes_txt);
|
||||||
if (id == R.raw.releasenotes_txt)
|
if (id == R.raw.releasenotes_txt)
|
||||||
tv.setText("Release Notes for Release " + Util.getOurVersion(getActivity()) + "\n\n" +
|
tv.setText("Release Notes for Release " + Util.getOurVersion(getActivity()) + "\n\n" +
|
||||||
getResourceAsString(id));
|
getResourceAsString(id));
|
||||||
else
|
else
|
||||||
tv.setText(getResourceAsString(id));
|
tv.setText(getResourceAsString(id));
|
||||||
return v;
|
b.setView(v)
|
||||||
|
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialogInterface, int i) {
|
||||||
|
dialogInterface.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return b.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getResourceAsString(int id) {
|
private String getResourceAsString(int id) {
|
||||||
@ -49,15 +60,18 @@ public class TextResourceDialog extends DialogFragment {
|
|||||||
byte buf[] = new byte[1024];
|
byte buf[] = new byte[1024];
|
||||||
try {
|
try {
|
||||||
in = getResources().openRawResource(id);
|
in = getResources().openRawResource(id);
|
||||||
|
|
||||||
int read;
|
int read;
|
||||||
while ( (read = in.read(buf)) != -1)
|
while ((read = in.read(buf)) != -1)
|
||||||
out.write(buf, 0, read);
|
out.write(buf, 0, read);
|
||||||
|
|
||||||
} catch (IOException | Resources.NotFoundException re) {
|
} catch (IOException | Resources.NotFoundException re) {
|
||||||
System.err.println("resource error " + re);
|
System.err.println("resource error " + re);
|
||||||
} finally {
|
} finally {
|
||||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
if (in != null) try {
|
||||||
|
in.close();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return out.toString("UTF-8");
|
return out.toString("UTF-8");
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package net.i2p.android.router.dialog;
|
package net.i2p.android.router.dialog;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.app.DialogFragment;
|
import android.support.v4.app.DialogFragment;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
|
||||||
import net.i2p.android.router.I2PActivityBase;
|
import net.i2p.android.I2PActivityBase;
|
||||||
import net.i2p.android.router.MainFragment;
|
import net.i2p.android.router.MainFragment;
|
||||||
import net.i2p.android.router.R;
|
import net.i2p.android.router.R;
|
||||||
import net.i2p.android.router.util.Util;
|
import net.i2p.android.router.util.Util;
|
||||||
|
@ -3,6 +3,7 @@ package net.i2p.android.router.log;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
@ -11,7 +12,7 @@ import android.widget.AdapterView;
|
|||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
import android.widget.Spinner;
|
import android.widget.Spinner;
|
||||||
|
|
||||||
import net.i2p.android.router.I2PActivityBase;
|
import net.i2p.android.I2PActivityBase;
|
||||||
import net.i2p.android.router.R;
|
import net.i2p.android.router.R;
|
||||||
import net.i2p.android.router.SettingsActivity;
|
import net.i2p.android.router.SettingsActivity;
|
||||||
|
|
||||||
@ -28,21 +29,20 @@ public class LogActivity extends I2PActivityBase implements
|
|||||||
private String[] mLevels;
|
private String[] mLevels;
|
||||||
private Spinner mSpinner;
|
private Spinner mSpinner;
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean canUseTwoPanes() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_multipane);
|
||||||
|
|
||||||
|
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
|
||||||
mLevels = getResources().getStringArray(R.array.log_level_list);
|
mLevels = getResources().getStringArray(R.array.log_level_list);
|
||||||
|
|
||||||
mSpinner = (Spinner) findViewById(R.id.main_spinner);
|
mSpinner = (Spinner) findViewById(R.id.main_spinner);
|
||||||
mSpinner.setVisibility(View.VISIBLE);
|
mSpinner.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
mDrawerToggle.setDrawerIndicatorEnabled(false);
|
|
||||||
|
|
||||||
if (findViewById(R.id.detail_fragment) != null) {
|
if (findViewById(R.id.detail_fragment) != null) {
|
||||||
// The detail container view will be present only in the
|
// The detail container view will be present only in the
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package net.i2p.android.router.log;
|
package net.i2p.android.router.log;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import net.i2p.android.router.I2PActivityBase;
|
import android.support.v7.widget.Toolbar;
|
||||||
|
|
||||||
|
import net.i2p.android.I2PActivityBase;
|
||||||
import net.i2p.android.router.R;
|
import net.i2p.android.router.R;
|
||||||
|
|
||||||
public class LogDetailActivity extends I2PActivityBase {
|
public class LogDetailActivity extends I2PActivityBase {
|
||||||
@ -10,7 +12,11 @@ public class LogDetailActivity extends I2PActivityBase {
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
mDrawerToggle.setDrawerIndicatorEnabled(false);
|
setContentView(R.layout.activity_onepane);
|
||||||
|
|
||||||
|
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
String entry = getIntent().getStringExtra(LogDetailFragment.LOG_ENTRY);
|
String entry = getIntent().getStringExtra(LogDetailFragment.LOG_ENTRY);
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package net.i2p.android.router.log;
|
package net.i2p.android.router.log;
|
||||||
|
|
||||||
import net.i2p.android.router.I2PFragmentBase;
|
import android.annotation.TargetApi;
|
||||||
import net.i2p.android.router.R;
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@ -16,12 +14,15 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import net.i2p.android.router.I2PFragmentBase;
|
||||||
|
import net.i2p.android.router.R;
|
||||||
|
|
||||||
public class LogDetailFragment extends I2PFragmentBase {
|
public class LogDetailFragment extends I2PFragmentBase {
|
||||||
public static final String LOG_ENTRY = "log_entry";
|
public static final String LOG_ENTRY = "log_entry";
|
||||||
|
|
||||||
private String mEntry;
|
private String mEntry;
|
||||||
|
|
||||||
public static LogDetailFragment newInstance (String entry) {
|
public static LogDetailFragment newInstance(String entry) {
|
||||||
LogDetailFragment f = new LogDetailFragment();
|
LogDetailFragment f = new LogDetailFragment();
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
args.putString(LOG_ENTRY, entry);
|
args.putString(LOG_ENTRY, entry);
|
||||||
@ -37,7 +38,7 @@ public class LogDetailFragment extends I2PFragmentBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
View v = inflater.inflate(R.layout.fragment_log_entry, container, false);
|
View v = inflater.inflate(R.layout.fragment_log_entry, container, false);
|
||||||
|
|
||||||
mEntry = getArguments().getString(LOG_ENTRY);
|
mEntry = getArguments().getString(LOG_ENTRY);
|
||||||
@ -58,15 +59,10 @@ public class LogDetailFragment extends I2PFragmentBase {
|
|||||||
// Handle presses on the action bar items
|
// Handle presses on the action bar items
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case R.id.action_copy_logs:
|
case R.id.action_copy_logs:
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB)
|
||||||
android.text.ClipboardManager clipboard = (android.text.ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
|
copyToClipbardLegacy();
|
||||||
clipboard.setText(mEntry);
|
else
|
||||||
} else {
|
copyToClipboardHoneycomb();
|
||||||
android.content.ClipboardManager clipboard = (android.content.ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
|
|
||||||
android.content.ClipData clip = android.content.ClipData.newPlainText(
|
|
||||||
getString(R.string.i2p_android_logs), mEntry);
|
|
||||||
clipboard.setPrimaryClip(clip);
|
|
||||||
}
|
|
||||||
|
|
||||||
Toast.makeText(getActivity(), R.string.logs_copied_to_clipboard, Toast.LENGTH_SHORT).show();
|
Toast.makeText(getActivity(), R.string.logs_copied_to_clipboard, Toast.LENGTH_SHORT).show();
|
||||||
return true;
|
return true;
|
||||||
@ -74,4 +70,17 @@ public class LogDetailFragment extends I2PFragmentBase {
|
|||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void copyToClipbardLegacy() {
|
||||||
|
android.text.ClipboardManager clipboard = (android.text.ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
|
clipboard.setText(mEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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(
|
||||||
|
getString(R.string.i2p_android_logs), mEntry);
|
||||||
|
clipboard.setPrimaryClip(clip);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ import java.util.List;
|
|||||||
public class LogFragment extends ListFragment implements
|
public class LogFragment extends ListFragment implements
|
||||||
LoaderManager.LoaderCallbacks<List<String>> {
|
LoaderManager.LoaderCallbacks<List<String>> {
|
||||||
public static final String LOG_LEVEL = "log_level";
|
public static final String LOG_LEVEL = "log_level";
|
||||||
|
public static final String LOG_LEVEL_ERROR = "ERROR";
|
||||||
/**
|
/**
|
||||||
* The serialization (saved instance state) Bundle key representing the
|
* The serialization (saved instance state) Bundle key representing the
|
||||||
* activated item position. Only used on tablets.
|
* activated item position. Only used on tablets.
|
||||||
@ -48,7 +49,7 @@ public class LogFragment extends ListFragment implements
|
|||||||
|
|
||||||
// Container Activity must implement this interface
|
// Container Activity must implement this interface
|
||||||
public interface OnEntrySelectedListener {
|
public interface OnEntrySelectedListener {
|
||||||
public void onEntrySelected(String entry);
|
void onEntrySelected(String entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LogFragment newInstance(String level) {
|
public static LogFragment newInstance(String level) {
|
||||||
@ -113,11 +114,11 @@ public class LogFragment extends ListFragment implements
|
|||||||
|
|
||||||
I2PAppContext ctx = I2PAppContext.getCurrentContext();
|
I2PAppContext ctx = I2PAppContext.getCurrentContext();
|
||||||
if (ctx != null) {
|
if (ctx != null) {
|
||||||
setEmptyText("ERROR".equals(mLogLevel) ?
|
setEmptyText(getString(LOG_LEVEL_ERROR.equals(mLogLevel) ?
|
||||||
"No error messages" : "No messages");
|
R.string.no_error_messages : R.string.no_messages));
|
||||||
|
|
||||||
setListShown(false);
|
setListShown(false);
|
||||||
getLoaderManager().initLoader("ERROR".equals(mLogLevel) ?
|
getLoaderManager().initLoader(LOG_LEVEL_ERROR.equals(mLogLevel) ?
|
||||||
LEVEL_ERROR : LEVEL_ALL, null, this);
|
LEVEL_ERROR : LEVEL_ALL, null, this);
|
||||||
} else
|
} else
|
||||||
setEmptyText(getResources().getString(
|
setEmptyText(getResources().getString(
|
||||||
@ -163,7 +164,7 @@ public class LogFragment extends ListFragment implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isError = "ERROR".equals(mLogLevel);
|
boolean isError = LOG_LEVEL_ERROR.equals(mLogLevel);
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
|
||||||
android.text.ClipboardManager clipboard = (android.text.ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
|
android.text.ClipboardManager clipboard = (android.text.ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
clipboard.setText(logText);
|
clipboard.setText(logText);
|
||||||
@ -205,20 +206,12 @@ public class LogFragment extends ListFragment implements
|
|||||||
mActivatedPosition = position;
|
mActivatedPosition = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** fixme plurals */
|
private static String getHeader(Context ctx, int sz, boolean errorsOnly) {
|
||||||
private static String getHeader(int sz, boolean errorsOnly) {
|
if (sz > 0)
|
||||||
if (errorsOnly) {
|
return ctx.getResources().getQuantityString(errorsOnly ?
|
||||||
if (sz == 0)
|
R.plurals.log_error_messages : R.plurals.log_messages, sz, sz);
|
||||||
return "No error messages";
|
else
|
||||||
if (sz == 1)
|
return ctx.getString(errorsOnly ? R.string.no_error_messages : R.string.no_messages);
|
||||||
return "1 error message";
|
|
||||||
return sz + " error messages, newest first";
|
|
||||||
}
|
|
||||||
if (sz == 0)
|
|
||||||
return "No messages";
|
|
||||||
if (sz == 1)
|
|
||||||
return "1 message";
|
|
||||||
return sz + " messages, newest first";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoaderManager.LoaderCallbacks<List<String>>
|
// LoaderManager.LoaderCallbacks<List<String>>
|
||||||
@ -230,14 +223,14 @@ public class LogFragment extends ListFragment implements
|
|||||||
|
|
||||||
public void onLoadFinished(Loader<List<String>> loader,
|
public void onLoadFinished(Loader<List<String>> loader,
|
||||||
List<String> data) {
|
List<String> data) {
|
||||||
if (loader.getId() == ("ERROR".equals(mLogLevel) ?
|
if (loader.getId() == (LOG_LEVEL_ERROR.equals(mLogLevel) ?
|
||||||
LEVEL_ERROR : LEVEL_ALL)) {
|
LEVEL_ERROR : LEVEL_ALL)) {
|
||||||
synchronized (mLogEntries) {
|
synchronized (mLogEntries) {
|
||||||
mLogEntries.clear();
|
mLogEntries.clear();
|
||||||
mLogEntries.addAll(data);
|
mLogEntries.addAll(data);
|
||||||
}
|
}
|
||||||
mAdapter.setData(data);
|
mAdapter.setData(data);
|
||||||
String header = getHeader(data.size(), ("ERROR".equals(mLogLevel)));
|
String header = getHeader(getActivity(), data.size(), (LOG_LEVEL_ERROR.equals(mLogLevel)));
|
||||||
mHeaderView.setText(header);
|
mHeaderView.setText(header);
|
||||||
|
|
||||||
if (isResumed()) {
|
if (isResumed()) {
|
||||||
@ -249,7 +242,7 @@ public class LogFragment extends ListFragment implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onLoaderReset(Loader<List<String>> loader) {
|
public void onLoaderReset(Loader<List<String>> loader) {
|
||||||
if (loader.getId() == ("ERROR".equals(mLogLevel) ?
|
if (loader.getId() == (LOG_LEVEL_ERROR.equals(mLogLevel) ?
|
||||||
LEVEL_ERROR : LEVEL_ALL)) {
|
LEVEL_ERROR : LEVEL_ALL)) {
|
||||||
mAdapter.setData(null);
|
mAdapter.setData(null);
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package net.i2p.android.router.log;
|
package net.i2p.android.router.log;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.v4.content.AsyncTaskLoader;
|
import android.support.v4.content.AsyncTaskLoader;
|
||||||
|
|
||||||
|
import net.i2p.I2PAppContext;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class LogLoader extends AsyncTaskLoader<List<String>> {
|
public class LogLoader extends AsyncTaskLoader<List<String>> {
|
||||||
private I2PAppContext mCtx;
|
private I2PAppContext mCtx;
|
||||||
private String mLogLevel;
|
private String mLogLevel;
|
||||||
@ -24,7 +24,7 @@ public class LogLoader extends AsyncTaskLoader<List<String>> {
|
|||||||
@Override
|
@Override
|
||||||
public List<String> loadInBackground() {
|
public List<String> loadInBackground() {
|
||||||
List<String> msgs;
|
List<String> msgs;
|
||||||
if ("ERROR".equals(mLogLevel)) {
|
if (LogFragment.LOG_LEVEL_ERROR.equals(mLogLevel)) {
|
||||||
msgs = mCtx.logManager().getBuffer().getMostRecentCriticalMessages();
|
msgs = mCtx.logManager().getBuffer().getMostRecentCriticalMessages();
|
||||||
} else {
|
} else {
|
||||||
msgs = mCtx.logManager().getBuffer().getMostRecentMessages();
|
msgs = mCtx.logManager().getBuffer().getMostRecentMessages();
|
||||||
|
@ -3,12 +3,13 @@ package net.i2p.android.router.netdb;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
import android.widget.Spinner;
|
import android.widget.Spinner;
|
||||||
|
|
||||||
import net.i2p.android.router.I2PActivityBase;
|
import net.i2p.android.I2PActivityBase;
|
||||||
import net.i2p.android.router.R;
|
import net.i2p.android.router.R;
|
||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
|
|
||||||
@ -26,14 +27,14 @@ public class NetDbActivity extends I2PActivityBase implements
|
|||||||
|
|
||||||
private Spinner mSpinner;
|
private Spinner mSpinner;
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean canUseTwoPanes() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_multipane);
|
||||||
|
|
||||||
|
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
|
||||||
mSpinner = (Spinner) findViewById(R.id.main_spinner);
|
mSpinner = (Spinner) findViewById(R.id.main_spinner);
|
||||||
mSpinner.setVisibility(View.VISIBLE);
|
mSpinner.setVisibility(View.VISIBLE);
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
package net.i2p.android.router.netdb;
|
package net.i2p.android.router.netdb;
|
||||||
|
|
||||||
import net.i2p.android.router.I2PActivityBase;
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
|
|
||||||
|
import net.i2p.android.I2PActivityBase;
|
||||||
import net.i2p.android.router.R;
|
import net.i2p.android.router.R;
|
||||||
import net.i2p.android.router.util.Util;
|
import net.i2p.android.router.util.Util;
|
||||||
import net.i2p.data.DataFormatException;
|
import net.i2p.data.DataFormatException;
|
||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
|
|
||||||
public class NetDbDetailActivity extends I2PActivityBase implements
|
public class NetDbDetailActivity extends I2PActivityBase implements
|
||||||
NetDbListFragment.OnEntrySelectedListener {
|
NetDbListFragment.OnEntrySelectedListener {
|
||||||
@ -15,7 +17,10 @@ public class NetDbDetailActivity extends I2PActivityBase implements
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
mDrawerToggle.setDrawerIndicatorEnabled(false);
|
setContentView(R.layout.activity_onepane);
|
||||||
|
|
||||||
|
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
boolean isRI = getIntent().getBooleanExtra(NetDbDetailFragment.IS_RI, true);
|
boolean isRI = getIntent().getBooleanExtra(NetDbDetailFragment.IS_RI, true);
|
||||||
|
@ -3,6 +3,7 @@ package net.i2p.android.router.netdb;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.v4.content.AsyncTaskLoader;
|
import android.support.v4.content.AsyncTaskLoader;
|
||||||
|
|
||||||
|
import net.i2p.android.router.util.Util;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
import net.i2p.data.LeaseSet;
|
import net.i2p.data.LeaseSet;
|
||||||
import net.i2p.data.router.RouterInfo;
|
import net.i2p.data.router.RouterInfo;
|
||||||
@ -15,13 +16,11 @@ import java.util.Set;
|
|||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
public class NetDbEntryLoader extends AsyncTaskLoader<List<NetDbEntry>> {
|
public class NetDbEntryLoader extends AsyncTaskLoader<List<NetDbEntry>> {
|
||||||
private RouterContext mRContext;
|
|
||||||
private boolean mRouters;
|
private boolean mRouters;
|
||||||
private List<NetDbEntry> mData;
|
private List<NetDbEntry> mData;
|
||||||
|
|
||||||
public NetDbEntryLoader(Context context, RouterContext rContext, boolean routers) {
|
public NetDbEntryLoader(Context context, boolean routers) {
|
||||||
super(context);
|
super(context);
|
||||||
mRContext = rContext;
|
|
||||||
mRouters = routers;
|
mRouters = routers;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,6 +31,13 @@ public class NetDbEntryLoader extends AsyncTaskLoader<List<NetDbEntry>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class LeaseSetComparator implements Comparator<LeaseSet> {
|
private class LeaseSetComparator implements Comparator<LeaseSet> {
|
||||||
|
private RouterContext mRContext;
|
||||||
|
|
||||||
|
public LeaseSetComparator(RouterContext rContext) {
|
||||||
|
super();
|
||||||
|
mRContext = rContext;
|
||||||
|
}
|
||||||
|
|
||||||
public int compare(LeaseSet l, LeaseSet r) {
|
public int compare(LeaseSet l, LeaseSet r) {
|
||||||
Destination dl = l.getDestination();
|
Destination dl = l.getDestination();
|
||||||
Destination dr = r.getDestination();
|
Destination dr = r.getDestination();
|
||||||
@ -46,19 +52,20 @@ public class NetDbEntryLoader extends AsyncTaskLoader<List<NetDbEntry>> {
|
|||||||
@Override
|
@Override
|
||||||
public List<NetDbEntry> loadInBackground() {
|
public List<NetDbEntry> loadInBackground() {
|
||||||
List<NetDbEntry> ret = new ArrayList<>();
|
List<NetDbEntry> ret = new ArrayList<>();
|
||||||
if (mRContext.netDb().isInitialized()) {
|
RouterContext routerContext = Util.getRouterContext();
|
||||||
|
if (routerContext != null && routerContext.netDb().isInitialized()) {
|
||||||
if (mRouters) {
|
if (mRouters) {
|
||||||
Set<RouterInfo> routers = new TreeSet<>(new RouterInfoComparator());
|
Set<RouterInfo> routers = new TreeSet<>(new RouterInfoComparator());
|
||||||
routers.addAll(mRContext.netDb().getRouters());
|
routers.addAll(routerContext.netDb().getRouters());
|
||||||
for (RouterInfo ri : routers) {
|
for (RouterInfo ri : routers) {
|
||||||
NetDbEntry entry = NetDbEntry.fromRouterInfo(mRContext, ri);
|
NetDbEntry entry = NetDbEntry.fromRouterInfo(routerContext, ri);
|
||||||
ret.add(entry);
|
ret.add(entry);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Set<LeaseSet> leases = new TreeSet<>(new LeaseSetComparator());
|
Set<LeaseSet> leases = new TreeSet<>(new LeaseSetComparator(routerContext));
|
||||||
leases.addAll(mRContext.netDb().getLeases());
|
leases.addAll(routerContext.netDb().getLeases());
|
||||||
for (LeaseSet ls : leases) {
|
for (LeaseSet ls : leases) {
|
||||||
NetDbEntry entry = NetDbEntry.fromLeaseSet(mRContext, ls);
|
NetDbEntry entry = NetDbEntry.fromLeaseSet(routerContext, ls);
|
||||||
ret.add(entry);
|
ret.add(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,16 +11,13 @@ import android.view.MenuItem;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
|
|
||||||
import net.i2p.android.router.I2PFragmentBase;
|
|
||||||
import net.i2p.android.router.I2PFragmentBase.RouterContextProvider;
|
|
||||||
import net.i2p.android.router.R;
|
import net.i2p.android.router.R;
|
||||||
|
import net.i2p.android.router.util.Util;
|
||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
import net.i2p.router.RouterContext;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class NetDbListFragment extends ListFragment implements
|
public class NetDbListFragment extends ListFragment implements
|
||||||
I2PFragmentBase.RouterContextUser,
|
|
||||||
LoaderManager.LoaderCallbacks<List<NetDbEntry>> {
|
LoaderManager.LoaderCallbacks<List<NetDbEntry>> {
|
||||||
public static final String SHOW_ROUTERS = "show_routers";
|
public static final String SHOW_ROUTERS = "show_routers";
|
||||||
|
|
||||||
@ -32,8 +29,6 @@ public class NetDbListFragment extends ListFragment implements
|
|||||||
*/
|
*/
|
||||||
private static final String STATE_ACTIVATED_POSITION = "activated_position";
|
private static final String STATE_ACTIVATED_POSITION = "activated_position";
|
||||||
|
|
||||||
private boolean mOnActivityCreated;
|
|
||||||
private RouterContextProvider mRouterContextProvider;
|
|
||||||
private OnEntrySelectedListener mEntrySelectedCallback;
|
private OnEntrySelectedListener mEntrySelectedCallback;
|
||||||
private NetDbEntryAdapter mAdapter;
|
private NetDbEntryAdapter mAdapter;
|
||||||
private boolean mRouters;
|
private boolean mRouters;
|
||||||
@ -45,22 +40,13 @@ public class NetDbListFragment extends ListFragment implements
|
|||||||
|
|
||||||
// Container Activity must implement this interface
|
// Container Activity must implement this interface
|
||||||
public interface OnEntrySelectedListener {
|
public interface OnEntrySelectedListener {
|
||||||
public void onEntrySelected(boolean isRouterInfo, Hash entryHash);
|
void onEntrySelected(boolean isRouterInfo, Hash entryHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Activity activity) {
|
public void onAttach(Activity activity) {
|
||||||
super.onAttach(activity);
|
super.onAttach(activity);
|
||||||
|
|
||||||
// This makes sure that the container activity has implemented
|
|
||||||
// the callback interface. If not, it throws an exception
|
|
||||||
try {
|
|
||||||
mRouterContextProvider = (RouterContextProvider) activity;
|
|
||||||
} catch (ClassCastException e) {
|
|
||||||
throw new ClassCastException(activity.toString()
|
|
||||||
+ " must implement RouterContextProvider");
|
|
||||||
}
|
|
||||||
|
|
||||||
// This makes sure that the container activity has implemented
|
// This makes sure that the container activity has implemented
|
||||||
// the callback interface. If not, it throws an exception
|
// the callback interface. If not, it throws an exception
|
||||||
try {
|
try {
|
||||||
@ -104,22 +90,18 @@ public class NetDbListFragment extends ListFragment implements
|
|||||||
|
|
||||||
setListAdapter(mAdapter);
|
setListAdapter(mAdapter);
|
||||||
|
|
||||||
mOnActivityCreated = true;
|
if (Util.getRouterContext() == null)
|
||||||
if (getRouterContext() != null)
|
|
||||||
onRouterConnectionReady();
|
|
||||||
else
|
|
||||||
setEmptyText(getResources().getString(
|
setEmptyText(getResources().getString(
|
||||||
R.string.router_not_running));
|
R.string.router_not_running));
|
||||||
}
|
else {
|
||||||
|
setEmptyText(getResources().getString((mRouters ?
|
||||||
public void onRouterConnectionReady() {
|
R.string.netdb_routers_empty :
|
||||||
setEmptyText(getResources().getString((mRouters ?
|
|
||||||
R.string.netdb_routers_empty :
|
|
||||||
R.string.netdb_leases_empty)));
|
R.string.netdb_leases_empty)));
|
||||||
|
|
||||||
setListShown(false);
|
setListShown(false);
|
||||||
getLoaderManager().initLoader(mRouters ? ROUTER_LOADER_ID
|
getLoaderManager().initLoader(mRouters ? ROUTER_LOADER_ID
|
||||||
: LEASESET_LOADER_ID, null, this);
|
: LEASESET_LOADER_ID, null, this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -149,7 +131,7 @@ public class NetDbListFragment extends ListFragment implements
|
|||||||
// Handle presses on the action bar items
|
// Handle presses on the action bar items
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case R.id.action_refresh:
|
case R.id.action_refresh:
|
||||||
if (getRouterContext() != null) {
|
if (Util.getRouterContext() != null) {
|
||||||
setListShown(false);
|
setListShown(false);
|
||||||
getLoaderManager().restartLoader(mRouters ? ROUTER_LOADER_ID
|
getLoaderManager().restartLoader(mRouters ? ROUTER_LOADER_ID
|
||||||
: LEASESET_LOADER_ID, null, this);
|
: LEASESET_LOADER_ID, null, this);
|
||||||
@ -178,23 +160,10 @@ public class NetDbListFragment extends ListFragment implements
|
|||||||
mActivatedPosition = position;
|
mActivatedPosition = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Duplicated from I2PFragmentBase because this extends ListFragment
|
|
||||||
private RouterContext getRouterContext() {
|
|
||||||
return mRouterContextProvider.getRouterContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
// I2PFragmentBase.RouterContextUser
|
|
||||||
|
|
||||||
public void onRouterBind() {
|
|
||||||
if (mOnActivityCreated)
|
|
||||||
onRouterConnectionReady();
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoaderManager.LoaderCallbacks<List<NetDbEntry>>
|
// LoaderManager.LoaderCallbacks<List<NetDbEntry>>
|
||||||
|
|
||||||
public Loader<List<NetDbEntry>> onCreateLoader(int id, Bundle args) {
|
public Loader<List<NetDbEntry>> onCreateLoader(int id, Bundle args) {
|
||||||
return new NetDbEntryLoader(getActivity(),
|
return new NetDbEntryLoader(getActivity(), mRouters);
|
||||||
getRouterContext(), mRouters);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onLoadFinished(Loader<List<NetDbEntry>> loader,
|
public void onLoadFinished(Loader<List<NetDbEntry>> loader,
|
||||||
|
@ -3,6 +3,7 @@ package net.i2p.android.router.netdb;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.v4.content.AsyncTaskLoader;
|
import android.support.v4.content.AsyncTaskLoader;
|
||||||
|
|
||||||
|
import net.i2p.android.router.R;
|
||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
import net.i2p.data.router.RouterAddress;
|
import net.i2p.data.router.RouterAddress;
|
||||||
import net.i2p.data.router.RouterInfo;
|
import net.i2p.data.router.RouterInfo;
|
||||||
@ -59,7 +60,7 @@ public class NetDbStatsLoader extends AsyncTaskLoader<List<ObjectCounter<String>
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret.add(versions);
|
ret.add(versions);
|
||||||
ret.add(countries);
|
//ret.add(countries);
|
||||||
ret.add(transports);
|
ret.add(transports);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -69,14 +70,28 @@ public class NetDbStatsLoader extends AsyncTaskLoader<List<ObjectCounter<String>
|
|||||||
private static final int SSUI = 2;
|
private static final int SSUI = 2;
|
||||||
private static final int NTCP = 4;
|
private static final int NTCP = 4;
|
||||||
private static final int IPV6 = 8;
|
private static final int IPV6 = 8;
|
||||||
private static final String[] TNAMES = { "Hidden or starting up", "SSU", "SSU with introducers", "",
|
private static final int[] TNAMES = {
|
||||||
"NTCP", "NTCP and SSU", "NTCP and SSU with introducers", "",
|
R.string.tname_0,
|
||||||
"", "IPv6 SSU", "IPv6 Only SSU, introducers", "IPv6 SSU, introducers",
|
R.string.tname_1,
|
||||||
"IPv6 NTCP", "IPv6 NTCP, SSU", "IPv6 Only NTCP, SSU, introducers", "IPv6 NTCP, SSU, introducers" };
|
R.string.tname_2,
|
||||||
|
0,
|
||||||
|
R.string.tname_4,
|
||||||
|
R.string.tname_5,
|
||||||
|
R.string.tname_6,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
R.string.tname_9,
|
||||||
|
R.string.tname_10,
|
||||||
|
R.string.tname_11,
|
||||||
|
R.string.tname_12,
|
||||||
|
R.string.tname_13,
|
||||||
|
R.string.tname_14,
|
||||||
|
R.string.tname_15,
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* what transport types
|
* what transport types
|
||||||
*/
|
*/
|
||||||
private static String classifyTransports(RouterInfo info) {
|
private String classifyTransports(RouterInfo info) {
|
||||||
int rv = 0;
|
int rv = 0;
|
||||||
for (RouterAddress addr : info.getAddresses()) {
|
for (RouterAddress addr : info.getAddresses()) {
|
||||||
String style = addr.getTransportStyle();
|
String style = addr.getTransportStyle();
|
||||||
@ -93,7 +108,7 @@ public class NetDbStatsLoader extends AsyncTaskLoader<List<ObjectCounter<String>
|
|||||||
rv |= IPV6;
|
rv |= IPV6;
|
||||||
|
|
||||||
}
|
}
|
||||||
return TNAMES[rv];
|
return getContext().getString(TNAMES[rv]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,11 +1,5 @@
|
|||||||
package net.i2p.android.router.netdb;
|
package net.i2p.android.router.netdb;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import net.i2p.android.router.I2PFragmentBase;
|
|
||||||
import net.i2p.android.router.R;
|
|
||||||
import net.i2p.android.router.util.Util;
|
|
||||||
import net.i2p.util.ObjectCounter;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.app.FragmentManager;
|
import android.support.v4.app.FragmentManager;
|
||||||
@ -20,6 +14,13 @@ import android.view.MenuItem;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import net.i2p.android.router.I2PFragmentBase;
|
||||||
|
import net.i2p.android.router.R;
|
||||||
|
import net.i2p.android.router.util.Util;
|
||||||
|
import net.i2p.util.ObjectCounter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class NetDbSummaryPagerFragment extends I2PFragmentBase implements
|
public class NetDbSummaryPagerFragment extends I2PFragmentBase implements
|
||||||
LoaderManager.LoaderCallbacks<List<ObjectCounter<String>>> {
|
LoaderManager.LoaderCallbacks<List<ObjectCounter<String>>> {
|
||||||
private NetDbPagerAdapter mNetDbPagerAdapter;
|
private NetDbPagerAdapter mNetDbPagerAdapter;
|
||||||
@ -33,8 +34,7 @@ public class NetDbSummaryPagerFragment extends I2PFragmentBase implements
|
|||||||
|
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
View v = inflater.inflate(R.layout.parentfragment_viewpager, container, false);
|
return inflater.inflate(R.layout.parentfragment_viewpager, container, false);
|
||||||
return v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -113,18 +113,18 @@ public class NetDbSummaryPagerFragment extends I2PFragmentBase implements
|
|||||||
if (mData == null)
|
if (mData == null)
|
||||||
return 0;
|
return 0;
|
||||||
else
|
else
|
||||||
return 3;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CharSequence getPageTitle(int i) {
|
public CharSequence getPageTitle(int i) {
|
||||||
switch (i) {
|
switch (i) {
|
||||||
case 1:
|
case 1:
|
||||||
return "Countries";
|
// return getString(R.string.countries);
|
||||||
case 2:
|
//case 2:
|
||||||
return "Transports";
|
return getString(R.string.settings_label_transports);
|
||||||
default:
|
default:
|
||||||
return "Versions";
|
return getString(R.string.versions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ public class NetDbSummaryTableFragment extends Fragment {
|
|||||||
|
|
||||||
switch (mCategory) {
|
switch (mCategory) {
|
||||||
case 1:
|
case 1:
|
||||||
case 2:
|
//case 2:
|
||||||
Collections.sort(objects);
|
Collections.sort(objects);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -83,16 +83,16 @@ public class NetDbSummaryTableFragment extends Fragment {
|
|||||||
|
|
||||||
switch (mCategory) {
|
switch (mCategory) {
|
||||||
case 1:
|
case 1:
|
||||||
tl1.setText("Transports");
|
// tl1.setText(R.string.country);
|
||||||
break;
|
// break;
|
||||||
case 2:
|
//case 2:
|
||||||
tl1.setText("Country");
|
tl1.setText(R.string.transport);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
tl1.setText("Version");
|
tl1.setText(R.string.version);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tl2.setText("Count");
|
tl2.setText(R.string.count);
|
||||||
|
|
||||||
titleRow.addView(tl1);
|
titleRow.addView(tl1);
|
||||||
titleRow.addView(tl2);
|
titleRow.addView(tl2);
|
||||||
|
@ -9,14 +9,10 @@ import java.io.File;
|
|||||||
|
|
||||||
class Init {
|
class Init {
|
||||||
|
|
||||||
private final Context ctx;
|
|
||||||
private final String myDir;
|
private final String myDir;
|
||||||
private final String _ourVersion;
|
|
||||||
|
|
||||||
public Init(Context c) {
|
public Init(Context c) {
|
||||||
ctx = c;
|
|
||||||
myDir = c.getFilesDir().getAbsolutePath();
|
myDir = c.getFilesDir().getAbsolutePath();
|
||||||
_ourVersion = Util.getOurVersion(c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void initialize() {
|
void initialize() {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package net.i2p.android.router.service;
|
package net.i2p.android.router.service;
|
||||||
|
|
||||||
import java.io.IOException;
|
import android.content.Context;
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
|
||||||
import net.i2p.BOB.BOB;
|
import net.i2p.BOB.BOB;
|
||||||
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.addressbook.DaemonThread;
|
import net.i2p.addressbook.DaemonThread;
|
||||||
import net.i2p.android.apps.NewsFetcher;
|
import net.i2p.android.apps.NewsFetcher;
|
||||||
import net.i2p.android.router.util.Notifications;
|
import net.i2p.android.router.util.Notifications;
|
||||||
@ -14,6 +14,8 @@ import net.i2p.router.JobImpl;
|
|||||||
import net.i2p.router.RouterContext;
|
import net.i2p.router.RouterContext;
|
||||||
import net.i2p.util.I2PAppThread;
|
import net.i2p.util.I2PAppThread;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load the clients we want.
|
* Load the clients we want.
|
||||||
*
|
*
|
||||||
@ -35,6 +37,7 @@ import net.i2p.util.I2PAppThread;
|
|||||||
*/
|
*/
|
||||||
class LoadClientsJob extends JobImpl {
|
class LoadClientsJob extends JobImpl {
|
||||||
|
|
||||||
|
private Context mCtx;
|
||||||
private Notifications _notif;
|
private Notifications _notif;
|
||||||
private DaemonThread _addressbook;
|
private DaemonThread _addressbook;
|
||||||
private BOB _bob;
|
private BOB _bob;
|
||||||
@ -43,8 +46,9 @@ class LoadClientsJob extends JobImpl {
|
|||||||
private static final long LOAD_DELAY = 90*1000;
|
private static final long LOAD_DELAY = 90*1000;
|
||||||
|
|
||||||
|
|
||||||
public LoadClientsJob(RouterContext ctx, Notifications notif) {
|
public LoadClientsJob(Context ctx, RouterContext rCtx, Notifications notif) {
|
||||||
super(ctx);
|
super(rCtx);
|
||||||
|
mCtx = ctx;
|
||||||
_notif = notif;
|
_notif = notif;
|
||||||
getTiming().setStartAfter(getContext().clock().now() + LOAD_DELAY);
|
getTiming().setStartAfter(getContext().clock().now() + LOAD_DELAY);
|
||||||
}
|
}
|
||||||
@ -59,7 +63,7 @@ class LoadClientsJob extends JobImpl {
|
|||||||
t.setPriority(Thread.NORM_PRIORITY - 1);
|
t.setPriority(Thread.NORM_PRIORITY - 1);
|
||||||
t.start();
|
t.start();
|
||||||
|
|
||||||
NewsFetcher fetcher = NewsFetcher.getInstance(getContext(), _notif);
|
NewsFetcher fetcher = NewsFetcher.getInstance(mCtx, getContext(), _notif);
|
||||||
t = new I2PAppThread(fetcher, "NewsFetcher", true);
|
t = new I2PAppThread(fetcher, "NewsFetcher", true);
|
||||||
t.start();
|
t.start();
|
||||||
|
|
||||||
@ -88,8 +92,13 @@ class LoadClientsJob extends JobImpl {
|
|||||||
public void runJob() {
|
public void runJob() {
|
||||||
Util.d("Starting i2ptunnel");
|
Util.d("Starting i2ptunnel");
|
||||||
TunnelControllerGroup tcg = TunnelControllerGroup.getInstance();
|
TunnelControllerGroup tcg = TunnelControllerGroup.getInstance();
|
||||||
int sz = tcg.getControllers().size();
|
try {
|
||||||
Util.d("i2ptunnel started " + sz + " clients");
|
tcg.startup();
|
||||||
|
int sz = tcg.getControllers().size();
|
||||||
|
Util.d("i2ptunnel started " + sz + " clients");
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
Util.e("i2ptunnel failed to start", iae);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ import net.i2p.android.router.receiver.I2PReceiver;
|
|||||||
import net.i2p.android.router.util.Connectivity;
|
import net.i2p.android.router.util.Connectivity;
|
||||||
import net.i2p.android.router.util.Notifications;
|
import net.i2p.android.router.util.Notifications;
|
||||||
import net.i2p.android.router.util.Util;
|
import net.i2p.android.router.util.Util;
|
||||||
|
import net.i2p.android.util.LocaleManager;
|
||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
import net.i2p.router.Job;
|
import net.i2p.router.Job;
|
||||||
import net.i2p.router.Router;
|
import net.i2p.router.Router;
|
||||||
@ -47,6 +48,12 @@ public class RouterService extends Service {
|
|||||||
*/
|
*/
|
||||||
public static final String LOCAL_BROADCAST_STATE_CHANGED = "net.i2p.android.LOCAL_BROADCAST_STATE_CHANGED";
|
public static final String LOCAL_BROADCAST_STATE_CHANGED = "net.i2p.android.LOCAL_BROADCAST_STATE_CHANGED";
|
||||||
public static final String LOCAL_BROADCAST_EXTRA_STATE = "net.i2p.android.STATE";
|
public static final String LOCAL_BROADCAST_EXTRA_STATE = "net.i2p.android.STATE";
|
||||||
|
/**
|
||||||
|
* The locale has just changed.
|
||||||
|
*/
|
||||||
|
public static final String LOCAL_BROADCAST_LOCALE_CHANGED = "net.i2p.android.LOCAL_BROADCAST_LOCALE_CHANGED";
|
||||||
|
|
||||||
|
private LocaleManager localeManager = new LocaleManager();
|
||||||
|
|
||||||
private RouterContext _context;
|
private RouterContext _context;
|
||||||
private String _myDir;
|
private String _myDir;
|
||||||
@ -60,7 +67,6 @@ public class RouterService extends Service {
|
|||||||
private final Object _stateLock = new Object();
|
private final Object _stateLock = new Object();
|
||||||
private Handler _handler;
|
private Handler _handler;
|
||||||
private Runnable _updater;
|
private Runnable _updater;
|
||||||
private boolean mStartCalled;
|
|
||||||
private static final String SHARED_PREFS = "net.i2p.android.router";
|
private static final String SHARED_PREFS = "net.i2p.android.router";
|
||||||
private static final String LAST_STATE = "service.lastState";
|
private static final String LAST_STATE = "service.lastState";
|
||||||
private static final String EXTRA_RESTART = "restart";
|
private static final String EXTRA_RESTART = "restart";
|
||||||
@ -76,7 +82,6 @@ public class RouterService extends Service {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
mStartCalled = false;
|
|
||||||
State lastState = getSavedState();
|
State lastState = getSavedState();
|
||||||
setState(State.INIT);
|
setState(State.INIT);
|
||||||
Util.d(this + " onCreate called"
|
Util.d(this + " onCreate called"
|
||||||
@ -96,8 +101,9 @@ public class RouterService extends Service {
|
|||||||
_binder = new RouterBinder(this);
|
_binder = new RouterBinder(this);
|
||||||
_handler = new Handler();
|
_handler = new Handler();
|
||||||
_updater = new Updater();
|
_updater = new Updater();
|
||||||
LocalBroadcastManager.getInstance(this).registerReceiver(onStateRequested,
|
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this);
|
||||||
new IntentFilter(LOCAL_BROADCAST_REQUEST_STATE));
|
lbm.registerReceiver(onStateRequested, new IntentFilter(LOCAL_BROADCAST_REQUEST_STATE));
|
||||||
|
lbm.registerReceiver(onLocaleChanged, new IntentFilter(LOCAL_BROADCAST_LOCALE_CHANGED));
|
||||||
if(lastState == State.RUNNING || lastState == State.ACTIVE) {
|
if(lastState == State.RUNNING || lastState == State.ACTIVE) {
|
||||||
Intent intent = new Intent(this, RouterService.class);
|
Intent intent = new Intent(this, RouterService.class);
|
||||||
intent.putExtra(EXTRA_RESTART, true);
|
intent.putExtra(EXTRA_RESTART, true);
|
||||||
@ -120,6 +126,13 @@ public class RouterService extends Service {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private BroadcastReceiver onLocaleChanged = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
localeManager.updateServiceLocale(RouterService.this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NOT called by system if it restarts us after a crash
|
* NOT called by system if it restarts us after a crash
|
||||||
*/
|
*/
|
||||||
@ -130,7 +143,6 @@ public class RouterService extends Service {
|
|||||||
+ " Flags is: " + flags
|
+ " Flags is: " + flags
|
||||||
+ " ID is: " + startId
|
+ " ID is: " + startId
|
||||||
+ " Current state is: " + _state);
|
+ " Current state is: " + _state);
|
||||||
mStartCalled = true;
|
|
||||||
boolean restart = intent != null && intent.getBooleanExtra(EXTRA_RESTART, false);
|
boolean restart = intent != null && intent.getBooleanExtra(EXTRA_RESTART, false);
|
||||||
if(restart) {
|
if(restart) {
|
||||||
Util.d(this + " RESTARTING");
|
Util.d(this + " RESTARTING");
|
||||||
@ -143,15 +155,15 @@ public class RouterService extends Service {
|
|||||||
_receiver = new I2PReceiver(this);
|
_receiver = new I2PReceiver(this);
|
||||||
if(Connectivity.isConnected(this)) {
|
if(Connectivity.isConnected(this)) {
|
||||||
if(restart) {
|
if(restart) {
|
||||||
_statusBar.replace(StatusBar.ICON_STARTING, "I2P is restarting");
|
_statusBar.replace(StatusBar.ICON_STARTING, R.string.notification_status_restarting);
|
||||||
} else {
|
} else {
|
||||||
_statusBar.replace(StatusBar.ICON_STARTING, "I2P is starting up");
|
_statusBar.replace(StatusBar.ICON_STARTING, R.string.notification_status_starting);
|
||||||
}
|
}
|
||||||
setState(State.STARTING);
|
setState(State.STARTING);
|
||||||
_starterThread = new Thread(new Starter());
|
_starterThread = new Thread(new Starter());
|
||||||
_starterThread.start();
|
_starterThread.start();
|
||||||
} else {
|
} else {
|
||||||
_statusBar.replace(StatusBar.ICON_WAITING_NETWORK, "I2P is waiting for a network connection");
|
_statusBar.replace(StatusBar.ICON_WAITING_NETWORK, R.string.notification_status_waiting);
|
||||||
setState(State.WAITING);
|
setState(State.WAITING);
|
||||||
_handler.postDelayed(new Waiter(), 10 * 1000);
|
_handler.postDelayed(new Waiter(), 10 * 1000);
|
||||||
}
|
}
|
||||||
@ -180,7 +192,7 @@ public class RouterService extends Service {
|
|||||||
if(_state != State.WAITING) {
|
if(_state != State.WAITING) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_statusBar.replace(StatusBar.ICON_STARTING, "Network connected, I2P is starting up");
|
_statusBar.replace(StatusBar.ICON_STARTING, R.string.notification_status_starting_after_waiting);
|
||||||
setState(State.STARTING);
|
setState(State.STARTING);
|
||||||
_starterThread = new Thread(new Starter());
|
_starterThread = new Thread(new Starter());
|
||||||
_starterThread.start();
|
_starterThread.start();
|
||||||
@ -208,13 +220,13 @@ public class RouterService extends Service {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setState(State.RUNNING);
|
setState(State.RUNNING);
|
||||||
_statusBar.replace(StatusBar.ICON_RUNNING, "I2P is running");
|
_statusBar.replace(StatusBar.ICON_RUNNING, R.string.notification_status_running);
|
||||||
_context = Util.getRouterContext();
|
_context = Util.getRouterContext();
|
||||||
if (_context == null) {
|
if (_context == null) {
|
||||||
throw new IllegalStateException("No contexts. This is usually because the router is either starting up or shutting down.");
|
throw new IllegalStateException("No contexts. This is usually because the router is either starting up or shutting down.");
|
||||||
}
|
}
|
||||||
_context.router().setKillVMOnEnd(false);
|
_context.router().setKillVMOnEnd(false);
|
||||||
Job loadJob = new LoadClientsJob(_context, _notif);
|
Job loadJob = new LoadClientsJob(RouterService.this, _context, _notif);
|
||||||
_context.jobQueue().addJob(loadJob);
|
_context.jobQueue().addJob(loadJob);
|
||||||
_context.addShutdownTask(new ShutdownHook());
|
_context.addShutdownTask(new ShutdownHook());
|
||||||
_context.addFinalShutdownTask(new FinalShutdownHook());
|
_context.addFinalShutdownTask(new FinalShutdownHook());
|
||||||
@ -261,9 +273,10 @@ public class RouterService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String text =
|
String text =
|
||||||
getResources().getString(R.string.notification_status_bw,
|
getResources().getString(R.string.notification_status_text,
|
||||||
fmt.format(inBW), fmt.format(outBW));
|
Util.formatSize(inBW), Util.formatSize(outBW));
|
||||||
|
|
||||||
|
// TODO change string resource after 0.9.20 to use Util.formatSize()
|
||||||
String bigText =
|
String bigText =
|
||||||
getResources().getString(R.string.notification_status_bw,
|
getResources().getString(R.string.notification_status_bw,
|
||||||
fmt.format(inBW), fmt.format(outBW)) + '\n'
|
fmt.format(inBW), fmt.format(outBW)) + '\n'
|
||||||
@ -278,23 +291,23 @@ public class RouterService extends Service {
|
|||||||
if (isGracefulShutdownInProgress()) {
|
if (isGracefulShutdownInProgress()) {
|
||||||
long ms = ctx.router().getShutdownTimeRemaining();
|
long ms = ctx.router().getShutdownTimeRemaining();
|
||||||
if (ms > 1000) {
|
if (ms > 1000) {
|
||||||
_currTitle = "Stopping I2P in " + DataHelper.formatDuration(ms);
|
_currTitle = getString(R.string.notification_status_graceful, DataHelper.formatDuration(ms));
|
||||||
} else {
|
} else {
|
||||||
_currTitle = "Stopping I2P";
|
_currTitle = getString(R.string.notification_status_stopping);
|
||||||
}
|
}
|
||||||
} else if (haveTunnels != _hadTunnels) {
|
} else if (haveTunnels != _hadTunnels) {
|
||||||
if(haveTunnels) {
|
if(haveTunnels) {
|
||||||
_currTitle = "Client tunnels are ready";
|
_currTitle = getString(R.string.notification_status_client_ready);
|
||||||
setState(State.ACTIVE);
|
setState(State.ACTIVE);
|
||||||
_statusBar.replace(StatusBar.ICON_ACTIVE, _currTitle);
|
_statusBar.replace(StatusBar.ICON_ACTIVE, _currTitle);
|
||||||
} else {
|
} else {
|
||||||
_currTitle = "Client tunnels are down";
|
_currTitle = getString(R.string.notification_status_client_down);
|
||||||
setState(State.RUNNING);
|
setState(State.RUNNING);
|
||||||
_statusBar.replace(StatusBar.ICON_RUNNING, _currTitle);
|
_statusBar.replace(StatusBar.ICON_RUNNING, _currTitle);
|
||||||
}
|
}
|
||||||
_hadTunnels = haveTunnels;
|
_hadTunnels = haveTunnels;
|
||||||
} else if (_currTitle == null || _currTitle.equals(""))
|
} else if (_currTitle == null || _currTitle.equals(""))
|
||||||
_currTitle = "I2P is running";
|
_currTitle = getString(R.string.notification_status_running);
|
||||||
_statusBar.update(_currTitle, text, bigText);
|
_statusBar.update(_currTitle, text, bigText);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,7 +347,10 @@ public class RouterService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isStarted() throws RemoteException {
|
public boolean isStarted() throws RemoteException {
|
||||||
return mStartCalled;
|
return _state != State.INIT &&
|
||||||
|
_state != State.STOPPED &&
|
||||||
|
_state != State.MANUAL_STOPPED &&
|
||||||
|
_state != State.MANUAL_QUITTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
public State getState() throws RemoteException {
|
public State getState() throws RemoteException {
|
||||||
@ -379,7 +395,7 @@ public class RouterService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean canManualStop() {
|
public boolean canManualStop() {
|
||||||
return _state == State.WAITING || _state == State.STARTING ||
|
return _state == State.WAITING ||
|
||||||
_state == State.RUNNING || _state == State.ACTIVE ||
|
_state == State.RUNNING || _state == State.ACTIVE ||
|
||||||
_state == State.GRACEFUL_SHUTDOWN;
|
_state == State.GRACEFUL_SHUTDOWN;
|
||||||
}
|
}
|
||||||
@ -401,7 +417,7 @@ public class RouterService extends Service {
|
|||||||
}
|
}
|
||||||
if(_state == State.STARTING || _state == State.RUNNING ||
|
if(_state == State.STARTING || _state == State.RUNNING ||
|
||||||
_state == State.ACTIVE || _state == State.GRACEFUL_SHUTDOWN) {
|
_state == State.ACTIVE || _state == State.GRACEFUL_SHUTDOWN) {
|
||||||
_statusBar.replace(StatusBar.ICON_STOPPING, "Stopping I2P");
|
_statusBar.replace(StatusBar.ICON_STOPPING, R.string.notification_status_stopping);
|
||||||
Thread stopperThread = new Thread(new Stopper(State.MANUAL_STOPPING, State.MANUAL_STOPPED));
|
Thread stopperThread = new Thread(new Stopper(State.MANUAL_STOPPING, State.MANUAL_STOPPED));
|
||||||
stopperThread.start();
|
stopperThread.start();
|
||||||
}
|
}
|
||||||
@ -423,7 +439,7 @@ public class RouterService extends Service {
|
|||||||
}
|
}
|
||||||
if(_state == State.STARTING || _state == State.RUNNING ||
|
if(_state == State.STARTING || _state == State.RUNNING ||
|
||||||
_state == State.ACTIVE || _state == State.GRACEFUL_SHUTDOWN) {
|
_state == State.ACTIVE || _state == State.GRACEFUL_SHUTDOWN) {
|
||||||
_statusBar.replace(StatusBar.ICON_STOPPING, "Stopping I2P");
|
_statusBar.replace(StatusBar.ICON_STOPPING, R.string.notification_status_stopping);
|
||||||
Thread stopperThread = new Thread(new Stopper(State.MANUAL_QUITTING, State.MANUAL_QUITTED));
|
Thread stopperThread = new Thread(new Stopper(State.MANUAL_QUITTING, State.MANUAL_QUITTED));
|
||||||
stopperThread.start();
|
stopperThread.start();
|
||||||
} else if(_state == State.WAITING) {
|
} else if(_state == State.WAITING) {
|
||||||
@ -445,7 +461,7 @@ public class RouterService extends Service {
|
|||||||
}
|
}
|
||||||
if(_state == State.STARTING || _state == State.RUNNING ||
|
if(_state == State.STARTING || _state == State.RUNNING ||
|
||||||
_state == State.ACTIVE || _state == State.GRACEFUL_SHUTDOWN) {
|
_state == State.ACTIVE || _state == State.GRACEFUL_SHUTDOWN) {
|
||||||
_statusBar.replace(StatusBar.ICON_STOPPING, "Network disconnected, stopping I2P");
|
_statusBar.replace(StatusBar.ICON_STOPPING, R.string.notification_status_stopping_after_net);
|
||||||
// don't change state, let the shutdown hook do it
|
// don't change state, let the shutdown hook do it
|
||||||
Thread stopperThread = new Thread(new Stopper(State.NETWORK_STOPPING, State.NETWORK_STOPPING));
|
Thread stopperThread = new Thread(new Stopper(State.NETWORK_STOPPING, State.NETWORK_STOPPING));
|
||||||
stopperThread.start();
|
stopperThread.start();
|
||||||
@ -465,7 +481,7 @@ public class RouterService extends Service {
|
|||||||
if(!canManualStart()) {
|
if(!canManualStart()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_statusBar.replace(StatusBar.ICON_STARTING, "I2P is starting up");
|
_statusBar.replace(StatusBar.ICON_STARTING, R.string.notification_status_starting);
|
||||||
setState(State.STARTING);
|
setState(State.STARTING);
|
||||||
_starterThread = new Thread(new Starter());
|
_starterThread = new Thread(new Starter());
|
||||||
_starterThread.start();
|
_starterThread.start();
|
||||||
@ -513,9 +529,12 @@ public class RouterService extends Service {
|
|||||||
_oldTitle = _currTitle;
|
_oldTitle = _currTitle;
|
||||||
long ms = ctx.router().getShutdownTimeRemaining();
|
long ms = ctx.router().getShutdownTimeRemaining();
|
||||||
if (ms > 1000) {
|
if (ms > 1000) {
|
||||||
_statusBar.replace(StatusBar.ICON_STOPPING, "Stopping I2P in " + DataHelper.formatDuration(ms));
|
_statusBar.replace(
|
||||||
|
StatusBar.ICON_STOPPING,
|
||||||
|
getString(R.string.notification_status_graceful, DataHelper.formatDuration(ms))
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
_statusBar.replace(StatusBar.ICON_STOPPING, "Stopping I2P");
|
_statusBar.replace(StatusBar.ICON_STOPPING, R.string.notification_status_stopping);
|
||||||
}
|
}
|
||||||
setState(State.GRACEFUL_SHUTDOWN);
|
setState(State.GRACEFUL_SHUTDOWN);
|
||||||
}
|
}
|
||||||
@ -542,10 +561,10 @@ public class RouterService extends Service {
|
|||||||
_currTitle = _oldTitle;
|
_currTitle = _oldTitle;
|
||||||
if (_hadTunnels) {
|
if (_hadTunnels) {
|
||||||
setState(State.ACTIVE);
|
setState(State.ACTIVE);
|
||||||
_statusBar.replace(StatusBar.ICON_ACTIVE, "Shutdown cancelled");
|
_statusBar.replace(StatusBar.ICON_ACTIVE, R.string.notification_status_shutdown_cancelled);
|
||||||
} else {
|
} else {
|
||||||
setState(State.RUNNING);
|
setState(State.RUNNING);
|
||||||
_statusBar.replace(StatusBar.ICON_RUNNING, "Shutdown cancelled");
|
_statusBar.replace(StatusBar.ICON_RUNNING, R.string.notification_status_shutdown_cancelled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -608,6 +627,7 @@ public class RouterService extends Service {
|
|||||||
_statusBar.remove();
|
_statusBar.remove();
|
||||||
|
|
||||||
LocalBroadcastManager.getInstance(this).unregisterReceiver(onStateRequested);
|
LocalBroadcastManager.getInstance(this).unregisterReceiver(onStateRequested);
|
||||||
|
LocalBroadcastManager.getInstance(this).unregisterReceiver(onLocaleChanged);
|
||||||
|
|
||||||
I2PReceiver rcvr = _receiver;
|
I2PReceiver rcvr = _receiver;
|
||||||
if(rcvr != null) {
|
if(rcvr != null) {
|
||||||
@ -628,7 +648,7 @@ public class RouterService extends Service {
|
|||||||
if(_state == State.STARTING || _state == State.RUNNING ||
|
if(_state == State.STARTING || _state == State.RUNNING ||
|
||||||
_state == State.ACTIVE || _state == State.GRACEFUL_SHUTDOWN) {
|
_state == State.ACTIVE || _state == State.GRACEFUL_SHUTDOWN) {
|
||||||
// should this be in a thread?
|
// should this be in a thread?
|
||||||
_statusBar.replace(StatusBar.ICON_SHUTTING_DOWN, "I2P is shutting down");
|
_statusBar.replace(StatusBar.ICON_SHUTTING_DOWN, R.string.notification_status_shutting_down);
|
||||||
Thread stopperThread = new Thread(new Stopper(State.STOPPING, State.STOPPED));
|
Thread stopperThread = new Thread(new Stopper(State.STOPPING, State.STOPPED));
|
||||||
stopperThread.start();
|
stopperThread.start();
|
||||||
}
|
}
|
||||||
@ -683,7 +703,7 @@ public class RouterService extends Service {
|
|||||||
public void run() {
|
public void run() {
|
||||||
Util.d(this + " shutdown hook"
|
Util.d(this + " shutdown hook"
|
||||||
+ " Current state is: " + _state);
|
+ " Current state is: " + _state);
|
||||||
_statusBar.replace(StatusBar.ICON_SHUTTING_DOWN, "I2P is shutting down");
|
_statusBar.replace(StatusBar.ICON_SHUTTING_DOWN, R.string.notification_status_shutting_down);
|
||||||
I2PReceiver rcvr = _receiver;
|
I2PReceiver rcvr = _receiver;
|
||||||
if(rcvr != null) {
|
if(rcvr != null) {
|
||||||
synchronized(rcvr) {
|
synchronized(rcvr) {
|
||||||
|
@ -6,12 +6,13 @@ import android.app.PendingIntent;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.support.v4.app.NotificationCompat;
|
import android.support.v4.app.NotificationCompat;
|
||||||
import net.i2p.android.router.MainActivity;
|
|
||||||
|
import net.i2p.android.I2PActivity;
|
||||||
import net.i2p.android.router.R;
|
import net.i2p.android.router.R;
|
||||||
|
|
||||||
class StatusBar {
|
class StatusBar {
|
||||||
|
|
||||||
private final Context ctx;
|
private Context mCtx;
|
||||||
private final NotificationManager mNotificationManager;
|
private final NotificationManager mNotificationManager;
|
||||||
private final NotificationCompat.Builder mNotifyBuilder;
|
private final NotificationCompat.Builder mNotifyBuilder;
|
||||||
private Notification mNotif;
|
private Notification mNotif;
|
||||||
@ -25,8 +26,8 @@ class StatusBar {
|
|||||||
public static final int ICON_SHUTTING_DOWN = R.drawable.ic_stat_router_shutting_down;
|
public static final int ICON_SHUTTING_DOWN = R.drawable.ic_stat_router_shutting_down;
|
||||||
public static final int ICON_WAITING_NETWORK = R.drawable.ic_stat_router_waiting_network;
|
public static final int ICON_WAITING_NETWORK = R.drawable.ic_stat_router_waiting_network;
|
||||||
|
|
||||||
StatusBar(Context cx) {
|
StatusBar(Context ctx) {
|
||||||
ctx = cx;
|
mCtx = ctx;
|
||||||
mNotificationManager = (NotificationManager) ctx.getSystemService(
|
mNotificationManager = (NotificationManager) ctx.getSystemService(
|
||||||
Context.NOTIFICATION_SERVICE);
|
Context.NOTIFICATION_SERVICE);
|
||||||
Thread.currentThread().setUncaughtExceptionHandler(
|
Thread.currentThread().setUncaughtExceptionHandler(
|
||||||
@ -34,7 +35,7 @@ class StatusBar {
|
|||||||
|
|
||||||
int icon = ICON_STARTING;
|
int icon = ICON_STARTING;
|
||||||
// won't be shown if replace() is called
|
// won't be shown if replace() is called
|
||||||
String text = "Starting I2P";
|
String text = ctx.getString(R.string.notification_status_starting);
|
||||||
|
|
||||||
mNotifyBuilder = new NotificationCompat.Builder(ctx)
|
mNotifyBuilder = new NotificationCompat.Builder(ctx)
|
||||||
.setContentText(text)
|
.setContentText(text)
|
||||||
@ -42,22 +43,25 @@ class StatusBar {
|
|||||||
.setOngoing(true)
|
.setOngoing(true)
|
||||||
.setOnlyAlertOnce(true);
|
.setOnlyAlertOnce(true);
|
||||||
|
|
||||||
Intent intent = new Intent(ctx, MainActivity.class);
|
Intent intent = new Intent(ctx, I2PActivity.class);
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
PendingIntent pi = PendingIntent.getActivity(ctx, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
PendingIntent pi = PendingIntent.getActivity(ctx, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
mNotifyBuilder.setContentIntent(pi);
|
mNotifyBuilder.setContentIntent(pi);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void replace(int icon, String text) {
|
public void replace(int icon, int textResource) {
|
||||||
mNotifyBuilder.setSmallIcon(icon)
|
replace(icon, mCtx.getString(textResource));
|
||||||
.setStyle(null)
|
|
||||||
.setTicker(text);
|
|
||||||
update(text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update(String text) {
|
public void replace(int icon, String title) {
|
||||||
String title = "I2P Status";
|
mNotifyBuilder.setSmallIcon(icon)
|
||||||
update(title, text);
|
.setStyle(null)
|
||||||
|
.setTicker(title);
|
||||||
|
update(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(String title) {
|
||||||
|
update(title, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update(String title, String text, String bigText) {
|
public void update(String title, String text, String bigText) {
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
package net.i2p.android.router.service;
|
package net.i2p.android.router.service;
|
||||||
|
|
||||||
import java.util.Observable;
|
|
||||||
import java.util.Observer;
|
|
||||||
|
|
||||||
import com.androidplot.xy.SimpleXYSeries;
|
import com.androidplot.xy.SimpleXYSeries;
|
||||||
import com.androidplot.xy.XYSeries;
|
import com.androidplot.xy.XYSeries;
|
||||||
|
|
||||||
@ -11,6 +8,9 @@ import net.i2p.stat.Rate;
|
|||||||
import net.i2p.stat.RateStat;
|
import net.i2p.stat.RateStat;
|
||||||
import net.i2p.stat.RateSummaryListener;
|
import net.i2p.stat.RateSummaryListener;
|
||||||
|
|
||||||
|
import java.util.Observable;
|
||||||
|
import java.util.Observer;
|
||||||
|
|
||||||
public class SummaryListener implements RateSummaryListener {
|
public class SummaryListener implements RateSummaryListener {
|
||||||
public static final int HISTORY_SIZE = 30;
|
public static final int HISTORY_SIZE = 30;
|
||||||
|
|
||||||
@ -45,8 +45,7 @@ public class SummaryListener implements RateSummaryListener {
|
|||||||
|
|
||||||
public void add(double totalValue, long eventCount, double totalEventTime,
|
public void add(double totalValue, long eventCount, double totalEventTime,
|
||||||
long period) {
|
long period) {
|
||||||
long now = now();
|
long when = now();
|
||||||
long when = now / 1000;
|
|
||||||
double val = eventCount > 0 ? (totalValue / eventCount) : 0d;
|
double val = eventCount > 0 ? (totalValue / eventCount) : 0d;
|
||||||
|
|
||||||
if (_series.size() > HISTORY_SIZE)
|
if (_series.size() > HISTORY_SIZE)
|
||||||
@ -70,7 +69,6 @@ public class SummaryListener implements RateSummaryListener {
|
|||||||
long period = _rate.getPeriod();
|
long period = _rate.getPeriod();
|
||||||
_name = rs.getName() + "." + period;
|
_name = rs.getName() + "." + period;
|
||||||
_series = new SimpleXYSeries(_name);
|
_series = new SimpleXYSeries(_name);
|
||||||
_series.useImplicitXVals();
|
|
||||||
_rate.setSummaryListener(this);
|
_rate.setSummaryListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,15 +1,22 @@
|
|||||||
package net.i2p.android.router.stats;
|
package net.i2p.android.router.stats;
|
||||||
|
|
||||||
import net.i2p.android.router.I2PActivityBase;
|
import android.os.Bundle;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
|
|
||||||
|
import net.i2p.android.I2PActivityBase;
|
||||||
import net.i2p.android.router.R;
|
import net.i2p.android.router.R;
|
||||||
import net.i2p.android.router.service.RouterService;
|
import net.i2p.android.router.service.RouterService;
|
||||||
import android.os.Bundle;
|
|
||||||
|
|
||||||
public class PeersActivity extends I2PActivityBase {
|
public class PeersActivity extends I2PActivityBase {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
mDrawerToggle.setDrawerIndicatorEnabled(false);
|
setContentView(R.layout.activity_onepane);
|
||||||
|
|
||||||
|
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
|
||||||
// Start with the base view
|
// Start with the base view
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
PeersFragment f = new PeersFragment();
|
PeersFragment f = new PeersFragment();
|
||||||
|
@ -1,23 +1,21 @@
|
|||||||
package net.i2p.android.router.stats;
|
package net.i2p.android.router.stats;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.support.v4.app.DialogFragment;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
import android.widget.Spinner;
|
import android.widget.Spinner;
|
||||||
|
|
||||||
import net.i2p.android.router.I2PActivityBase;
|
import net.i2p.android.I2PActivityBase;
|
||||||
import net.i2p.android.router.R;
|
import net.i2p.android.router.R;
|
||||||
import net.i2p.android.router.SettingsActivity;
|
import net.i2p.android.router.SettingsActivity;
|
||||||
import net.i2p.android.router.service.StatSummarizer;
|
import net.i2p.android.router.service.StatSummarizer;
|
||||||
import net.i2p.android.router.service.SummaryListener;
|
import net.i2p.android.router.service.SummaryListener;
|
||||||
|
import net.i2p.android.router.util.Util;
|
||||||
import net.i2p.stat.Rate;
|
import net.i2p.stat.Rate;
|
||||||
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
@ -36,9 +34,35 @@ public class RateGraphActivity extends I2PActivityBase {
|
|||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
mDrawerToggle.setDrawerIndicatorEnabled(false);
|
setContentView(R.layout.activity_onepane);
|
||||||
|
|
||||||
if (StatSummarizer.instance() != null) {
|
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
|
||||||
|
if (Util.getRouterContext() == null) {
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
|
builder.setMessage(R.string.router_not_running)
|
||||||
|
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
dialog.dismiss();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setCancelable(false);
|
||||||
|
builder.show();
|
||||||
|
} else if (StatSummarizer.instance() == null) {
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
|
builder.setMessage(R.string.graphs_not_ready)
|
||||||
|
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
dialog.dismiss();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setCancelable(false);
|
||||||
|
builder.show();
|
||||||
|
} else {
|
||||||
// Get the rates currently being graphed
|
// Get the rates currently being graphed
|
||||||
List<SummaryListener> listeners = StatSummarizer.instance().getListeners();
|
List<SummaryListener> listeners = StatSummarizer.instance().getListeners();
|
||||||
TreeSet<SummaryListener> ordered = new TreeSet<>(new AlphaComparator());
|
TreeSet<SummaryListener> ordered = new TreeSet<>(new AlphaComparator());
|
||||||
@ -79,57 +103,27 @@ public class RateGraphActivity extends I2PActivityBase {
|
|||||||
} else
|
} else
|
||||||
selectRate(0);
|
selectRate(0);
|
||||||
} else {
|
} else {
|
||||||
DialogFragment df = new DialogFragment() {
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
@NonNull
|
builder.setMessage(R.string.no_graphs_configured)
|
||||||
@Override
|
.setPositiveButton(R.string.configure_graphs, new DialogInterface.OnClickListener() {
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
dialog.dismiss();
|
||||||
builder.setMessage(R.string.no_graphs_configured)
|
mFinishOnResume = true;
|
||||||
.setPositiveButton(R.string.configure_graphs, new DialogInterface.OnClickListener() {
|
Intent i = new Intent(RateGraphActivity.this, SettingsActivity.class);
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
i.putExtra(SettingsActivity.PREFERENCE_CATEGORY, SettingsActivity.PREFERENCE_CATEGORY_GRAPHS);
|
||||||
dialog.dismiss();
|
startActivity(i);
|
||||||
mFinishOnResume = true;
|
}
|
||||||
Intent i = new Intent(RateGraphActivity.this, SettingsActivity.class);
|
})
|
||||||
// Navigation to a sub-category doesn't seem to work yet
|
.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
|
@Override
|
||||||
i.setAction(SettingsActivity.ACTION_PREFS_GRAPHS);
|
public void onClick(DialogInterface dialog, int i) {
|
||||||
} else {
|
dialog.cancel();
|
||||||
i.putExtra("settings", "graphs");
|
finish();
|
||||||
}
|
}
|
||||||
startActivity(i);
|
})
|
||||||
}
|
.setCancelable(false);
|
||||||
})
|
builder.show();
|
||||||
.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int i) {
|
|
||||||
dialog.cancel();
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.setCancelable(false);
|
|
||||||
return builder.create();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
df.show(getSupportFragmentManager(), "nographs");
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
DialogFragment df = new DialogFragment() {
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
|
||||||
builder.setMessage(R.string.graphs_not_ready)
|
|
||||||
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
dialog.dismiss();
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.setCancelable(false);
|
|
||||||
return builder.create();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
df.show(getSupportFragmentManager(), "graphsnotready");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,9 +8,9 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import com.androidplot.Plot;
|
import com.androidplot.xy.BarFormatter;
|
||||||
|
import com.androidplot.xy.BarRenderer;
|
||||||
import com.androidplot.xy.BoundaryMode;
|
import com.androidplot.xy.BoundaryMode;
|
||||||
import com.androidplot.xy.LineAndPointFormatter;
|
|
||||||
import com.androidplot.xy.XYPlot;
|
import com.androidplot.xy.XYPlot;
|
||||||
import com.androidplot.xy.XYSeries;
|
import com.androidplot.xy.XYSeries;
|
||||||
import com.androidplot.xy.XYStepMode;
|
import com.androidplot.xy.XYStepMode;
|
||||||
@ -21,25 +21,22 @@ import net.i2p.android.router.service.StatSummarizer;
|
|||||||
import net.i2p.android.router.service.SummaryListener;
|
import net.i2p.android.router.service.SummaryListener;
|
||||||
import net.i2p.android.router.util.Util;
|
import net.i2p.android.router.util.Util;
|
||||||
|
|
||||||
|
import java.text.DateFormat;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.text.FieldPosition;
|
import java.text.FieldPosition;
|
||||||
import java.text.Format;
|
import java.text.Format;
|
||||||
import java.text.ParsePosition;
|
import java.text.ParsePosition;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.Observable;
|
import java.util.Observable;
|
||||||
import java.util.Observer;
|
import java.util.Observer;
|
||||||
|
|
||||||
public class RateGraphFragment extends I2PFragmentBase {
|
public class RateGraphFragment extends I2PFragmentBase {
|
||||||
// redraws a plot whenever an update is received:
|
// redraws a plot whenever an update is received:
|
||||||
private class MyPlotUpdater implements Observer {
|
private class MyPlotUpdater implements Observer {
|
||||||
Plot plot;
|
|
||||||
|
|
||||||
public MyPlotUpdater(Plot plot) {
|
|
||||||
this.plot = plot;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void update(Observable o, Object arg) {
|
public void update(Observable o, Object arg) {
|
||||||
Util.d("Redrawing plot");
|
Util.d("Redrawing plot");
|
||||||
plot.redraw();
|
updatePlot();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,6 +48,7 @@ public class RateGraphFragment extends I2PFragmentBase {
|
|||||||
private SummaryListener _listener;
|
private SummaryListener _listener;
|
||||||
private XYPlot _ratePlot;
|
private XYPlot _ratePlot;
|
||||||
private MyPlotUpdater _plotUpdater;
|
private MyPlotUpdater _plotUpdater;
|
||||||
|
private int _k;
|
||||||
|
|
||||||
public static RateGraphFragment newInstance(String name, long period) {
|
public static RateGraphFragment newInstance(String name, long period) {
|
||||||
RateGraphFragment f = new RateGraphFragment();
|
RateGraphFragment f = new RateGraphFragment();
|
||||||
@ -70,7 +68,7 @@ public class RateGraphFragment extends I2PFragmentBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
View v = inflater.inflate(R.layout.fragment_graph, container, false);
|
View v = inflater.inflate(R.layout.fragment_graph, container, false);
|
||||||
|
|
||||||
_ratePlot = (XYPlot) v.findViewById(R.id.rate_stat_plot);
|
_ratePlot = (XYPlot) v.findViewById(R.id.rate_stat_plot);
|
||||||
@ -99,6 +97,9 @@ public class RateGraphFragment extends I2PFragmentBase {
|
|||||||
public void run() {
|
public void run() {
|
||||||
String rateName = getArguments().getString(RATE_NAME);
|
String rateName = getArguments().getString(RATE_NAME);
|
||||||
long period = getArguments().getLong(RATE_PERIOD);
|
long period = getArguments().getLong(RATE_PERIOD);
|
||||||
|
_k = 1000;
|
||||||
|
if (rateName.startsWith("bw.") || rateName.contains("Size") || rateName.contains("Bps") || rateName.contains("memory"))
|
||||||
|
_k = 1024;
|
||||||
|
|
||||||
Util.d("Setting up " + rateName + "." + period);
|
Util.d("Setting up " + rateName + "." + period);
|
||||||
if (StatSummarizer.instance() == null) {
|
if (StatSummarizer.instance() == null) {
|
||||||
@ -115,38 +116,68 @@ public class RateGraphFragment extends I2PFragmentBase {
|
|||||||
|
|
||||||
XYSeries rateSeries = _listener.getSeries();
|
XYSeries rateSeries = _listener.getSeries();
|
||||||
|
|
||||||
_plotUpdater = new MyPlotUpdater(_ratePlot);
|
_plotUpdater = new MyPlotUpdater();
|
||||||
|
|
||||||
_ratePlot.addSeries(rateSeries, new LineAndPointFormatter(Color.rgb(0, 0, 0), null, Color.rgb(0, 80, 0), null));
|
_ratePlot.addSeries(rateSeries, new BarFormatter(Color.argb(200, 0, 80, 0), Color.argb(200, 0, 80, 0)));
|
||||||
|
_ratePlot.calculateMinMaxVals();
|
||||||
|
long maxX = _ratePlot.getCalculatedMaxX().longValue();
|
||||||
|
|
||||||
Util.d("Adding plot updater to listener");
|
Util.d("Adding plot updater to listener");
|
||||||
_listener.addObserver(_plotUpdater);
|
_listener.addObserver(_plotUpdater);
|
||||||
|
|
||||||
_ratePlot.setDomainStepMode(XYStepMode.SUBDIVIDE);
|
// Only one line, so hide the legend
|
||||||
_ratePlot.setDomainStepValue(SummaryListener.HISTORY_SIZE);
|
_ratePlot.getLegendWidget().setVisible(false);
|
||||||
|
|
||||||
// thin out domain/range tick labels so they dont overlap each other:
|
BarRenderer renderer = (BarRenderer) _ratePlot.getRenderer(BarRenderer.class);
|
||||||
_ratePlot.setTicksPerDomainLabel(5);
|
renderer.setBarWidthStyle(BarRenderer.BarWidthStyle.VARIABLE_WIDTH);
|
||||||
_ratePlot.setTicksPerRangeLabel(3);
|
renderer.setBarGap(0);
|
||||||
|
|
||||||
|
_ratePlot.setDomainUpperBoundary(maxX, BoundaryMode.GROW);
|
||||||
|
_ratePlot.setDomainStep(XYStepMode.INCREMENT_BY_VAL, 15 * 60 * 1000);
|
||||||
|
_ratePlot.setTicksPerDomainLabel(4);
|
||||||
|
|
||||||
_ratePlot.setRangeLowerBoundary(0, BoundaryMode.FIXED);
|
_ratePlot.setRangeLowerBoundary(0, BoundaryMode.FIXED);
|
||||||
|
_ratePlot.setTicksPerRangeLabel(5);
|
||||||
|
|
||||||
|
_ratePlot.setDomainValueFormat(new Format() {
|
||||||
|
private DateFormat dateFormat = SimpleDateFormat.getTimeInstance(DateFormat.SHORT);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StringBuffer format(Object obj, @NonNull StringBuffer toAppendTo,
|
||||||
|
@NonNull FieldPosition pos) {
|
||||||
|
long when = ((Number) obj).longValue();
|
||||||
|
Date date = new Date(when);
|
||||||
|
return dateFormat.format(date, toAppendTo, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object parseObject(String s, @NonNull ParsePosition parsePosition) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final int finalK = _k;
|
||||||
_ratePlot.setRangeValueFormat(new Format() {
|
_ratePlot.setRangeValueFormat(new Format() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StringBuffer format(Object obj, @NonNull StringBuffer toAppendTo,
|
public StringBuffer format(Object obj, @NonNull StringBuffer toAppendTo,
|
||||||
@NonNull FieldPosition pos) {
|
@NonNull FieldPosition pos) {
|
||||||
double val = ((Number) obj).doubleValue();
|
double val = ((Number) obj).doubleValue();
|
||||||
if (val >= 10 * 1000 * 1000)
|
double maxY = _ratePlot.getCalculatedMaxY().doubleValue();
|
||||||
return new DecimalFormat("0 M").format(val / (1000 * 1000), toAppendTo, pos);
|
|
||||||
else if (val >= 8 * 100 * 1000)
|
if (val == 0 || maxY < finalK) {
|
||||||
return new DecimalFormat("0.0 M").format(val / (1000 * 1000), toAppendTo, pos);
|
|
||||||
else if (val >= 10 * 1000)
|
|
||||||
return new DecimalFormat("0 k").format(val / (1000), toAppendTo, pos);
|
|
||||||
else if (val >= 8 * 100)
|
|
||||||
return new DecimalFormat("0.0 k").format(val / (1000), toAppendTo, pos);
|
|
||||||
else
|
|
||||||
return new DecimalFormat("0").format(val, toAppendTo, pos);
|
return new DecimalFormat("0").format(val, toAppendTo, pos);
|
||||||
|
} else if (maxY < finalK * finalK) {
|
||||||
|
if (val < 10 * finalK)
|
||||||
|
return new DecimalFormat("0.0 k").format(val / (1000), toAppendTo, pos);
|
||||||
|
else
|
||||||
|
return new DecimalFormat("0 k").format(val / (1000), toAppendTo, pos);
|
||||||
|
} else {
|
||||||
|
if (val < 10 * finalK * finalK)
|
||||||
|
return new DecimalFormat("0.0 M").format(val / (finalK * finalK), toAppendTo, pos);
|
||||||
|
else
|
||||||
|
return new DecimalFormat("0 M").format(val / (finalK * finalK), toAppendTo, pos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -157,7 +188,45 @@ public class RateGraphFragment extends I2PFragmentBase {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Util.d("Redrawing plot");
|
Util.d("Redrawing plot");
|
||||||
_ratePlot.redraw();
|
updatePlot();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updatePlot() {
|
||||||
|
_ratePlot.calculateMinMaxVals();
|
||||||
|
double maxY = _ratePlot.getCalculatedMaxY().doubleValue();
|
||||||
|
_ratePlot.setRangeStep(XYStepMode.INCREMENT_BY_VAL, getRangeStep(maxY, _k));
|
||||||
|
|
||||||
|
_ratePlot.redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
private double getRangeStep(double maxY, int k) {
|
||||||
|
if (maxY >= k * k)
|
||||||
|
return getRangeStepForScale(maxY, k * k);
|
||||||
|
else if (maxY >= k)
|
||||||
|
return getRangeStepForScale(maxY, k);
|
||||||
|
else
|
||||||
|
return getRangeStepForScale(maxY, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double getRangeStepForScale(double maxY, int scale) {
|
||||||
|
if (maxY >= 400 * scale)
|
||||||
|
return 40 * scale;
|
||||||
|
else if (maxY >= 200 * scale)
|
||||||
|
return 20 * scale;
|
||||||
|
else if (maxY >= 100 * scale)
|
||||||
|
return 10 * scale;
|
||||||
|
else if (maxY >= 40 * scale)
|
||||||
|
return 4 * scale;
|
||||||
|
else if (maxY >= 20 * scale)
|
||||||
|
return 2 * scale;
|
||||||
|
else if (maxY >= 10 * scale)
|
||||||
|
return scale;
|
||||||
|
else if (maxY >= 4 * scale)
|
||||||
|
return 0.4 * scale;
|
||||||
|
else if (maxY >= 2 * scale)
|
||||||
|
return 0.2 * scale;
|
||||||
|
else
|
||||||
|
return 0.1 * scale;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,14 +15,27 @@ public class IntListPreference extends ListPreference {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean persistString(String value) {
|
protected boolean persistString(String value) {
|
||||||
|
if (getSharedPreferences().contains(getKey())) {
|
||||||
|
try {
|
||||||
|
getPersistedInt(0);
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
// Fix for where this preference was previously stored in a ListPreference
|
||||||
|
getSharedPreferences().edit().remove(getKey()).commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return value != null && persistInt(Integer.valueOf(value));
|
return value != null && persistInt(Integer.valueOf(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getPersistedString(String defaultReturnValue) {
|
protected String getPersistedString(String defaultReturnValue) {
|
||||||
if(getSharedPreferences().contains(getKey())) {
|
if(getSharedPreferences().contains(getKey())) {
|
||||||
int intValue = getPersistedInt(0);
|
try {
|
||||||
return String.valueOf(intValue);
|
int intValue = getPersistedInt(0);
|
||||||
|
return String.valueOf(intValue);
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
return super.getPersistedString("0");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return defaultReturnValue;
|
return defaultReturnValue;
|
||||||
}
|
}
|
||||||
|
@ -6,13 +6,21 @@ import android.content.pm.PackageInfo;
|
|||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
|
import net.i2p.android.preferences.GraphsPreferenceFragment;
|
||||||
import net.i2p.android.router.I2PConstants;
|
import net.i2p.android.router.I2PConstants;
|
||||||
|
import net.i2p.android.router.R;
|
||||||
|
import net.i2p.android.router.service.State;
|
||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
|
import net.i2p.data.router.RouterAddress;
|
||||||
|
import net.i2p.data.router.RouterInfo;
|
||||||
|
import net.i2p.router.CommSystemFacade;
|
||||||
import net.i2p.router.Router;
|
import net.i2p.router.Router;
|
||||||
import net.i2p.router.RouterContext;
|
import net.i2p.router.RouterContext;
|
||||||
import net.i2p.router.transport.TransportManager;
|
import net.i2p.router.transport.TransportManager;
|
||||||
|
import net.i2p.router.transport.TransportUtil;
|
||||||
import net.i2p.router.transport.udp.UDPTransport;
|
import net.i2p.router.transport.udp.UDPTransport;
|
||||||
import net.i2p.util.OrderedProperties;
|
import net.i2p.util.OrderedProperties;
|
||||||
|
|
||||||
@ -20,6 +28,7 @@ import java.io.File;
|
|||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.text.DecimalFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -40,7 +49,8 @@ public abstract class Util implements I2PConstants {
|
|||||||
//System.err.println("APK Path" + ": " + _apkPath);
|
//System.err.println("APK Path" + ": " + _apkPath);
|
||||||
if (pi.versionName != null)
|
if (pi.versionName != null)
|
||||||
return pi.versionName;
|
return pi.versionName;
|
||||||
} catch (Exception e) {}
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
return "??";
|
return "??";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,7 +61,7 @@ public abstract class Util implements I2PConstants {
|
|||||||
*/
|
*/
|
||||||
public static RouterContext getRouterContext() {
|
public static RouterContext getRouterContext() {
|
||||||
List<RouterContext> contexts = RouterContext.listContexts();
|
List<RouterContext> contexts = RouterContext.listContexts();
|
||||||
if ( !((contexts == null) || (contexts.isEmpty())) ) {
|
if (!((contexts == null) || (contexts.isEmpty()))) {
|
||||||
return contexts.get(0);
|
return contexts.get(0);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -64,8 +74,8 @@ public abstract class Util implements I2PConstants {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log to the context logger if available (which goes to the console buffer
|
* Log to the context logger if available (which goes to the console buffer
|
||||||
* and to logcat), else just to logcat.
|
* and to logcat), else just to logcat.
|
||||||
*/
|
*/
|
||||||
public static void e(String m, Throwable t) {
|
public static void e(String m, Throwable t) {
|
||||||
I2PAppContext ctx = I2PAppContext.getCurrentContext();
|
I2PAppContext ctx = I2PAppContext.getCurrentContext();
|
||||||
@ -110,6 +120,7 @@ public abstract class Util implements I2PConstants {
|
|||||||
android.util.Log.i(ANDROID_TAG, m);
|
android.util.Log.i(ANDROID_TAG, m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void d(String m) {
|
public static void d(String m) {
|
||||||
d(m, null);
|
d(m, null);
|
||||||
}
|
}
|
||||||
@ -126,7 +137,9 @@ public abstract class Util implements I2PConstants {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** copied from various private components */
|
/**
|
||||||
|
* copied from various private components
|
||||||
|
*/
|
||||||
final static String PROP_I2NP_NTCP_PORT = "i2np.ntcp.port";
|
final static String PROP_I2NP_NTCP_PORT = "i2np.ntcp.port";
|
||||||
final static String PROP_I2NP_NTCP_AUTO_PORT = "i2np.ntcp.autoport";
|
final static String PROP_I2NP_NTCP_AUTO_PORT = "i2np.ntcp.autoport";
|
||||||
|
|
||||||
@ -165,13 +178,30 @@ public abstract class Util implements I2PConstants {
|
|||||||
String string = all.get(x).toString();
|
String string = all.get(x).toString();
|
||||||
String inverted = Boolean.toString(!Boolean.parseBoolean(string));
|
String inverted = Boolean.toString(!Boolean.parseBoolean(string));
|
||||||
routerProps.setProperty(x, inverted);
|
routerProps.setProperty(x, inverted);
|
||||||
|
} else if (x.equals(context.getString(R.string.PREF_LANGUAGE))) {
|
||||||
|
String language[] = TextUtils.split(all.get(x).toString(), "_");
|
||||||
|
|
||||||
|
if (language[0].equals(context.getString(R.string.DEFAULT_LANGUAGE))) {
|
||||||
|
toRemove.setProperty("routerconsole.lang", "");
|
||||||
|
toRemove.setProperty("routerconsole.country", "");
|
||||||
|
} else {
|
||||||
|
routerProps.setProperty("routerconsole.lang", language[0].toLowerCase());
|
||||||
|
if (language.length == 2)
|
||||||
|
routerProps.setProperty("routerconsole.country", language[1].toUpperCase());
|
||||||
|
else
|
||||||
|
toRemove.setProperty("routerconsole.country", "");
|
||||||
|
}
|
||||||
} else if (!x.startsWith(ANDROID_PREF_PREFIX)) { // Skip over UI-related I2P Android settings
|
} else if (!x.startsWith(ANDROID_PREF_PREFIX)) { // Skip over UI-related I2P Android settings
|
||||||
String string = all.get(x).toString();
|
String string = all.get(x).toString();
|
||||||
routerProps.setProperty(x, string);
|
routerProps.setProperty(x, string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (statSummaries.isEmpty()) {
|
if (statSummaries.isEmpty()) {
|
||||||
routerProps.setProperty("stat.summaries", "");
|
// If the graph preferences have not yet been seen, they should be the default
|
||||||
|
if (preferences.getBoolean(GraphsPreferenceFragment.GRAPH_PREFERENCES_SEEN, false))
|
||||||
|
routerProps.setProperty("stat.summaries", "");
|
||||||
|
else
|
||||||
|
toRemove.setProperty("stat.summaries", "");
|
||||||
} else {
|
} else {
|
||||||
Iterator<String> iter = statSummaries.iterator();
|
Iterator<String> iter = statSummaries.iterator();
|
||||||
StringBuilder buf = new StringBuilder(iter.next());
|
StringBuilder buf = new StringBuilder(iter.next());
|
||||||
@ -203,6 +233,7 @@ public abstract class Util implements I2PConstants {
|
|||||||
// propName -> defaultValue
|
// propName -> defaultValue
|
||||||
private static HashMap<String, Boolean> booleanOptionsRequiringRestart = new HashMap<>();
|
private static HashMap<String, Boolean> booleanOptionsRequiringRestart = new HashMap<>();
|
||||||
private static HashMap<String, String> stringOptionsRequiringRestart = new HashMap<>();
|
private static HashMap<String, String> stringOptionsRequiringRestart = new HashMap<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
HashMap<String, Boolean> boolToAdd = new HashMap<>();
|
HashMap<String, Boolean> boolToAdd = new HashMap<>();
|
||||||
HashMap<String, String> strToAdd = new HashMap<>();
|
HashMap<String, String> strToAdd = new HashMap<>();
|
||||||
@ -219,6 +250,7 @@ public abstract class Util implements I2PConstants {
|
|||||||
booleanOptionsRequiringRestart.putAll(boolToAdd);
|
booleanOptionsRequiringRestart.putAll(boolToAdd);
|
||||||
stringOptionsRequiringRestart.putAll(strToAdd);
|
stringOptionsRequiringRestart.putAll(strToAdd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function performs two tasks:
|
* This function performs two tasks:
|
||||||
* <ul><li>
|
* <ul><li>
|
||||||
@ -230,7 +262,7 @@ public abstract class Util implements I2PConstants {
|
|||||||
* changed that will require a router restart.
|
* changed that will require a router restart.
|
||||||
* </li></ul>
|
* </li></ul>
|
||||||
*
|
*
|
||||||
* @param props a Properties object containing the router.config
|
* @param props a Properties object containing the router.config
|
||||||
* @param toRemove a Collection of properties that will be removed
|
* @param toRemove a Collection of properties that will be removed
|
||||||
* @return true if the router needs to be restarted.
|
* @return true if the router needs to be restarted.
|
||||||
*/
|
*/
|
||||||
@ -271,26 +303,26 @@ public abstract class Util implements I2PConstants {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write properties to a file. If the file does not exist, it is created.
|
* Write properties to a file. If the file does not exist, it is created.
|
||||||
* If the properties already exist in the file, they are updated.
|
* If the properties already exist in the file, they are updated.
|
||||||
*
|
*
|
||||||
* @param dir the file directory
|
* @param dir the file directory
|
||||||
* @param file relative to dir
|
* @param file relative to dir
|
||||||
* @param props properties to set
|
* @param props properties to set
|
||||||
*/
|
*/
|
||||||
public static void writePropertiesToFile(Context ctx, String dir, String file, Properties props) {
|
public static void writePropertiesToFile(Context ctx, String dir, String file, Properties props) {
|
||||||
mergeResourceToFile(ctx, dir, file, 0, props, null);
|
mergeResourceToFile(ctx, dir, file, 0, props, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load defaults from resource, then add props from settings, and write back.
|
* Load defaults from resource, then add props from settings, and write back.
|
||||||
* If resID is 0, defaults are not written over the existing file content.
|
* If resID is 0, defaults are not written over the existing file content.
|
||||||
*
|
*
|
||||||
* @param dir the file directory
|
* @param dir the file directory
|
||||||
* @param file relative to dir
|
* @param file relative to dir
|
||||||
* @param resID the ID of the default resource, or 0
|
* @param resID the ID of the default resource, or 0
|
||||||
* @param userProps local properties or null
|
* @param userProps local properties or null
|
||||||
* @param toRemove properties to remove, or null
|
* @param toRemove properties to remove, or null
|
||||||
*/
|
*/
|
||||||
public static void mergeResourceToFile(Context ctx, String dir, String file, int resID,
|
public static void mergeResourceToFile(Context ctx, String dir, String file, int resID,
|
||||||
Properties userProps, Collection<String> toRemove) {
|
Properties userProps, Collection<String> toRemove) {
|
||||||
@ -317,7 +349,7 @@ public abstract class Util implements I2PConstants {
|
|||||||
if (resID > 0)
|
if (resID > 0)
|
||||||
in = ctx.getResources().openRawResource(resID);
|
in = ctx.getResources().openRawResource(resID);
|
||||||
if (in != null)
|
if (in != null)
|
||||||
DataHelper.loadProps(props, in);
|
DataHelper.loadProps(props, in);
|
||||||
|
|
||||||
// override with user settings
|
// override with user settings
|
||||||
if (userProps != null)
|
if (userProps != null)
|
||||||
@ -330,12 +362,232 @@ public abstract class Util implements I2PConstants {
|
|||||||
|
|
||||||
File path = new File(dir, file);
|
File path = new File(dir, file);
|
||||||
DataHelper.storeProps(props, path);
|
DataHelper.storeProps(props, path);
|
||||||
Util.d("Saved " + props.size() +" properties in " + file);
|
Util.d("Saved " + props.size() + " properties in " + file);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
} catch (Resources.NotFoundException nfe) {
|
} catch (Resources.NotFoundException nfe) {
|
||||||
} finally {
|
} finally {
|
||||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
if (in != null) try {
|
||||||
if (fin != null) try { fin.close(); } catch (IOException ioe) {}
|
in.close();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
}
|
||||||
|
if (fin != null) try {
|
||||||
|
fin.close();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isStopping(State state) {
|
||||||
|
return state == State.STOPPING ||
|
||||||
|
state == State.MANUAL_STOPPING ||
|
||||||
|
state == State.MANUAL_QUITTING;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isStopped(State state) {
|
||||||
|
return state == State.STOPPED ||
|
||||||
|
state == State.MANUAL_STOPPED ||
|
||||||
|
state == State.MANUAL_QUITTED ||
|
||||||
|
state == State.WAITING;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class NetStatus {
|
||||||
|
public enum Level {
|
||||||
|
ERROR,
|
||||||
|
WARN,
|
||||||
|
INFO,
|
||||||
|
}
|
||||||
|
|
||||||
|
public final Level level;
|
||||||
|
public final String status;
|
||||||
|
|
||||||
|
public NetStatus(Level level, String status) {
|
||||||
|
this.level = level;
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NetStatus getNetStatus(Context ctx, RouterContext rCtx) {
|
||||||
|
if (rCtx.commSystem().isDummy())
|
||||||
|
return new NetStatus(NetStatus.Level.INFO, ctx.getString(R.string.vm_comm_system));
|
||||||
|
if (rCtx.router().getUptime() > 60 * 1000 && (!rCtx.router().gracefulShutdownInProgress()) &&
|
||||||
|
!rCtx.clientManager().isAlive()) // not a router problem but the user should know
|
||||||
|
return new NetStatus(NetStatus.Level.ERROR, ctx.getString(R.string.net_status_error_i2cp));
|
||||||
|
// Warn based on actual skew from peers, not update status, so if we successfully offset
|
||||||
|
// the clock, we don't complain.
|
||||||
|
//if (!rCtx.clock().getUpdatedSuccessfully())
|
||||||
|
long skew = rCtx.commSystem().getFramedAveragePeerClockSkew(33);
|
||||||
|
// Display the actual skew, not the offset
|
||||||
|
if (Math.abs(skew) > 30 * 1000)
|
||||||
|
return new NetStatus(NetStatus.Level.ERROR,
|
||||||
|
ctx.getString(R.string.net_status_error_skew,
|
||||||
|
DataHelper.formatDuration2(Math.abs(skew))
|
||||||
|
.replace("−", "-")
|
||||||
|
.replace(" ", " ")));
|
||||||
|
if (rCtx.router().isHidden())
|
||||||
|
return new NetStatus(NetStatus.Level.INFO, ctx.getString(R.string.hidden));
|
||||||
|
RouterInfo routerInfo = rCtx.router().getRouterInfo();
|
||||||
|
if (routerInfo == null)
|
||||||
|
return new NetStatus(NetStatus.Level.INFO, ctx.getString(R.string.testing));
|
||||||
|
|
||||||
|
CommSystemFacade.Status status = rCtx.commSystem().getStatus();
|
||||||
|
switch (status) {
|
||||||
|
case OK:
|
||||||
|
case IPV4_OK_IPV6_UNKNOWN:
|
||||||
|
case IPV4_OK_IPV6_FIREWALLED:
|
||||||
|
case IPV4_UNKNOWN_IPV6_OK:
|
||||||
|
case IPV4_DISABLED_IPV6_OK:
|
||||||
|
case IPV4_SNAT_IPV6_OK:
|
||||||
|
RouterAddress ra = routerInfo.getTargetAddress("NTCP");
|
||||||
|
if (ra == null)
|
||||||
|
return new NetStatus(NetStatus.Level.INFO, toStatusString(ctx, status));
|
||||||
|
byte[] ip = ra.getIP();
|
||||||
|
if (ip == null)
|
||||||
|
return new NetStatus(NetStatus.Level.ERROR, ctx.getString(R.string.net_status_error_unresolved_tcp));
|
||||||
|
// TODO set IPv6 arg based on configuration?
|
||||||
|
if (TransportUtil.isPubliclyRoutable(ip, true))
|
||||||
|
return new NetStatus(NetStatus.Level.INFO, toStatusString(ctx, status));
|
||||||
|
return new NetStatus(NetStatus.Level.ERROR, ctx.getString(R.string.net_status_error_private_tcp));
|
||||||
|
|
||||||
|
case IPV4_SNAT_IPV6_UNKNOWN:
|
||||||
|
case DIFFERENT:
|
||||||
|
return new NetStatus(NetStatus.Level.ERROR, ctx.getString(R.string.symmetric_nat));
|
||||||
|
|
||||||
|
case REJECT_UNSOLICITED:
|
||||||
|
case IPV4_DISABLED_IPV6_FIREWALLED:
|
||||||
|
if (routerInfo.getTargetAddress("NTCP") != null)
|
||||||
|
return new NetStatus(NetStatus.Level.WARN, ctx.getString(R.string.net_status_warn_firewalled_inbound_tcp));
|
||||||
|
// fall through...
|
||||||
|
case IPV4_FIREWALLED_IPV6_OK:
|
||||||
|
case IPV4_FIREWALLED_IPV6_UNKNOWN:
|
||||||
|
if (rCtx.netDb().floodfillEnabled())
|
||||||
|
return new NetStatus(NetStatus.Level.WARN, ctx.getString(R.string.net_status_warn_firewalled_floodfill));
|
||||||
|
//if (rCtx.router().getRouterInfo().getCapabilities().indexOf('O') >= 0)
|
||||||
|
// return _("WARN-Firewalled and Fast");
|
||||||
|
return new NetStatus(NetStatus.Level.INFO, toStatusString(ctx, status));
|
||||||
|
|
||||||
|
case DISCONNECTED:
|
||||||
|
return new NetStatus(NetStatus.Level.INFO, ctx.getString(R.string.net_status_info_disconnected));
|
||||||
|
|
||||||
|
case HOSED:
|
||||||
|
return new NetStatus(NetStatus.Level.ERROR, ctx.getString(R.string.net_status_error_udp_port));
|
||||||
|
|
||||||
|
case UNKNOWN:
|
||||||
|
case IPV4_UNKNOWN_IPV6_FIREWALLED:
|
||||||
|
case IPV4_DISABLED_IPV6_UNKNOWN:
|
||||||
|
default:
|
||||||
|
ra = routerInfo.getTargetAddress("SSU");
|
||||||
|
if (ra == null && rCtx.router().getUptime() > 5 * 60 * 1000) {
|
||||||
|
if (rCtx.commSystem().countActivePeers() <= 0)
|
||||||
|
return new NetStatus(NetStatus.Level.ERROR, ctx.getString(R.string.net_status_error_no_active_peers));
|
||||||
|
else if (rCtx.getProperty(ctx.getString(R.string.PROP_I2NP_NTCP_HOSTNAME)) == null ||
|
||||||
|
rCtx.getProperty(ctx.getString(R.string.PROP_I2NP_NTCP_PORT)) == null)
|
||||||
|
return new NetStatus(NetStatus.Level.ERROR, ctx.getString(R.string.net_status_error_udp_disabled_tcp_not_set));
|
||||||
|
else
|
||||||
|
return new NetStatus(NetStatus.Level.WARN, ctx.getString(R.string.net_status_warn_firewalled_udp_disabled));
|
||||||
|
}
|
||||||
|
return new NetStatus(NetStatus.Level.INFO, toStatusString(ctx, status));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toStatusString(Context ctx, CommSystemFacade.Status status) {
|
||||||
|
String ipv4Status = "";
|
||||||
|
String ipv6Status = "";
|
||||||
|
switch (status) {
|
||||||
|
case OK:
|
||||||
|
return ctx.getString(android.R.string.ok);
|
||||||
|
case IPV4_OK_IPV6_UNKNOWN:
|
||||||
|
ipv4Status = ctx.getString(android.R.string.ok);
|
||||||
|
ipv6Status = ctx.getString(R.string.testing);
|
||||||
|
break;
|
||||||
|
case IPV4_OK_IPV6_FIREWALLED:
|
||||||
|
ipv4Status = ctx.getString(android.R.string.ok);
|
||||||
|
ipv6Status = ctx.getString(R.string.firewalled);
|
||||||
|
break;
|
||||||
|
case IPV4_UNKNOWN_IPV6_OK:
|
||||||
|
ipv4Status = ctx.getString(R.string.testing);
|
||||||
|
ipv6Status = ctx.getString(android.R.string.ok);
|
||||||
|
break;
|
||||||
|
case IPV4_FIREWALLED_IPV6_OK:
|
||||||
|
ipv4Status = ctx.getString(R.string.firewalled);
|
||||||
|
ipv6Status = ctx.getString(android.R.string.ok);
|
||||||
|
break;
|
||||||
|
case IPV4_DISABLED_IPV6_OK:
|
||||||
|
ipv4Status = ctx.getString(R.string.disabled);
|
||||||
|
ipv6Status = ctx.getString(android.R.string.ok);
|
||||||
|
break;
|
||||||
|
case IPV4_SNAT_IPV6_OK:
|
||||||
|
ipv4Status = ctx.getString(R.string.symmetric_nat);
|
||||||
|
ipv6Status = ctx.getString(android.R.string.ok);
|
||||||
|
break;
|
||||||
|
case DIFFERENT:
|
||||||
|
return ctx.getString(R.string.symmetric_nat);
|
||||||
|
case IPV4_SNAT_IPV6_UNKNOWN:
|
||||||
|
ipv4Status = ctx.getString(R.string.symmetric_nat);
|
||||||
|
ipv6Status = ctx.getString(R.string.testing);
|
||||||
|
break;
|
||||||
|
case IPV4_FIREWALLED_IPV6_UNKNOWN:
|
||||||
|
ipv4Status = ctx.getString(R.string.firewalled);
|
||||||
|
ipv6Status = ctx.getString(R.string.testing);
|
||||||
|
break;
|
||||||
|
case REJECT_UNSOLICITED:
|
||||||
|
return ctx.getString(R.string.firewalled);
|
||||||
|
case IPV4_UNKNOWN_IPV6_FIREWALLED:
|
||||||
|
ipv4Status = ctx.getString(R.string.testing);
|
||||||
|
ipv6Status = ctx.getString(R.string.firewalled);
|
||||||
|
break;
|
||||||
|
case IPV4_DISABLED_IPV6_UNKNOWN:
|
||||||
|
ipv4Status = ctx.getString(R.string.disabled);
|
||||||
|
ipv6Status = ctx.getString(R.string.testing);
|
||||||
|
break;
|
||||||
|
case IPV4_DISABLED_IPV6_FIREWALLED:
|
||||||
|
ipv4Status = ctx.getString(R.string.disabled);
|
||||||
|
ipv6Status = ctx.getString(R.string.firewalled);
|
||||||
|
break;
|
||||||
|
case UNKNOWN:
|
||||||
|
return ctx.getString(R.string.testing);
|
||||||
|
default:
|
||||||
|
return status.toStatusString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.getString(R.string.net_status_ipv4_ipv6, ipv4Status, ipv6Status);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String formatSize(double size) {
|
||||||
|
int scale;
|
||||||
|
for (scale = 0; size >= 1024.0D; size /= 1024.0D) {
|
||||||
|
++scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
// control total width
|
||||||
|
DecimalFormat fmt;
|
||||||
|
if (size >= 1000) {
|
||||||
|
fmt = new DecimalFormat("#0");
|
||||||
|
} else if (size >= 100) {
|
||||||
|
fmt = new DecimalFormat("#0.0");
|
||||||
|
} else {
|
||||||
|
fmt = new DecimalFormat("#0.00");
|
||||||
|
}
|
||||||
|
|
||||||
|
String str = fmt.format(size);
|
||||||
|
switch (scale) {
|
||||||
|
case 1:
|
||||||
|
return str + "K";
|
||||||
|
case 2:
|
||||||
|
return str + "M";
|
||||||
|
case 3:
|
||||||
|
return str + "G";
|
||||||
|
case 4:
|
||||||
|
return str + "T";
|
||||||
|
case 5:
|
||||||
|
return str + "P";
|
||||||
|
case 6:
|
||||||
|
return str + "E";
|
||||||
|
case 7:
|
||||||
|
return str + "Z";
|
||||||
|
case 8:
|
||||||
|
return str + "Y";
|
||||||
|
default:
|
||||||
|
return str + "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,12 +11,7 @@ import android.webkit.HttpAuthHandler;
|
|||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
import android.webkit.WebViewClient;
|
import android.webkit.WebViewClient;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import net.i2p.android.apps.EepGetFetcher;
|
import net.i2p.android.apps.EepGetFetcher;
|
||||||
import net.i2p.android.router.provider.CacheProvider;
|
import net.i2p.android.router.provider.CacheProvider;
|
||||||
import net.i2p.android.router.util.AppCache;
|
import net.i2p.android.router.util.AppCache;
|
||||||
@ -25,6 +20,13 @@ import net.i2p.android.router.util.Util;
|
|||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
import net.i2p.util.EepGet;
|
import net.i2p.util.EepGet;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
public class I2PWebViewClient extends WebViewClient {
|
public class I2PWebViewClient extends WebViewClient {
|
||||||
|
|
||||||
private BGLoad _lastTask;
|
private BGLoad _lastTask;
|
||||||
@ -262,14 +264,14 @@ public class I2PWebViewClient extends WebViewClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Integer doInBackground(final String... urls) {
|
protected Integer doInBackground(final String... urls) {
|
||||||
publishProgress(Integer.valueOf(-1));
|
publishProgress(-1);
|
||||||
_view.post(new Runnable() {
|
_view.post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
_view.loadUrl(urls[0]);
|
_view.loadUrl(urls[0]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return Integer.valueOf(0);
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -316,10 +318,10 @@ public class I2PWebViewClient extends WebViewClient {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
// 1 means show the cache toast message
|
// 1 means show the cache toast message
|
||||||
return Integer.valueOf(1);
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
publishProgress(Integer.valueOf(-1));
|
publishProgress(-1);
|
||||||
//EepGetFetcher fetcher = new EepGetFetcher(url);
|
//EepGetFetcher fetcher = new EepGetFetcher(url);
|
||||||
OutputStream out = null;
|
OutputStream out = null;
|
||||||
try {
|
try {
|
||||||
@ -330,7 +332,7 @@ public class I2PWebViewClient extends WebViewClient {
|
|||||||
boolean success = fetcher.fetch();
|
boolean success = fetcher.fetch();
|
||||||
if (isCancelled()) {
|
if (isCancelled()) {
|
||||||
Util.d("Fetch cancelled for " + url);
|
Util.d("Fetch cancelled for " + url);
|
||||||
return Integer.valueOf(0);
|
return 0;
|
||||||
}
|
}
|
||||||
try { out.close(); } catch (IOException ioe) {}
|
try { out.close(); } catch (IOException ioe) {}
|
||||||
if (success) {
|
if (success) {
|
||||||
@ -342,7 +344,7 @@ public class I2PWebViewClient extends WebViewClient {
|
|||||||
} else {
|
} else {
|
||||||
AppCache.getInstance(_view.getContext()).removeCacheFile(uri);
|
AppCache.getInstance(_view.getContext()).removeCacheFile(uri);
|
||||||
Util.d("cache create error");
|
Util.d("cache create error");
|
||||||
return Integer.valueOf(0);
|
return 0;
|
||||||
}
|
}
|
||||||
Util.d("loading data, base URL: " + uri + " content URL: " + content);
|
Util.d("loading data, base URL: " + uri + " content URL: " + content);
|
||||||
_view.post(new Runnable() {
|
_view.post(new Runnable() {
|
||||||
@ -394,14 +396,14 @@ public class I2PWebViewClient extends WebViewClient {
|
|||||||
} finally {
|
} finally {
|
||||||
if (out != null) try { out.close(); } catch (IOException ioe) {}
|
if (out != null) try { out.close(); } catch (IOException ioe) {}
|
||||||
}
|
}
|
||||||
return Integer.valueOf(0);
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onProgressUpdate(Integer... progress) {
|
protected void onProgressUpdate(Integer... progress) {
|
||||||
if (isCancelled())
|
if (isCancelled())
|
||||||
return;
|
return;
|
||||||
int prog = progress[0].intValue();
|
int prog = progress[0];
|
||||||
if (prog < 0) {
|
if (prog < 0) {
|
||||||
_dialog.setTitle("Contacting...");
|
_dialog.setTitle("Contacting...");
|
||||||
_dialog.setMessage(_host);
|
_dialog.setMessage(_host);
|
||||||
@ -443,7 +445,7 @@ public class I2PWebViewClient extends WebViewClient {
|
|||||||
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {}
|
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {}
|
||||||
|
|
||||||
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {
|
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {
|
||||||
publishProgress(Integer.valueOf(Math.max(0, (int) (alreadyTransferred + bytesTransferred))));
|
publishProgress(Math.max(0, (int) (alreadyTransferred + bytesTransferred)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {}
|
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {}
|
||||||
@ -454,7 +456,7 @@ public class I2PWebViewClient extends WebViewClient {
|
|||||||
if (key.equalsIgnoreCase("Content-Length")) {
|
if (key.equalsIgnoreCase("Content-Length")) {
|
||||||
try {
|
try {
|
||||||
_total = Integer.parseInt(val.trim());
|
_total = Integer.parseInt(val.trim());
|
||||||
publishProgress(Integer.valueOf(0));
|
publishProgress(0);
|
||||||
} catch (NumberFormatException nfe) {}
|
} catch (NumberFormatException nfe) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
package net.i2p.android.router.web;
|
package net.i2p.android.router.web;
|
||||||
|
|
||||||
import net.i2p.android.router.I2PActivityBase;
|
|
||||||
import net.i2p.android.router.R;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import net.i2p.android.I2PActivityBase;
|
||||||
|
import net.i2p.android.router.R;
|
||||||
|
|
||||||
public class WebActivity extends I2PActivityBase {
|
public class WebActivity extends I2PActivityBase {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_single_fragment);
|
||||||
// Start with the base view
|
// Start with the base view
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
WebFragment f = new WebFragment();
|
WebFragment f = new WebFragment();
|
||||||
@ -18,13 +20,13 @@ public class WebActivity extends I2PActivityBase {
|
|||||||
} else
|
} else
|
||||||
f.setArguments(getIntent().getExtras());
|
f.setArguments(getIntent().getExtras());
|
||||||
getSupportFragmentManager().beginTransaction()
|
getSupportFragmentManager().beginTransaction()
|
||||||
.add(R.id.main_fragment, f).commit();
|
.add(R.id.fragment, f).commit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
WebFragment f = (WebFragment) getSupportFragmentManager().findFragmentById(R.id.main_fragment);
|
WebFragment f = (WebFragment) getSupportFragmentManager().findFragmentById(R.id.fragment);
|
||||||
if (!f.onBackPressed())
|
if (!f.onBackPressed())
|
||||||
super.onBackPressed();
|
super.onBackPressed();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,76 @@
|
|||||||
|
package net.i2p.android.util;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.eowise.recyclerview.stickyheaders.StickyHeadersAdapter;
|
||||||
|
|
||||||
|
import net.i2p.android.router.R;
|
||||||
|
|
||||||
|
public class AlphanumericHeaderAdapter implements StickyHeadersAdapter<AlphanumericHeaderAdapter.ViewHolder> {
|
||||||
|
public interface SortedAdapter {
|
||||||
|
@NonNull
|
||||||
|
String getSortString(int position);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String NUMBERS = "0123456789";
|
||||||
|
|
||||||
|
private SortedAdapter mAdapter;
|
||||||
|
private boolean mCombineNumeric;
|
||||||
|
|
||||||
|
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
TextView character;
|
||||||
|
|
||||||
|
public ViewHolder(View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
character = (TextView) itemView.findViewById(R.id.character);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AlphanumericHeaderAdapter(SortedAdapter adapter) {
|
||||||
|
this(adapter, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AlphanumericHeaderAdapter(SortedAdapter adapter, boolean combineNumeric) {
|
||||||
|
this.mAdapter = adapter;
|
||||||
|
this.mCombineNumeric = combineNumeric;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ViewHolder onCreateViewHolder(ViewGroup parent) {
|
||||||
|
View v = LayoutInflater.from(parent.getContext())
|
||||||
|
.inflate(R.layout.header_alphanumeric, parent, false);
|
||||||
|
return new ViewHolder(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(ViewHolder headerViewHolder, int position) {
|
||||||
|
String sortString = mAdapter.getSortString(position).toUpperCase();
|
||||||
|
if (sortString.isEmpty())
|
||||||
|
headerViewHolder.itemView.setVisibility(View.GONE);
|
||||||
|
else {
|
||||||
|
CharSequence character = sortString.subSequence(0, 1);
|
||||||
|
if (mCombineNumeric && NUMBERS.contains(character))
|
||||||
|
character = "0-9";
|
||||||
|
headerViewHolder.character.setText(character);
|
||||||
|
headerViewHolder.itemView.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getHeaderId(int position) {
|
||||||
|
String sortString = mAdapter.getSortString(position).toUpperCase();
|
||||||
|
if (sortString.isEmpty())
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
CharSequence character = sortString.subSequence(0, 1);
|
||||||
|
if (mCombineNumeric && NUMBERS.contains(character))
|
||||||
|
return "0-9".hashCode();
|
||||||
|
|
||||||
|
return sortString.charAt(0);
|
||||||
|
}
|
||||||
|
}
|
33
app/src/main/java/net/i2p/android/util/FragmentUtils.java
Normal file
33
app/src/main/java/net/i2p/android/util/FragmentUtils.java
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package net.i2p.android.util;
|
||||||
|
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v4.app.FragmentActivity;
|
||||||
|
|
||||||
|
public class FragmentUtils {
|
||||||
|
public interface TwoPaneProvider {
|
||||||
|
boolean isTwoPane();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param frag
|
||||||
|
* The Fragment whose parent is to be found
|
||||||
|
* @param callbackInterface
|
||||||
|
* The interface class that the parent should implement
|
||||||
|
* @return The parent of frag that implements the callbackInterface or null
|
||||||
|
* if no such parent can be found
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked") // Casts are checked using runtime methods
|
||||||
|
public static <T> T getParent(Fragment frag, Class<T> callbackInterface) {
|
||||||
|
Fragment parentFragment = frag.getParentFragment();
|
||||||
|
if (parentFragment != null
|
||||||
|
&& callbackInterface.isInstance(parentFragment)) {
|
||||||
|
return (T) parentFragment;
|
||||||
|
} else {
|
||||||
|
FragmentActivity activity = frag.getActivity();
|
||||||
|
if (activity != null && callbackInterface.isInstance(activity)) {
|
||||||
|
return (T) activity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
65
app/src/main/java/net/i2p/android/util/LocaleManager.java
Normal file
65
app/src/main/java/net/i2p/android/util/LocaleManager.java
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package net.i2p.android.util;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import net.i2p.android.router.R;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class LocaleManager {
|
||||||
|
private Locale currentLocale;
|
||||||
|
|
||||||
|
public void onCreate(Activity activity) {
|
||||||
|
currentLocale = getSelectedLocale(activity);
|
||||||
|
setContextLocale(activity, currentLocale);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onResume(Activity activity) {
|
||||||
|
// If the activity has the incorrect locale, restart it
|
||||||
|
if (!currentLocale.equals(getSelectedLocale(activity))) {
|
||||||
|
Intent intent = activity.getIntent();
|
||||||
|
activity.finish();
|
||||||
|
activity.overridePendingTransition(0, 0);
|
||||||
|
activity.startActivity(intent);
|
||||||
|
activity.overridePendingTransition(0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateServiceLocale(Service service) {
|
||||||
|
currentLocale = getSelectedLocale(service);
|
||||||
|
setContextLocale(service, currentLocale);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Locale getSelectedLocale(Context context) {
|
||||||
|
String defaultLanguage = context.getString(R.string.DEFAULT_LANGUAGE);
|
||||||
|
String selectedLanguage = PreferenceManager.getDefaultSharedPreferences(context).getString(
|
||||||
|
context.getResources().getString(R.string.PREF_LANGUAGE),
|
||||||
|
defaultLanguage
|
||||||
|
);
|
||||||
|
String language[] = TextUtils.split(selectedLanguage, "_");
|
||||||
|
|
||||||
|
if (language[0].equals(defaultLanguage))
|
||||||
|
return Locale.getDefault();
|
||||||
|
else if (language.length == 2)
|
||||||
|
return new Locale(language[0], language[1]);
|
||||||
|
else
|
||||||
|
return new Locale(language[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setContextLocale(Context context, Locale selectedLocale) {
|
||||||
|
Configuration configuration = context.getResources().getConfiguration();
|
||||||
|
if (!configuration.locale.equals(selectedLocale)) {
|
||||||
|
configuration.locale = selectedLocale;
|
||||||
|
context.getResources().updateConfiguration(
|
||||||
|
configuration,
|
||||||
|
context.getResources().getDisplayMetrics()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package net.i2p.android.util;
|
||||||
|
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v4.app.FragmentManager;
|
||||||
|
import android.support.v4.app.FragmentPagerAdapter;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public abstract class MemoryFragmentPagerAdapter extends FragmentPagerAdapter {
|
||||||
|
private FragmentManager mFragmentManager;
|
||||||
|
private Map<Integer, String> mFragmentTags;
|
||||||
|
|
||||||
|
public MemoryFragmentPagerAdapter(FragmentManager fm) {
|
||||||
|
super(fm);
|
||||||
|
mFragmentManager = fm;
|
||||||
|
mFragmentTags = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object instantiateItem(ViewGroup container, int position) {
|
||||||
|
Object obj = super.instantiateItem(container, position);
|
||||||
|
if (obj instanceof Fragment) {
|
||||||
|
// record the fragment tag here.
|
||||||
|
Fragment f = (Fragment) obj;
|
||||||
|
String tag = f.getTag();
|
||||||
|
mFragmentTags.put(position, tag);
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Fragment getFragment(int position) {
|
||||||
|
String tag = mFragmentTags.get(position);
|
||||||
|
if (tag == null)
|
||||||
|
return null;
|
||||||
|
return mFragmentManager.findFragmentByTag(tag);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
package net.i2p.android.widget;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.v4.view.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;
|
||||||
|
|
||||||
|
public CustomViewPager(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
mEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onTouchEvent(MotionEvent event) {
|
||||||
|
return mEnabled && super.onTouchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onInterceptTouchEvent(MotionEvent event) {
|
||||||
|
return mEnabled && super.onInterceptTouchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setCurrentItem(int item) {
|
||||||
|
if (mEnabled || item == 0)
|
||||||
|
super.setCurrentItem(item);
|
||||||
|
else
|
||||||
|
Toast.makeText(getContext(), Util.getRouterContext() == null ?
|
||||||
|
R.string.router_not_running : R.string.router_shutting_down,
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPagingEnabled(boolean enabled) {
|
||||||
|
mEnabled = enabled;
|
||||||
|
updatePagingState();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updatePagingState() {
|
||||||
|
if (!mEnabled && getCurrentItem() != 0)
|
||||||
|
setCurrentItem(0);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.i2p.android.widget;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
|
||||||
|
|
||||||
|
private static final int[] ATTRS = new int[]{
|
||||||
|
android.R.attr.listDivider
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
|
||||||
|
|
||||||
|
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
|
||||||
|
|
||||||
|
private Drawable mDivider;
|
||||||
|
|
||||||
|
private int mOrientation;
|
||||||
|
|
||||||
|
public DividerItemDecoration(Context context, int orientation) {
|
||||||
|
final TypedArray a = context.obtainStyledAttributes(ATTRS);
|
||||||
|
mDivider = a.getDrawable(0);
|
||||||
|
a.recycle();
|
||||||
|
setOrientation(orientation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrientation(int orientation) {
|
||||||
|
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
|
||||||
|
throw new IllegalArgumentException("invalid orientation");
|
||||||
|
}
|
||||||
|
mOrientation = orientation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
|
||||||
|
if (mOrientation == VERTICAL_LIST) {
|
||||||
|
drawVertical(c, parent);
|
||||||
|
} else {
|
||||||
|
drawHorizontal(c, parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawVertical(Canvas c, RecyclerView parent) {
|
||||||
|
final int left = parent.getPaddingLeft();
|
||||||
|
final int right = parent.getWidth() - parent.getPaddingRight();
|
||||||
|
|
||||||
|
final int childCount = parent.getChildCount();
|
||||||
|
for (int i = 0; i < childCount; i++) {
|
||||||
|
final View child = parent.getChildAt(i);
|
||||||
|
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
|
||||||
|
.getLayoutParams();
|
||||||
|
final int top = child.getBottom() + params.bottomMargin;
|
||||||
|
final int bottom = top + mDivider.getIntrinsicHeight();
|
||||||
|
mDivider.setBounds(left, top, right, bottom);
|
||||||
|
mDivider.draw(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawHorizontal(Canvas c, RecyclerView parent) {
|
||||||
|
final int top = parent.getPaddingTop();
|
||||||
|
final int bottom = parent.getHeight() - parent.getPaddingBottom();
|
||||||
|
|
||||||
|
final int childCount = parent.getChildCount();
|
||||||
|
for (int i = 0; i < childCount; i++) {
|
||||||
|
final View child = parent.getChildAt(i);
|
||||||
|
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
|
||||||
|
.getLayoutParams();
|
||||||
|
final int left = child.getRight() + params.rightMargin;
|
||||||
|
final int right = left + mDivider.getIntrinsicHeight();
|
||||||
|
mDivider.setBounds(left, top, right, bottom);
|
||||||
|
mDivider.draw(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
|
||||||
|
RecyclerView.State state) {
|
||||||
|
if (mOrientation == VERTICAL_LIST) {
|
||||||
|
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
|
||||||
|
} else {
|
||||||
|
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,133 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.i2p.android.widget;
|
||||||
|
|
||||||
|
import android.animation.ObjectAnimator;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.graphics.drawable.NinePatchDrawable;
|
||||||
|
import android.support.v4.view.ViewCompat;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
|
import net.i2p.android.router.R;
|
||||||
|
|
||||||
|
public class DrawShadowFrameLayout extends FrameLayout {
|
||||||
|
private Drawable mShadowDrawable;
|
||||||
|
private NinePatchDrawable mShadowNinePatchDrawable;
|
||||||
|
private int mShadowTopOffset;
|
||||||
|
private boolean mShadowVisible;
|
||||||
|
private int mWidth, mHeight;
|
||||||
|
private ObjectAnimator mAnimator;
|
||||||
|
private float mAlpha = 1f;
|
||||||
|
|
||||||
|
public DrawShadowFrameLayout(Context context) {
|
||||||
|
this(context, null, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DrawShadowFrameLayout(Context context, AttributeSet attrs) {
|
||||||
|
this(context, attrs, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DrawShadowFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
final TypedArray a = context.obtainStyledAttributes(attrs,
|
||||||
|
R.styleable.DrawShadowFrameLayout, 0, 0);
|
||||||
|
|
||||||
|
mShadowDrawable = a.getDrawable(R.styleable.DrawShadowFrameLayout_shadowDrawable);
|
||||||
|
if (mShadowDrawable != null) {
|
||||||
|
mShadowDrawable.setCallback(this);
|
||||||
|
if (mShadowDrawable instanceof NinePatchDrawable) {
|
||||||
|
mShadowNinePatchDrawable = (NinePatchDrawable) mShadowDrawable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mShadowVisible = a.getBoolean(R.styleable.DrawShadowFrameLayout_shadowVisible, true);
|
||||||
|
setWillNotDraw(!mShadowVisible || mShadowDrawable == null);
|
||||||
|
|
||||||
|
a.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||||
|
super.onSizeChanged(w, h, oldw, oldh);
|
||||||
|
mWidth = w;
|
||||||
|
mHeight = h;
|
||||||
|
updateShadowBounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateShadowBounds() {
|
||||||
|
if (mShadowDrawable != null) {
|
||||||
|
mShadowDrawable.setBounds(0, mShadowTopOffset, mWidth, mHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(Canvas canvas) {
|
||||||
|
super.draw(canvas);
|
||||||
|
if (mShadowDrawable != null && mShadowVisible) {
|
||||||
|
if (mShadowNinePatchDrawable != null) {
|
||||||
|
mShadowNinePatchDrawable.getPaint().setAlpha((int) (255 * mAlpha));
|
||||||
|
}
|
||||||
|
mShadowDrawable.draw(canvas);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShadowTopOffset(int shadowTopOffset) {
|
||||||
|
this.mShadowTopOffset = shadowTopOffset;
|
||||||
|
updateShadowBounds();
|
||||||
|
ViewCompat.postInvalidateOnAnimation(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unnecessary (for now)
|
||||||
|
public void setShadowVisible(boolean shadowVisible, boolean animate) {
|
||||||
|
this.mShadowVisible = shadowVisible;
|
||||||
|
if (mAnimator != null) {
|
||||||
|
mAnimator.cancel();
|
||||||
|
mAnimator = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (animate && mShadowDrawable != null) {
|
||||||
|
mAnimator = ObjectAnimator.ofFloat(this, SHADOW_ALPHA,
|
||||||
|
shadowVisible ? 0f : 1f,
|
||||||
|
shadowVisible ? 1f : 0f);
|
||||||
|
mAnimator.setDuration(1000);
|
||||||
|
mAnimator.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewCompat.postInvalidateOnAnimation(this);
|
||||||
|
setWillNotDraw(!mShadowVisible || mShadowDrawable == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Property<DrawShadowFrameLayout, Float> SHADOW_ALPHA
|
||||||
|
= new Property<DrawShadowFrameLayout, Float>(Float.class, "shadowAlpha") {
|
||||||
|
@Override
|
||||||
|
public Float get(DrawShadowFrameLayout dsfl) {
|
||||||
|
return dsfl.mAlpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(DrawShadowFrameLayout dsfl, Float value) {
|
||||||
|
dsfl.mAlpha = value;
|
||||||
|
ViewCompat.postInvalidateOnAnimation(dsfl);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
package net.i2p.android.widget;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import com.pnikosis.materialishprogress.ProgressWheel;
|
||||||
|
|
||||||
|
public class LoadingRecyclerView extends RecyclerView {
|
||||||
|
private View mLoadingView;
|
||||||
|
private ProgressWheel mLoadingWheel;
|
||||||
|
private boolean mLoading;
|
||||||
|
final private AdapterDataObserver observer = new AdapterDataObserver() {
|
||||||
|
@Override
|
||||||
|
public void onChanged() {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemRangeInserted(int positionStart, int itemCount) {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemRangeRemoved(int positionStart, int itemCount) {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public LoadingRecyclerView(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoadingRecyclerView(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoadingRecyclerView(Context context, AttributeSet attrs, int defStyle) {
|
||||||
|
super(context, attrs, defStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateLoading() {
|
||||||
|
if (mLoadingView != null) {
|
||||||
|
mLoadingView.setVisibility(mLoading ? VISIBLE : GONE);
|
||||||
|
setVisibility(mLoading ? GONE : VISIBLE);
|
||||||
|
if (mLoadingWheel != null) {
|
||||||
|
if (mLoading && !mLoadingWheel.isSpinning())
|
||||||
|
mLoadingWheel.spin();
|
||||||
|
else if (!mLoading && mLoadingWheel.isSpinning())
|
||||||
|
mLoadingWheel.stopSpinning();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAdapter(Adapter adapter) {
|
||||||
|
final Adapter oldAdapter = getAdapter();
|
||||||
|
if (oldAdapter != null) {
|
||||||
|
oldAdapter.unregisterAdapterDataObserver(observer);
|
||||||
|
}
|
||||||
|
super.setAdapter(adapter);
|
||||||
|
if (adapter != null) {
|
||||||
|
adapter.registerAdapterDataObserver(observer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the views to use for showing state.
|
||||||
|
* <p/>
|
||||||
|
* This method also sets the state to "loading".
|
||||||
|
*
|
||||||
|
* @param loadingView The view to show in place of the RecyclerView while loading.
|
||||||
|
* @param progressWheel The indeterminate ProgressWheel to spin while loading, if any.
|
||||||
|
*/
|
||||||
|
public void setLoadingView(View loadingView, ProgressWheel progressWheel) {
|
||||||
|
mLoadingView = loadingView;
|
||||||
|
mLoadingWheel = progressWheel;
|
||||||
|
setLoading(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLoading(boolean loading) {
|
||||||
|
mLoading = loading;
|
||||||
|
updateLoading();
|
||||||
|
}
|
||||||
|
}
|
325
app/src/main/java/net/i2p/android/widget/SlidingTabLayout.java
Normal file
325
app/src/main/java/net/i2p/android/widget/SlidingTabLayout.java
Normal file
@ -0,0 +1,325 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.i2p.android.widget;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.support.v4.view.PagerAdapter;
|
||||||
|
import android.support.v4.view.ViewPager;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.HorizontalScrollView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To be used with ViewPager to provide a tab indicator component which give constant feedback as to
|
||||||
|
* the user's scroll progress.
|
||||||
|
* <p>
|
||||||
|
* To use the component, simply add it to your view hierarchy. Then in your
|
||||||
|
* {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call
|
||||||
|
* {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for.
|
||||||
|
* <p>
|
||||||
|
* The colors can be customized in two ways. The first and simplest is to provide an array of colors
|
||||||
|
* via {@link #setSelectedIndicatorColors(int...)}. The
|
||||||
|
* alternative is via the {@link TabColorizer} interface which provides you complete control over
|
||||||
|
* which color is used for any individual position.
|
||||||
|
* <p>
|
||||||
|
* The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)},
|
||||||
|
* providing the layout ID of your custom layout.
|
||||||
|
*/
|
||||||
|
public class SlidingTabLayout extends HorizontalScrollView {
|
||||||
|
/**
|
||||||
|
* Allows complete control over the colors drawn in the tab layout. Set with
|
||||||
|
* {@link #setCustomTabColorizer(TabColorizer)}.
|
||||||
|
*/
|
||||||
|
public interface TabColorizer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return return the color of the indicator used when {@code position} is selected.
|
||||||
|
*/
|
||||||
|
int getIndicatorColor(int position);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int TITLE_OFFSET_DIPS = 24;
|
||||||
|
private static final int TAB_VIEW_PADDING_DIPS = 16;
|
||||||
|
private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
|
||||||
|
|
||||||
|
private int mTitleOffset;
|
||||||
|
|
||||||
|
private int mTabViewLayoutId;
|
||||||
|
private int mTabViewTextViewId;
|
||||||
|
private boolean mDistributeEvenly;
|
||||||
|
|
||||||
|
private ViewPager mViewPager;
|
||||||
|
private SparseArray<String> mContentDescriptions = new SparseArray<String>();
|
||||||
|
private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;
|
||||||
|
|
||||||
|
private final SlidingTabStrip mTabStrip;
|
||||||
|
|
||||||
|
public SlidingTabLayout(Context context) {
|
||||||
|
this(context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SlidingTabLayout(Context context, AttributeSet attrs) {
|
||||||
|
this(context, attrs, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {
|
||||||
|
super(context, attrs, defStyle);
|
||||||
|
|
||||||
|
// Disable the Scroll Bar
|
||||||
|
setHorizontalScrollBarEnabled(false);
|
||||||
|
// Make sure that the Tab Strips fills this View
|
||||||
|
setFillViewport(true);
|
||||||
|
|
||||||
|
mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
|
||||||
|
|
||||||
|
mTabStrip = new SlidingTabStrip(context);
|
||||||
|
addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the custom {@link TabColorizer} to be used.
|
||||||
|
*
|
||||||
|
* If you only require simple custmisation then you can use
|
||||||
|
* {@link #setSelectedIndicatorColors(int...)} to achieve
|
||||||
|
* similar effects.
|
||||||
|
*/
|
||||||
|
public void setCustomTabColorizer(TabColorizer tabColorizer) {
|
||||||
|
mTabStrip.setCustomTabColorizer(tabColorizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDistributeEvenly(boolean distributeEvenly) {
|
||||||
|
mDistributeEvenly = distributeEvenly;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the colors to be used for indicating the selected tab. These colors are treated as a
|
||||||
|
* circular array. Providing one color will mean that all tabs are indicated with the same color.
|
||||||
|
*/
|
||||||
|
public void setSelectedIndicatorColors(int... colors) {
|
||||||
|
mTabStrip.setSelectedIndicatorColors(colors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are
|
||||||
|
* required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so
|
||||||
|
* that the layout can update it's scroll position correctly.
|
||||||
|
*
|
||||||
|
* @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener)
|
||||||
|
*/
|
||||||
|
public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
|
||||||
|
mViewPagerPageChangeListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the custom layout to be inflated for the tab views.
|
||||||
|
*
|
||||||
|
* @param layoutResId Layout id to be inflated
|
||||||
|
* @param textViewId id of the {@link TextView} in the inflated view
|
||||||
|
*/
|
||||||
|
public void setCustomTabView(int layoutResId, int textViewId) {
|
||||||
|
mTabViewLayoutId = layoutResId;
|
||||||
|
mTabViewTextViewId = textViewId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the associated view pager. Note that the assumption here is that the pager content
|
||||||
|
* (number of tabs and tab titles) does not change after this call has been made.
|
||||||
|
*/
|
||||||
|
public void setViewPager(ViewPager viewPager) {
|
||||||
|
mTabStrip.removeAllViews();
|
||||||
|
|
||||||
|
mViewPager = viewPager;
|
||||||
|
if (viewPager != null) {
|
||||||
|
viewPager.setOnPageChangeListener(new InternalViewPagerListener());
|
||||||
|
populateTabStrip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a default view to be used for tabs. This is called if a custom tab view is not set via
|
||||||
|
* {@link #setCustomTabView(int, int)}.
|
||||||
|
*/
|
||||||
|
protected TextView createDefaultTabView(Context context) {
|
||||||
|
TextView textView = new TextView(context);
|
||||||
|
textView.setGravity(Gravity.CENTER);
|
||||||
|
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
|
||||||
|
textView.setTypeface(Typeface.DEFAULT_BOLD);
|
||||||
|
textView.setLayoutParams(new LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||||
|
TypedValue outValue = new TypedValue();
|
||||||
|
getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
|
||||||
|
outValue, true);
|
||||||
|
textView.setBackgroundResource(outValue.resourceId);
|
||||||
|
}
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||||
|
textView.setAllCaps(true);
|
||||||
|
|
||||||
|
int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density);
|
||||||
|
textView.setPadding(padding, padding, padding, padding);
|
||||||
|
|
||||||
|
return textView;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void populateTabStrip() {
|
||||||
|
final PagerAdapter adapter = mViewPager.getAdapter();
|
||||||
|
final View.OnClickListener tabClickListener = new TabClickListener();
|
||||||
|
|
||||||
|
for (int i = 0; i < adapter.getCount(); i++) {
|
||||||
|
View tabView = null;
|
||||||
|
TextView tabTitleView = null;
|
||||||
|
|
||||||
|
if (mTabViewLayoutId != 0) {
|
||||||
|
// If there is a custom tab view layout id set, try and inflate it
|
||||||
|
tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
|
||||||
|
false);
|
||||||
|
tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tabView == null) {
|
||||||
|
tabView = createDefaultTabView(getContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tabTitleView == null && TextView.class.isInstance(tabView)) {
|
||||||
|
tabTitleView = (TextView) tabView;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDistributeEvenly) {
|
||||||
|
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) tabView.getLayoutParams();
|
||||||
|
lp.width = 0;
|
||||||
|
lp.weight = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
tabTitleView.setText(adapter.getPageTitle(i));
|
||||||
|
tabView.setOnClickListener(tabClickListener);
|
||||||
|
String desc = mContentDescriptions.get(i, null);
|
||||||
|
if (desc != null) {
|
||||||
|
tabView.setContentDescription(desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
mTabStrip.addView(tabView);
|
||||||
|
if (i == mViewPager.getCurrentItem()) {
|
||||||
|
tabView.setSelected(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContentDescription(int i, String desc) {
|
||||||
|
mContentDescriptions.put(i, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onAttachedToWindow() {
|
||||||
|
super.onAttachedToWindow();
|
||||||
|
|
||||||
|
if (mViewPager != null) {
|
||||||
|
scrollToTab(mViewPager.getCurrentItem(), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scrollToTab(int tabIndex, int positionOffset) {
|
||||||
|
final int tabStripChildCount = mTabStrip.getChildCount();
|
||||||
|
if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
View selectedChild = mTabStrip.getChildAt(tabIndex);
|
||||||
|
if (selectedChild != null) {
|
||||||
|
int targetScrollX = selectedChild.getLeft() + positionOffset;
|
||||||
|
|
||||||
|
if (tabIndex > 0 || positionOffset > 0) {
|
||||||
|
// If we're not at the first child and are mid-scroll, make sure we obey the offset
|
||||||
|
targetScrollX -= mTitleOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollTo(targetScrollX, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
|
||||||
|
private int mScrollState;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||||
|
int tabStripChildCount = mTabStrip.getChildCount();
|
||||||
|
if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mTabStrip.onViewPagerPageChanged(position, positionOffset);
|
||||||
|
|
||||||
|
View selectedTitle = mTabStrip.getChildAt(position);
|
||||||
|
int extraOffset = (selectedTitle != null)
|
||||||
|
? (int) (positionOffset * selectedTitle.getWidth())
|
||||||
|
: 0;
|
||||||
|
scrollToTab(position, extraOffset);
|
||||||
|
|
||||||
|
if (mViewPagerPageChangeListener != null) {
|
||||||
|
mViewPagerPageChangeListener.onPageScrolled(position, positionOffset,
|
||||||
|
positionOffsetPixels);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageScrollStateChanged(int state) {
|
||||||
|
mScrollState = state;
|
||||||
|
|
||||||
|
if (mViewPagerPageChangeListener != null) {
|
||||||
|
mViewPagerPageChangeListener.onPageScrollStateChanged(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageSelected(int position) {
|
||||||
|
if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
|
||||||
|
mTabStrip.onViewPagerPageChanged(position, 0f);
|
||||||
|
scrollToTab(position, 0);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < mTabStrip.getChildCount(); i++) {
|
||||||
|
mTabStrip.getChildAt(i).setSelected(position == i);
|
||||||
|
}
|
||||||
|
if (mViewPagerPageChangeListener != null) {
|
||||||
|
mViewPagerPageChangeListener.onPageSelected(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TabClickListener implements View.OnClickListener {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
for (int i = 0; i < mTabStrip.getChildCount(); i++) {
|
||||||
|
if (v == mTabStrip.getChildAt(i)) {
|
||||||
|
mViewPager.setCurrentItem(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
167
app/src/main/java/net/i2p/android/widget/SlidingTabStrip.java
Normal file
167
app/src/main/java/net/i2p/android/widget/SlidingTabStrip.java
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.i2p.android.widget;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
class SlidingTabStrip extends LinearLayout {
|
||||||
|
|
||||||
|
private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 0;
|
||||||
|
private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
|
||||||
|
private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 3;
|
||||||
|
private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;
|
||||||
|
|
||||||
|
private final int mBottomBorderThickness;
|
||||||
|
private final Paint mBottomBorderPaint;
|
||||||
|
|
||||||
|
private final int mSelectedIndicatorThickness;
|
||||||
|
private final Paint mSelectedIndicatorPaint;
|
||||||
|
|
||||||
|
private final int mDefaultBottomBorderColor;
|
||||||
|
|
||||||
|
private int mSelectedPosition;
|
||||||
|
private float mSelectionOffset;
|
||||||
|
|
||||||
|
private SlidingTabLayout.TabColorizer mCustomTabColorizer;
|
||||||
|
private final SimpleTabColorizer mDefaultTabColorizer;
|
||||||
|
|
||||||
|
SlidingTabStrip(Context context) {
|
||||||
|
this(context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
SlidingTabStrip(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
setWillNotDraw(false);
|
||||||
|
|
||||||
|
final float density = getResources().getDisplayMetrics().density;
|
||||||
|
|
||||||
|
TypedValue outValue = new TypedValue();
|
||||||
|
context.getTheme().resolveAttribute(android.R.attr.colorForeground, outValue, true);
|
||||||
|
final int themeForegroundColor = outValue.data;
|
||||||
|
|
||||||
|
mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor,
|
||||||
|
DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
|
||||||
|
|
||||||
|
mDefaultTabColorizer = new SimpleTabColorizer();
|
||||||
|
mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);
|
||||||
|
|
||||||
|
mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
|
||||||
|
mBottomBorderPaint = new Paint();
|
||||||
|
mBottomBorderPaint.setColor(mDefaultBottomBorderColor);
|
||||||
|
|
||||||
|
mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
|
||||||
|
mSelectedIndicatorPaint = new Paint();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) {
|
||||||
|
mCustomTabColorizer = customTabColorizer;
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSelectedIndicatorColors(int... colors) {
|
||||||
|
// Make sure that the custom colorizer is removed
|
||||||
|
mCustomTabColorizer = null;
|
||||||
|
mDefaultTabColorizer.setIndicatorColors(colors);
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onViewPagerPageChanged(int position, float positionOffset) {
|
||||||
|
mSelectedPosition = position;
|
||||||
|
mSelectionOffset = positionOffset;
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(Canvas canvas) {
|
||||||
|
final int height = getHeight();
|
||||||
|
final int childCount = getChildCount();
|
||||||
|
final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
|
||||||
|
? mCustomTabColorizer
|
||||||
|
: mDefaultTabColorizer;
|
||||||
|
|
||||||
|
// Thick colored underline below the current selection
|
||||||
|
if (childCount > 0) {
|
||||||
|
View selectedTitle = getChildAt(mSelectedPosition);
|
||||||
|
int left = selectedTitle.getLeft();
|
||||||
|
int right = selectedTitle.getRight();
|
||||||
|
int color = tabColorizer.getIndicatorColor(mSelectedPosition);
|
||||||
|
|
||||||
|
if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
|
||||||
|
int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
|
||||||
|
if (color != nextColor) {
|
||||||
|
color = blendColors(nextColor, color, mSelectionOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the selection partway between the tabs
|
||||||
|
View nextTitle = getChildAt(mSelectedPosition + 1);
|
||||||
|
left = (int) (mSelectionOffset * nextTitle.getLeft() +
|
||||||
|
(1.0f - mSelectionOffset) * left);
|
||||||
|
right = (int) (mSelectionOffset * nextTitle.getRight() +
|
||||||
|
(1.0f - mSelectionOffset) * right);
|
||||||
|
}
|
||||||
|
|
||||||
|
mSelectedIndicatorPaint.setColor(color);
|
||||||
|
|
||||||
|
canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
|
||||||
|
height, mSelectedIndicatorPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thin underline along the entire bottom edge
|
||||||
|
canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the alpha value of the {@code color} to be the given {@code alpha} value.
|
||||||
|
*/
|
||||||
|
private static int setColorAlpha(int color, byte alpha) {
|
||||||
|
return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blend {@code color1} and {@code color2} using the given ratio.
|
||||||
|
*
|
||||||
|
* @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
|
||||||
|
* 0.0 will return {@code color2}.
|
||||||
|
*/
|
||||||
|
private static int blendColors(int color1, int color2, float ratio) {
|
||||||
|
final float inverseRation = 1f - ratio;
|
||||||
|
float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
|
||||||
|
float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
|
||||||
|
float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
|
||||||
|
return Color.rgb((int) r, (int) g, (int) b);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer {
|
||||||
|
private int[] mIndicatorColors;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int getIndicatorColor(int position) {
|
||||||
|
return mIndicatorColors[position % mIndicatorColors.length];
|
||||||
|
}
|
||||||
|
|
||||||
|
void setIndicatorColors(int... colors) {
|
||||||
|
mIndicatorColors = colors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -34,7 +34,7 @@ public class Conditional implements ModelCallbacks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public interface Condition {
|
public interface Condition {
|
||||||
public boolean isSatisfied();
|
boolean isSatisfied();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class EqualCondition<T> implements Condition {
|
public class EqualCondition<T> implements Condition {
|
||||||
|
@ -22,6 +22,6 @@ import java.util.ArrayList;
|
|||||||
* Represents a node in the page tree. Can either be a single page, or a page container.
|
* Represents a node in the page tree. Can either be a single page, or a page container.
|
||||||
*/
|
*/
|
||||||
public interface PageTreeNode {
|
public interface PageTreeNode {
|
||||||
public Page findByKey(String key);
|
Page findByKey(String key);
|
||||||
public void flattenCurrentPageSequence(ArrayList<Page> dest);
|
void flattenCurrentPageSequence(ArrayList<Page> dest);
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user