Compare commits

...

271 Commits

Author SHA1 Message Date
c52bc45910 Client library 0.5.1
i2p.i2p tag: i2p-0.9.18
2015-03-02 21:16:57 +00:00
064ebc6857 Moved missing class into client library 2015-03-02 11:03:02 +00:00
6352cd9412 Client library 0.5
i2p.i2p tag: i2p-0.9.18
2015-03-02 00:51:30 +00:00
db6c74b4b8 Updated translations 2015-03-02 00:02:40 +00:00
a8d699bea2 (Hopefully) prevent NPE 2015-03-01 23:49:13 +00:00
5d9cb0029f Fixed style API bug 2015-03-01 23:28:11 +00:00
51f7fca1ea Updated translations 2015-03-01 11:08:55 +00:00
c61ccd32ba UDP and NTCP ports, part 2 2015-02-28 12:36:59 +00:00
1debd64bc3 Pull property definitions into one location 2015-02-28 10:19:42 +00:00
561bcfe3fa New translations 2015-02-28 04:59:15 +00:00
1c1f77f5c5 Updated translations 2015-02-28 04:58:27 +00:00
420526a7c4 Updated changelog 2015-02-28 04:57:10 +00:00
99496be412 Settings options for UDP and NTCP ports 2015-02-28 04:49:18 +00:00
b6f1cdc769 Enable Java 1.7 features 2015-02-28 00:46:49 +00:00
75e4153f4e Fix stringOptionsRequiringRestart handling; add NTCP and SSU port parsing 2015-02-27 23:28:01 +00:00
eefa5b8064 Prep work for supporting removal of router.config properties 2015-02-27 22:52:39 +00:00
9d32e44547 Logger: Configurable flush interval
From i2p.i2p rev 2f451e3579eb5df2d316c1b507950d119d59a8da
2015-02-27 11:04:03 +00:00
42a0d552c7 Fixed potential leak 2015-02-27 10:45:42 +00:00
65848dd22b Fix NPE in AddressEntryLoader 2015-02-27 10:20:00 +00:00
610de188a4 Downgrade to Gradle tools 1.0.1
1.1.* cause "Cannot resolve symbol" errors in Android Studio, even though Gradle
can build the app.
2015-02-27 09:50:34 +00:00
f1cd3032c5 Updated FAB library 2015-02-27 01:54:29 +00:00
1a922ba04a Updated build tools 2015-02-27 01:54:17 +00:00
bdd59734ec Updated Android support libraries 2015-02-26 23:34:49 +00:00
fe162e4f5c Updated Android Gradle build tools 2015-02-26 23:34:31 +00:00
69e30e97b8 Updated TODO 2015-02-26 23:33:53 +00:00
513fbe0c9f Updated translations 2015-01-15 10:34:13 +00:00
de23a76e6b Option to start I2P on boot 2015-01-15 10:31:25 +00:00
c9936894d8 Updated TODO 2015-01-15 05:47:01 +00:00
cb6efd9ed8 Updated translations 2015-01-01 23:18:19 +00:00
ad245003bf Updated browser config instructions for Firefox and Orfox 2014-12-29 11:40:04 +00:00
9f27aedc49 Added postman's tunnels to default i2ptunnel.config 2014-12-27 21:43:00 +00:00
d8f883dce8 Feedback from zzz 2014-12-25 09:48:26 +00:00
7db1fbac94 Updated translations 2014-12-23 23:39:13 +00:00
19464124d6 Fixed NPE on browser refresh 2014-12-23 22:30:04 +00:00
5ba616facc Updated TODO (thx for testing zzz) 2014-12-22 00:22:54 +00:00
590a8183aa Fix settings menu Intents in debug build 2014-12-17 02:42:13 +00:00
9a45bbd18c 0.9.17.1
i2p.i2p tag: i2p-0.9.17
2014-12-14 05:15:16 +00:00
c5eedc0a5e Updated translations 2014-12-14 04:28:50 +00:00
715302c813 Upgraded floatingactionbutton to 1.3.0, dropped unused cardview-v7 2014-12-14 04:26:38 +00:00
3327ed722a I2PTunnel: Ask to install app for tunnel, if none are installed when opening 2014-12-12 13:59:02 +00:00
40afd69a54 Enable debug versions to be installed alongside release versions 2014-12-12 13:55:31 +00:00
241381c7fa Fix copy tasks to run every time 2014-12-12 13:17:48 +00:00
7a7ba093db Added CHANGELOG 2014-12-12 06:03:41 +00:00
f0e6744760 Prevent NPE 2014-12-10 12:03:34 +00:00
99002c1c5c Updated translations 2014-12-10 11:51:41 +00:00
605a6c1cf4 Bumped support-v4 dependency in client library to 21.0.2 2014-12-09 21:43:26 +00:00
954a9cc46b Bumped gradle plugin to 1.0.0 2014-12-09 11:56:19 +00:00
1ef838b966 Fixed NPE 2014-12-04 06:56:52 +00:00
c672ca05f5 0.9.17
i2p.i2p tag: i2p-0.9.17
2014-12-02 00:40:17 +00:00
c493e73889 Client library 0.4
i2p.i2p tag: i2p-0.9.17
2014-12-01 08:40:48 +00:00
2b7c280f5b Tunnel details 2014-12-01 08:39:25 +00:00
23eab8a90a FloatingActionButton for I2PTunnel and addressbook 2014-12-01 03:57:21 +00:00
c59103eb76 Comments 2014-12-01 03:14:55 +00:00
f00a35ee09 Move versionCode and versionName definitions to build.gradle 2014-11-30 21:24:47 +00:00
af93725c01 Binding for Services 2014-11-30 11:35:15 +00:00
3953301c57 Fix intent generation 2014-11-30 10:55:34 +00:00
2dab9d5d4f Upgraded support libraries to 21.0.2 2014-11-27 11:42:48 +00:00
b77666fa26 New client library translations for ro 2014-11-25 11:44:46 +00:00
eca931c1b5 Updated translations 2014-11-25 11:44:22 +00:00
86ae77701f 0.9.16-rc1
i2p.i2p: cb66382d9716f7d9cd9441830face910705253e0
2014-11-25 04:48:48 +00:00
c1ee0c4d9e Updated Gradle Witness
Source: https://github.com/WhisperSystems/gradle-witness
Git commit: 10f1269c0aafdc1d478efc005ed48f3a47d44278
2014-11-25 03:51:52 +00:00
e632b35862 Bind to IRouterState using explicit intents (required for API 21 and up) 2014-11-16 18:49:11 +00:00
20d2dcd891 Updated translations 2014-11-16 03:06:29 +00:00
61d5ba5a7c Toolbar back navigation 2014-11-10 11:59:54 +00:00
339f688b7c I2PTunnel help 2014-11-10 11:55:33 +00:00
fed11e703a Don't start new activity if we are already there 2014-11-10 11:33:36 +00:00
438df8142a Settings back navigation 2014-11-10 11:27:11 +00:00
7b3730be24 Updated i2ptunnel.config, dropped Telecomix and Nameless 2014-11-10 10:43:11 +00:00
d6c20bafb3 Material design: one-line text lists 2014-11-10 10:42:05 +00:00
b8998db3a3 Revert to general Intent for selected addressbook entries 2014-11-10 05:39:48 +00:00
9ab1c84878 Material design: settings 2014-11-10 03:41:49 +00:00
2f3335d361 Material design: list items 2014-11-10 02:30:00 +00:00
0e8d900ed4 I2PTunnel secondary action to open linkable tunnels 2014-11-10 01:56:35 +00:00
2c7ce0b7c6 News fragment margins 2014-11-09 12:24:59 +00:00
3e2bdacf89 Material design: I2PTunnel list 2014-11-09 12:13:32 +00:00
64c32076a1 Show correct browser config activity 2014-11-08 07:50:50 +00:00
4d75ce7de1 Missing files 2014-11-08 07:50:30 +00:00
269ae2f569 Help details back nav in toolbar 2014-11-08 06:46:04 +00:00
a42a4b2c99 Reworked help navigation 2014-11-08 03:41:14 +00:00
96f5c2b488 Change property names to match original script (better interop with other repos) 2014-11-08 03:40:48 +00:00
09ab9779aa Addressbook doesn't use two columns yet 2014-11-05 00:50:55 +00:00
97ed0a3a8f NPE fix 2014-11-04 12:18:56 +00:00
ec6d225dc6 Icons for uninstalled supported browsers 2014-11-04 12:07:41 +00:00
800a332691 Swap Orfox with Firefox in supported browsers (no official Orfox release yet) 2014-11-04 12:02:02 +00:00
45eae17561 Simplify browser list UX 2014-11-04 11:54:22 +00:00
092365cab2 Help twopane view 2014-11-04 03:06:03 +00:00
c98c2f101d Updated TODO 2014-11-04 02:46:20 +00:00
8e86634a41 Recommended browsers, Orfox config, link to app store from browser list 2014-11-04 02:43:34 +00:00
7424e5b707 Browser configuration guides for embedded, Orweb, unknown and unsupported 2014-11-04 01:03:59 +00:00
5175c937a9 Help: List browsers, detect ones we know can (not) be configured for I2P 2014-11-03 12:26:58 +00:00
2692a567ab BetterAsyncTaskLoader from Bote 2014-11-03 12:23:24 +00:00
2de971fb52 New translations 2014-11-03 11:46:17 +00:00
403b2e8cd9 Material design: toolbar for help screen 2014-11-03 03:22:29 +00:00
22141e723a Fix back button when drawer indicator disabled 2014-11-02 23:20:13 +00:00
419758125e Updated translations 2014-11-02 12:42:44 +00:00
6db307c681 Client library 0.3
i2p.i2p tag: i2p-0.9.16
2014-11-02 01:46:33 +00:00
cdec6d2f98 Updated README 2014-11-02 01:36:11 +00:00
18a6dc9719 Use mavenLocal() repository instead of a local file repo 2014-11-02 01:35:55 +00:00
fb92d7858a Upgraded build tools to 21.0.2 2014-10-30 23:10:36 +00:00
b8e005bdd5 Copy tasks can only have one destination directory 2014-10-30 23:10:00 +00:00
6568489b27 Upgraded gradle wrapper and build tools 2014-10-30 12:15:49 +00:00
3ff3e14fc2 New Gradle Witness build
Includes a fix for Gradle 2.+ that is not yet pulled in:
https://github.com/WhisperSystems/gradle-witness/pull/3
2014-10-30 12:15:04 +00:00
092591f4ec Material theme: toolbar
Navigation tabs have all been replaced with spinners, because there are no nice
tab libraries for API 9+.
2014-10-29 11:47:53 +00:00
895a3a1dcc Fix deprecation 2014-10-27 21:37:52 +00:00
dc4ce3f8c7 Updated translations 2014-10-27 07:31:36 +00:00
7e3b9d5065 Updated translations 2014-10-22 21:15:55 +00:00
e58ffc9fd4 Material theme: nav drawer 2014-10-19 06:48:33 +00:00
0d029988c3 Material theme color definitions 2014-10-19 06:05:08 +00:00
6f01989a4b Nav drawer icon changed 2014-10-19 06:04:07 +00:00
19aeaec406 New material theme menu icons 2014-10-19 06:03:14 +00:00
b4d1241a9e Implement material styles properly with appcompat 2014-10-18 21:08:33 +00:00
fef4aa0e86 Initial migration to API 21 2014-10-18 06:11:39 +00:00
766266b147 Updated translations 2014-10-18 02:36:53 +00:00
93410c07bb Updated ignores 2014-10-17 08:02:24 +00:00
dc27a782b0 Fixed NPE in client library with State unparceling
This only fixes the symptom, but the crash log on Google Play doesn't shed light
on what the problem is.
2014-10-17 06:54:26 +00:00
zzz
b633df73c0 Update imports for move of router data structures 2014-10-16 23:10:32 +00:00
41d1200df7 0.9.15.1 2014-10-16 10:47:27 +00:00
c9a62fba9a Drop interface that isn't in source yet 2014-10-16 10:25:12 +00:00
c9598fa831 Update default subscriptions 2014-10-16 10:16:06 +00:00
9965c31b2d Pass dir and file to Util.mergeResourceToFile() in the correct order 2014-10-16 10:15:13 +00:00
43de6425b2 New translations for client library 2014-10-16 10:13:56 +00:00
b98cdf3e0b 0.9.15 2014-10-16 04:32:52 +00:00
26c11ebaed Updated translations 2014-10-16 04:30:49 +00:00
c9c3de1d04 Updated Androidplot to 0.6.1 2014-10-16 04:30:25 +00:00
9b29b56982 Upgrade support libraries to v20.0.0 2014-10-15 22:19:58 +00:00
8956fd7ce0 Updated translation strings 2014-10-15 21:52:46 +00:00
f1f94ea3e0 New translations 2014-10-15 21:49:32 +00:00
94b433aead Padding fix 2014-10-15 21:31:27 +00:00
5623d54114 Basic help page with HTML content 2014-10-15 21:21:11 +00:00
9133a2f848 Prepare for material theme 2014-10-15 10:36:55 +00:00
70324c3ecc Missing class 2014-10-15 02:50:37 +00:00
5613d21324 Replace first start warning dialog with browser config dialog 2014-10-14 11:15:14 +00:00
9036bf3f6b Re-enable (blank) help activity 2014-10-14 11:14:28 +00:00
94991d2df3 New type and status styles for tunnel list 2014-10-07 06:22:09 +00:00
76f23946f0 Don't show dialog on new version until we have something new to tell them 2014-10-05 04:12:29 +00:00
862e856913 Fixed graphs settings page notice when router not running 2014-10-05 03:38:17 +00:00
cf3de34cb6 Removed "participating" option from settings UI
Temporarily hidden because of problems with changing defaults. We don't want to
cause network instability because we have thousands of new unstable routers
churning the network, and can't change their default settings.

In future, we should look at the user's network and settings, and try to have 
"smart" defaults based on a user-chosen profile, or based on net type - see
Connectivity.isConnectionFast().
2014-10-05 02:59:18 +00:00
81de79c725 Remove "enable I2CP" from settings UI
Android apps should use the client library to connect to I2P Android. The TCP    
interface would only be useful if using the Android device as a gateway or 
dedicated router, but it would need to listen globally, not locally. If someone
asks for that in future, we can add this back and improve the configuration.
2014-10-05 02:53:09 +00:00
228b6f1baa Comment out disabling UPnP until other changes are finished 2014-10-05 01:17:31 +00:00
8e395cfd4e Detect more config options requiring restart 2014-10-05 01:16:17 +00:00
6d6123df9b Inform user that changing uPnP setting will require a router restart 2014-10-03 01:09:32 +00:00
321a49156c Display news in TextView instead of WebView 2014-10-03 01:04:26 +00:00
e8a47e17fb Developer note in default router.config 2014-10-02 11:02:06 +00:00
9df27ea168 Move property file writing methods to Util 2014-10-02 04:06:17 +00:00
cb6b7c4f48 getRouterContext() helper function 2014-10-02 03:55:24 +00:00
ca623e6b18 New class for connectivity checks
Source: https://gist.github.com/str4d/22cac7a3f70bc227cdca
License: http://opensource.org/licenses/MIT
2014-10-02 02:02:26 +00:00
8b6e02136e Fix TextResourceDialog scrolling 2014-10-01 21:06:07 +00:00
6a0493a578 Updated release notes 2014-10-01 20:58:22 +00:00
bf2a437a82 Initial news space fixes 2014-10-01 20:47:02 +00:00
ac949e3f5e Don't use wide view for news webview 2014-10-01 10:50:09 +00:00
7483251393 Copy log entries to clipboard, tweak copy text 2014-10-01 10:31:28 +00:00
d690b7d861 Copy logs to clipboard 2014-10-01 10:23:36 +00:00
829695d690 Move licenses menu option to about dialog button 2014-10-01 03:31:18 +00:00
05c2dbd388 Javadoc improvements
This is the client library 0.2 commit
2014-09-29 23:54:28 +00:00
c8e1643326 New client library translations for ru 2014-09-29 23:53:48 +00:00
d72c936a0e Fix addressbook settings header Intent bug 2014-09-29 23:52:59 +00:00
06d4d7d10d New client lib translations for fr 2014-09-29 04:08:30 +00:00
b506b5e740 New translations for client library 2014-09-26 23:19:38 +00:00
2d65bd373c Configure Transifex for client library strings 2014-09-26 12:28:15 +00:00
7c869adf58 Client library helper class
Based on OrbotHelper from libnetcipher, with logic from i2p.i2p-bote.android
2014-09-26 12:27:51 +00:00
61a7566007 Client library 0.2
i2p.i2p tag: i2p-0.9.15
2014-09-25 05:00:28 +00:00
9d42901079 Updated translations 2014-09-25 04:57:47 +00:00
fb31818a3c 0.9.14.1-rc3 2014-08-23 01:24:52 +00:00
6355214b5f Updated translations 2014-08-23 01:01:37 +00:00
d5c0704477 Updated TODO 2014-08-23 00:45:18 +00:00
411131b8a6 Updated translations 2014-08-23 00:45:04 +00:00
10ed266d2c Clarify property inversion 2014-08-23 00:25:40 +00:00
bccfe03b5d Sync settings XML defaults with router.config defaults 2014-08-22 23:30:35 +00:00
a44ac8a45c Another domain name fix 2014-08-22 14:02:23 +00:00
5610752c6d Updated old domain names 2014-08-22 14:01:19 +00:00
7047913b45 Updated translations 2014-08-22 13:57:33 +00:00
a41aa79920 Explain to users that graphs need to be configured 2014-08-22 13:22:38 +00:00
4fcc1121b7 Padding tweak 2014-08-22 12:09:55 +00:00
514aa51224 Fixed images in peers WebView 2014-08-22 11:50:53 +00:00
0c46dc9bd0 Display auto-start setting in I2PTunnel details 2014-08-22 11:27:11 +00:00
4b7f951e32 0.9.14.1-rc2 2014-08-21 14:37:40 +00:00
a58a9d7540 Fixed "called on wrong thread" issue in browser 2014-08-21 14:33:39 +00:00
d3eaebd324 Temporary fix for a settings bug 2014-08-21 13:40:59 +00:00
37c366a528 Client library 0.1.1 2014-08-21 10:55:32 +00:00
8f6289984b 0.9.14.1-rc1 2014-08-20 11:19:33 +00:00
7629bb54ce Feature graphic for Google Play 2014-08-20 06:41:41 +00:00
ee7d227990 Fixed sq translation 2014-08-20 04:34:44 +00:00
4cc940c995 Updated TODO 2014-08-20 04:28:19 +00:00
2336eebdd0 New translations: sq U id 2014-08-20 04:02:50 +00:00
62035050c5 Padding tweak, prevent status headings flicker when opening app 2014-08-20 03:37:20 +00:00
6775d57c22 Rearranged nav drawer, only show graphs peers and netdb for advanced users 2014-08-20 03:02:29 +00:00
d3a1910b2e Return null on unknown State in Parcel 2014-08-20 02:37:27 +00:00
aa43d960dc Revert AndroidManifest.xml package name change
Changing this requires changing the import path of the generated R file, which
will unnecessarily affect most of the code. R is built in a package based on
the AndroidManifest.xml package name, whereas the final app package name is
overridden in app/build.gradle for free and donate versions.
2014-08-18 14:50:54 +00:00
2e3047274e Added legacy flavor with old app ID, fixed package names in manifests 2014-08-18 13:11:47 +00:00
a3cef11e08 Updated ignores 2014-08-17 23:13:23 +00:00
543fb51d76 Simplify adding client library to a local flatDir repo 2014-08-15 01:41:16 +00:00
4328db1908 Fixed source and javadoc jar creation 2014-08-07 13:25:02 +00:00
69fbb5dc92 Updated translations 2014-08-07 06:57:43 +00:00
0c5d8f8e9e Moved jbigi to client library, so clients get benefit 2014-08-06 22:45:06 +00:00
b88e150803 Final changes, x86 and MIPS libjbigi.so now build
Untested, we need to find x86 and MIPS devices to test with.
2014-08-06 10:52:22 +00:00
35fe44fc59 Initial x86 and MIPS support (not enabled, missing --host parameters) 2014-08-06 00:08:03 +00:00
464adb9e71 More non-ABI-specific script 2014-08-05 23:31:01 +00:00
66d370abeb Reorganized to run non-ABI-specific parts of script first 2014-08-05 23:25:10 +00:00
11aded07ca Refactored to pull out ABI-specific settings 2014-08-04 01:29:04 +00:00
5d0861e22e Only allow numbers in I2PTunnel wizard port fields (ticket #1331) 2014-08-03 21:18:05 +00:00
5778eb9d1c Updated translations 2014-07-26 02:44:45 +00:00
0e47bc5042 Updated translations 2014-07-19 23:26:55 +00:00
8f9a6922ad Bugfixes 2014-07-17 06:40:17 +00:00
05cc0634b7 Updated README 2014-07-17 06:31:04 +00:00
583666695c Improved Maven upload code, collected parameters in gradle.properties
maven-push.gradle source:
https://github.com/chrisbanes/gradle-mvn-push
2014-07-17 06:29:40 +00:00
e67ba59e51 Uploading to the Central Repository 2014-07-17 05:41:57 +00:00
ab619f904d Reverted to Enum for State, make it Parcelable
Talked with zzz and did more research, the overhead of Enum is minimal compared
to the benefits it provides.
2014-07-17 00:56:04 +00:00
f2f7418c8b Ignore old Enum state 2014-07-16 05:26:44 +00:00
23c55d50fb Remove I2CP port starting, clients can use domain sockets now 2014-07-16 04:20:40 +00:00
e0acb322a5 Lint 2014-07-16 03:47:17 +00:00
2a1427054d Use static int constants for State instead of Enums
Enums often require more than twice as much memory as static constants, and
should be strictly avoided on Android.
https://developer.android.com/training/articles/memory.html#Overhead

The advantage of this change is that client library users can directly compare
the status values they get from IRouterState to the constants, instead of
parsing a string representation of an Enum.
2014-07-16 01:28:49 +00:00
d878d2d8a4 Moved AIDL interfaces to client library 2014-07-16 01:25:19 +00:00
5386829edf Implemented I2CP connections over Android Local[Server]Sockets 2014-07-15 13:03:33 +00:00
5d74e7ffef Comments 2014-07-15 11:13:55 +00:00
332ec1e0ad Corrected client library package group 2014-07-15 11:08:39 +00:00
060262ee52 Separated client library version 2014-07-15 10:16:07 +00:00
c75fe55e56 Updated client library package 2014-07-15 08:56:41 +00:00
bccf5e0965 Bundle I2P libs in client AAR 2014-07-15 06:19:31 +00:00
6bd905a027 Fixed client building (thanks alkemist from Freenode#gradle) 2014-07-15 03:40:47 +00:00
fc0b393b14 Markdown fix 2014-07-15 01:52:59 +00:00
f2acde73fe Specify full version for Android Gradle plugin so script compiles when offline 2014-07-14 12:39:32 +00:00
77a7f5f603 Client library AndroidManifest.xml 2014-07-13 21:56:26 +00:00
d235da093f Build script for client library 2014-07-11 05:18:46 +00:00
795d3ab314 Updated translations 2014-07-07 02:58:49 +00:00
dd40931a23 Correct version code for current release
If we move to increment-by-one instead of calculating the version code, we need
to keep the code above the current highest generated code, so that F-Droid will
order new builds correctly above old builds.

If we decide to go back to calculating version codes later, it is possible - we
can increment the version code 2047 times before it will clash with the one for
0.9.14.
2014-07-07 02:43:16 +00:00
8b71e4fc2e Fixed CacheProvider authorities clash 2014-07-07 02:38:51 +00:00
ed61f0414e Padding tweak 2014-07-07 01:24:57 +00:00
06ef95c7ac Better layout padding for main view 2014-07-07 01:14:58 +00:00
2936bfc2b7 wrap long lines 2014-07-04 00:41:33 +00:00
9c655ffebf move from cat|grep|sed to awk 2014-07-04 00:41:17 +00:00
9d8fb684d2 Fixed building armeabi libjbigi.so 2014-07-03 22:36:20 +00:00
0d744e269c Static verification of remote dependencies using Gradle Witness
https://github.com/WhisperSystems/gradle-witness
2014-07-01 05:06:48 +00:00
36ffb6eda4 Default to ../i2p.i2p for copying I2P resources 2014-07-01 04:54:19 +00:00
df7ee4bd05 Updated README 2014-06-29 02:58:32 +00:00
d98d6abff3 Updated android gradle plugin version 2014-06-29 02:33:47 +00:00
260cc8a5a2 Updated gradle specified in wrapper to 1.12 2014-06-28 23:42:13 +00:00
zzz
a0a1df8093 Remove local copies of I2P Secure* utilities since we are
now on Java 6 (API 9).
2014-06-28 15:37:59 +00:00
e4110eb894 Signing instructions 2014-06-28 04:21:45 +00:00
d0264bf475 Addressbook license is copied over, should not be checked in 2014-06-28 02:04:12 +00:00
7ec8b0a592 Copy I2P resources 2014-06-28 02:03:39 +00:00
e3ecac8fec Rquirement: Android Support Repository 2014-06-28 00:40:02 +00:00
4fdc7940dd Version 2014-06-27 17:26:04 +00:00
9920ad34cd Renamed package to net.i2p.android, added free and donate flavors 2014-06-27 17:08:44 +00:00
8a7025038a New translations 2014-06-27 16:40:17 +00:00
a47c80df8c Updated Transifex config 2014-06-27 16:40:10 +00:00
a1a5aeaf6c Don't abort on lint errors 2014-06-27 16:34:01 +00:00
3a8eeabe3e Improved handling of signing keys 2014-06-27 16:29:43 +00:00
3e34bac295 Updated README 2014-06-27 16:19:36 +00:00
66d0dce40c Dropped Eclipse files 2014-06-27 15:55:20 +00:00
c8d3ee7aac Updated ignores 2014-06-27 15:51:55 +00:00
959537adc2 Reflowed AndroidManifest.xml, added missing parent meta-data for below API 16 2014-06-27 15:46:34 +00:00
7ca050fdf5 Cleaned up routerjars 2014-06-27 15:20:58 +00:00
07130abf23 Update min API to 9 (Gingerbread) 2014-06-27 15:15:15 +00:00
ba82d59b89 merge of '3a37cc3435714b0ed267c4f01159b77e1e59cd87'
and 'b1d84ce6fa493115c9ddc12b5a5a5a62c4dadee6'
2014-06-27 15:13:02 +00:00
8819dc5f30 Build scripts 2014-06-27 15:11:49 +00:00
zzz
a034b78dfd Update min API to 9 (Gingerbread).
API 8 (Froyo) is now less than 1% of active devices, and
these devices are generally too low-powered to run I2P effectively.
This allows us to move the remaining I2P jars to Java 6.
2014-06-27 13:52:29 +00:00
2dc56d57d4 Reorganized files 2014-06-27 11:42:20 +00:00
3aff1c4f75 Added gradle wrapper 2014-06-27 06:56:12 +00:00
55509adda6 Ask user to enable I2CP if app requests start with I2CP disabled 2014-06-20 02:33:22 +00:00
zzz
19def413c1 Make shared clients (IRC) delay-open since the HTTP proxy
is not a shared client any more.
2014-06-14 11:31:27 +00:00
63a0e2117f Copy i2ptunnel.config from resource for new installs 2014-06-12 23:17:05 +00:00
21e0b2a667 Listen for START_I2P action 2014-06-03 23:49:10 +00:00
6ce15e27de Updated translations 2014-06-02 22:08:00 +00:00
7a0a56373d Fixed remote usage of IRouterStatus 2014-06-02 21:40:23 +00:00
37da05ca98 Pull in changes to defaults while maintaining user settings 2014-06-01 02:50:15 +00:00
f003bbbfa4 Fixed settings parsing 2014-05-31 23:56:30 +00:00
zzz
a6978bb161 fix net status text 2014-04-27 18:59:34 +00:00
b1ec76de5a Missing change in landscape layout 2014-04-18 11:28:36 +00:00
dc58796c97 Updated TODO 2014-04-09 02:03:38 +00:00
c7075c3fc4 Pull API level from routerjars/AndroidManifest.xml.in 2014-04-09 02:03:25 +00:00
8d4f1b174d Check routerjars/local.properties for NDK dir (ticket #1032) 2014-04-09 01:52:39 +00:00
471 changed files with 12293 additions and 3279 deletions

View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
<classpathentry combineaccessrules="false" kind="src" path="/i2p_sdk"/>
<classpathentry combineaccessrules="false" kind="src" path="/i2p_router"/>
<classpathentry combineaccessrules="false" kind="src" path="/i2ptunnel"/>
<classpathentry combineaccessrules="false" kind="src" path="/BOB"/>
<classpathentry combineaccessrules="false" kind="src" path="/addressbook"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>

View File

@ -24,19 +24,28 @@ _jsp\.java$
/classes/
# Android-specific ignores
^bin
^gen
^routerjars/bin
^routerjars/gen
^routerjars/lib
AndroidManifest.xml
lint.xml
^routerjars/libs
local.properties
signing.properties
#IntelliJ IDEA
^.idea
.*.iml
.*.ipr
.*.iws
#Gradle
^.gradle
build
# I2P-specific ignores
^res/raw/blocklist_txt
^res/raw/certificates_zip
^res/raw/hosts_txt
^res/raw/license_
^app/src/main/res/drawable/i2plogo.png
^app/src/main/res/raw/blocklist_txt
^app/src/main/res/raw/hosts_txt
^app/src/main/res/raw/license_
^app/src/main/res/raw/certificates_zip
^app/src/main/assets/themes/console/images
^app/src/main/assets/themes/console/light/console.css
^app/src/main/assets/themes/console/light/images/header.png
^scripts/build.number
^scripts/version.properties

View File

@ -1,33 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>I2P_Android</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@ -3,8 +3,15 @@ host = https://www.transifex.com
lang_map = pt_BR: pt-rBR, ru_RU: ru, sv_SE: sv, tr_TR: tr, zh_CN: zh
[I2P.android]
file_filter = res/values-<lang>/strings.xml
source_file = res/values/strings.xml
file_filter = app/src/main/res/values-<lang>/strings.xml
source_file = app/src/main/res/values/strings.xml
source_lang = en
type = ANDROID
minimum_perc = 50
[I2P.android_lib_client]
file_filter = client/src/main/res/values-<lang>/strings.xml
source_file = client/src/main/res/values/strings.xml
source_lang = en
type = ANDROID
minimum_perc = 50

View File

@ -1,119 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.i2p.android.router"
android:versionCode="0"
android:versionName="0.0.0-0_b0-API8"
android:installLocation="auto"
>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-sdk android:minSdkVersion="8"
android:targetSdkVersion="19" />
<application android:label="@string/app_name"
android:theme="@style/Theme.AppCompat"
android:icon="@drawable/ic_launcher_itoopie" >
<service android:name=".service.RouterService"
android:label="@string/app_name"
android:icon="@drawable/ic_launcher_itoopie" />
<provider android:name=".provider.CacheProvider"
android:authorities="net.i2p.android.router" />
<activity android:name=".MainActivity"
android:label="@string/app_name"
android:icon="@drawable/ic_launcher_itoopie"
android:launchMode="singleTop" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".NewsActivity"
android:label="I2P News"
android:configChanges="orientation|keyboardHidden" >
</activity>
<activity android:name=".HelpActivity"
android:label="Help"
android:parentActivityName=".MainActivity" >
</activity>
<activity android:name=".LicenseActivity"
android:label="I2P License Information"
android:parentActivityName=".HelpActivity" >
</activity>
<activity android:name=".web.WebActivity"
android:label="I2P Web Browser"
android:configChanges="orientation|keyboardHidden" >
<!-- Disabled, this browser is not very secure
Temporarily enabled until an alternative browser is ready -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:host="*.i2p" android:scheme="http" />
</intent-filter>
</activity>
<activity android:name=".SettingsActivity"
android:label="I2P Settings"
android:parentActivityName=".MainActivity" >
</activity>
<activity android:name=".addressbook.AddressbookSettingsActivity"
android:label="I2P Addressbook Settings"
android:parentActivityName=".addressbook.AddressbookActivity"
android:launchMode="singleTop" >
</activity>
<activity android:name=".addressbook.AddressbookActivity"
android: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 android:name=".addressbook.AddressbookAddWizardActivity"
android:label="Add new Destination"
android:parentActivityName=".addressbook.AddressbookActivity" >
</activity>
<activity android:name="net.i2p.android.i2ptunnel.TunnelListActivity"
android:label="I2PTunnel"
android:launchMode="singleTop" >
</activity>
<activity android:name="net.i2p.android.i2ptunnel.TunnelDetailActivity"
android:label="I2PTunnel"
android:parentActivityName="net.i2p.android.i2ptunnel.TunnelListActivity" >
</activity>
<activity android:name="net.i2p.android.i2ptunnel.TunnelWizardActivity"
android:label="Tunnel Creation Wizard"
android:parentActivityName="net.i2p.android.i2ptunnel.TunnelListActivity" >
</activity>
<activity android:name=".log.LogActivity"
android:label="I2P Logs"
android:parentActivityName=".MainActivity" >
</activity>
<activity android:name=".log.LogDetailActivity"
android:label="Log Entry"
android:parentActivityName=".log.LogActivity" >
</activity>
<activity android:name=".stats.RateGraphActivity"
android:label="Rate Graph"
android:parentActivityName=".MainActivity" >
</activity>
<activity android:name=".stats.PeersActivity"
android:label="I2P Peers and Transport Status"
android:configChanges="orientation|keyboardHidden"
android:launchMode="singleTop" >
</activity>
<activity android:name=".netdb.NetDbActivity"
android:label="NetDB"
android:parentActivityName=".MainActivity" >
</activity>
<activity android:name=".netdb.NetDbDetailActivity"
android:label="NetDB Detail"
android:parentActivityName=".netdb.NetDbActivity" >
</activity>
</application>
</manifest>

27
CHANGELOG Normal file
View File

@ -0,0 +1,27 @@
0.9.18
* I2P can start automatically when phone boots (configure in Setting)
* Updated browser configuration guides for Orfox and Firefox
* Tunnels for postman's mail server added to defaults (for new installs)
* Settings options for configuring UDP and TCP ports
* Bug fixes and translation updates
0.9.17.1 /2014-12-14 / cd8bb5e3ac4238efac12179c78c4fa517fcaabec
* Fixed crashes in addressbook and netDb status page
* Fixed crash when opening an IRC client tunnel
* Updated translations
0.9.17 / 2014-12-01 / bcf947f433876f643e0f6dff81aac88848b797d3
* Migrated to the new Material design from Android Lollipop
* Added a browser configuration guide
* Improved the help screen
* Upgraded the I2P router to 0.9.17
* Various bug fixes and translation updates
0.9.15.1 / 2014-10-16 / 9cc4e80134cf9becb3838ed3cc78e0bed1363165
* Fixed a configuration bug
0.9.15 / 2014-10-16 / 9b51f78b791c28a580d57f1a5019c94493de6231
* Upgraded the I2P router to 0.9.15
* Added a help screen with some basic information
* Logs can now be copied to the clipboard
* Various user interface improvements and fixes

151
README.md Normal file
View File

@ -0,0 +1,151 @@
# I2P Android
## Build process
### Dependencies:
- Java SDK (preferably Oracle/Sun or OpenJDK) 1.6.0 or higher
- Apache Ant 1.8.0 or higher
- I2P source
- Android SDK for API 21
- Android Build Tools 21.0.2
- Android Support Repository
- Gradle 2.1
### Gradle
The build system is based on Gradle. There are several methods for setting Gradle up:
* It can be downloaded from [the Gradle website](http://www.gradle.org/downloads).
* Most distributions will have Gradle packages. Be careful to check the
provided version; Debian and Ubuntu have old versions in their main
repositories. There is a [PPA](https://launchpad.net/~cwchien/+archive/gradle)
for Ubuntu with the latest version of Gradle.
* A Gradle wrapper is provided in the codebase. It takes all the same commands
as the regular `gradle` command. The first time that any command is run, it
will automatically download, cache and use the correct version of Gradle.
This is the simplest way to get started with the codebase. To use it, replace
`gradle` with `./gradlew` (or `./gradlew.bat` on Windows) in the commands
below.
Gradle will pull dependencies over the clearnet by default. To send all Gradle
connections from your user over Tor, create a `gradle.properties` file in
`~/.gradle/` containing:
```
systemProp.socksProxyHost=localhost
systemProp.socksProxyPort=9150
```
### Preparation
1. Download the Android SDK. The simplest method is to download [Android Studio](https://developer.android.com/sdk/installing/studio.html).
* If you are using an existing Android SDK, install the Android Support
Repository via the SDK Manager.
2. Check out the [`i2p.i2p`](https://github.com/i2p/i2p.i2p) repository.
3. Create a `local.properties` file in `i2p.android.base/routerjars` containing:
```
i2psrc=/path/to/i2p.i2p
```
### Building from the command line
1. Create a `local.properties` file in `i2p.android.base` containing:
```
sdk.dir=/path/to/android-studio/sdk
```
2. `gradle assembleDebug`
3. The APK will be placed in `i2p.android.base/app/build/outputs/apk`.
### Building with Android Studio
1. Import `i2p.android.base` into Android Studio. (This creates the `local.properties` file automatically).
2. Build and run the app (`Shift+F10`).
### Signing release builds
1. Create a `signing.properties` file in `i2p.android.base` containing:
```
STORE_FILE=/path/to/android.keystore
STORE_PASSWORD=store.password
KEY_ALIAS=key.alias
KEY_PASSWORD=key.password
```
2. `gradle assembleRelease`
## Client library
### "Uploading" to the local Maven repository (to use a local build of the library in a project)
1. `gradle :client:installArchives`
2. Add the local Maven repository to your project. For Gradle projects, add the following above any existing repositories (so it is checked first):
```
repositories {
mavenLocal()
}
```
### Uploading to Maven Central via Sonatype OSSRH
1. Add the following lines to your `~/.gradle/gradle.properties` (filling in the blanks):
```
signing.keyId=
signing.password=
signing.secretKeyRingFile=/path/to/secring.gpg
ossrhUsername=
ossrhPassword=
```
2. `gradle :client:uploadArchives`
### Commands from the old build instructions that might be useful
```
# Create the android 4.4 (API 19) virtual device
# (don't make a custom hardware profile)
../android-sdk-linux/tools/android create avd --name i2p --target android-19
# then run the emulator:
# This may take a LONG time the first time (half an hour or more)...
# Run the debugger to ensure it is making progress
# -no-boot-anim for faster boot
# -dns-server 8.8.8.8 if the router can't reseed
# ../android-sdk-linux/tools/emulator -avd i2p -no-boot-anim -dns-server 8.8.8.8 &
../android-sdk-linux/tools/emulator -avd i2p &
# or to talk to a real device in debug mode:
# You have to do this if you get a permission error -
# Stop ddms, unplug the device, do the following,
# then plug in the device, then start ddms
adb kill-server
sudo adb start-server
adb devices
# Anyway, with I2P installed, click on the I2P icon on your device and enjoy!
#other helpful commands
../android-sdk-linux/platform-tools/adb shell
../android-sdk-linux/platform-tools/adb pull /some/file/on/emulator some-local-dir/
# copy the Dev Tools app from the emulator to your device
adb -e pull /system/app/Development.apk ./Development.apk
adb -d install Development.apk
# reinstall an existing apk onto the emulator
adb -e install -r bin/I2PAndroid-debug.apk
```

View File

@ -1,116 +0,0 @@
These instructions are for a recent Android SDK (Rev 20 or better) on Linux.
Windows building is not currently supported.
These instructions were last updated for SDK Tools Version 20 with
SDK Platform-tools Version 12 from updates.
The i2p source must be installed in ../i2p.i2p,
or else add i2psrc=/path/to/source in the local.properties file.
=====================
Dependencies:
- Java SDK (preferably Oracle/Sun or OpenJDK) 1.6.0 or higher
- Apache Ant 1.8.0 or higher
- I2P source in ../i2p.i2p
- Android SDK (tested with Rev 22.3 and platform-tools version 19)
=====================
Instructions:
# Download the SDK from http://developer.android.com/sdk/index.html
# Unzip the android SDK in ../
# So then the android tools will be in ../android-sdk-linux/tools/
#
# Run the GUI updater, which you must do to get an SDK Platform:
../android-sdk-linux/tools/android &
# now go to the available packages tab, check the box and click refresh,
# and download an SDK Platform
# Since I2P is targeted at 4.4 (API 19)
# download at least that one. Otherwise you must change the
# target in project.properties from android-19 to andriod-x
# where x is the API version.
# I2P is configured to run on 2.2 (API 8) or higher using the
# Android Support Library, so download that as well
# (it's under "Extras").
# update the compatibility project
../android-sdk-linux/tools/android update lib-project -p ../android-sdk-linux/extras/android/support/v7/appcompat -t android-19
# To run the debugger (ddms) you also need to download the
# "Android SDK Platform-Tools" package from the GUI updater.
# create a file local.properties with the following line (without the leading # of course),
# do NOT use a relative path
# sdk.dir=/path/to/your/android-sdk-linux
# Copy this file to the routerjars/ directory, it is needed in both places.
# If your SDK is not in ../android-sdk-linux/ then you must
# override the location of the Android Support Library. Add
# the following line to local.properties
# do NOT use an absolute path
# android.library.reference.2=path/to/your/android-sdk-linux/extras/android/support/v7/appcompat
# Don't add it to the local.properties in the routerjars/ directory.
# DO NOT create a new project or anything. It's all set up right here for you.
# Create the android 4.4 (API 19) virtual device
# (don't make a custom hardware profile)
../android-sdk-linux/tools/android create avd --name i2p --target android-19
# then run the emulator:
# This may take a LONG time the first time (half an hour or more)...
# Run the debugger to ensure it is making progress
# -no-boot-anim for faster boot
# -dns-server 8.8.8.8 if the router can't reseed
# ../android-sdk-linux/tools/emulator -avd i2p -no-boot-anim -dns-server 8.8.8.8 &
../android-sdk-linux/tools/emulator -avd i2p &
# or to talk to a real device in debug mode:
# You have to do this if you get a permission error -
# Stop ddms, unplug the device, do the following,
# then plug in the device, then start ddms
adb kill-server
sudo adb start-server
adb devices
# then wait a couple minutes until the emulator or device is up
# compile and install for a release
ant release
ant installr
# or compile and install for a debug version
ant debug
ant installd
# then run the debugger
../android-sdk-linux/tools/ddms &
# to rebuild and reinstall to emulator or device:
ant clean
# then do which ever from the above compile and install choices.
# to uninstall
ant uninstall
# or use your device's menu.
# Other ant tagets are available, just type
ant
# Anyway, with I2P installed, click on the I2P icon on your device and enjoy!
#other helpful commands
../android-sdk-linux/platform-tools/adb shell
../android-sdk-linux/platform-tools/adb pull /some/file/on/emulator some-local-dir/
# copy the Dev Tools app from the emulator to your device
adb -e pull /system/app/Development.apk ./Development.apk
adb -d install Development.apk
# reinstall an existing apk onto the emulator
adb -e install -r bin/I2PAndroid-debug.apk

46
TODO
View File

@ -1,10 +1,35 @@
# Required for release
# 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
<zzz> in the tunnel create wizard:
<zzz> 'this could be the full base 64 destination key, or an i2p url from your address book"
<zzz> 'host name' better than 'URL'. Technically speaking, a host name is not a URL
<zzz> hmm would be nice if they could be shared-client or have an option
<zzz> was setting up email tunnels
- I2PTunnel details
<zzz> on the i2ptunnel details, it lists both a 'target' and 'access point' for clients, with the same info
<zzz> generally we use 'target' for servers and 'access point' for clients, never both
- Browser
<zzzccc> Bug report: i2p browser treats 302 as an error
<zzzccc> Bug 2: rotate screen in i2p browser seems to go back one page
# Short-term
- Graceful shutdown option
<zzzccc> Request: graceful shutdown
- 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
- I2PTunnel
- Show all messages somewhere
- Improve detail page, expose advanced settings
- Add edit page
- Progress feedback for addressbook subscriptions reload
- Display release notes directly on new router version
- Text content
- Move help content from release notes to help page
- Rewrite release notes to be release-specific
- Fill out help page
- Fill out help pages
- Rewrite release notes to be release-specific
- Fix release notes UI, either make back button use clear or add buttons
- NetDB tablet view fixes
- Refresh detail fragment when changing tab
@ -12,20 +37,19 @@
- Create nav history when viewing RI from LS
- Include GeoIP db for country info
- Maybe change router-off mechanic for various pages? Enable as they become available?
- Check default configs in res/raw/ against upstream defaults
# Short-term
# Medium-term
- Network profiles
- User selects profile in settings
- Change network participation etc. based on profile
- Also look at connection type: Connectivity.isConnectionFast()
- Expose log level overrides
- Improve graphs
- Show time on bottom axis
- Show fixed x range, not only available data
- Think about pan/zoom
- How to persist data across restarts?
- I2PTunnel
- Show all messages somewhere
- Improve detail page, expose advanced settings
- Add edit page
# Long-term

View File

@ -1,4 +0,0 @@
application-package=net.i2p.router
key.store=${user.home}/.android/${application-package}.keystore
key.alias=${application-package}
key.store.password=android

186
app/build.gradle Normal file
View File

@ -0,0 +1,186 @@
apply plugin: 'com.android.application'
apply plugin: 'witness'
android {
compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION)
buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION
defaultConfig {
versionCode 4745225
versionName '0.9.17.1'
minSdkVersion 9
targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION)
}
signingConfigs {
release
}
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
debug {
applicationIdSuffix '.debug'
versionNameSuffix '-DEBUG'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
lintOptions {
abortOnError false
}
productFlavors {
free {
applicationId 'net.i2p.android'
}
donate {
applicationId 'net.i2p.android.donate'
}
legacy {
applicationId 'net.i2p.android.router'
}
}
}
dependencies {
compile project(':routerjars')
compile project(':client')
compile 'com.android.support:support-v4:21.0.3'
compile 'com.android.support:appcompat-v7:21.0.3'
compile 'com.android.support:recyclerview-v7:21.0.3'
compile 'net.i2p.android.ext:floatingactionbutton:1.8.0'
compile files('libs/androidplot-core-0.6.1.jar')
}
dependencyVerification {
verify = [
'com.android.support:support-v4:703572d3015a088cc5604b7e38885af3d307c829d0c5ceaf8654ff41c71cd160',
'com.android.support:appcompat-v7:5dbeb5316d0a6027d646ae552804c3baa5e3bd53f7f33db50904d51505c8a0e5',
'com.android.support:recyclerview-v7:e525ad3f33c84bb12b73d2dc975b55364a53f0f2d0697e043efba59ba73e22d2',
'net.i2p.android.ext:floatingactionbutton:a20d1f0cae15f8965b81486ba31245937968ae6ee5fa6e8a3ea21d7f6c6243ab',
]
}
project.ext.i2pbase = '../i2p.i2p'
def Properties props = new Properties()
def propFile = new File(project(':routerjars').projectDir, 'local.properties')
if (propFile.canRead()) {
props.load(new FileInputStream(propFile))
if (props != null &&
props.containsKey('i2psrc')) {
i2pbase = props['i2psrc']
} else {
println 'local.properties found but some entries are missing'
}
} else {
println 'local.properties not found'
}
task certificatesZip(type: Zip) {
archiveName = 'certificates_zip'
from files('' + i2pbase + '/installer/resources/certificates')
}
task copyI2PResources(type: Copy) {
// Force this to always run: Copy only detects source changes, not if missing in destination
outputs.upToDateWhen { false }
into 'src/main/res'
into('drawable') {
from file(i2pbase + '/installer/resources/themes/console/images/i2plogo.png')
}
into('raw') {
from(i2pbase + '/installer/resources/blocklist.txt') { rename { 'blocklist_txt' } }
from(i2pbase + '/installer/resources/hosts.txt') { rename { 'hosts_txt' } }
from('../LICENSE.txt') { rename { 'license_app_txt' } }
from('../licenses/LICENSE-Apache2.0.txt') { rename { 'license_apache20_txt' } }
from(i2pbase + '/licenses') {
include { elem ->
elem.name in [
'LICENSE-ElGamalDSA.txt',
'LICENSE-SHA256.txt',
'LICENSE-BSD.txt',
'LICENSE-SNTP.txt',
'LICENSE-LGPLv2.1.txt',
'LICENSE-InstallCert.txt',
'LICENSE-BlockFile.txt',
'LICENSE-GPLv2.txt',
'LICENSE-GPLv3.txt',
'LICENSE-LGPLv3.txt',
'LICENSE-FatCowIcons.txt',
'LICENSE-Addressbook.txt',
]
}
rename { String name ->
String part = name.substring(8, name.lastIndexOf('.txt'))
String.format('license_%s_txt',
part.toLowerCase(Locale.US).replace('.', '_'))
}
}
from certificatesZip
}
}
// For peers WebView
task copyI2PAssets(type: Copy) {
// Force this to always run: Copy only detects source changes, not if missing in destination
outputs.upToDateWhen { false }
into 'src/main/assets/themes/console'
into('images') {
from file(i2pbase + '/installer/resources/themes/console/images/i2plogo.png')
from file(i2pbase + '/installer/resources/themes/console/images/inbound.png')
from file(i2pbase + '/installer/resources/themes/console/images/outbound.png')
}
into('light') {
from file(i2pbase + '/installer/resources/themes/console/light/console.css')
}
into('light/images') {
from file(i2pbase + '/installer/resources/themes/console/light/images/header.png')
}
}
preBuild.dependsOn copyI2PResources
preBuild.dependsOn copyI2PAssets
task cleanI2PResources(type: Delete) {
delete file('src/main/res/drawable/i2plogo.png')
delete fileTree('src/main/res/raw') {
include 'blocklist_txt'
include 'hosts_txt'
include 'license_*'
include 'certificates_zip'
}
}
task cleanI2PAssets(type: Delete) {
delete fileTree('src/main/assets/themes/console/images')
delete file('src/main/assets/themes/console/light/console.css')
delete file('src/main/assets/themes/console/light/images/header.png')
}
clean.dependsOn cleanI2PResources
clean.dependsOn cleanI2PAssets
props = new Properties()
propFile = new File(project.rootDir, 'signing.properties')
if (propFile.canRead()) {
props.load(new FileInputStream(propFile))
if (props != null &&
props.containsKey('STORE_FILE') &&
props.containsKey('STORE_PASSWORD') &&
props.containsKey('KEY_ALIAS') &&
props.containsKey('KEY_PASSWORD')) {
android.signingConfigs.release.storeFile = file(props['STORE_FILE'])
android.signingConfigs.release.storePassword = props['STORE_PASSWORD']
android.signingConfigs.release.keyAlias = props['KEY_ALIAS']
android.signingConfigs.release.keyPassword = props['KEY_PASSWORD']
} else {
println 'signing.properties found but some entries are missing'
android.buildTypes.release.signingConfig = null
}
} else {
println 'signing.properties not found'
android.buildTypes.release.signingConfig = null
}

Binary file not shown.

View File

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

View File

@ -0,0 +1,37 @@
<?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>

View File

@ -0,0 +1,32 @@
<?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>

View File

@ -0,0 +1,37 @@
<?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.donate"
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>

View File

@ -0,0 +1,32 @@
<?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.donate"
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.donate"
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.donate"
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.donate"
android:targetClass="net.i2p.android.router.addressbook.AddressbookSettingsActivity" />
</Preference>
<Preference android:title="@string/settings_label_advanced">
<intent
android:targetPackage="net.i2p.android.donate"
android:targetClass="net.i2p.android.router.SettingsActivity"
android:action="net.i2p.android.router.PREFS_ADVANCED" />
</Preference>
</PreferenceScreen>

View File

@ -0,0 +1,212 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.i2p.android.router"
android:installLocation="auto">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:icon="@drawable/ic_launcher_itoopie"
android:label="@string/app_name"
android:theme="@style/Theme.I2P">
<service
android:name=".service.RouterService"
android:icon="@drawable/ic_launcher_itoopie"
android:label="@string/app_name">
<intent-filter>
<action android:name="net.i2p.android.router.service.IRouterState" />
</intent-filter>
</service>
<provider
android:name=".provider.CacheProvider"
android:authorities="${applicationId}.provider" />
<receiver android:name=".receiver.OnBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<activity
android:name=".MainActivity"
android:icon="@drawable/ic_launcher_itoopie"
android:label="@string/app_name"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="net.i2p.android.router.START_I2P" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name=".NewsActivity"
android:configChanges="orientation|keyboardHidden"
android:label="I2P News"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="net.i2p.android.router.MainActivity" />
</activity>
<activity
android:name="net.i2p.android.help.HelpActivity"
android:label="Help"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="net.i2p.android.router.MainActivity" />
</activity>
<activity
android:name="net.i2p.android.help.BrowserConfigActivity"
android:label="Browser Configuration"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="net.i2p.android.router.MainActivity" />
</activity>
<activity
android:name=".LicenseActivity"
android:label="I2P License Information"
android:parentActivityName="net.i2p.android.help.HelpActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="net.i2p.android.help.HelpActivity" />
</activity>
<activity
android:name=".web.WebActivity"
android:configChanges="orientation|keyboardHidden"
android:label="I2P Web Browser">
<!-- Disabled, this browser is not very secure
Temporarily enabled until an alternative browser is ready -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="*.i2p"
android:scheme="http" />
</intent-filter>
</activity>
<activity
android:name=".SettingsActivity"
android:label="I2P Settings"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="net.i2p.android.router.MainActivity" />
</activity>
<activity
android:name=".addressbook.AddressbookSettingsActivity"
android:label="Addressbook Settings"
android:launchMode="singleTop"
android:parentActivityName=".addressbook.AddressbookActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="net.i2p.android.router.addressbook.AddressbookActivity" />
</activity>
<activity
android:name=".addressbook.AddressbookActivity"
android: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
android:name=".addressbook.AddressbookAddWizardActivity"
android:label="Add new Destination"
android:parentActivityName=".addressbook.AddressbookActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="net.i2p.android.router.addressbook.AddressbookActivity" />
</activity>
<activity
android:name="net.i2p.android.i2ptunnel.TunnelListActivity"
android: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
android:name="net.i2p.android.i2ptunnel.TunnelDetailActivity"
android:label="I2PTunnel"
android:parentActivityName="net.i2p.android.i2ptunnel.TunnelListActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="net.i2p.android.router.i2ptunnel.TunnelListActivity" />
</activity>
<activity
android:name="net.i2p.android.i2ptunnel.TunnelWizardActivity"
android:label="Tunnel Creation Wizard"
android:parentActivityName="net.i2p.android.i2ptunnel.TunnelListActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="net.i2p.android.router.i2ptunnel.TunnelListActivity" />
</activity>
<activity
android:name=".log.LogActivity"
android:label="I2P Logs"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="net.i2p.android.router.MainActivity" />
</activity>
<activity
android:name=".log.LogDetailActivity"
android:label="Log Entry"
android:parentActivityName=".log.LogActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="net.i2p.android.router.log.LogActivity" />
</activity>
<activity
android:name=".stats.RateGraphActivity"
android:label="Rate Graph"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="net.i2p.android.router.MainActivity" />
</activity>
<activity
android:name=".stats.PeersActivity"
android:configChanges="orientation|keyboardHidden"
android:label="I2P Peers and Transport Status"
android:launchMode="singleTop"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="net.i2p.android.router.MainActivity" />
</activity>
<activity
android:name=".netdb.NetDbActivity"
android:label="NetDB"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="net.i2p.android.router.MainActivity" />
</activity>
<activity
android:name=".netdb.NetDbDetailActivity"
android:label="NetDB Detail"
android:parentActivityName=".netdb.NetDbActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="net.i2p.android.router.netdb.NetDbActivity" />
</activity>
</application>
</manifest>

View File

@ -0,0 +1,82 @@
package net.i2p.android.help;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
public class Browser implements Comparable<Browser> {
public final String packageName;
public final CharSequence label;
public final Drawable icon;
public final boolean isInstalled;
public final boolean isKnown;
public final boolean isSupported;
public final boolean isRecommended;
/**
* A browser that we don't know about.
*
* @param pm the PackageManager used to find the browser
* @param browser the browser
*/
public Browser(PackageManager pm, ResolveInfo browser) {
this(
browser.activityInfo.packageName,
browser.loadLabel(pm),
browser.loadIcon(pm),
true, false, false, false
);
}
/**
* A browser that we know about.
*
* @param pm the PackageManager used to find the browser
* @param browser the browser
* @param supported can this browser be used with I2P?
*/
public Browser(PackageManager pm, ResolveInfo browser, boolean supported, boolean recommended) {
this(
browser.activityInfo.packageName,
browser.loadLabel(pm),
browser.loadIcon(pm),
true, true, supported, recommended
);
}
public Browser(String pn, CharSequence l, Drawable ic, boolean i, boolean k, boolean s, boolean r) {
packageName = pn;
label = l;
icon = ic;
isInstalled = i;
isKnown = k;
isSupported = s;
isRecommended = r;
}
@Override
public int compareTo(Browser browser) {
// Sort order: supported -> unknown -> unsupported
int a = getOrder(this);
int b = getOrder(browser);
if (a < b)
return -1;
else if (a > b)
return 1;
return label.toString().compareTo(browser.label.toString());
}
private static int getOrder(Browser browser) {
if (browser.isKnown) {
if (browser.isRecommended)
return 0;
else if (browser.isSupported)
return 1;
else
return 3;
} else
return 2;
}
}

View File

@ -0,0 +1,117 @@
package net.i2p.android.help;
import android.content.Context;
import android.content.Intent;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.net.Uri;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import net.i2p.android.router.R;
public class BrowserAdapter extends RecyclerView.Adapter<BrowserAdapter.ViewHolder> {
private Context mCtx;
private Browser[] mBrowsers;
private OnBrowserSelectedListener mListener;
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
public static class ViewHolder extends RecyclerView.ViewHolder {
public ImageView mIcon;
public TextView mLabel;
public ImageView mStatus;
public ViewHolder(View v) {
super(v);
mIcon = (ImageView) v.findViewById(R.id.browser_icon);
mLabel = (TextView) v.findViewById(R.id.browser_label);
mStatus = (ImageView) v.findViewById(R.id.browser_status_icon);
}
}
public static interface OnBrowserSelectedListener {
public void onBrowserSelected(Browser browser);
}
public BrowserAdapter(Context ctx, OnBrowserSelectedListener listener) {
mCtx = ctx;
mListener = listener;
}
public void setBrowsers(Browser[] browsers) {
mBrowsers = browsers;
notifyDataSetChanged();
}
public void clear() {
mBrowsers = null;
notifyDataSetChanged();
}
// Create new views (invoked by the layout manager)
@Override
public BrowserAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.listitem_browser, parent, false);
return new ViewHolder(v);
}
// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
final Browser browser = mBrowsers[position];
holder.mIcon.setImageDrawable(browser.icon);
holder.mLabel.setText(browser.label);
if (browser.isKnown) {
if (browser.isRecommended && browser.isInstalled) {
holder.mStatus.setImageDrawable(
mCtx.getResources().getDrawable(R.drawable.ic_stars_white_24dp));
holder.mStatus.setVisibility(View.VISIBLE);
} else if (browser.isSupported && !browser.isInstalled) {
holder.mStatus.setImageDrawable(
mCtx.getResources().getDrawable(R.drawable.ic_shop_white_24dp));
holder.mStatus.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String uriMarket = "market://search?q=pname:" + browser.packageName;
Uri uri = Uri.parse(uriMarket);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
mCtx.startActivity(intent);
}
});
holder.mStatus.setVisibility(View.VISIBLE);
} else if (!browser.isSupported) {
// Make the icon gray-scale to show it is unsupported
ColorMatrix matrix = new ColorMatrix();
matrix.setSaturation(0);
ColorMatrixColorFilter filter = new ColorMatrixColorFilter(matrix);
holder.mIcon.setColorFilter(filter);
holder.mLabel.setTextColor(
mCtx.getResources().getColor(R.color.primary_text_disabled_material_dark));
}
}
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mListener.onBrowserSelected(browser);
}
});
}
// Return the size of the dataset (invoked by the layout manager)
@Override
public int getItemCount() {
if (mBrowsers != null)
return mBrowsers.length;
return 0;
}
}

View File

@ -0,0 +1,94 @@
package net.i2p.android.help;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import net.i2p.android.router.R;
import java.lang.reflect.Field;
public class BrowserConfigActivity extends ActionBarActivity implements
BrowserAdapter.OnBrowserSelectedListener {
/**
* Whether or not the activity is in two-pane mode, i.e. running on a tablet
* device.
*/
private boolean mTwoPane;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_help);
// Set the action bar
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
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) {
getSupportFragmentManager().beginTransaction()
.add(R.id.main_fragment, new BrowserListFragment())
.commit();
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
// BrowserAdapter.OnBrowserSelected
@Override
public void onBrowserSelected(Browser browser) {
int file;
if (browser.isKnown) {
if (browser.isSupported) {
// Check for embedded browser
if (browser.packageName.startsWith("net.i2p.android"))
file = R.raw.help_embedded_browser;
else {
// Load the configuration guide for this browser
try {
String name = "help_" + browser.packageName.replace('.', '_');
Class res = R.raw.class;
Field field = res.getField(name);
file = field.getInt(null);
} catch (Exception e) {
file = R.raw.help_unknown_browser;
}
}
} else
file = R.raw.help_unsupported_browser;
} else
file = R.raw.help_unknown_browser;
HelpHtmlFragment configFrag = HelpHtmlFragment.newInstance(file);
if (mTwoPane) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.detail_fragment, configFrag)
.commit();
} else {
getSupportFragmentManager().beginTransaction()
.replace(R.id.main_fragment, configFrag)
.addToBackStack("config" + browser.packageName)
.commit();
}
}
}

View File

@ -0,0 +1,187 @@
package net.i2p.android.help;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import net.i2p.android.router.R;
import net.i2p.android.router.util.BetterAsyncTaskLoader;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class BrowserListFragment extends Fragment implements
LoaderManager.LoaderCallbacks<List<Browser>> {
private static final int BROWSER_LOADER_ID = 1;
private BrowserAdapter.OnBrowserSelectedListener mCallback;
private BrowserAdapter mAdapter;
@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 = (BrowserAdapter.OnBrowserSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnBrowserSelectedListener");
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_help_browsers, container, false);
RecyclerView mRecyclerView = (RecyclerView) v.findViewById(R.id.browser_list);
// use a linear layout manager
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new BrowserAdapter(getActivity(), mCallback);
mRecyclerView.setAdapter(mAdapter);
return v;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getLoaderManager().initLoader(BROWSER_LOADER_ID, null, this);
}
// LoaderManager.LoaderCallbacks<List<Browser>>
@Override
public Loader<List<Browser>> onCreateLoader(int id, Bundle args) {
return new BrowserLoader(getActivity());
}
public static class BrowserLoader extends BetterAsyncTaskLoader<List<Browser>> {
private List<String> recommended;
private List<String> recommendedLabels;
private List<String> supported;
private List<String> supportedLabels;
private List<String> unsupported;
public BrowserLoader(Context context) {
super(context);
recommended = Arrays.asList(
getContext().getResources().getStringArray(R.array.recommended_browsers));
recommendedLabels = Arrays.asList(
getContext().getResources().getStringArray(R.array.recommended_browser_labels));
supported = Arrays.asList(
getContext().getResources().getStringArray(R.array.supported_browsers));
supportedLabels = Arrays.asList(
getContext().getResources().getStringArray(R.array.supported_browser_labels));
unsupported = Arrays.asList(
context.getResources().getStringArray(R.array.unsupported_browsers));
}
@Override
public List<Browser> loadInBackground() {
List<Browser> browsers = new ArrayList<Browser>();
Map<String, String> recommendedMap = new HashMap<String, String>();
for (int i = 0; i < recommended.size(); i++) {
recommendedMap.put(recommended.get(i), recommendedLabels.get(i));
}
Map<String, String> supportedMap = new HashMap<String, String>();
for (int i = 0; i < supported.size(); i++) {
supportedMap.put(supported.get(i), supportedLabels.get(i));
}
// Find all installed browsers that listen for ".i2p"
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://stats.i2p"));
final PackageManager pm = getContext().getPackageManager();
List<ResolveInfo> installedBrowsers = pm.queryIntentActivities(intent, 0);
for (ResolveInfo browser : installedBrowsers) {
if (recommended.contains(browser.activityInfo.packageName)) {
browsers.add(new Browser(pm, browser, true, true));
recommendedMap.remove(browser.activityInfo.packageName);
} else if (supported.contains(browser.activityInfo.packageName) ||
browser.activityInfo.packageName.startsWith("net.i2p.android")) {
browsers.add(new Browser(pm, browser, true, false));
supportedMap.remove(browser.activityInfo.packageName);
} else if (unsupported.contains(browser.activityInfo.packageName))
browsers.add(new Browser(pm, browser, false, false));
else
browsers.add(new Browser(pm, browser));
}
// Now add the remaining recommended and supported browsers
for (Map.Entry<String, String> browser : recommendedMap.entrySet()) {
browsers.add(new Browser(browser.getKey(), browser.getValue(),
getDrawableForPackage(browser.getKey()),
false, true, true, true));
}
for (Map.Entry<String, String> browser : supportedMap.entrySet()) {
browsers.add(new Browser(browser.getKey(), browser.getValue(),
getDrawableForPackage(browser.getKey()),
false, true, true, false));
}
Collections.sort(browsers);
return browsers;
}
private Drawable getDrawableForPackage(String packageName) {
try {
String name = "icon_" + packageName.replace('.', '_');
Class res = R.drawable.class;
Field field = res.getField(name);
int drawable = field.getInt(null);
return getContext().getResources().getDrawable(drawable);
} catch (Exception e) {
return null;
}
}
@Override
protected void onStartMonitoring() {
}
@Override
protected void onStopMonitoring() {
}
@Override
protected void releaseResources(List<Browser> data) {
}
}
@Override
public void onLoadFinished(Loader<List<Browser>> listLoader, List<Browser> browsers) {
if (listLoader.getId() == BROWSER_LOADER_ID)
mAdapter.setBrowsers(browsers.toArray(new Browser[browsers.size()]));
}
@Override
public void onLoaderReset(Loader<List<Browser>> listLoader) {
if (listLoader.getId() == BROWSER_LOADER_ID)
mAdapter.clear();
}
}

View File

@ -0,0 +1,125 @@
package net.i2p.android.help;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import net.i2p.android.router.LicenseActivity;
import net.i2p.android.router.R;
import net.i2p.android.router.dialog.TextResourceDialog;
public class HelpActivity extends ActionBarActivity implements
HelpListFragment.OnEntrySelectedListener {
public static final String CATEGORY = "help_category";
public static final int CAT_MAIN = 0;
public static final int CAT_CONFIGURE_BROWSER = 1;
public static final int CAT_ADDRESSBOOK = 2;
public static final int CAT_I2PTUNNEL = 3;
/**
* Whether or not the activity is in two-pane mode, i.e. running on a tablet
* device.
*/
private boolean mTwoPane;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_help);
// Set the action bar
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
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) {
getSupportFragmentManager().beginTransaction()
.add(R.id.main_fragment, new HelpListFragment())
.commit();
}
int category = getIntent().getIntExtra(CATEGORY, -1);
if (category >= 0)
showCategory(category);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_help_actions, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
return true;
case R.id.menu_help_licenses:
Intent lic = new Intent(HelpActivity.this, LicenseActivity.class);
startActivity(lic);
return true;
case R.id.menu_help_release_notes:
TextResourceDialog dialog = 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);
dialog.setArguments(args);
dialog.show(getSupportFragmentManager(), "release_notes");
return true;
default:
return super.onOptionsItemSelected(item);
}
}
// HelpListFragment.OnEntrySelectedListener
@Override
public void onEntrySelected(int entry) {
if (entry == CAT_CONFIGURE_BROWSER) {
Intent i = new Intent(this, BrowserConfigActivity.class);
startActivity(i);
} else
showCategory(entry);
}
private void showCategory(int category) {
int file;
switch (category) {
case CAT_ADDRESSBOOK:
file = R.raw.help_addressbook;
break;
case CAT_I2PTUNNEL:
file = R.raw.help_i2ptunnel;
break;
case CAT_MAIN:
default:
file = R.raw.help_main;
break;
}
HelpHtmlFragment f = HelpHtmlFragment.newInstance(file);
if (mTwoPane) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.detail_fragment, f).commit();
} else {
getSupportFragmentManager().beginTransaction()
.replace(R.id.main_fragment, f)
.addToBackStack("help" + category)
.commit();
}
}
}

View File

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

View File

@ -0,0 +1,47 @@
package net.i2p.android.help;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import net.i2p.android.router.R;
public class HelpListFragment extends ListFragment {
OnEntrySelectedListener mEntrySelectedCallback;
// Container Activity must implement this interface
public interface OnEntrySelectedListener {
public void onEntrySelected(int entry);
}
@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 {
mEntrySelectedCallback = (OnEntrySelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnEntrySelectedListener");
}
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setListAdapter(ArrayAdapter.createFromResource(getActivity(),
R.array.help_categories, R.layout.listitem_text));
}
@Override
public void onListItemClick(ListView parent, View view, int pos, long id) {
super.onListItemClick(parent, view, pos, id);
mEntrySelectedCallback.onEntrySelected(pos);
}
}

View File

@ -18,6 +18,7 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.TextView;
import android.widget.Toast;
@ -95,11 +96,17 @@ public class TunnelDetailFragment extends Fragment {
TextView description = (TextView) v.findViewById(R.id.tunnel_description);
description.setText(mTunnel.getDescription());
TextView details = (TextView) v.findViewById(R.id.tunnel_details);
details.setText(mTunnel.getDetails());
TextView targetIfacePort = (TextView) v.findViewById(R.id.tunnel_target_interface_port);
targetIfacePort.setText(mTunnel.getIfacePort());
targetIfacePort.setText(mTunnel.getTunnelLink(false));
TextView accessIfacePort = (TextView) v.findViewById(R.id.tunnel_access_interface_port);
accessIfacePort.setText(mTunnel.getIfacePort());
accessIfacePort.setText(mTunnel.getTunnelLink(false));
CheckBox autoStart = (CheckBox) v.findViewById(R.id.tunnel_autostart);
autoStart.setChecked(mTunnel.startAutomatically());
}
return v;

View File

@ -4,6 +4,7 @@ import java.util.List;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.widget.Toast;
import net.i2p.android.i2ptunnel.util.TunnelConfig;
import net.i2p.android.i2ptunnel.util.TunnelUtil;
@ -90,6 +91,16 @@ public class TunnelEntry {
else return NOT_RUNNING;
}
public boolean isRunning() {
switch (getStatus()) {
case STANDBY:
case RUNNING:
return true;
default:
return false;
}
}
public boolean isClient() {
return TunnelUtil.isClient(mController.getType());
}
@ -100,18 +111,42 @@ public class TunnelEntry {
return Boolean.parseBoolean(mController.getSharedClient());
}
/**
* Call this to see if it is okay to linkify getClientLink()
* @return true if getClientLink() can be linkified, false otherwise.
*/
public boolean isClientLinkValid() {
return ("ircclient".equals(mController.getType())) &&
mController.getListenOnInterface() != null &&
mController.getListenPort() != null;
}
/**
* @return valid host:port only if isClientLinkValid() is true
*/
public String getClientLink(boolean linkify) {
String host = getClientInterface();
String port = getClientPort();
String link = host + ":" + port;
if (linkify) {
if ("ircclient".equals(mController.getType()))
link = "irc://" + link;
}
return link;
}
public String getClientInterface() {
String rv;
if ("streamrclient".equals(mController.getType()))
return mController.getTargetHost();
rv = mController.getTargetHost();
else
return mController.getListenOnInterface();
rv = mController.getListenOnInterface();
return rv != null ? rv : "";
}
public String getClientPort() {
String rv = mController.getListenPort();
if (rv != null)
return rv;
return "";
return rv != null ? rv : "";
}
public String getClientDestination() {
@ -128,10 +163,10 @@ public class TunnelEntry {
/* Server tunnel data */
/**
* Call this to see if it is okay to linkify getServerTarget()
* @return true if getServerTarget() can be linkified, false otherwise.
* Call this to see if it is okay to linkify getServerLink()
* @return true if getServerLink() can be linkified, false otherwise.
*/
public boolean isServerTargetLinkValid() {
public boolean isServerLinkValid() {
return ("httpserver".equals(mController.getType()) ||
"httpbidirserver".equals(mController.getType())) &&
mController.getTargetHost() != null &&
@ -139,9 +174,9 @@ public class TunnelEntry {
}
/**
* @return valid host:port only if isServerTargetLinkValid() is true
* @return valid host:port only if isServerLinkValid() is true
*/
public String getServerTarget() {
public String getServerLink(boolean linkify) {
String host;
if ("streamrserver".equals(getInternalType()))
host = mController.getListenOnInterface();
@ -152,7 +187,13 @@ public class TunnelEntry {
if (port == null) port = "";
if (host.indexOf(':') >= 0)
host = '[' + host + ']';
return host + ":" + port;
String link = host + ":" + port;
if (linkify) {
if ("httpserver".equals(mController.getType()) ||
"httpbidirserver".equals(mController.getType()))
link = "http://" + link;
}
return link;
}
public String getDestinationBase64() {
@ -183,18 +224,25 @@ public class TunnelEntry {
/* Other output formats */
public String getIfacePort() {
if (isClient()) {
String host;
if ("streamrclient".equals(getInternalType()))
host = mController.getTargetHost();
else
host = mController.getListenOnInterface();
String port = mController.getListenPort();
if (host == null) host = "";
if (port == null) port = "";
return host + ":" + port;
} else return getServerTarget();
public boolean isTunnelLinkValid() {
if (isClient()) return isClientLinkValid();
else return isServerLinkValid();
}
public String getTunnelLink(boolean linkify) {
if (isClient()) return getClientLink(linkify);
else return getServerLink(linkify);
}
public Uri getRecommendedAppForTunnel() {
int resId = 0;
if ("ircclient".equals(mController.getType()))
resId = R.string.market_irc;
if (resId > 0)
return Uri.parse(mContext.getString(resId));
else
return null;
}
public String getDetails() {
@ -209,16 +257,29 @@ public class TunnelEntry {
public Drawable getStatusIcon() {
switch (getStatus()) {
case STANDBY:
return mContext.getResources()
.getDrawable(R.drawable.ic_schedule_black_24dp);
case STARTING:
return mContext.getResources()
.getDrawable(R.drawable.local_inprogress);
case RUNNING:
return mContext.getResources()
.getDrawable(R.drawable.local_up);
case NOT_RUNNING:
default:
return mContext.getResources()
.getDrawable(R.drawable.local_down);
return null;
}
}
public Drawable getStatusBackground() {
switch (getStatus()) {
case STANDBY:
case STARTING:
return mContext.getResources()
.getDrawable(R.drawable.tunnel_yellow);
case RUNNING:
return mContext.getResources()
.getDrawable(R.drawable.tunnel_green);
case NOT_RUNNING:
default:
return mContext.getResources()
.getDrawable(R.drawable.tunnel_red);
}
}
}

View File

@ -0,0 +1,94 @@
package net.i2p.android.i2ptunnel;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import net.i2p.android.router.R;
import java.util.List;
public class TunnelEntryAdapter extends ArrayAdapter<TunnelEntry> {
private final LayoutInflater mInflater;
public TunnelEntryAdapter(Context context) {
super(context, android.R.layout.simple_list_item_2);
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public void setData(List<TunnelEntry> tunnels) {
clear();
if (tunnels != null) {
for (TunnelEntry tunnel : tunnels) {
add(tunnel);
}
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = mInflater.inflate(R.layout.listitem_i2ptunnel, parent, false);
final TunnelEntry tunnel = getItem(position);
ImageView status = (ImageView) v.findViewById(R.id.tunnel_status);
status.setImageDrawable(tunnel.getStatusIcon());
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
status.setBackgroundDrawable(tunnel.getStatusBackground());
else
status.setBackground(tunnel.getStatusBackground());
TextView name = (TextView) v.findViewById(R.id.tunnel_name);
name.setText(tunnel.getName());
TextView type = (TextView) v.findViewById(R.id.tunnel_description);
type.setText(tunnel.getDescription());
TextView ifacePort = (TextView) v.findViewById(R.id.tunnel_interface_port);
ifacePort.setText(tunnel.getTunnelLink(false));
if (tunnel.isRunning() && tunnel.isTunnelLinkValid()) {
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;
}
}

View File

@ -1,11 +1,14 @@
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;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBar.Tab;
public class TunnelListActivity extends I2PActivityBase implements
TunnelListFragment.OnTunnelSelectedListener,
@ -16,7 +19,10 @@ public class TunnelListActivity extends I2PActivityBase implements
*/
private boolean mTwoPane;
private static final String SELECTED_TAB = "selected_tab";
private static final String SELECTED_PAGE = "selected_page";
private static final int PAGE_CLIENT = 0;
private Spinner mSpinner;
@Override
protected boolean canUseTwoPanes() {
@ -27,34 +33,22 @@ public class TunnelListActivity extends I2PActivityBase implements
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set up action bar for tabs
ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
mSpinner = (Spinner) findViewById(R.id.main_spinner);
mSpinner.setVisibility(View.VISIBLE);
// Client tunnels tab
TunnelListFragment cf = new TunnelListFragment();
Bundle args = new Bundle();
args.putBoolean(TunnelListFragment.SHOW_CLIENT_TUNNELS, true);
cf.setArguments(args);
Tab tab = actionBar.newTab()
.setText(R.string.label_i2ptunnel_client)
.setTabListener(new TabListener(cf));
actionBar.addTab(tab);
mSpinner.setAdapter(ArrayAdapter.createFromResource(this,
R.array.i2ptunnel_pages, android.R.layout.simple_spinner_dropdown_item));
// Server tunnels tab
TunnelListFragment sf = new TunnelListFragment();
args = new Bundle();
args.putBoolean(TunnelListFragment.SHOW_CLIENT_TUNNELS, false);
sf.setArguments(args);
tab = actionBar.newTab()
.setText(R.string.label_i2ptunnel_server)
.setTabListener(new TabListener(sf));
actionBar.addTab(tab);
mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
selectPage(i);
}
if (savedInstanceState != null) {
int selected = savedInstanceState.getInt(SELECTED_TAB);
actionBar.setSelectedNavigationItem(selected);
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
if (findViewById(R.id.detail_fragment) != null) {
// The detail container view will be present only in the
@ -62,19 +56,34 @@ public class TunnelListActivity extends I2PActivityBase implements
// res/values-sw600dp). If this view is present, then the
// activity should be in two-pane mode.
mTwoPane = true;
// In two-pane mode, list items should be given the
// 'activated' state when touched.
cf.setActivateOnItemClick(true);
sf.setActivateOnItemClick(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_TAB,
getSupportActionBar().getSelectedNavigationIndex());
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
@ -86,7 +95,7 @@ public class TunnelListActivity extends I2PActivityBase implements
// fragment transaction.
TunnelDetailFragment detailFrag = TunnelDetailFragment.newInstance(tunnelId);
getSupportFragmentManager().beginTransaction()
.replace(R.id.detail_fragment, detailFrag).commit();
.replace(R.id.detail_fragment, detailFrag).commit();
} else {
// In single-pane mode, simply start the detail activity
// for the selected item ID.
@ -105,11 +114,11 @@ public class TunnelListActivity extends I2PActivityBase implements
TunnelDetailFragment detailFrag = TunnelDetailFragment.newInstance(
(tunnelId > 0 ? tunnelId - 1 : 0));
getSupportFragmentManager().beginTransaction()
.replace(R.id.detail_fragment, detailFrag).commit();
.replace(R.id.detail_fragment, detailFrag).commit();
} else {
TunnelDetailFragment detailFrag = (TunnelDetailFragment) getSupportFragmentManager().findFragmentById(R.id.detail_fragment);
getSupportFragmentManager().beginTransaction()
.remove(detailFrag).commit();
.remove(detailFrag).commit();
}
}
}

View File

@ -1,27 +1,32 @@
package net.i2p.android.i2ptunnel;
import java.util.List;
import net.i2p.android.i2ptunnel.util.TunnelConfig;
import net.i2p.android.router.HelpActivity;
import net.i2p.android.router.I2PFragmentBase;
import net.i2p.android.router.R;
import net.i2p.android.router.I2PFragmentBase.RouterContextProvider;
import net.i2p.i2ptunnel.TunnelControllerGroup;
import net.i2p.router.RouterContext;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
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.FrameLayout;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.Toast;
import net.i2p.android.help.HelpActivity;
import net.i2p.android.i2ptunnel.util.TunnelConfig;
import net.i2p.android.router.I2PFragmentBase;
import net.i2p.android.router.I2PFragmentBase.RouterContextProvider;
import net.i2p.android.router.R;
import net.i2p.i2ptunnel.TunnelControllerGroup;
import net.i2p.router.RouterContext;
import java.util.List;
public class TunnelListFragment extends ListFragment implements
I2PFragmentBase.RouterContextUser,
LoaderManager.LoaderCallbacks<List<TunnelEntry>> {
@ -50,6 +55,8 @@ public class TunnelListFragment extends ListFragment implements
private int mActivatedPosition = ListView.INVALID_POSITION;
private boolean mActivateOnItemClick = false;
private ImageButton mNewTunnel;
// Container Activity must implement this interface
public interface OnTunnelSelectedListener {
public void onTunnelSelected(int tunnelId);
@ -85,6 +92,27 @@ public class TunnelListFragment extends ListFragment implements
setHasOptionsMenu(true);
}
@Override
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);
FrameLayout listContainer = (FrameLayout) v.findViewById(R.id.list_container);
listContainer.addView(listFragmentView);
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;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
@ -162,7 +190,7 @@ public class TunnelListFragment extends ListFragment implements
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.fragment_i2ptunnel_list_actions, menu);
if (getRouterContext() == null) {
menu.findItem(R.id.action_add_tunnel).setVisible(false);
mNewTunnel.setVisibility(View.GONE);
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);
@ -174,27 +202,22 @@ public class TunnelListFragment extends ListFragment implements
// Handle presses on the action bar items
List<String> msgs;
switch (item.getItemId()) {
case R.id.action_add_tunnel:
Intent wi = new Intent(getActivity(), TunnelWizardActivity.class);
startActivityForResult(wi, TUNNEL_WIZARD_REQUEST);
return true;
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;
// TODO: Enable when Help page finished
//case R.id.action_i2ptunnel_help:
// Intent hi = new Intent(getActivity(), HelpActivity.class);
// hi.putExtra(HelpActivity.REFERRER, "i2ptunnel");
// startActivity(hi);
// return true;
default:
return super.onOptionsItemSelected(item);
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)
@ -252,7 +275,7 @@ public class TunnelListFragment extends ListFragment implements
}
public void onLoadFinished(Loader<List<TunnelEntry>> loader,
List<TunnelEntry> data) {
List<TunnelEntry> data) {
if (loader.getId() == (mClientTunnels ?
CLIENT_LOADER_ID : SERVER_LOADER_ID)) {
mAdapter.setData(data);

View File

@ -90,6 +90,7 @@ public class TunnelWizardModel extends AbstractWizardModel {
new SingleTextFieldPage(this, res.getString(R.string.i2ptunnel_wizard_k_target_port))
.setDescription(res.getString(R.string.i2ptunnel_wizard_desc_target_port))
.setNumeric(true)
.setRequired(true)
.setEqualCondition(cTunnelType, res.getString(R.string.i2ptunnel_wizard_v_server)),
@ -110,6 +111,7 @@ public class TunnelWizardModel extends AbstractWizardModel {
new SingleTextFieldPage(this, res.getString(R.string.i2ptunnel_wizard_k_binding_port))
.setDescription(res.getString(R.string.i2ptunnel_wizard_k_binding_port))
.setNumeric(true)
.setRequired(true)
.setEqualCondition(cTunnelType, res.getString(R.string.i2ptunnel_wizard_v_client))
.setEqualCondition(cServerType, res.getString(R.string.i2ptunnel_type_httpbidirserver)),

View File

@ -7,22 +7,24 @@ import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v4.app.ActionBarDrawerToggle;
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.ActionBarActivity;
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.R;
import net.i2p.android.router.addressbook.AddressbookActivity;
import net.i2p.android.router.log.LogActivity;
import net.i2p.android.router.netdb.NetDbActivity;
@ -46,12 +48,10 @@ public abstract class I2PActivityBase extends ActionBarActivity implements
private CharSequence mDrawerTitle;
private CharSequence mTitle;
private String[] mActivityTitles;
/**
* Router variables
*/
protected String _myDir;
protected boolean _isBound;
protected boolean _triedBind;
protected ServiceConnection _connection;
@ -60,13 +60,16 @@ public abstract class I2PActivityBase extends ActionBarActivity implements
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 */
/**
* 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() {
@ -76,20 +79,21 @@ public abstract class I2PActivityBase extends ActionBarActivity implements
/**
* 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. */
/**
* Called when the activity is first created.
*/
@Override
public void onCreate(Bundle savedInstanceState)
{
public void onCreate(Bundle savedInstanceState) {
Util.d(this + " onCreate called");
super.onCreate(savedInstanceState);
_sharedPrefs = getSharedPreferences(SHARED_PREFS, 0);
_myDir = getFilesDir().getAbsolutePath();
// If the Activity wants to use a ViewPager, provide it.
// If the Activity can make use of two panes (if available),
@ -102,8 +106,19 @@ public abstract class I2PActivityBase extends ActionBarActivity implements
else
setContentView(R.layout.activity_navdrawer_onepane);
// Set the action bar
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
setSupportActionBar(toolbar);
mTitle = mDrawerTitle = getTitle();
mActivityTitles = getResources().getStringArray(R.array.navdrawer_activity_titles);
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);
@ -112,16 +127,12 @@ public abstract class I2PActivityBase extends ActionBarActivity implements
mDrawerList.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
// Set the adapter for the list view
mDrawerList.setAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, mActivityTitles));
R.layout.listitem_navdrawer, activityTitles));
// Set the list's click listener
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
// Enable ActionBar app icon to behave as action to toggle nav drawer
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close) {
R.string.drawer_open, R.string.drawer_close) {
private boolean wasDragged = false;
/** Called when a drawer has settled in a completely closed state. */
@ -160,57 +171,63 @@ public abstract class I2PActivityBase extends ActionBarActivity implements
private void selectItem(int pos) {
switch (pos) {
case 1:
Intent ab = new Intent(I2PActivityBase.this, AddressbookActivity.class);
startActivity(ab);
break;
case 2:
Intent itb = new Intent(I2PActivityBase.this, TunnelListActivity.class);
startActivity(itb);
break;
case 3:
Intent log = new Intent(I2PActivityBase.this, LogActivity.class);
startActivity(log);
break;
case 4:
Intent active = new Intent(I2PActivityBase.this, RateGraphActivity.class);
startActivity(active);
break;
case 5:
Intent peers = new Intent(I2PActivityBase.this, PeersActivity.class);
startActivity(peers);
break;
case 6:
Intent netdb = new Intent(I2PActivityBase.this, NetDbActivity.class);
startActivity(netdb);
break;
case 7:
Intent wp = new Intent(I2PActivityBase.this, WebActivity.class);
wp.putExtra(WebFragment.HTML_RESOURCE_ID, R.raw.welcome_html);
startActivity(wp);
break;
case 8:
Intent news = new Intent(I2PActivityBase.this, NewsActivity.class);
startActivity(news);
break;
default:
Intent main = new Intent(I2PActivityBase.this, MainActivity.class);
startActivity(main);
break;
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()
{
public void onRestart() {
Util.d(this + " onRestart called");
super.onRestart();
}
@Override
public void onStart()
{
public void onStart() {
Util.d(this + " onStart called");
super.onStart();
if (_sharedPrefs.getBoolean(PREF_AUTO_START, DEFAULT_AUTO_START))
@ -219,24 +236,32 @@ public abstract class I2PActivityBase extends ActionBarActivity implements
bindRouter(false);
}
/** @param def default */
/**
* @param def default
*/
public boolean getPref(String pref, boolean def) {
return _sharedPrefs.getBoolean(pref, def);
}
/** @param def default */
/**
* @param def default
*/
public String getPref(String pref, String def) {
return _sharedPrefs.getString(pref, def);
}
/** @return success */
/**
* @return success
*/
public boolean setPref(String pref, boolean val) {
SharedPreferences.Editor edit = _sharedPrefs.edit();
edit.putBoolean(pref, val);
return edit.commit();
}
/** @return success */
/**
* @return success
*/
public boolean setPref(String pref, String val) {
SharedPreferences.Editor edit = _sharedPrefs.edit();
edit.putString(pref, val);
@ -244,37 +269,32 @@ public abstract class I2PActivityBase extends ActionBarActivity implements
}
@Override
public void onResume()
{
public void onResume() {
Util.d(this + " onResume called");
super.onResume();
}
@Override
public void onPause()
{
public void onPause() {
Util.d(this + " onPause called");
super.onPause();
}
@Override
public void onSaveInstanceState(Bundle outState)
{
public void onSaveInstanceState(Bundle outState) {
Util.d(this + " onSaveInstanceState called");
super.onSaveInstanceState(outState);
}
@Override
public void onStop()
{
public void onStop() {
Util.d(this + " onStop called");
unbindRouter();
super.onStop();
}
@Override
public void onDestroy()
{
public void onDestroy() {
Util.d(this + " onDestroy called");
super.onDestroy();
}
@ -294,6 +314,7 @@ public abstract class I2PActivityBase extends ActionBarActivity implements
/**
* 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) {
@ -303,8 +324,11 @@ public abstract class I2PActivityBase extends ActionBarActivity implements
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)) {
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
@ -354,7 +378,7 @@ public abstract class I2PActivityBase extends ActionBarActivity implements
////// Service stuff
/**
* Start the service and bind to it
* Start the service and bind to it
*/
protected boolean startRouter() {
Intent intent = new Intent();
@ -362,7 +386,7 @@ public abstract class I2PActivityBase extends ActionBarActivity implements
Util.d(this + " calling startService");
ComponentName name = startService(intent);
if (name == null)
Util.d(this + " XXXXXXXXXXXXXXXXXXXX got from startService: " + name);
Util.d(this + " XXXXXXXXXXXXXXXXXXXX got null from startService!");
Util.d(this + " got from startService: " + name);
boolean success = bindRouter(true);
if (!success)
@ -371,7 +395,7 @@ public abstract class I2PActivityBase extends ActionBarActivity implements
}
/**
* Bind only
* Bind only
*/
protected boolean bindRouter(boolean autoCreate) {
Intent intent = new Intent(RouterBinder.class.getName());
@ -386,7 +410,7 @@ public abstract class I2PActivityBase extends ActionBarActivity implements
protected void unbindRouter() {
Util.d(this + " unbindRouter called with _isBound:" + _isBound + " _connection:" + _connection + " _triedBind:" + _triedBind);
if (_triedBind && _connection != null)
unbindService(_connection);
unbindService(_connection);
_triedBind = false;
_connection = null;
@ -417,7 +441,9 @@ public abstract class I2PActivityBase extends ActionBarActivity implements
}
}
/** callback from ServiceConnection, override as necessary */
/**
* callback from ServiceConnection, override as necessary
*/
protected void onRouterBind(RouterService svc) {
Fragment f = getSupportFragmentManager().findFragmentById(R.id.main_fragment);
if (f instanceof I2PFragmentBase)
@ -434,8 +460,11 @@ public abstract class I2PActivityBase extends ActionBarActivity implements
}
}
/** callback from ServiceConnection, override as necessary */
protected void onRouterUnbind() {}
/**
* callback from ServiceConnection, override as necessary
*/
protected void onRouterUnbind() {
}
// I2PFragmentBase.RouterContextProvider

View File

@ -0,0 +1,5 @@
package net.i2p.android.router;
public interface I2PConstants {
public static final String ANDROID_PREF_PREFIX = "i2pandroid.";
}

View File

@ -3,20 +3,20 @@ package net.i2p.android.router;
import android.content.Context;
import android.content.res.Resources;
import android.os.Build;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import net.i2p.android.router.R;
import net.i2p.android.router.util.Util;
import net.i2p.data.DataHelper;
import net.i2p.util.FileUtil;
import net.i2p.util.OrderedProperties;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
// Wouldn't this be better as a private class in MainActivity?
@ -33,8 +33,7 @@ class InitActivities {
public InitActivities(Context c) {
ctx = c;
// This needs to be changed so that we can have an alternative place
myDir = c.getFilesDir().getAbsolutePath();
myDir = Util.getFileDir(c);
_ourVersion = Util.getOurVersion(c);
}
@ -55,25 +54,31 @@ class InitActivities {
Util.d("MODEL" + ": " + Build.MODEL);
Util.d("DISPLAY" + ": " + Build.DISPLAY);
Util.d("VERSION" + ": " + Build.VERSION.RELEASE);
Util.d("SDK" + ": " + Build.VERSION.SDK);
Util.d("SDK" + ": " + Build.VERSION.SDK_INT);
}
void initialize() {
if (checkNewVersion()) {
Properties props = new Properties();
List<Properties> lProps = Util.getPropertiesFromPreferences(ctx);
Properties props = lProps.get(0);
props.setProperty("i2p.dir.temp", myDir + "/tmp");
props.setProperty("i2p.dir.pid", myDir + "/tmp");
// Time disabled in default router.config
// But lots of time problems on Android, not all carriers support NITZ
// and there was no NTP before 3.0. Tablets should be fine?
// Phones in airplane mode with wifi enabled still a problem.
// Deactivated phones in airplane mode definatly won't have correct time.
// Deactivated phones in airplane mode definitely won't have correct time.
if (Build.VERSION.SDK_INT < 11) // Honeycomb 3.0
props.setProperty("time.disabled", "false");
mergeResourceToFile(R.raw.router_config, "router.config", props);
mergeResourceToFile(R.raw.logger_config, "logger.config", null);
mergeResourceToFile(R.raw.i2ptunnel_config, "i2ptunnel.config", null);
mergeResourceToFile(R.raw.logger_config, "logger.config", lProps.get(1));
// This is not needed for now, i2ptunnel.config only contains tunnel
// settings, which can now be configured manually. We don't want to
// overwrite the user's tunnels.
//mergeResourceToFile(R.raw.i2ptunnel_config, "i2ptunnel.config", null);
copyResourceToFileIfAbsent(R.raw.i2ptunnel_config, "i2ptunnel.config");
// FIXME this is a memory hog to merge this way
mergeResourceToFile(R.raw.hosts_txt, "hosts.txt", null);
mergeResourceToFile(R.raw.more_hosts_txt, "hosts.txt", null);
@ -137,6 +142,15 @@ class InitActivities {
System.setProperty("wrapper.logfile", myDir + "/wrapper.log");
}
/**
* @param f relative to base dir
*/
private void copyResourceToFileIfAbsent(int resID, String f) {
File file = new File(myDir, f);
if (!file.exists())
copyResourceToFile(resID, f);
}
/**
* @param f relative to base dir
*/
@ -212,47 +226,14 @@ class InitActivities {
/**
* Load defaults from resource,
* then add props from file,
* and write back
* For now, do it backwards so we can override with new apks.
* When we have user configurable stuff, switch it back.
* then add props from settings,
* and write back.
*
* @param f relative to base dir
* @param props local overrides or null
* @param overrides local overrides or null
*/
public void mergeResourceToFile(int resID, String f, Properties overrides) {
InputStream in = null;
InputStream fin = null;
byte buf[] = new byte[4096];
try {
in = ctx.getResources().openRawResource(resID);
Properties props = new OrderedProperties();
// keep user settings
//DataHelper.loadProps(props, in);
try {
fin = new FileInputStream(new File(myDir, f));
DataHelper.loadProps(props, fin);
Util.d("Merging resource into file " + f);
} catch (IOException ioe) {
Util.d("Creating file " + f + " from resource");
}
// override user settings
DataHelper.loadProps(props, in);
if (overrides != null)
props.putAll(overrides);
File path = new File(myDir, f);
DataHelper.storeProps(props, path);
Util.d("Saved " + props.size() +" properties in " + f);
} catch (IOException ioe) {
} catch (Resources.NotFoundException nfe) {
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
if (fin != null) try { fin.close(); } catch (IOException ioe) {}
}
private void mergeResourceToFile(int resID, String f, Properties overrides) {
Util.mergeResourceToFile(ctx, myDir, f, resID, overrides, null);
}
/**

View File

@ -1,7 +1,5 @@
package net.i2p.android.router;
import java.io.File;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
@ -13,18 +11,25 @@ import android.os.RemoteException;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import net.i2p.android.router.R;
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.IRouterState;
import net.i2p.android.router.service.IRouterStateCallback;
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;
import java.lang.ref.WeakReference;
public class MainActivity extends I2PActivityBase implements
MainFragment.RouterControlListener {
IRouterState mStateService = null;
MainFragment mMainFragment = null;
private boolean mAutoStartFromIntent = false;
@Override
public void onCreate(Bundle savedInstanceState) {
@ -50,6 +55,42 @@ public class MainActivity extends I2PActivityBase implements
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
@ -60,9 +101,9 @@ public class MainActivity extends I2PActivityBase implements
if (mStateService.isStarted()) {
// Update for the current state.
Util.d("Fetching state.");
String curState = mStateService.getState();
State curState = mStateService.getState();
Message msg = mHandler.obtainMessage(STATE_MSG);
msg.getData().putString("state", curState);
msg.getData().putParcelable(MSG_DATA, curState);
mHandler.sendMessage(msg);
} else {
Util.d("StateService not started yet");
@ -92,18 +133,11 @@ public class MainActivity extends I2PActivityBase implements
dialog.show(getSupportFragmentManager(), "about");
return true;
// TODO: Unhide when Help page finished
//case R.id.menu_help:
// Intent hi = new Intent(MainActivity.this, HelpActivity.class);
// hi.putExtra(HelpActivity.REFERRER, "main");
// startActivity(hi);
// return true;
// TODO: Remove when help page finished
case R.id.menu_help_licenses:
Intent lic = new Intent(MainActivity.this, LicenseActivity.class);
startActivity(lic);
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();
@ -158,14 +192,15 @@ public class MainActivity extends I2PActivityBase implements
mStateService.registerCallback(mStateCallback);
// Update for the current state.
Util.d("Fetching state.");
String curState = mStateService.getState();
State curState = mStateService.getState();
Message msg = mHandler.obtainMessage(STATE_MSG);
msg.getData().putString("state", curState);
msg.getData().putParcelable(MSG_DATA, curState);
mHandler.sendMessage(msg);
} else {
// Unbind
unbindService(mStateConnection);
mStateService = null;
mTriedBindState = false;
}
} catch (RemoteException e) {
// In this case the service has crashed before we could even
@ -190,49 +225,74 @@ public class MainActivity extends I2PActivityBase implements
* NOT be running in our main thread like most other things -- so,
* to update the UI, we need to use a Handler to hop over there.
*/
public void stateChanged(String newState) throws RemoteException {
public void stateChanged(State newState) throws RemoteException {
Message msg = mHandler.obtainMessage(STATE_MSG);
msg.getData().putString("state", newState);
msg.getData().putParcelable(MSG_DATA, newState);
mHandler.sendMessage(msg);
}
};
private static final int STATE_MSG = 1;
private static final String MSG_DATA = "state";
private Handler mHandler = new Handler() {
private String lastRouterState = null;
private Handler mHandler = new StateHandler(new WeakReference<>(this));
private static class StateHandler extends Handler {
WeakReference<MainActivity> mReference;
public StateHandler(WeakReference<MainActivity> reference) {
mReference = reference;
}
private State lastRouterState = null;
@Override
public void handleMessage(Message msg) {
MainActivity parent = mReference.get();
if (parent == null)
return;
switch (msg.what) {
case STATE_MSG:
String state = msg.getData().getString("state");
if (lastRouterState == null || !lastRouterState.equals(state)) {
if (mMainFragment == null)
mMainFragment = (MainFragment) getSupportFragmentManager().findFragmentById(R.id.main_fragment);
if (mMainFragment != null) {
mMainFragment.updateState(state);
State state = msg.getData().getParcelable(MSG_DATA);
if (lastRouterState == null || lastRouterState != state) {
if (parent.mMainFragment == null)
parent.mMainFragment = (MainFragment) parent.getSupportFragmentManager().findFragmentById(R.id.main_fragment);
if (parent.mMainFragment != null) {
parent.mMainFragment.updateState(state);
lastRouterState = state;
}
if (state == State.RUNNING && parent.mAutoStartFromIntent) {
parent.setResult(RESULT_OK);
parent.finish();
}
}
break;
default:
super.handleMessage(msg);
}
}
};
}
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() {
RouterService svc = _routerService;
return (((svc == null) || (!_isBound) || svc.canManualStart())
&& Util.isConnected(this))
|| (svc != null && _isBound && svc.canManualStop());
return (canStart() && Connectivity.isConnected(this)) || canStop();
}
public boolean shouldBeOn() {
RouterService svc = _routerService;
return svc != null && _isBound && svc.canManualStop();
String action = getIntent().getAction();
return (canStop()) ||
(action != null && action.equals("net.i2p.android.router.START_I2P"));
}
public void onStartRouterClicked() {
@ -241,7 +301,7 @@ public class MainActivity extends I2PActivityBase implements
setPref(PREF_AUTO_START, true);
svc.manualStart();
} else {
(new File(_myDir, "wrapper.log")).delete();
(new File(Util.getFileDir(this), "wrapper.log")).delete();
startRouter();
}
}

View File

@ -2,30 +2,27 @@ package net.i2p.android.router;
import android.app.Activity;
import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.ToggleButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.ToggleButton;
import java.text.Collator;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import net.i2p.android.router.R;
import net.i2p.android.router.dialog.ConfigureBrowserDialog;
import net.i2p.android.router.dialog.FirstStartDialog;
import net.i2p.android.router.dialog.VersionDialog;
import net.i2p.android.router.service.State;
import net.i2p.android.router.util.Connectivity;
import net.i2p.android.router.util.LongToggleButton;
import net.i2p.android.router.util.Util;
import net.i2p.data.DataHelper;
@ -36,6 +33,13 @@ import net.i2p.router.RouterContext;
import net.i2p.router.TunnelPoolSettings;
import net.i2p.util.Translate;
import java.text.Collator;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class MainFragment extends I2PFragmentBase {
private Handler _handler;
@ -44,6 +48,7 @@ public class MainFragment extends I2PFragmentBase {
private String _savedStatus;
private boolean _keep = true;
private boolean _startPressed = false;
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_SHOW_STATS = "i2pandroid.main.showStats";
protected static final String PROP_NEW_INSTALL = "i2p.newInstall";
@ -159,7 +164,7 @@ public class MainFragment extends I2PFragmentBase {
}
private void updateOneShot() {
_handler.postDelayed(_oneShotUpdate, 100);
_handler.postDelayed(_oneShotUpdate, 10);
}
private class OneShotUpdate implements Runnable {
@ -198,14 +203,14 @@ public class MainFragment extends I2PFragmentBase {
// is not received. Ensure that the state image is correct.
// TODO: Fix the race between RouterService shutdown and
// IRouterState unbinding.
updateState("INIT");
updateState(State.INIT);
}
}
public boolean onBackPressed() {
RouterContext ctx = getRouterContext();
// RouterService svc = _routerService; Which is better to use?!
_keep = Util.isConnected(getActivity()) && (ctx != null || _startPressed);
_keep = Connectivity.isConnected(getActivity()) && (ctx != null || _startPressed);
Util.d("*********************************************************");
Util.d("Back pressed, Keep? " + _keep);
Util.d("*********************************************************");
@ -235,25 +240,25 @@ public class MainFragment extends I2PFragmentBase {
}
}
public void updateState(String newState) {
public void updateState(State newState) {
final ImageView lightImage = (ImageView) getView().findViewById(R.id.main_lights);
if ("INIT".equals(newState) ||
"STOPPED".equals(newState) ||
"MANUAL_STOPPED".equals(newState) ||
"MANUAL_QUITTED".equals(newState) ||
"NETWORK_STOPPED".equals(newState)) {
if (newState == State.INIT ||
newState == State.STOPPED ||
newState == State.MANUAL_STOPPED ||
newState == State.MANUAL_QUITTED ||
newState == State.NETWORK_STOPPED) {
lightImage.setImageResource(R.drawable.routerlogo_0);
} else if ("STARTING".equals(newState) ||
"STOPPING".equals(newState) ||
"MANUAL_STOPPING".equals(newState) ||
"MANUAL_QUITTING".equals(newState) ||
"NETWORK_STOPPING".equals(newState)) {
} else if (newState == State.STARTING ||
newState == State.STOPPING ||
newState == State.MANUAL_STOPPING ||
newState == State.MANUAL_QUITTING ||
newState == State.NETWORK_STOPPING) {
lightImage.setImageResource(R.drawable.routerlogo_1);
} else if ("RUNNING".equals(newState)) {
} else if (newState == State.RUNNING) {
lightImage.setImageResource(R.drawable.routerlogo_2);
} else if ("ACTIVE".equals(newState)) {
} else if (newState == State.ACTIVE) {
lightImage.setImageResource(R.drawable.routerlogo_3);
} else if ("WAITING".equals(newState)) {
} else if (newState == State.WAITING) {
lightImage.setImageResource(R.drawable.routerlogo_4);
} // Ignore unknown states.
}
@ -264,9 +269,9 @@ public class MainFragment extends I2PFragmentBase {
LinearLayout vStatus = (LinearLayout) getActivity().findViewById(R.id.main_status);
TextView vStatusText = (TextView) getActivity().findViewById(R.id.main_status_text);
if(!Util.isConnected(getActivity())) {
if(!Connectivity.isConnected(getActivity())) {
// Manually set state, RouterService won't be running
updateState("WAITING");
updateState(State.WAITING);
vStatusText.setText("No Internet connection is available");
vStatus.setVisibility(View.VISIBLE);
sv.setVisibility(View.VISIBLE);
@ -292,18 +297,17 @@ public class MainFragment extends I2PFragmentBase {
String msgDelay = DataHelper.formatDuration(ctx.throttle().getMessageDelay());
String uptime = DataHelper.formatDuration(ctx.router().getUptime());
String netstatus = "Unknown";
if(reach == net.i2p.router.CommSystemFacade.STATUS_DIFFERENT) {
netstatus = "Different";
}
if(reach == net.i2p.router.CommSystemFacade.STATUS_HOSED) {
netstatus = "Hosed";
}
if(reach == net.i2p.router.CommSystemFacade.STATUS_OK) {
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";
}
if(reach == net.i2p.router.CommSystemFacade.STATUS_REJECT_UNSOLICITED) {
netstatus = "Reject Unsolicited";
} else if (reach == net.i2p.router.CommSystemFacade.STATUS_REJECT_UNSOLICITED) {
netstatus = "Firewalled";
} else {
netstatus = "Unknown";
}
String tunnelStatus = ctx.throttle().getTunnelStatus();
//ctx.commSystem().getReachabilityStatus();
@ -357,8 +361,9 @@ public class MainFragment extends I2PFragmentBase {
_savedStatus = status + participate + details;
vStatusText.setText(_savedStatus);
vStatus.setVisibility(View.VISIBLE);
} else
vStatus.setVisibility(View.INVISIBLE);
} else {
vStatus.setVisibility(View.GONE);
}
sv.setVisibility(View.VISIBLE);
} else {
// network but no router context
@ -397,22 +402,24 @@ public class MainFragment extends I2PFragmentBase {
String name = getName(ctx, client);
Hash h = client.calculateHash();
TableRow dest = new TableRow(getActivity());
//dest.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
dest.setPadding(16, 4, 0, 4);
// Client or server
ImageView type = new ImageView(getActivity());
TextView type = new TextView(getActivity());
type.setTextColor(getResources().getColor(android.R.color.primary_text_light));
type.setTypeface(Typeface.DEFAULT_BOLD);
type.setGravity(Gravity.CENTER);
if (ctx.clientManager().shouldPublishLeaseSet(h))
type.setImageDrawable(getActivity().getResources()
.getDrawable(R.drawable.server));
type.setText(R.string.char_server_tunnel);
else
type.setImageDrawable(getActivity().getResources()
.getDrawable(R.drawable.client));
type.setText(R.string.char_client_tunnel);
dest.addView(type);
// Name
TextView destName = new TextView(getActivity());
destName.setPadding(16, 0, 0, 0);
destName.setGravity(Gravity.CENTER_VERTICAL);
destName.setText(name);
//destName.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
dest.addView(destName);
// Status
@ -421,14 +428,14 @@ public class MainFragment extends I2PFragmentBase {
long timeToExpire = ls.getEarliestLeaseDate() - ctx.clock().now();
if (timeToExpire < 0) {
// red or yellow light
type.setBackgroundColor(Color.TRANSPARENT);
type.setBackgroundResource(R.drawable.tunnel_yellow);
} else {
// green light
type.setBackgroundColor(Color.GREEN);
type.setBackgroundResource(R.drawable.tunnel_green);
}
} else {
// yellow light
type.setBackgroundColor(Color.TRANSPARENT);
type.setBackgroundResource(R.drawable.tunnel_yellow);
}
dests.addView(dest);
@ -436,7 +443,7 @@ public class MainFragment extends I2PFragmentBase {
} else {
TableRow empty = new TableRow(getActivity());
TextView emptyText = new TextView(getActivity());
emptyText.setText("No client tunnels are running yet.");
emptyText.setText(R.string.no_client_tunnels_running);
empty.addView(emptyText);
dests.addView(empty);
}
@ -485,14 +492,23 @@ public class MainFragment extends I2PFragmentBase {
}
private void checkDialog() {
VersionDialog dialog = new VersionDialog();
I2PActivityBase ab = (I2PActivityBase) getActivity();
boolean configureBrowser = ab.getPref(PREF_CONFIGURE_BROWSER, true);
if (configureBrowser) {
ConfigureBrowserDialog dialog = new ConfigureBrowserDialog();
dialog.show(getActivity().getSupportFragmentManager(), "configurebrowser");
ab.setPref(PREF_CONFIGURE_BROWSER, false);
}
/*VersionDialog dialog = new VersionDialog();
String oldVersion = ((I2PActivityBase) getActivity()).getPref(PREF_INSTALLED_VERSION, "??");
if(oldVersion.equals("??")) {
// TODO Don't show this dialog until it is reworked
Bundle args = new Bundle();
args.putInt(VersionDialog.DIALOG_TYPE, VersionDialog.DIALOG_NEW_INSTALL);
dialog.setArguments(args);
dialog.show(getActivity().getSupportFragmentManager(), "newinstall");
} else {
// TODO Don't show dialog on new version until we have something new to tell them
String currentVersion = Util.getOurVersion(getActivity());
if(!oldVersion.equals(currentVersion)) {
Bundle args = new Bundle();
@ -500,7 +516,7 @@ public class MainFragment extends I2PFragmentBase {
dialog.setArguments(args);
dialog.show(getActivity().getSupportFragmentManager(), "newversion");
}
}
}*/
}
private void checkFirstStart() {

View File

@ -0,0 +1,95 @@
package net.i2p.android.router;
import android.content.res.Resources;
import android.os.Bundle;
import android.text.Html;
import android.text.SpannableStringBuilder;
import android.text.method.LinkMovementMethod;
import android.text.style.URLSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import net.i2p.android.apps.NewsFetcher;
import net.i2p.android.router.util.Util;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
public class NewsFragment extends I2PFragmentBase {
private long _lastChanged;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_news, container, false);
}
@Override
public void onResume() {
super.onResume();
NewsFetcher nf = NewsFetcher.getInstance();
if (nf != null) {
// Always update the status
TextView tv = (TextView) getActivity().findViewById(R.id.news_status);
tv.setText(nf.status().replace("&nbsp;", " "));
tv.setVisibility(View.VISIBLE);
}
// Only update the content if we need to
File newsFile = new File(Util.getFileDir(getActivity()), "docs/news.xml");
boolean newsExists = newsFile.exists();
if (_lastChanged > 0 && ((!newsExists) || newsFile.lastModified() < _lastChanged))
return;
_lastChanged = System.currentTimeMillis();
InputStream in = null;
ByteArrayOutputStream out = new ByteArrayOutputStream(2048);
byte buf[] = new byte[1024];
try {
if (newsExists) {
in = new FileInputStream(newsFile);
} else {
in = getResources().openRawResource(R.raw.initialnews_html);
}
int read;
while ( (read = in.read(buf)) != -1)
out.write(buf, 0, read);
} catch (IOException ioe) {
System.err.println("news error " + ioe);
} catch (Resources.NotFoundException nfe) {
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
}
String news = "";
try {
news = out.toString("UTF-8");
} catch (UnsupportedEncodingException uee) {
}
// Get SpannableStringBuilder object from HTML code
CharSequence sequence = Html.fromHtml(news);
SpannableStringBuilder strBuilder = new SpannableStringBuilder(sequence);
// Get an array of URLSpan from SpannableStringBuilder object
URLSpan[] urlSpans = strBuilder.getSpans(0, strBuilder.length(), URLSpan.class);
// Remove URLSpans with relative paths, which can't be clicked on
for (final URLSpan span : urlSpans) {
if (span.getURL().startsWith("/"))
strBuilder.removeSpan(span);
}
TextView tv = (TextView) getActivity().findViewById(R.id.news_content);
tv.setText(strBuilder);
tv.setMovementMethod(LinkMovementMethod.getInstance());
}
}

View File

@ -2,28 +2,22 @@ package net.i2p.android.router;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.SortedSet;
import java.util.TreeSet;
import net.i2p.android.router.R;
import net.i2p.I2PAppContext;
import net.i2p.android.router.service.StatSummarizer;
import net.i2p.android.router.util.IntEditTextPreference;
import net.i2p.android.router.util.Util;
import net.i2p.router.RouterContext;
import net.i2p.stat.FrequencyStat;
@ -31,15 +25,22 @@ import net.i2p.stat.Rate;
import net.i2p.stat.RateStat;
import net.i2p.stat.StatManager;
import net.i2p.util.LogManager;
import net.i2p.util.OrderedProperties;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.SortedSet;
public class SettingsActivity extends PreferenceActivity {
// Actions for legacy settings
private static final String ACTION_PREFS_NET = "net.i2p.android.router.PREFS_NET";
private static final String ACTION_PREFS_GRAPHS = "net.i2p.android.router.PREFS_GRAPHS";
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
protected void onCreate(Bundle savedInstanceState) {
@ -51,27 +52,20 @@ public class SettingsActivity extends PreferenceActivity {
addPreferencesFromResource(R.xml.settings_net);
} else if (ACTION_PREFS_GRAPHS.equals(action)){
addPreferencesFromResource(R.xml.settings_graphs);
setupGraphSettings(this, getPreferenceScreen(), getRouterContext());
setupGraphSettings(this, getPreferenceScreen(), Util.getRouterContext());
} else if (ACTION_PREFS_LOGGING.equals(action)) {
addPreferencesFromResource(R.xml.settings_logging);
RouterContext ctx = getRouterContext();
if (ctx != null)
setupLoggingSettings(this, getPreferenceScreen(), ctx);
setupLoggingSettings(this, getPreferenceScreen(), Util.getRouterContext());
} else if (ACTION_PREFS_ADVANCED.equals(action)) {
addPreferencesFromResource(R.xml.settings_advanced);
setupAdvancedSettings(this, getPreferenceScreen(), Util.getRouterContext());
}
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
// Load the legacy preferences headers
addPreferencesFromResource(R.xml.settings_headers_legacy);
}
}
protected static RouterContext getRouterContext() {
List<RouterContext> contexts = RouterContext.listContexts();
if ( !((contexts == null) || (contexts.isEmpty())) ) {
return contexts.get(0);
}
return null;
mToolbar.setTitle(getTitle());
}
protected static void setupGraphSettings(Context context, PreferenceScreen ps, RouterContext ctx) {
@ -153,10 +147,30 @@ public class SettingsActivity extends PreferenceActivity {
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(Context context, PreferenceScreen ps, 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);
IntEditTextPreference udpPort = (IntEditTextPreference) ps.findPreference(udpPortKey);
IntEditTextPreference ntcpPort = (IntEditTextPreference) ps.findPreference(ntcpPortKey);
String udpCurrentPort = ctx.getProperty(udpPortKey, "0");
udpPort.setText(udpCurrentPort);
String ntcpCurrentPort = ctx.getProperty(ntcpPortKey);
if (ntcpCurrentPort != null && ntcpCurrentPort.length() > 0)
ntcpPort.setText(ntcpCurrentPort);
else
ntcpPort.setText(udpCurrentPort);
}
}
@ -170,100 +184,66 @@ public class SettingsActivity extends PreferenceActivity {
loadHeadersFromResource(R.xml.settings_headers, target);
}
@Override
public void setContentView(int layoutResID) {
ViewGroup contentView = (ViewGroup) LayoutInflater.from(this).inflate(
R.layout.activity_settings,
(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
protected void onPause() {
// TODO: Rewrite this code to fix default setting
// Copy prefs
Properties props = new OrderedProperties();
List<Properties> lProps = Util.getPropertiesFromPreferences(this);
Properties props = lProps.get(0);
Properties propsToRemove = lProps.get(1);
Properties logSettings = lProps.get(2);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
Set toRemove = propsToRemove.keySet();
// List to store stats for graphing
List<String> statSummaries = new ArrayList<String>();
boolean restartRequired = Util.checkAndCorrectRouterConfig(this, props, toRemove);
// List to store Log settings
Map<String, String> logSettings = new HashMap<String, String>();
Map<String, ?> all = preferences.getAll();
Iterator<String> iterator = all.keySet().iterator();
// get values from the Map and make them strings.
// This loop avoids needing to convert each one, or even know it's type, or if it exists yet.
while (iterator.hasNext()) {
String x = iterator.next();
// special exception, we must invert the bool for this property only.
if(x.equals("router.hiddenMode")) {
String string = all.get(x).toString();
String what="true";
if(string.equals(what)) {
what="false";
}
props.setProperty(x, what);
} else if ( x.startsWith("stat.summaries.")) {
String stat = x.substring("stat.summaries.".length());
String checked = all.get(x).toString();
if (checked.equals("true")) {
statSummaries.add(stat);
}
} else if ( x.startsWith("logger.")) {
logSettings.put(x, all.get(x).toString());
} else if ( x.startsWith("i2pandroid.")) {
// Don't save UI-related I2P Android settings in router.config
continue;
} else if(! x.startsWith("DO_NOT_SAVE")) {
// Disabled?
@SuppressWarnings("deprecation")
Preference findPreference = findPreference(x);
if (findPreference == null)
continue;
if ( findPreference.isEnabled() ) {
String string = all.get(x).toString();
props.setProperty(x, string);
} else {
String summary[] = findPreference.getSummary().toString().split("default=");
String defaultval = summary[summary.length - 1].trim();
if (defaultval.endsWith(")")) {
// strip the ")" off the tail end, this is the default value!
String string = defaultval.substring(0, defaultval.length() - 1);
Util.d("Resetting property '" + x + "' to default '" + string +"'");
props.setProperty(x, string);
}
}
}
}
if (statSummaries.isEmpty()) {
props.setProperty("stat.summaries", "");
} else {
Iterator<String> iter = statSummaries.iterator();
StringBuilder buf = new StringBuilder(iter.next());
while (iter.hasNext()) {
buf.append(",").append(iter.next());
}
props.setProperty("stat.summaries", buf.toString());
}
// Merge in new config settings, write the file.
InitActivities init = new InitActivities(this);
init.mergeResourceToFile(R.raw.router_config, "router.config", props);
// Apply new config if we are running.
List<RouterContext> contexts = RouterContext.listContexts();
if ( !((contexts == null) || (contexts.isEmpty())) ) {
RouterContext _context = contexts.get(0);
_context.router().saveConfig(props, null);
RouterContext rCtx = Util.getRouterContext();
if (rCtx != null) {
rCtx.router().saveConfig(props, toRemove);
// Merge in new log settings
saveLoggingChanges(_context, logSettings);
saveLoggingChanges(rCtx, logSettings);
} else {
// Merge in new config settings, write the file.
Util.mergeResourceToFile(this, Util.getFileDir(this), "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(this, R.string.settings_router_restart_required, Toast.LENGTH_LONG).show();
}
private void saveLoggingChanges(RouterContext ctx, Map<String, String> logSettings) {
private void saveLoggingChanges(I2PAppContext ctx, Properties logSettings) {
boolean shouldSave = false;
for (String key : logSettings.keySet()) {
for (Object key : logSettings.keySet()) {
if ("logger.defaultLevel".equals(key)) {
String defaultLevel = logSettings.get(key);
String defaultLevel = (String) logSettings.get(key);
String oldDefault = ctx.logManager().getDefaultLimit();
if (!defaultLevel.equals(oldDefault)) {
shouldSave = true;
@ -288,16 +268,13 @@ public class SettingsActivity extends PreferenceActivity {
addPreferencesFromResource(R.xml.settings_net);
} else if ("graphs".equals(settings)) {
addPreferencesFromResource(R.xml.settings_graphs);
RouterContext ctx = getRouterContext();
if (ctx != null)
setupGraphSettings(getActivity(), getPreferenceScreen(), ctx);
setupGraphSettings(getActivity(), getPreferenceScreen(), Util.getRouterContext());
} else if ("logging".equals(settings)) {
addPreferencesFromResource(R.xml.settings_logging);
RouterContext ctx = getRouterContext();
if (ctx != null)
setupLoggingSettings(getActivity(), getPreferenceScreen(), ctx);
setupLoggingSettings(getActivity(), getPreferenceScreen(), Util.getRouterContext());
} else if ("advanced".equals(settings)) {
addPreferencesFromResource(R.xml.settings_advanced);
setupAdvancedSettings(getActivity(), getPreferenceScreen(), Util.getRouterContext());
}
}
}

View File

@ -14,7 +14,7 @@ public class AddressEntryAdapter extends ArrayAdapter<AddressEntry> {
private final LayoutInflater mInflater;
public AddressEntryAdapter(Context context) {
super(context, R.layout.addressbook_list_item);
super(context, R.layout.listitem_text);
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@ -29,7 +29,7 @@ public class AddressEntryAdapter extends ArrayAdapter<AddressEntry> {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = mInflater.inflate(R.layout.addressbook_list_item, parent, false);
View v = mInflater.inflate(R.layout.listitem_text, parent, false);
AddressEntry address = getItem(position);
TextView text = (TextView) v.findViewById(R.id.text);

View File

@ -1,38 +1,43 @@
package net.i2p.android.router.addressbook;
import android.content.Context;
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.Util;
import net.i2p.client.naming.NamingService;
import net.i2p.data.Destination;
import net.i2p.router.RouterContext;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import net.i2p.android.router.util.NamingServiceUtil;
import net.i2p.android.router.util.Util;
import net.i2p.client.naming.NamingService;
import net.i2p.data.Destination;
import net.i2p.router.RouterContext;
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
public class AddressEntryLoader extends AsyncTaskLoader<List<AddressEntry>> {
private RouterContext mRContext;
private I2PFragmentBase.RouterContextProvider mRContextProvider;
private String mBook;
private String mFilter;
private List<AddressEntry> mData;
public AddressEntryLoader(Context context, RouterContext rContext,
public AddressEntryLoader(Context context, I2PFragmentBase.RouterContextProvider rContextProvider,
String book, String filter) {
super(context);
mRContext = rContext;
mRContextProvider = rContextProvider;
mBook = book;
mFilter = filter;
}
@Override
public List<AddressEntry> loadInBackground() {
RouterContext routerContext = mRContextProvider.getRouterContext();
if (routerContext == null)
return null;
// get the names
NamingService ns = NamingServiceUtil.getNamingService(mRContext, mBook);
NamingService ns = NamingServiceUtil.getNamingService(routerContext, mBook);
Util.d("NamingService: " + ns.getName());
// After router shutdown we get nothing... why?
List<AddressEntry> ret = new ArrayList<AddressEntry>();

View File

@ -1,9 +1,5 @@
package net.i2p.android.router.addressbook;
import net.i2p.android.router.I2PActivityBase;
import net.i2p.android.router.R;
import net.i2p.android.router.web.WebActivity;
import net.i2p.android.router.web.WebFragment;
import android.app.Activity;
import android.app.SearchManager;
import android.content.Context;
@ -12,11 +8,16 @@ import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBar.Tab;
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,
@ -27,47 +28,36 @@ public class AddressbookActivity extends I2PActivityBase
*/
private boolean mTwoPane;
private static final String SELECTED_TAB = "selected_tab";
private static final String SELECTED_PAGE = "selected_page";
private static final int PAGE_ROUTER = 0;
private Spinner mSpinner;
@Override
protected boolean canUseTwoPanes() {
return true;
return false;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set up action bar for tabs
ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
mSpinner = (Spinner) findViewById(R.id.main_spinner);
mSpinner.setVisibility(View.VISIBLE);
// Router book tab
AddressbookFragment rf = new AddressbookFragment();
Bundle args = new Bundle();
args.putString(AddressbookFragment.BOOK_NAME,
AddressbookFragment.ROUTER_BOOK);
rf.setArguments(args);
Tab tab = actionBar.newTab()
.setText("Router")
.setTabListener(new TabListener(rf));
actionBar.addTab(tab);
mSpinner.setAdapter(ArrayAdapter.createFromResource(this,
R.array.addressbook_pages, android.R.layout.simple_spinner_dropdown_item));
// Private book tab
AddressbookFragment pf = new AddressbookFragment();
args = new Bundle();
args.putString(AddressbookFragment.BOOK_NAME,
AddressbookFragment.PRIVATE_BOOK);
pf.setArguments(args);
tab = actionBar.newTab()
.setText("Private")
.setTabListener(new TabListener(pf));
actionBar.addTab(tab);
mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
selectPage(i);
}
if (savedInstanceState != null) {
int selected = savedInstanceState.getInt(SELECTED_TAB);
actionBar.setSelectedNavigationItem(selected);
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
if (findViewById(R.id.detail_fragment) != null) {
// The detail container view will be present only in the
@ -76,13 +66,30 @@ public class AddressbookActivity extends I2PActivityBase
// 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_TAB,
getSupportActionBar().getSelectedNavigationIndex());
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
@ -99,18 +106,14 @@ public class AddressbookActivity extends I2PActivityBase
// AddressbookFragment.OnAddressSelectedListener
public void onAddressSelected(CharSequence host) {
if (getIntent().getAction() == Intent.ACTION_PICK) {
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));
// XXX: Temporarily reverting to inbuilt browser
// until an alternative browser is ready.
Intent i = new Intent(this, WebActivity.class);
i.putExtra(WebFragment.HTML_URI, "http://" + host + '/');
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse("http://" + host));
startActivity(i);
}
}

View File

@ -7,25 +7,29 @@ import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.text.TextUtils;
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.FrameLayout;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.List;
import net.i2p.addressbook.Daemon;
import net.i2p.android.router.HelpActivity;
import net.i2p.android.help.HelpActivity;
import net.i2p.android.router.I2PFragmentBase;
import net.i2p.android.router.R;
import net.i2p.android.router.I2PFragmentBase.RouterContextProvider;
import net.i2p.android.router.R;
import net.i2p.android.router.util.NamingServiceUtil;
import net.i2p.client.naming.NamingService;
import net.i2p.router.RouterContext;
import java.util.List;
public class AddressbookFragment extends ListFragment implements
I2PFragmentBase.RouterContextUser,
LoaderManager.LoaderCallbacks<List<AddressEntry>> {
@ -34,18 +38,20 @@ public class AddressbookFragment extends ListFragment implements
public static final String PRIVATE_BOOK = "privatehosts.txt";
public static final String ADD_WIZARD_DATA = "add_wizard_data";
static final int ADD_WIZARD_REQUEST = 1;
private static final int ADD_WIZARD_REQUEST = 1;
private static final int ROUTER_LOADER_ID = 1;
private static final int PRIVATE_LOADER_ID = 2;
private boolean mOnActivityCreated;
RouterContextProvider mRouterContextProvider;
OnAddressSelectedListener mCallback;
private RouterContextProvider mRouterContextProvider;
private OnAddressSelectedListener mCallback;
private AddressEntryAdapter mAdapter;
private String mBook;
private String mCurFilter;
private ImageButton mAddToAddressbook;
// Set in onActivityResult()
private Intent mAddWizardData;
@ -84,6 +90,27 @@ public class AddressbookFragment extends ListFragment implements
setHasOptionsMenu(true);
}
@Override
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);
FrameLayout listContainer = (FrameLayout) v.findViewById(R.id.list_container);
listContainer.addView(listFragmentView);
mAddToAddressbook = (ImageButton) v.findViewById(R.id.promoted_action);
mAddToAddressbook.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent wi = new Intent(getActivity(), AddressbookAddWizardActivity.class);
startActivityForResult(wi, ADD_WIZARD_REQUEST);
}
});
return v;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
@ -111,8 +138,8 @@ public class AddressbookFragment extends ListFragment implements
// Show actions
if (mSearchAddressbook != null)
mSearchAddressbook.setVisible(true);
if (mAddToAddressbook != null)
mAddToAddressbook.setVisible(false);
if (mAddToAddressbook != null && mAddToAddressbook.getVisibility() != View.VISIBLE)
mAddToAddressbook.setVisibility(View.VISIBLE);
if (mAddWizardData != null) {
// Save the new entry
@ -140,24 +167,26 @@ public class AddressbookFragment extends ListFragment implements
}
private MenuItem mSearchAddressbook;
private MenuItem mAddToAddressbook;
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.fragment_addressbook_actions, menu);
mSearchAddressbook = menu.findItem(R.id.action_search_addressbook);
mAddToAddressbook = menu.findItem(R.id.action_add_to_addressbook);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
// Hide until needed
if (getRouterContext() == null) {
mSearchAddressbook.setVisible(false);
mAddToAddressbook.setVisible(false);
if (mAddToAddressbook != null)
mAddToAddressbook.setVisibility(View.GONE);
}
// Only allow adding to private book
if (!PRIVATE_BOOK.equals(mBook)) {
mAddToAddressbook.setVisible(false);
if (!PRIVATE_BOOK.equals(mBook) && mAddToAddressbook != null) {
mAddToAddressbook.setVisibility(View.GONE);
mAddToAddressbook = null;
}
}
@ -167,27 +196,22 @@ public class AddressbookFragment extends ListFragment implements
// Handle presses on the action bar items
switch (item.getItemId()) {
case R.id.action_add_to_addressbook:
Intent wi = new Intent(getActivity(), AddressbookAddWizardActivity.class);
startActivityForResult(wi, ADD_WIZARD_REQUEST);
return true;
case R.id.action_reload_subscriptions:
Daemon.wakeup();
Toast.makeText(getActivity(), "Reloading subscriptions...",
Toast.LENGTH_SHORT).show();
return true;
case R.id.action_addressbook_settings:
Intent si = new Intent(getActivity(), AddressbookSettingsActivity.class);
startActivity(si);
return true;
// TODO: Enable when Help page finished
//case R.id.action_addressbook_help:
// Intent hi = new Intent(getActivity(), HelpActivity.class);
// hi.putExtra(HelpActivity.REFERRER, "addressbook");
// startActivity(hi);
// return true;
default:
return super.onOptionsItemSelected(item);
case R.id.action_reload_subscriptions:
Daemon.wakeup();
Toast.makeText(getActivity(), "Reloading subscriptions...",
Toast.LENGTH_SHORT).show();
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:
return super.onOptionsItemSelected(item);
}
}
@ -225,27 +249,32 @@ public class AddressbookFragment extends ListFragment implements
public Loader<List<AddressEntry>> onCreateLoader(int id, Bundle args) {
return new AddressEntryLoader(getActivity(),
getRouterContext(), mBook, mCurFilter);
mRouterContextProvider, mBook, mCurFilter);
}
public void onLoadFinished(Loader<List<AddressEntry>> loader,
List<AddressEntry> data) {
List<AddressEntry> data) {
if (loader.getId() == (PRIVATE_BOOK.equals(mBook) ?
PRIVATE_LOADER_ID : ROUTER_LOADER_ID)) {
mAdapter.setData(data);
if (data == null)
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("");
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);
if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
}
}
}

View File

@ -1,23 +1,25 @@
package net.i2p.android.router.addressbook;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.Menu;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import net.i2p.android.router.R;
import net.i2p.util.FileUtil;
public class AddressbookSettingsActivity extends Activity {
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
protected EditText text_content_subscriptions;
protected Button btn_save_subscriptions;
public class AddressbookSettingsActivity extends ActionBarActivity {
private EditText text_content_subscriptions;
private Button btn_save_subscriptions;
private String filename = "/addressbook/subscriptions.txt";
private File i2pDir;
@ -25,6 +27,12 @@ public class AddressbookSettingsActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_addressbook_settings);
// Set the action bar
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
text_content_subscriptions = (EditText) findViewById(R.id.subscriptions_content);
btn_save_subscriptions = (Button) findViewById(R.id.button_save_subscriptions);
init_actions();
@ -32,12 +40,6 @@ public class AddressbookSettingsActivity extends Activity {
load();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_addressbook_settings, menu);
return true;
}
private void init_actions() {
btn_save_subscriptions.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {

View File

@ -1,10 +1,13 @@
package net.i2p.android.router.dialog;
import net.i2p.android.router.LicenseActivity;
import net.i2p.android.router.R;
import net.i2p.android.router.util.I2Patterns;
import net.i2p.android.router.util.Util;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.text.util.Linkify;
@ -33,7 +36,14 @@ public class AboutDialog extends DialogFragment {
AlertDialog.Builder b = new AlertDialog.Builder(getActivity());
b.setTitle(R.string.menu_about)
.setView(view);
.setView(view)
.setNeutralButton(R.string.label_licenses, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
Intent lic = new Intent(getActivity(), LicenseActivity.class);
startActivity(lic);
}
});
return b.create();
}
}

View File

@ -0,0 +1,42 @@
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.v4.app.DialogFragment;
import android.text.util.Linkify;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import net.i2p.android.help.BrowserConfigActivity;
import net.i2p.android.help.HelpActivity;
import net.i2p.android.router.R;
import net.i2p.android.router.util.I2Patterns;
public class ConfigureBrowserDialog extends DialogFragment {
@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();
}
}

View File

@ -4,6 +4,7 @@ import net.i2p.android.router.R;
import net.i2p.android.router.util.I2Patterns;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.text.util.Linkify;
@ -13,7 +14,7 @@ import android.widget.TextView;
public class FirstStartDialog extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle SavedInstanceState) {
public Dialog onCreateDialog(Bundle savedInstanceState) {
LayoutInflater li = LayoutInflater.from(getActivity());
View view = li.inflate(R.layout.fragment_dialog_first_start, null);
@ -24,7 +25,7 @@ public class FirstStartDialog extends DialogFragment {
AlertDialog.Builder b = new AlertDialog.Builder(getActivity());
b.setTitle(R.string.first_start_title)
.setView(view);
.setView(view);
return b.create();
}
}

View File

@ -3,17 +3,18 @@ package net.i2p.android.router.dialog;
import android.content.res.Resources;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.text.method.ScrollingMovementMethod;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import net.i2p.android.router.R;
import net.i2p.android.router.util.Util;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import net.i2p.android.router.R;
import net.i2p.android.router.util.Util;
/**
* Display a raw text resource.
@ -30,7 +31,6 @@ public class TextResourceDialog extends DialogFragment {
{
View v = inflater.inflate(R.layout.fragment_dialog_text_resource, container, false);
TextView tv = (TextView) v.findViewById(R.id.text_resource_text);
tv.setMovementMethod(ScrollingMovementMethod.getInstance());
String title = getArguments().getString(TEXT_DIALOG_TITLE);
if (title != null)
getDialog().setTitle(title);

View File

@ -1,17 +1,19 @@
package net.i2p.android.router.log;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
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;
import net.i2p.android.router.SettingsActivity;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.ArrayAdapter;
import android.widget.SpinnerAdapter;
public class LogActivity extends I2PActivityBase implements
LogFragment.OnEntrySelectedListener {
@ -23,6 +25,9 @@ public class LogActivity extends I2PActivityBase implements
private static final String SELECTED_LEVEL = "selected_level";
private String[] mLevels;
private Spinner mSpinner;
@Override
protected boolean canUseTwoPanes() {
return true;
@ -32,9 +37,10 @@ public class LogActivity extends I2PActivityBase implements
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set up action bar for drop-down list
ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
mLevels = getResources().getStringArray(R.array.log_level_list);
mSpinner = (Spinner) findViewById(R.id.main_spinner);
mSpinner.setVisibility(View.VISIBLE);
mDrawerToggle.setDrawerIndicatorEnabled(false);
@ -46,31 +52,36 @@ public class LogActivity extends I2PActivityBase implements
mTwoPane = true;
}
SpinnerAdapter mSpinnerAdapter = ArrayAdapter.createFromResource(this,
R.array.log_level_list, android.R.layout.simple_spinner_dropdown_item);
mSpinner.setAdapter(ArrayAdapter.createFromResource(this,
R.array.log_level_list, android.R.layout.simple_spinner_dropdown_item));
ActionBar.OnNavigationListener mNavigationListener = new ActionBar.OnNavigationListener() {
String[] levels = getResources().getStringArray(R.array.log_level_list);
public boolean onNavigationItemSelected(int position, long itemId) {
String level = levels[position];
LogFragment f = LogFragment.newInstance(level);
// 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, levels[position]).commit();
return true;
mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
selectLevel(i);
}
};
actionBar.setListNavigationCallbacks(mSpinnerAdapter, mNavigationListener);
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
if (savedInstanceState != null) {
int selected = savedInstanceState.getInt(SELECTED_LEVEL);
actionBar.setSelectedNavigationItem(selected);
}
mSpinner.setSelection(selected);
} else
selectLevel(0);
}
private void selectLevel(int i) {
String level = mLevels[i];
LogFragment f = LogFragment.newInstance(level);
// 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, level).commit();
}
@Override
@ -106,8 +117,7 @@ public class LogActivity extends I2PActivityBase implements
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(SELECTED_LEVEL,
getSupportActionBar().getSelectedNavigationIndex());
outState.putInt(SELECTED_LEVEL, mSpinner.getSelectedItemPosition());
}
// LogFragment.OnEntrySelectedListener

View File

@ -0,0 +1,77 @@
package net.i2p.android.router.log;
import net.i2p.android.router.I2PFragmentBase;
import net.i2p.android.router.R;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
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.TextView;
import android.widget.Toast;
public class LogDetailFragment extends I2PFragmentBase {
public static final String LOG_ENTRY = "log_entry";
private String mEntry;
public static LogDetailFragment newInstance (String entry) {
LogDetailFragment f = new LogDetailFragment();
Bundle args = new Bundle();
args.putString(LOG_ENTRY, entry);
f.setArguments(args);
return f;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_log_entry, container, false);
mEntry = getArguments().getString(LOG_ENTRY);
TextView tv = (TextView) v.findViewById(R.id.log_entry);
tv.setMovementMethod(new ScrollingMovementMethod());
tv.setText(mEntry);
return v;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.fragment_log_actions, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle presses on the action bar items
switch (item.getItemId()) {
case R.id.action_copy_logs:
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
android.text.ClipboardManager clipboard = (android.text.ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
clipboard.setText(mEntry);
} else {
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();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}

View File

@ -1,13 +1,21 @@
package net.i2p.android.router.log;
import android.app.Activity;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
import net.i2p.I2PAppContext;
import net.i2p.android.router.R;
@ -25,6 +33,7 @@ public class LogFragment extends ListFragment implements
private static final int LEVEL_ALL = 2;
OnEntrySelectedListener mEntrySelectedCallback;
private final List<String> mLogEntries = new ArrayList<String>();
private LogAdapter mAdapter;
private TextView mHeaderView;
private String mLogLevel;
@ -34,6 +43,8 @@ public class LogFragment extends ListFragment implements
private int mActivatedPosition = ListView.INVALID_POSITION;
private boolean mActivateOnItemClick = false;
private MenuItem mCopyLogs;
// Container Activity must implement this interface
public interface OnEntrySelectedListener {
public void onEntrySelected(String entry);
@ -62,6 +73,12 @@ public class LogFragment extends ListFragment implements
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
@ -122,6 +139,53 @@ public class LogFragment extends ListFragment implements
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.fragment_log_actions, menu);
mCopyLogs = menu.findItem(R.id.action_copy_logs);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
mCopyLogs.setVisible(I2PAppContext.getCurrentContext() != null);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle presses on the action bar items
switch (item.getItemId()) {
case R.id.action_copy_logs:
String logText = "";
synchronized (mLogEntries) {
for (String logEntry : mLogEntries) {
logText += logEntry;
}
}
boolean isError = "ERROR".equals(mLogLevel);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
android.text.ClipboardManager clipboard = (android.text.ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
clipboard.setText(logText);
} else {
android.content.ClipboardManager clipboard = (android.content.ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
android.content.ClipData clip = android.content.ClipData.newPlainText(
isError ? getString(R.string.i2p_android_error_logs) : getString(R.string.i2p_android_logs),
logText);
clipboard.setPrimaryClip(clip);
}
int textId;
if (isError)
textId = R.string.error_logs_copied_to_clipboard;
else
textId = R.string.logs_copied_to_clipboard;
Toast.makeText(getActivity(), textId, Toast.LENGTH_SHORT).show();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
/**
* Turns on activate-on-click mode. When this mode is on, list items will be
* given the 'activated' state when touched.
@ -167,8 +231,12 @@ public class LogFragment extends ListFragment implements
List<String> data) {
if (loader.getId() == ("ERROR".equals(mLogLevel) ?
LEVEL_ERROR : LEVEL_ALL)) {
synchronized (mLogEntries) {
mLogEntries.clear();
mLogEntries.addAll(data);
}
mAdapter.setData(data);
String header = getHeader(data.size(), (mLogLevel == "ERROR"));
String header = getHeader(data.size(), ("ERROR".equals(mLogLevel)));
mHeaderView.setText(header);
if (isResumed()) {

View File

@ -0,0 +1,120 @@
package net.i2p.android.router.netdb;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
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;
import net.i2p.data.Hash;
public class NetDbActivity extends I2PActivityBase implements
NetDbListFragment.OnEntrySelectedListener {
/**
* 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_STATS = 0;
private static final int PAGE_ROUTERS = 1;
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.netdb_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_STATS);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(SELECTED_PAGE, mSpinner.getSelectedItemPosition());
}
private void selectPage(int page) {
Fragment f;
if (page == PAGE_STATS)
f = new NetDbSummaryPagerFragment();
else {
f = new NetDbListFragment();
Bundle args = new Bundle();
args.putBoolean(NetDbListFragment.SHOW_ROUTERS, page == PAGE_ROUTERS);
f.setArguments(args);
// In two-pane mode, list items should be given the
// 'activated' state when touched.
if (mTwoPane)
((NetDbListFragment) f).setActivateOnItemClick(true);
}
getSupportFragmentManager().beginTransaction()
.replace(R.id.main_fragment, f).commit();
}
// NetDbListFragment.OnEntrySelectedListener
public void onEntrySelected(boolean isRouterInfo, Hash entryHash) {
if (mTwoPane) {
// In two-pane mode, show the detail view in this activity by
// adding or replacing the detail fragment using a
// fragment transaction.
NetDbDetailFragment detailFrag = NetDbDetailFragment.newInstance(
isRouterInfo, entryHash);
getSupportFragmentManager().beginTransaction()
.replace(R.id.detail_fragment, detailFrag).commit();
// If we are coming from a LS to a RI, change the tab
int currentTab = mSpinner.getSelectedItemPosition();
if (isRouterInfo && currentTab != PAGE_ROUTERS)
selectPage(PAGE_ROUTERS);
} else {
// In single-pane mode, simply start the detail activity
// for the selected item ID.
Intent detailIntent = new Intent(this, NetDbDetailActivity.class);
detailIntent.putExtra(NetDbDetailFragment.IS_RI, isRouterInfo);
detailIntent.putExtra(NetDbDetailFragment.ENTRY_HASH,
entryHash.toBase64());
startActivity(detailIntent);
}
}
}

View File

@ -1,19 +1,5 @@
package net.i2p.android.router.netdb;
import java.util.Map;
import java.util.Set;
import net.i2p.android.router.I2PFragmentBase;
import net.i2p.android.router.R;
import net.i2p.android.router.netdb.NetDbListFragment.OnEntrySelectedListener;
import net.i2p.android.router.util.Util;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.data.Lease;
import net.i2p.data.LeaseSet;
import net.i2p.data.RouterAddress;
import net.i2p.data.RouterInfo;
import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
@ -26,6 +12,20 @@ import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
import net.i2p.android.router.I2PFragmentBase;
import net.i2p.android.router.R;
import net.i2p.android.router.netdb.NetDbListFragment.OnEntrySelectedListener;
import net.i2p.android.router.util.Util;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.data.Lease;
import net.i2p.data.LeaseSet;
import net.i2p.data.router.RouterAddress;
import net.i2p.data.router.RouterInfo;
import java.util.Map;
public class NetDbDetailFragment extends I2PFragmentBase {
public static final String IS_RI = "is_routerinfo";
public static final String ENTRY_HASH = "entry_hash";
@ -131,7 +131,7 @@ public class NetDbDetailFragment extends I2PFragmentBase {
TableLayout stats = (TableLayout) getView().findViewById(R.id.ri_stats);
Map<Object, Object> p = ri.getOptionsMap();
for (Map.Entry<Object,Object> e : (Set<Map.Entry<Object,Object>>) p.entrySet()) {
for (Map.Entry<Object,Object> e : p.entrySet()) {
String key = (String)e.getKey();
String val = (String)e.getValue();
addTableRow(stats, DataHelper.stripHTML(key), DataHelper.stripHTML(val));
@ -149,7 +149,7 @@ public class NetDbDetailFragment extends I2PFragmentBase {
addTableRow(table, "cost", ""+cost);
Map<Object, Object> p = addr.getOptionsMap();
for (Map.Entry<Object,Object> e : (Set<Map.Entry<Object,Object>>) p.entrySet()) {
for (Map.Entry<Object,Object> e : p.entrySet()) {
String key = (String)e.getKey();
String val = (String)e.getValue();
addTableRow(table, DataHelper.stripHTML(key), DataHelper.stripHTML(val));

View File

@ -7,7 +7,7 @@ import net.i2p.data.DatabaseEntry;
import net.i2p.data.Destination;
import net.i2p.data.Hash;
import net.i2p.data.LeaseSet;
import net.i2p.data.RouterInfo;
import net.i2p.data.router.RouterInfo;
import net.i2p.router.RouterContext;
import net.i2p.router.TunnelPoolSettings;
@ -31,7 +31,7 @@ public class NetDbEntry {
}
public static NetDbEntry fromLeaseSet(RouterContext ctx, LeaseSet ls) {
String nick = "";
String nick;
boolean local = false;
boolean unpublished = false;
Destination dest = ls.getDestination();
@ -55,8 +55,8 @@ public class NetDbEntry {
return new NetDbEntry(ls, nick, local, unpublished);
}
public NetDbEntry(RouterInfo ri,
boolean isUs, String country) {
private NetDbEntry(RouterInfo ri,
boolean isUs, String country) {
mIsRI = true;
mEntry = ri;
@ -67,8 +67,8 @@ public class NetDbEntry {
mLocal = mUnpublished = false;
}
public NetDbEntry(LeaseSet ls,
String nick, boolean local, boolean unpublished) {
private NetDbEntry(LeaseSet ls,
String nick, boolean local, boolean unpublished) {
mIsRI = false;
mEntry = ls;

View File

@ -8,7 +8,7 @@ import java.util.TreeSet;
import net.i2p.data.Destination;
import net.i2p.data.LeaseSet;
import net.i2p.data.RouterInfo;
import net.i2p.data.router.RouterInfo;
import net.i2p.router.RouterContext;
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;

View File

@ -1,14 +1,5 @@
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.I2PFragmentBase.RouterContextProvider;
import net.i2p.android.router.I2PFragmentBase.RouterContextUser;
import net.i2p.data.Hash;
import net.i2p.router.RouterContext;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
@ -20,6 +11,14 @@ import android.view.MenuItem;
import android.view.View;
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.data.Hash;
import net.i2p.router.RouterContext;
import java.util.List;
public class NetDbListFragment extends ListFragment implements
I2PFragmentBase.RouterContextUser,
LoaderManager.LoaderCallbacks<List<NetDbEntry>> {
@ -34,8 +33,8 @@ public class NetDbListFragment extends ListFragment implements
private static final String STATE_ACTIVATED_POSITION = "activated_position";
private boolean mOnActivityCreated;
RouterContextProvider mRouterContextProvider;
OnEntrySelectedListener mEntrySelectedCallback;
private RouterContextProvider mRouterContextProvider;
private OnEntrySelectedListener mEntrySelectedCallback;
private NetDbEntryAdapter mAdapter;
private boolean mRouters;
/**

View File

@ -7,8 +7,8 @@ import java.util.Set;
import java.util.TreeSet;
import net.i2p.data.Hash;
import net.i2p.data.RouterAddress;
import net.i2p.data.RouterInfo;
import net.i2p.data.router.RouterAddress;
import net.i2p.data.router.RouterInfo;
import net.i2p.router.RouterContext;
import net.i2p.util.ObjectCounter;
import android.content.Context;
@ -37,7 +37,7 @@ public class NetDbStatsLoader extends AsyncTaskLoader<List<ObjectCounter<String>
ObjectCounter<String> countries = new ObjectCounter<String>();
ObjectCounter<String> transports = new ObjectCounter<String>();
if (mRContext.netDb().isInitialized()) {
if (mRContext != null && mRContext.netDb() != null && mRContext.netDb().isInitialized()) {
Hash us = mRContext.routerHash();
Set<RouterInfo> routers = new TreeSet<RouterInfo>(new RouterInfoComparator());

View File

@ -22,7 +22,7 @@ import android.view.ViewGroup;
public class NetDbSummaryPagerFragment extends I2PFragmentBase implements
LoaderManager.LoaderCallbacks<List<ObjectCounter<String>>> {
NetDbPagerAdapter mNetDbPagerAdapter;
private NetDbPagerAdapter mNetDbPagerAdapter;
ViewPager mViewPager;
@Override

View File

@ -14,6 +14,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import net.i2p.android.apps.EepGetFetcher;
import net.i2p.android.router.BuildConfig;
import net.i2p.android.router.util.AppCache;
import net.i2p.android.router.util.Util;
@ -45,7 +46,7 @@ public class CacheProvider extends ContentProvider {
//private static final String NONCE = Integer.toString(Math.abs((new java.util.Random()).nextInt()));
private static final String NONCE = "0";
private static final String SCHEME = "content";
public static final String AUTHORITY = "net.i2p.android.router";
public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".provider";
/** includes the nonce */
public static final Uri CONTENT_URI = Uri.parse(SCHEME + "://" + AUTHORITY + '/' + NONCE);

View File

@ -11,6 +11,7 @@ import android.net.NetworkInfo;
import android.os.IBinder;
import net.i2p.android.router.service.RouterBinder;
import net.i2p.android.router.service.RouterService;
import net.i2p.android.router.util.Connectivity;
import net.i2p.android.router.util.Util;
public class I2PReceiver extends BroadcastReceiver {
@ -33,7 +34,7 @@ public class I2PReceiver extends BroadcastReceiver {
intents.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
@SuppressWarnings("LeakingThisInConstructor")
Intent registerReceiver = context.registerReceiver(this, intents);
_wasConnected = Util.isConnected(context);
_wasConnected = Connectivity.isConnected(context);
}
public void onReceive(Context context, Intent intent) {
@ -57,7 +58,7 @@ public class I2PReceiver extends BroadcastReceiver {
if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION) ||
action.equals(Intent.ACTION_TIME_TICK)) {
boolean connected = Util.isConnected(context);
boolean connected = Connectivity.isConnected(context);
if (_wasConnected && !connected) {
// notify + 2 timer ticks
if (++_unconnectedCount >= 3) {

View File

@ -0,0 +1,25 @@
package net.i2p.android.router.receiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import net.i2p.android.router.I2PConstants;
import net.i2p.android.router.service.RouterService;
public class OnBootReceiver extends BroadcastReceiver implements I2PConstants {
public static final String PREF_START_ON_BOOT = ANDROID_PREF_PREFIX + "startOnBoot";
@Override
public void onReceive(Context context, Intent intent) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean startOnBoot = prefs.getBoolean(PREF_START_ON_BOOT, false);
if (startOnBoot) {
Intent routerService = new Intent(context, RouterService.class);
context.startService(routerService);
}
}
}

View File

@ -49,7 +49,7 @@ class LoadClientsJob extends JobImpl {
getTiming().setStartAfter(getContext().clock().now() + LOAD_DELAY);
}
public String getName() { return "Start Clients"; };
public String getName() { return "Start Clients"; }
public void runJob() {
Job j = new RunI2PTunnel(getContext());
@ -83,7 +83,7 @@ class LoadClientsJob extends JobImpl {
super(ctx);
}
public String getName() { return "Start I2P Tunnel"; };
public String getName() { return "Start I2P Tunnel"; }
public void runJob() {
Util.d("Starting i2ptunnel");

View File

@ -20,6 +20,7 @@ import java.util.Random;
import net.i2p.android.router.R;
import net.i2p.android.router.receiver.I2PReceiver;
import net.i2p.android.router.util.Connectivity;
import net.i2p.android.router.util.Notifications;
import net.i2p.android.router.util.Util;
import net.i2p.data.DataHelper;
@ -34,18 +35,6 @@ import net.i2p.util.OrderedProperties;
*/
public class RouterService extends Service {
// These states persist even if we died... Yuck, it causes issues.
public enum State {
INIT, WAITING, STARTING, RUNNING, ACTIVE,
// unplanned (router stopped itself), next: killSelf()
STOPPING, STOPPED,
// button, don't kill service when stopped, stay in MANUAL_STOPPED
MANUAL_STOPPING, MANUAL_STOPPED,
// button, DO kill service when stopped, next: killSelf()
MANUAL_QUITTING, MANUAL_QUITTED,
// Stopped by listener (no network), next: WAITING (spin waiting for network)
NETWORK_STOPPING, NETWORK_STOPPED
}
private RouterContext _context;
private String _myDir;
//private String _apkPath;
@ -127,7 +116,7 @@ public class RouterService extends Service {
return START_NOT_STICKY;
}
_receiver = new I2PReceiver(this);
if(Util.isConnected(this)) {
if(Connectivity.isConnected(this)) {
if(restart) {
_statusBar.replace(StatusBar.ICON_STARTING, "I2P is restarting");
} else {
@ -161,7 +150,7 @@ public class RouterService extends Service {
Util.d(MARKER + this + " waiter handler"
+ " Current state is: " + _state);
if(_state == State.WAITING) {
if(Util.isConnected(RouterService.this)) {
if(Connectivity.isConnected(RouterService.this)) {
synchronized(_stateLock) {
if(_state != State.WAITING) {
return;
@ -334,12 +323,11 @@ public class RouterService extends Service {
return;
}
setState(State.RUNNING);
List<?> contexts = RouterContext.listContexts();
if((contexts == null) || (contexts.isEmpty())) {
_statusBar.replace(StatusBar.ICON_RUNNING, "I2P is running");
_context = Util.getRouterContext();
if (_context == null) {
throw new IllegalStateException("No contexts. This is usually because the router is either starting up or shutting down.");
}
_statusBar.replace(StatusBar.ICON_RUNNING, "I2P is running");
_context = (RouterContext) contexts.get(0);
_context.router().setKillVMOnEnd(false);
Job loadJob = new LoadClientsJob(_context, _notif);
_context.jobQueue().addJob(loadJob);
@ -457,8 +445,8 @@ public class RouterService extends Service {
return mStartCalled;
}
public String getState() throws RemoteException {
return _state.name();
public State getState() throws RemoteException {
return _state;
}
};
@ -596,7 +584,7 @@ public class RouterService extends Service {
public void handleMessage(Message msg) {
switch (msg.what) {
case STATE_MSG:
String state = _state.name();
final State state = _state;
// Broadcast to all clients the new state.
final int N = mStateCallbacks.beginBroadcast();
for (int i = 0; i < N; i++) {
@ -779,12 +767,11 @@ public class RouterService extends Service {
private State getSavedState() {
SharedPreferences prefs = getSharedPreferences(SHARED_PREFS, 0);
String stateString = prefs.getString(LAST_STATE, State.INIT.toString());
for(State s : State.values()) {
if(s.toString().equals(stateString)) {
return s;
}
try {
return State.valueOf(stateString);
} catch (IllegalArgumentException e) {
return State.INIT;
}
return State.INIT;
}
private void setState(State s) {

View File

@ -5,6 +5,7 @@ import java.util.List;
import java.util.StringTokenizer;
import java.util.concurrent.CopyOnWriteArrayList;
import net.i2p.android.router.util.Util;
import net.i2p.router.RouterContext;
import net.i2p.stat.Rate;
import net.i2p.stat.RateStat;
@ -18,15 +19,20 @@ public class StatSummarizer implements Runnable {
private Thread _thread;
public StatSummarizer() {
_context = RouterContext.listContexts().get(0);
_context = Util.getRouterContext();
_listeners = new CopyOnWriteArrayList<SummaryListener>();
_instance = this;
_context.addShutdownTask(new Shutdown());
if (_context != null)
_context.addShutdownTask(new Shutdown());
}
public static StatSummarizer instance() { return _instance; }
public void run() {
// We can't do anything without a RouterContext
if (_context == null)
return;
_thread = Thread.currentThread();
String specs = "";
while (_isRunning && _context.router().isAlive()) {

View File

@ -0,0 +1,167 @@
package net.i2p.android.router.stats;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
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;
import net.i2p.android.router.SettingsActivity;
import net.i2p.android.router.service.StatSummarizer;
import net.i2p.android.router.service.SummaryListener;
import net.i2p.stat.Rate;
import java.util.Comparator;
import java.util.List;
import java.util.TreeSet;
public class RateGraphActivity extends I2PActivityBase {
private static final String SELECTED_RATE = "selected_rate";
private String[] mRates;
private long[] mPeriods;
private Spinner mSpinner;
private boolean mFinishOnResume;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDrawerToggle.setDrawerIndicatorEnabled(false);
if (StatSummarizer.instance() != null) {
// Get the rates currently being graphed
List<SummaryListener> listeners = StatSummarizer.instance().getListeners();
TreeSet<SummaryListener> ordered = new TreeSet<SummaryListener>(new AlphaComparator());
ordered.addAll(listeners);
if (ordered.size() > 0) {
// Extract the rates and periods
mRates = new String[ordered.size()];
mPeriods = new long[ordered.size()];
int i = 0;
for (SummaryListener listener : ordered) {
Rate r = listener.getRate();
mRates[i] = r.getRateStat().getName();
mPeriods[i] = r.getPeriod();
i++;
}
mSpinner = (Spinner) findViewById(R.id.main_spinner);
mSpinner.setVisibility(View.VISIBLE);
mSpinner.setAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_dropdown_item, mRates));
mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
selectRate(i);
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
if (savedInstanceState != null) {
int selected = savedInstanceState.getInt(SELECTED_RATE);
mSpinner.setSelection(selected);
} else
selectRate(0);
} else {
DialogFragment df = new DialogFragment() {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage(R.string.no_graphs_configured)
.setPositiveButton(R.string.configure_graphs, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
mFinishOnResume = true;
Intent i = new Intent(RateGraphActivity.this, SettingsActivity.class);
// Navigation to a sub-category doesn't seem to work yet
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
i.setAction(SettingsActivity.ACTION_PREFS_GRAPHS);
} else {
i.putExtra("settings", "graphs");
}
startActivity(i);
}
})
.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() {
@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");
}
}
private void selectRate(int position) {
String rateName = mRates[position];
long period = mPeriods[position];
RateGraphFragment f = RateGraphFragment.newInstance(rateName, period);
getSupportFragmentManager().beginTransaction()
.replace(R.id.main_fragment, f, rateName).commit();
}
@Override
public void onResume() {
super.onResume();
if (mFinishOnResume) {
finish();
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mSpinner != null)
outState.putInt(SELECTED_RATE, mSpinner.getSelectedItemPosition());
}
private static class AlphaComparator implements Comparator<SummaryListener> {
public int compare(SummaryListener l, SummaryListener r) {
String lName = l.getRate().getRateStat().getName();
String rName = r.getRate().getRateStat().getName();
int rv = lName.compareTo(rName);
if (rv != 0)
return rv;
return (int) (l.getRate().getPeriod() - r.getRate().getPeriod());
}
}
}

View File

@ -0,0 +1,125 @@
package net.i2p.android.router.util;
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
public abstract class BetterAsyncTaskLoader<T> extends AsyncTaskLoader<T> {
protected T mData;
public BetterAsyncTaskLoader(Context context) {
super(context);
}
/**
* Called when there is new data to deliver to the client. The
* super class will take care of delivering it; the implementation
* here just adds a little more logic.
*/
@Override
public void deliverResult(T data) {
if (isReset()) {
// An async query came in while the loader is stopped. We
// don't need the result.
if (data != null) {
releaseResources(data);
}
}
// Hold a reference to the old data so it doesn't get garbage collected.
// We must protect it until the new data has been delivered.
T oldData = mData;
mData = data;
if (isStarted()) {
// If the Loader is currently started, we can immediately
// deliver its results.
super.deliverResult(data);
}
// Invalidate the old data as we don't need it any more.
if (oldData != null && oldData != data) {
releaseResources(oldData);
}
}
/**
* Handles a request to start the Loader.
*/
@Override
protected void onStartLoading() {
if (mData != null) {
// Deliver any previously loaded data immediately.
deliverResult(mData);
}
// Start watching for changes
onStartMonitoring();
if (takeContentChanged() || mData == null) {
// When the observer detects a change, it should call onContentChanged()
// on the Loader, which will cause the next call to takeContentChanged()
// to return true. If this is ever the case (or if the current data is
// null), we force a new load.
forceLoad();
}
}
/**
* Handles a request to stop the Loader.
*/
@Override
protected void onStopLoading() {
// The Loader is in a stopped state, so we should attempt to cancel the
// current load (if there is one).
cancelLoad();
// Note that we leave the observer as is. Loaders in a stopped state
// should still monitor the data source for changes so that the Loader
// will know to force a new load if it is ever started again.
}
/**
* Handles a request to completely reset the Loader.
*/
@Override
protected void onReset() {
super.onReset();
// Ensure the loader has been stopped.
onStopLoading();
// At this point we can release the resources associated with 'mData'.
if (mData != null) {
releaseResources(mData);
mData = null;
}
// Stop monitoring for changes.
onStopMonitoring();
}
/**
* Handles a request to cancel a load.
*/
@Override
public void onCanceled(T data) {
// Attempt to cancel the current asynchronous load.
super.onCanceled(data);
// The load has been canceled, so we should release the resources
// associated with 'data'.
releaseResources(data);
}
protected abstract void onStartMonitoring();
protected abstract void onStopMonitoring();
/**
* Helper function to take care of releasing resources associated
* with an actively loaded data set.
* For a simple List, there is nothing to do. For something like a Cursor, we
* would close it in this method. All resources associated with the Loader
* should be released here.
*/
protected abstract void releaseResources(T data);
}

View File

@ -0,0 +1,134 @@
// License: MIT
// http://opensource.org/licenses/MIT
package net.i2p.android.router.util;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.telephony.TelephonyManager;
/**
* Check device's network connectivity and speed.
*
* @author emil http://stackoverflow.com/users/220710/emil
* @author str4d
*/
public class Connectivity {
/**
* Get the network info.
*
* @param context the Context.
* @return the active NetworkInfo.
*/
public static NetworkInfo getNetworkInfo(Context context) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
return cm.getActiveNetworkInfo();
}
/**
* Check if there is any connectivity at all.
*
* @param context the Context.
* @return true if we are connected to a network, false otherwise.
*/
public static boolean isConnected(Context context) {
NetworkInfo info = Connectivity.getNetworkInfo(context);
// Works on emulator and devices.
// Note the use of isAvailable() - without this, isConnected() can
// return true when Wifi is disabled.
// http://stackoverflow.com/a/2937915
return (info != null && info.isAvailable() && info.isConnected());
}
/**
* Check if there is any connectivity to a Wifi network.
*
* @param context the Context.
* @return true if we are connected to a Wifi network, false otherwise.
*/
public static boolean isConnectedWifi(Context context) {
NetworkInfo info = Connectivity.getNetworkInfo(context);
return (info != null && info.isAvailable() && info.isConnected() &&
info.getType() == ConnectivityManager.TYPE_WIFI);
}
/**
* Check if there is any connectivity to a mobile network.
*
* @param context the Context.
* @return true if we are connected to a mobile network, false otherwise.
*/
public static boolean isConnectedMobile(Context context) {
NetworkInfo info = Connectivity.getNetworkInfo(context);
return (info != null && info.isAvailable() && info.isConnected() &&
info.getType() == ConnectivityManager.TYPE_MOBILE);
}
/**
* Check if there is fast connectivity.
*
* @param context the Context.
* @return true if we have "fast" connectivity, false otherwise.
*/
public static boolean isConnectedFast(Context context) {
NetworkInfo info = Connectivity.getNetworkInfo(context);
return (info != null && info.isAvailable() && info.isConnected() &&
Connectivity.isConnectionFast(info.getType(), info.getSubtype()));
}
/**
* Check if the connection is fast.
*
* @param type the network type.
* @param subType the network subtype.
* @return true if the provided type/subtype combination is classified as fast.
*/
public static boolean isConnectionFast(int type, int subType) {
if (type == ConnectivityManager.TYPE_WIFI) {
return true;
} else if (type == ConnectivityManager.TYPE_MOBILE) {
switch (subType) {
case TelephonyManager.NETWORK_TYPE_1xRTT:
return false; // ~ 50-100 kbps
case TelephonyManager.NETWORK_TYPE_CDMA:
return false; // ~ 14-64 kbps
case TelephonyManager.NETWORK_TYPE_EDGE:
return false; // ~ 50-100 kbps
case TelephonyManager.NETWORK_TYPE_EVDO_0:
return true; // ~ 400-1000 kbps
case TelephonyManager.NETWORK_TYPE_EVDO_A:
return true; // ~ 600-1400 kbps
case TelephonyManager.NETWORK_TYPE_GPRS:
return false; // ~ 100 kbps
case TelephonyManager.NETWORK_TYPE_HSDPA:
return true; // ~ 2-14 Mbps
case TelephonyManager.NETWORK_TYPE_HSPA:
return true; // ~ 700-1700 kbps
case TelephonyManager.NETWORK_TYPE_HSUPA:
return true; // ~ 1-23 Mbps
case TelephonyManager.NETWORK_TYPE_UMTS:
return true; // ~ 400-7000 kbps
/*
* Above API level 7, make sure to set android:targetSdkVersion
* to appropriate level to use these
*/
case TelephonyManager.NETWORK_TYPE_EHRPD: // API level 11
return true; // ~ 1-2 Mbps
case TelephonyManager.NETWORK_TYPE_EVDO_B: // API level 9
return true; // ~ 5 Mbps
case TelephonyManager.NETWORK_TYPE_HSPAP: // API level 13
return true; // ~ 10-20 Mbps
case TelephonyManager.NETWORK_TYPE_IDEN: // API level 8
return false; // ~25 kbps
case TelephonyManager.NETWORK_TYPE_LTE: // API level 11
return true; // ~ 10+ Mbps
// Unknown
case TelephonyManager.NETWORK_TYPE_UNKNOWN:
default:
return false;
}
} else {
return false;
}
}
}

View File

@ -0,0 +1,39 @@
package net.i2p.android.router.util;
import android.content.Context;
import android.preference.EditTextPreference;
import android.text.InputType;
import android.util.AttributeSet;
public class IntEditTextPreference extends EditTextPreference {
public IntEditTextPreference(Context context) {
super(context);
getEditText().setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED);
}
public IntEditTextPreference(Context context, AttributeSet attrs) {
super(context, attrs);
getEditText().setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED);
}
public IntEditTextPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
getEditText().setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED);
}
@Override
public CharSequence getSummary() {
return String.format((String) super.getSummary(), getText());
}
@Override
protected String getPersistedString(String defaultReturnValue) {
return String.valueOf(getPersistedInt(-1));
}
@Override
protected boolean persistString(String value) {
return persistInt(Integer.valueOf(value));
}
}

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