Compare commits

...

94 Commits

Author SHA1 Message Date
ffbd8cfb76 I2P Android 0.9.38 Release commit. 2019-01-27 14:23:08 +00:00
2d1664574d "Fix" for ticket #2404 2019-01-24 18:33:26 +00:00
5d3aa1f625 Strings update 2019-01-24 18:21:20 +00:00
3fa53c7654 Expanded code to hunt NPE - ticket #2389 2019-01-24 18:21:01 +00:00
84ecf55ff8 Play store required a new android number even the first wasn't published. 2018-10-10 18:07:28 +00:00
72ad40ecfc Hotfix update cause of play console warnings.
Short: We had to upgrade to sdk 26 to ensure the app can run on all devices.
2018-10-10 17:56:38 +00:00
2f48898235 Release commit for I2P Android 0.9.37 2018-10-10 17:00:45 +00:00
39758c8cf4 Disable the SSL option for now. See trac issue #2296 2018-10-10 16:09:19 +00:00
ecc5509007 Gradle wrapper update 2018-10-10 16:07:28 +00:00
0e75b3e957 Android studio gave me some warnings against using ellipsize and maxLines together and suggested singleLine. 2018-10-10 16:06:39 +00:00
7b4c80216d Translations update. 2018-10-10 16:03:39 +00:00
70bbc18054 merge of 'bc6eaecbdac3f21b425ed335e348b1c505094d70'
and 'cd8c7bf753e03b16fd44f0516d6fd4fbd649fc5f'
2018-08-27 16:14:56 +00:00
09fcef23a4 Updates for the new i2p android 0.9.36 release. 2018-08-26 15:45:25 +00:00
zzz
333f09073a minor updates from tx 2018-07-04 13:58:17 +00:00
58cb33aa77 Updates for the new i2p android 0.9.35 release. 2018-07-03 21:42:09 +00:00
9654fa24cc Pushing 0.9.34 changes. This is the release commit. 2018-04-25 14:34:08 +00:00
7843b37a7e Release process document 2018-02-18 21:17:41 +00:00
1db9128afc 0.9.33 2018-02-17 14:57:43 +00:00
c03d3a8b92 Updated translations 2018-02-17 14:07:52 +00:00
80b7455602 Add release date to CHANGELOG 2017-11-28 10:03:51 +00:00
1fcf5aa49b 0.9.32, helper 0.9.5 2017-11-28 09:44:50 +00:00
640803418d Bump to I2P 0.9.32 2017-11-28 09:42:39 +00:00
56fa0b0302 Rename _() for translation to _t() for Java 9 compatibility 2017-11-26 13:25:58 +00:00
9c10eef0e3 Attempt to fix WindowManager$BadTokenException crash
Use the parent fragment's Context, which should be more reliable than the one
from the WebView.
2017-11-26 13:17:00 +00:00
9fd5e43115 NPE fix 2017-11-25 23:58:38 +00:00
d5bd9b8eaa Unused import 2017-11-25 23:42:01 +00:00
5b1203a1c6 IAE fix 2017-11-25 23:39:58 +00:00
fbe79eee2e Replace PNG icons with SVG
This doesn't shrink the APK size (yet), as we still generate the PNGs for older
API levels.
2017-11-25 23:31:32 +00:00
ffa21fc1e0 Drop unused icons 2017-11-25 22:39:59 +00:00
5faf1f5bb0 Add a "sync" icon to more clearly indicate tunnel "starting" status 2017-11-25 22:33:10 +00:00
e15efb6537 ActivityNotFoundException fix 2017-11-25 22:06:47 +00:00
cd1702d53c Update classname in logger.config 2017-11-25 21:06:38 +00:00
5a6ca8a0a4 Add note about possible ANR to watch for 2017-11-25 19:58:35 +00:00
82d184cf90 Fix ANR when restarting all tunnels 2017-11-25 19:43:14 +00:00
5162bb604b Delay loading of tunnels and addressbook until router is running
This should help avoid a race condition where the RouterContext is available
before the Router is fully-initialised (see ticket #2092).
2017-11-25 19:22:49 +00:00
3aff2a7a9d SecurityException fix 2017-11-25 17:01:55 +00:00
cae565761d IAE fix 2017-11-25 16:00:46 +00:00
c2b6cee9a2 ActivityNotFoundException fix 2017-11-25 15:44:47 +00:00
84e7b1f41c Update Firefox browser config instructions for Firefox Quantum 2017-11-25 15:37:48 +00:00
7ebed1e6d2 NetworkOnMainThreadException fixes 2017-11-25 14:27:10 +00:00
2e68122b8d Updated translations 2017-11-25 14:18:52 +00:00
899a3f4cfc ISE fix 2017-11-24 17:33:38 +00:00
52d49a4ab6 NPE fix 2017-11-24 17:07:49 +00:00
75f705125f ISE fix 2017-11-24 17:00:11 +00:00
722ddf8a47 NPE fix
The advanced menu options used to be in a sub-toolbar, so onPrepareOptionsMenu
was called to set their visibility. When we moved to a FAM, we didn't remove
this call, and in some newer Android versions this results in a race condition.
2017-11-24 16:21:56 +00:00
524d21631e ISE fix
Switch to using Router directly, which means we don't have a potential race
condition looking up the RouterContext.
2017-11-24 16:08:56 +00:00
86e6060217 NPE fix 2017-11-24 15:54:24 +00:00
b5c7fad876 ISE fix 2017-11-24 15:50:49 +00:00
5f3ca0fe69 NPE fix 2017-11-24 15:34:10 +00:00
ddd9bea786 Upgrade test dependencies 2017-11-24 15:13:41 +00:00
6aac99e7ea Upgrade to Gradle 4.1 and Android Gradle Tools 3.0.1
Required dropping gradle-witness because it does not support modern Gradle.
2017-11-24 14:35:07 +00:00
da763a7c81 0.9.31 2017-08-19 22:22:56 +00:00
6cb46c3168 Helper library 0.9.4 2017-08-19 22:01:34 +00:00
610e963d22 Update translations 2017-08-19 21:54:46 +00:00
96b8ed43e0 Fix NFEs 2017-08-19 00:07:22 +00:00
812c28cd33 Fix tunnel details IOOBE, expose errors to user 2017-08-18 23:20:42 +00:00
3f7312653a Update translations 2017-08-18 23:10:45 +00:00
c89d3992c7 Add Firefox Focus to unsupported browsers list 2017-08-18 23:08:48 +00:00
d9394685c9 Show tunnel status on details page, use it for transition in place of text 2017-08-18 23:04:46 +00:00
b140158b24 Fix tunnel details IAE, expose errors to user 2017-08-18 21:30:53 +00:00
4f5b0bd21a Correct Ed25519 name 2017-08-18 21:14:00 +00:00
1d17d89ccb Change default tunnel SigType to Ed25519 2017-08-18 21:13:05 +00:00
b6074da7c4 NPE fix 2017-08-18 20:41:22 +00:00
c0fdb4aff7 Validate numbers in tunnel wizard
This shouldn't be necessary, because the TextView is constrained to only accept
numbers, but someone on Google Play ran into a NumberFormatException, so...
2017-08-18 20:28:53 +00:00
e00c9cc449 Try to fix obscure NPE where none should occur 2017-08-18 19:34:55 +00:00
423ca46672 Target SDK 25
Initial testing doesn't show any permissions problems, and enabling this will
help uncover user issues.
2017-08-18 19:18:45 +00:00
66ed9d94a7 Update Firefox browser config instructions
Closes #1977.
2017-08-18 18:03:04 +00:00
a68ef9d372 Update translations 2017-08-18 16:10:26 +00:00
cb389123c5 Hide peers FAM item
From 0.9.31 the backend used to render the HTML table has been moved. We either
need to create a dedicated Android UI for this at some point, or remove it.
2017-08-18 15:39:09 +00:00
62cca0ed50 Bump I2P dependencies to 0.9.31 2017-08-18 15:36:07 +00:00
c99e3c0b41 0.9.30 2017-05-20 14:35:50 +00:00
302c51ccfa Handle IAE 2017-05-20 10:59:12 +00:00
1e34bc2159 Ensure debug build is debuggable 2017-05-20 10:39:14 +00:00
24f6f4789d Fetch WebView Context on UI thread 2017-05-20 10:31:33 +00:00
2de11a4067 Update translations 2017-05-20 09:32:23 +00:00
d83a2f9919 Updated translations 2017-05-15 12:09:43 +00:00
bf36b4c2e6 Replace anonymous DialogFragment subclasses with full subclasses
Closes #1916, #1917, #1923.
2017-05-15 12:05:10 +00:00
daa0b739a3 Use NamingService.requestUpdate()
Closes #1972.
2017-05-15 01:07:41 +00:00
5e1b0d9b50 Upgrade Android build tools, Android and I2P dependencies 2017-05-14 23:58:44 +00:00
917742847a 0.9.29 2017-03-27 03:31:16 +00:00
9460e3202f Helper library 0.9.3 2017-03-27 01:36:52 +00:00
5f388a7c6b Updated translations 2017-03-27 01:22:51 +00:00
39d5de7eb4 Upgrade I2P dependencies to 0.9.29 2017-03-27 01:20:24 +00:00
0fb1ef881c Upgrade Android dependencies 2017-03-27 00:40:05 +00:00
8230769191 Upgrade Android support libraries to 25.3.0 2017-03-26 09:22:22 +00:00
19036a71cb 0.9.28 2017-01-02 12:33:56 +00:00
40f3fbf9c5 Add -dontobfuscate to ProGuard rules 2017-01-02 12:08:30 +00:00
1127fb0195 Helper library 0.9.2 2017-01-02 12:06:26 +00:00
df81efe6bc Fix height of address book list entries 2017-01-02 10:57:40 +00:00
784ca3691b Collate TODO items 2017-01-02 10:07:26 +00:00
0fa4241ce6 New translations 2017-01-02 09:44:58 +00:00
5063d276de Updated translations 2017-01-02 09:44:39 +00:00
81d0e43f0f Upgrade Android Gradle tools to 2.2.3 2017-01-02 09:42:40 +00:00
1637a9007d Upgrade Android support libraries to 25.0.1 2017-01-02 09:41:58 +00:00
234 changed files with 2230 additions and 622 deletions

View File

@ -4,15 +4,15 @@ lang_map = he: iw, id: in, pt_BR: pt-rBR, ru_RU: ru, sv_SE: sv, tr_TR: tr, uk_UA
[I2P.android]
file_filter = app/src/main/res/values-<lang>/strings.xml
minimum_perc = 50
source_file = app/src/main/res/values/strings.xml
source_lang = en
type = ANDROID
minimum_perc = 50
[I2P.android_lib_helper]
file_filter = lib/helper/src/main/res/values-<lang>/strings.xml
minimum_perc = 50
source_file = lib/helper/src/main/res/values/strings.xml
source_lang = en
type = ANDROID
minimum_perc = 50

View File

@ -1,4 +1,46 @@
0.9.27 / 2016-11-20
0.9.33 / 2018-02-18
* Translation updates
0.9.32 / 2017-11-28
* Fixed "Application Not Responding" error when restarting all tunnels
* Fixed crashes when:
* opening the console menu
* starting the router
* starting the router for the first time
* viewing tunnels
* viewing the addressbook
* opening the addressbook menu
* configuring addressbook subscriptions
* using a configuration wizard
* loading a B64 Destination from file
* viewing tunnel settings
* saving tunnel settings
* installing a tunnel's recommended app without a market app
* installing a browser without a market app
* rotating the screen while using the built-in browser
* Added a "sync" icon to more clearly indicate tunnel "starting" status
* Updated Firefox browser config instructions for Firefox Quantum
* Translation updates
0.9.31 / 2017-08-19
* Fixed various crashes in the Tunnels UI
* Updated Firefox browser config instructions
* Minor bug fixes
* Dependency and translation updates
0.9.30 / 2017-05-20
* Fixed crashes when creating or deleting tunnels, or adding names to the
private addressbook
* Minor bug fixes
* Dependency and translation updates
0.9.29 / 2017-03-27
* Dependency and translation updates
0.9.28 / 2017-01-02
* Bug fixes and translation updates
0.9.27 / 2016-11-20 / 64ff68efe98c345acb6ba1d0432fa49d1d650358
* Removed kytv's IRC server from default tunnel list
* Translation updates

25
RELEASE-PROCESS.md Normal file
View File

@ -0,0 +1,25 @@
# Release Process
1. Check out a clean copy of i2p.i2p at the correct release version.
2. Edit `routerjars/local.properties` to use the clean i2p.i2p copy.
3. Pull the latest translations with `tx pull -a` and commit them. (If you don't have the `tx` command, do `pip install transifex-client` )
4. Ensure that `signing.properties` contains the details of the release key.
5. Edit `gradle.properties` to bump the I2P version.
6. Edit `app/build.gradle` to bump the Android version number.
7. If the helper has changed since the last release, edit
`lib/helper/gradle.properties` to bump the version.
8. `./gradlew clean assembleRelease`
9. `./gradlew :lib:client:uploadArchives`
10. If the helper version was changed: `./gradlew :lib:helper:uploadArchives`
11. Check on Sonatype that everything worked, and close/release.
12. Update local fdroidserver repo
13. `cp app/build/outputs/apk/free/release/app-free-release.apk path/to/fdroid/repo/I2P-VERSION.apk`
14. Update `path/to/fdroid/metadata/net.i2p.android.txt`
15. `fdroid update`
16. Push to download server and put in place.
17. Check F-Droid repo works, and app works.
18. `mtn ci gradle.properties lib/helper/gradle.properties app/build.gradle`
19. Push free and donate builds to Google Play.
20. Tag the new release. Example `mtn tag h: android-0.9.36`

25
TODO
View File

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

View File

@ -1,12 +1,10 @@
apply plugin: 'com.android.application'
apply plugin: 'witness'
android {
compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION as String)
buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION as String
defaultConfig {
versionCode 4745234
versionName '0.9.27'
versionCode 4745246
versionName "$I2P_VERSION"
minSdkVersion 9
targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION as String)
@ -23,6 +21,7 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
debuggable true
applicationIdSuffix '.debug'
versionNameSuffix '-DEBUG'
}
@ -37,14 +36,18 @@ android {
packagingOptions {
exclude 'LICENSE.txt'
}
flavorDimensions 'tier'
productFlavors {
free {
dimension 'tier'
applicationId 'net.i2p.android'
}
donate {
dimension 'tier'
applicationId 'net.i2p.android.donate'
}
legacy {
dimension 'tier'
applicationId 'net.i2p.android.router'
}
}
@ -52,51 +55,34 @@ android {
dependencies {
// Local dependencies
compile project(':lib:client')
compile project(':lib:helper')
compile project(':routerjars')
implementation project(':lib:client')
implementation project(':lib:helper')
implementation project(path: ':routerjars', configuration: 'routerjars')
// Android Support Repository dependencies
def supportVersion = '25.0.0'
compile "com.android.support:support-v4:$supportVersion"
compile "com.android.support:appcompat-v7:$supportVersion"
compile "com.android.support:preference-v7:$supportVersion"
compile "com.android.support:preference-v14:$supportVersion"
compile "com.android.support:recyclerview-v7:$supportVersion"
def supportVersion = '25.3.1'
implementation "com.android.support:support-v4:$supportVersion"
implementation "com.android.support:appcompat-v7:$supportVersion"
implementation "com.android.support:preference-v7:$supportVersion"
implementation "com.android.support:preference-v14:$supportVersion"
implementation "com.android.support:recyclerview-v7:$supportVersion"
// Remote dependencies
compile 'com.androidplot:androidplot-core:0.9.8'
compile 'com.eowise:recyclerview-stickyheaders:0.5.2@aar'
compile ('com.mcxiaoke.viewpagerindicator:library:2.4.1') {
exclude group: 'com.android.support', module: 'support-v4'
}
compile 'com.pnikosis:materialish-progress:1.7'
compile 'net.i2p:router:0.9.28'
compile 'net.i2p.android.ext:floatingactionbutton:1.10.1'
compile 'org.sufficientlysecure:html-textview:1.6'
implementation 'com.androidplot:androidplot-core:1.4.1'
implementation 'com.eowise:recyclerview-stickyheaders:0.5.2@aar'
implementation 'com.inkapplications.viewpageindicator:library:2.4.4'
implementation 'com.pnikosis:materialish-progress:1.7'
implementation "net.i2p:router:$I2P_VERSION"
implementation 'net.i2p.android.ext:floatingactionbutton:1.10.1'
implementation 'org.sufficientlysecure:html-textview:3.1'
// Testing-only dependencies
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2') {
exclude group: 'com.android.support', module: 'support-annotations'
}
}
dependencyVerification {
verify = [
'com.android.support:support-v4:c25c657954152c4315584572f5008a6780b086169519f67acd20c2617b9da325',
'com.android.support:appcompat-v7:bc0b13b1ba63ed226ae509044c880fdfe225cb86d21924ef19f2fb97a71c877e',
'com.android.support:preference-v7:a3ebe066ad04787dd377562abb391255b89d056f8dc450365a504f14d74c825c',
'com.android.support:preference-v14:6a374214736eff84637b847ba0015d43c52edef6d320b9f6920ead580c8dbe05',
'com.android.support:recyclerview-v7:fc6d6a9b802ed3a26789812f29167135cac7b4d956f4deda54fc1f317721da64',
'com.androidplot:androidplot-core:e44d9e59e06f025330831f7d3c987d2778a3302025184cf0cef05714b5171212',
'com.eowise:recyclerview-stickyheaders:7b236da49b33b840e9ba6e7e4182218d1a2d9047236fdbc3ca947352f9b0883b',
'com.mcxiaoke.viewpagerindicator:library:1e8aad664137f68abdfee94889f6da3dc98be652a235176a403965a07a25de62',
'com.pnikosis:materialish-progress:da089a90d1dab61e9b50038c09081019398f81190d12b0b567ce94b83ef8cf93',
'net.i2p:router:de3cf0a0e99823662c938d6a1083f201f8feba7d0ebebaf3179fed7040863b7c',
'net.i2p.android.ext:floatingactionbutton:09d43e2d4ac04a91bf7a37e1ec48a8d220204e3a55dca72cd36cd9fa27461ade',
'org.sufficientlysecure:html-textview:c409b471618b675e3d2a8588f883c5fe8f3369d00df61ec84b29f29c648370ae',
]
}
project.ext.i2pbase = '../i2p.i2p'
project.ext.i2pbase = "../i2p.i2p"
def Properties props = new Properties()
def propFile = new File(project(':routerjars').projectDir, 'local.properties')

View File

@ -6,6 +6,10 @@
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
-dontobfuscate
-dontoptimize
-dontpreverify
-dontshrink
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
@ -21,8 +25,5 @@
# https://code.google.com/p/android/issues/detail?id=78377#c302
-keepattributes **
-keep class !android.support.v7.view.menu.**,** {*;}
-dontpreverify
-dontoptimize
-dontshrink
-dontwarn **
-dontnote **
-dontnote **

View File

@ -11,12 +11,12 @@ import net.i2p.router.RouterContext;
import net.i2p.router.news.NewsEntry;
import net.i2p.router.news.NewsMetadata;
import net.i2p.router.news.NewsXMLParser;
import net.i2p.router.util.RFC822Date;
import net.i2p.util.EepGet;
import net.i2p.util.FileUtil;
import net.i2p.util.Log;
import net.i2p.util.ReusableGZIPInputStream;
import net.i2p.util.SecureFileOutputStream;
import net.i2p.util.RFC822Date;
import java.io.BufferedWriter;
import java.io.File;

View File

@ -1,5 +1,6 @@
package net.i2p.android.help;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.graphics.ColorMatrix;
@ -11,6 +12,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import net.i2p.android.router.R;
@ -84,7 +86,11 @@ public class BrowserAdapter extends RecyclerView.Adapter<BrowserAdapter.ViewHold
String uriMarket = "market://search?q=pname:" + browser.packageName;
Uri uri = Uri.parse(uriMarket);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
mCtx.startActivity(intent);
try {
mCtx.startActivity(intent);
} catch (ActivityNotFoundException e) {
Toast.makeText(mCtx, R.string.no_market_app, Toast.LENGTH_LONG).show();
}
}
});
holder.mStatus.setVisibility(View.VISIBLE);

View File

@ -30,7 +30,7 @@ public class HelpHtmlFragment extends Fragment {
int padH = getResources().getDimensionPixelOffset(R.dimen.activity_horizontal_margin);
int padV = getResources().getDimensionPixelOffset(R.dimen.activity_vertical_margin);
text.setPadding(padH, padV, padH, padV);
text.setHtmlFromRawResource(getActivity(), getArguments().getInt(ARG_HTML_FILE), true);
text.setHtml(getArguments().getInt(ARG_HTML_FILE));
return scroller;
}
}

View File

@ -22,14 +22,17 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import net.i2p.I2PAppContext;
import net.i2p.android.i2ptunnel.util.TunnelUtil;
import net.i2p.android.router.R;
import net.i2p.android.router.util.Util;
import net.i2p.android.util.FragmentUtils;
import net.i2p.app.ClientAppState;
import net.i2p.i2ptunnel.TunnelController;
import net.i2p.i2ptunnel.TunnelControllerGroup;
import java.util.List;
@ -41,6 +44,7 @@ public class TunnelDetailFragment extends Fragment {
private TunnelControllerGroup mGroup;
private TunnelEntry mTunnel;
private Toolbar mToolbar;
private ImageView mStatus;
public static TunnelDetailFragment newInstance(int tunnelId) {
TunnelDetailFragment f = new TunnelDetailFragment();
@ -73,21 +77,33 @@ public class TunnelDetailFragment extends Fragment {
super.onCreate(savedInstanceState);
String error;
List<TunnelController> controllers;
try {
mGroup = TunnelControllerGroup.getInstance();
error = mGroup == null ? getResources().getString(R.string.i2ptunnel_not_initialized) : null;
controllers = mGroup.getControllers();
} catch (IllegalArgumentException iae) {
mGroup = null;
controllers = null;
error = iae.toString();
}
if (mGroup == null) {
// Show error
Toast.makeText(getActivity().getApplicationContext(),
error, Toast.LENGTH_LONG).show();
getActivity().finish();
} else if (getArguments().containsKey(TUNNEL_ID)) {
int tunnelId = getArguments().getInt(TUNNEL_ID);
mTunnel = new TunnelEntry(getActivity(),
mGroup.getControllers().get(tunnelId),
tunnelId);
try {
TunnelController controller = controllers.get(tunnelId);
mTunnel = new TunnelEntry(getActivity(), controller, tunnelId);
} catch (IndexOutOfBoundsException e) {
// Tunnel doesn't exist
Util.e("Could not load tunnel details", e);
Toast.makeText(getActivity().getApplicationContext(),
R.string.i2ptunnel_no_tunnel_details, Toast.LENGTH_LONG).show();
getActivity().finish();
}
}
}
@ -107,18 +123,18 @@ public class TunnelDetailFragment extends Fragment {
updateToolbar();
if (mTunnel != null) {
mStatus = (ImageView) v.findViewById(R.id.tunnel_status);
updateStatus();
ViewCompat.setTransitionName(mStatus, "status" + mTunnel.getId());
TextView name = (TextView) v.findViewById(R.id.tunnel_name);
name.setText(mTunnel.getName());
ViewCompat.setTransitionName(name,
getActivity().getString(R.string.TUNNEL_NAME) + mTunnel.getId());
TextView type = (TextView) v.findViewById(R.id.tunnel_type);
type.setText(mTunnel.getType());
TextView description = (TextView) v.findViewById(R.id.tunnel_description);
description.setText(mTunnel.getDescription());
ViewCompat.setTransitionName(description,
getActivity().getString(R.string.TUNNEL_DESCRIPTION) + mTunnel.getId());
if (!mTunnel.getDetails().isEmpty()) {
v.findViewById(R.id.tunnel_details_container).setVisibility(View.VISIBLE);
@ -204,7 +220,13 @@ public class TunnelDetailFragment extends Fragment {
Uri uri = mTunnel.getRecommendedAppForTunnel();
if (uri != null) {
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
Toast.makeText(getContext(),
R.string.no_market_app,
Toast.LENGTH_LONG).show();
}
}
}
})
@ -244,6 +266,14 @@ public class TunnelDetailFragment extends Fragment {
}
}
private void updateStatus() {
mStatus.setImageDrawable(mTunnel.getStatusIcon());
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
mStatus.setBackgroundDrawable(mTunnel.getStatusBackground());
else
mStatus.setBackground(mTunnel.getStatusBackground());
}
private boolean onToolbarItemSelected(MenuItem item) {
if (mTunnel == null)
return false;
@ -257,6 +287,8 @@ public class TunnelDetailFragment extends Fragment {
+ ' ' + mTunnel.getName(), Toast.LENGTH_LONG).show();
// Reload the toolbar to change the start/stop action
updateToolbar();
// Update the status icon
updateStatus();
return true;
case R.id.action_stop_tunnel:
mTunnel.getController().stopTunnel();
@ -265,42 +297,31 @@ public class TunnelDetailFragment extends Fragment {
+ ' ' + mTunnel.getName(), Toast.LENGTH_LONG).show();
// Reload the toolbar to change the start/stop action
updateToolbar();
// Update the status icon
updateStatus();
return true;
case R.id.action_edit_tunnel:
mCallback.onEditTunnel(mTunnel.getId());
return true;
case R.id.action_delete_tunnel:
DialogFragment dg = new DialogFragment() {
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity())
.setMessage(R.string.i2ptunnel_delete_confirm_message)
.setPositiveButton(R.string.i2ptunnel_delete_confirm_button,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
List<String> msgs = TunnelUtil.deleteTunnel(
I2PAppContext.getGlobalContext(),
mGroup, mTunnel.getId(), null);
dialog.dismiss();
Toast.makeText(getActivity().getApplicationContext(),
msgs.get(0), Toast.LENGTH_LONG).show();
mCallback.onTunnelDeleted(mTunnel.getId(),
mGroup.getControllers().size());
}
})
.setNegativeButton(android.R.string.cancel, null)
.create();
}
};
dg.show(getFragmentManager(), "delete_tunnel_dialog");
DialogFragment dg = DeleteTunnelDialogFragment.newInstance();
dg.show(getChildFragmentManager(), "delete_tunnel_dialog");
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void onDeleteTunnel() {
List<String> msgs = TunnelUtil.deleteTunnel(
I2PAppContext.getGlobalContext(),
mGroup, mTunnel.getId(), null);
Toast.makeText(getActivity().getApplicationContext(),
msgs.get(0), Toast.LENGTH_LONG).show();
mCallback.onTunnelDeleted(mTunnel.getId(),
mGroup.getControllers().size());
}
private void copyToClipbardLegacy() {
android.text.ClipboardManager clipboard = (android.text.ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
clipboard.setText(mTunnel.getDetails());
@ -313,4 +334,47 @@ public class TunnelDetailFragment extends Fragment {
mTunnel.getName(), mTunnel.getDetails());
clipboard.setPrimaryClip(clip);
}
public static class DeleteTunnelDialogFragment extends DialogFragment {
TunnelDetailFragment mListener;
public static DialogFragment newInstance() {
return new DeleteTunnelDialogFragment();
}
private void onAttachToParentFragment(Fragment fragment) {
// Verify that the host fragment implements the callback interface
try {
// Instantiate the TunnelDetailFragment so we can send events to the host
mListener = (TunnelDetailFragment) fragment;
} catch (ClassCastException e) {
// The fragment doesn't implement the interface, throw exception
throw new ClassCastException(fragment.toString()
+ " must be TunnelDetailFragment");
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
onAttachToParentFragment(getParentFragment());
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity())
.setMessage(R.string.i2ptunnel_delete_confirm_message)
.setPositiveButton(R.string.i2ptunnel_delete_confirm_button,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
mListener.onDeleteTunnel();
}
})
.setNegativeButton(android.R.string.cancel, null)
.create();
}
}
}

View File

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

View File

@ -144,16 +144,13 @@ public class TunnelEntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
tvh.status.setBackgroundDrawable(tunnel.getStatusBackground());
else
tvh.status.setBackground(tunnel.getStatusBackground());
ViewCompat.setTransitionName(tvh.status,
"status" + tunnel.getId());
tvh.name.setText(tunnel.getName());
tvh.description.setText(tunnel.getDescription());
tvh.interfacePort.setText(tunnel.getTunnelLink(false));
ViewCompat.setTransitionName(tvh.name,
mCtx.getString(R.string.TUNNEL_NAME) + tunnel.getId());
ViewCompat.setTransitionName(tvh.description,
mCtx.getString(R.string.TUNNEL_DESCRIPTION) + tunnel.getId());
tvh.itemView.setSelected(mTwoPane.isTwoPane() && position == mActivatedPosition);
tvh.itemView.setOnClickListener(new View.OnClickListener() {
@Override
@ -162,13 +159,10 @@ public class TunnelEntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
mActivatedPosition = position;
notifyItemChanged(oldPosition);
notifyItemChanged(position);
Pair<View, String> namePair = Pair.create(
(View)tvh.name,
ViewCompat.getTransitionName(tvh.name));
Pair<View, String> descPair = Pair.create(
(View)tvh.description,
ViewCompat.getTransitionName(tvh.description));
Pair<View, String>[] pairs = new Pair[]{ namePair, descPair};
Pair<View, String> statusPair = Pair.create(
(View)tvh.status,
ViewCompat.getTransitionName(tvh.status));
Pair<View, String>[] pairs = new Pair[]{ statusPair};
mListener.onTunnelSelected(tunnel.getId(), pairs);
}
});

View File

@ -150,14 +150,21 @@ public class TunnelListFragment extends Fragment implements
};
public void updateState(State state) {
if (state == State.STOPPING || state == State.STOPPED ||
state == State.MANUAL_STOPPING ||
state == State.MANUAL_STOPPED ||
state == State.MANUAL_QUITTING ||
state == State.MANUAL_QUITTED)
getLoaderManager().destroyLoader(mClientTunnels ? CLIENT_LOADER_ID : SERVER_LOADER_ID);
else
initTunnels();
try {
if (state == State.INIT ||
state == State.STARTING || // Wait until RouterContext is initialised
state == State.STOPPING ||
state == State.STOPPED ||
state == State.MANUAL_STOPPING ||
state == State.MANUAL_STOPPED ||
state == State.MANUAL_QUITTING ||
state == State.MANUAL_QUITTED)
getLoaderManager().destroyLoader(mClientTunnels ? CLIENT_LOADER_ID : SERVER_LOADER_ID);
else
initTunnels();
} catch (IllegalStateException ise) {
// Fragment isn't attached to any activity, so ignore state change
}
}
private void initTunnels() {

View File

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

View File

@ -65,6 +65,14 @@ public class TunnelsContainer extends Fragment implements
setHasOptionsMenu(true);
}
private boolean showActions() {
RouterContext rCtx = Util.getRouterContext();
TunnelControllerGroup tcg = TunnelControllerGroup.getInstance();
return rCtx != null && tcg != null &&
(tcg.getState() == ClientAppState.STARTING ||
tcg.getState() == ClientAppState.RUNNING);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.container_tunnels, container, false);
@ -72,6 +80,7 @@ public class TunnelsContainer extends Fragment implements
mViewPager = (ViewPager) v.findViewById(R.id.pager);
mPageIndicator = (TitlePageIndicator) v.findViewById(R.id.page_indicator);
mNewTunnel = (ImageButton) v.findViewById(R.id.promoted_action);
mNewTunnel.setVisibility(showActions() ? View.VISIBLE : View.GONE);
if (v.findViewById(R.id.detail_fragment) != null) {
// The detail container view will be present only in the
@ -154,17 +163,16 @@ public class TunnelsContainer extends Fragment implements
@Override
public void onPrepareOptionsMenu(Menu menu) {
RouterContext rCtx = Util.getRouterContext();
TunnelControllerGroup tcg = TunnelControllerGroup.getInstance();
boolean showActions = rCtx != null && tcg != null &&
(tcg.getState() == ClientAppState.STARTING ||
tcg.getState() == ClientAppState.RUNNING);
boolean showActions = showActions();
menu.findItem(R.id.action_start_all_tunnels).setVisible(showActions);
menu.findItem(R.id.action_stop_all_tunnels).setVisible(showActions);
menu.findItem(R.id.action_restart_all_tunnels).setVisible(showActions);
mNewTunnel.setVisibility(showActions ? View.VISIBLE : View.GONE);
// Was causing a NPE in version 4745238 (0.9.31)
if (mNewTunnel != null) {
mNewTunnel.setVisibility(showActions ? View.VISIBLE : View.GONE);
}
}
@Override
@ -183,7 +191,10 @@ public class TunnelsContainer extends Fragment implements
msgs = tcg.stopAllControllers();
break;
case R.id.action_restart_all_tunnels:
msgs = tcg.restartAllControllers();
// Do a manual stop-start cycle, because tcg.restartAllControllers() happens in the
// foreground, whereas tcg.startAllControllers() fires off threads for starting.
msgs = tcg.stopAllControllers();
msgs.addAll(tcg.startAllControllers());
break;
default:
return super.onOptionsItemSelected(item);
@ -205,10 +216,12 @@ public class TunnelsContainer extends Fragment implements
TunnelConfig cfg = TunnelUtil.createConfigFromWizard(getActivity(), tcg, tunnelData);
TunnelEntry tunnel = TunnelEntry.createNewTunnel(getActivity(), tcg, cfg);
if (tunnel.isClient() && mClientFrag != null)
mClientFrag.addTunnel(tunnel);
else if (mServerFrag != null)
mServerFrag.addTunnel(tunnel);
if (tunnel != null) {
if (tunnel.isClient() && mClientFrag != null)
mClientFrag.addTunnel(tunnel);
else if (mServerFrag != null)
mServerFrag.addTunnel(tunnel);
}
}
}
}

View File

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

View File

@ -1,5 +1,6 @@
package net.i2p.android.i2ptunnel.preferences;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.AsyncTask;
@ -110,7 +111,8 @@ public class GeneralTunnelPreferenceFragment extends BaseTunnelPreferenceFragmen
generalCategory.removePreference(generalCategory.findPreference(getString(R.string.TUNNEL_SHARED_CLIENT)));
addPreferencesFromResource(R.xml.tunnel_gen_server_port, portCategory);
portCategory.removePreference(portCategory.findPreference(getString(R.string.TUNNEL_TARGET_PORT)));
portCategory.removePreference(portCategory.findPreference(getString(R.string.TUNNEL_USE_SSL)));
// # TODO: See trac issue #2296
//portCategory.removePreference(portCategory.findPreference(getString(R.string.TUNNEL_USE_SSL)));
}
}
@ -135,14 +137,17 @@ public class GeneralTunnelPreferenceFragment extends BaseTunnelPreferenceFragmen
protected Void doInBackground(Void... voids) {
Set<String> interfaceSet = Addresses.getAllAddresses();
final String[] interfaces = interfaceSet.toArray(new String[interfaceSet.size()]);
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
reachableBy.setEntries(interfaces);
reachableBy.setEntryValues(interfaces);
reachableBy.setEnabled(true);
}
});
Activity activity = getActivity();
if (activity != null) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
reachableBy.setEntries(interfaces);
reachableBy.setEntryValues(interfaces);
reachableBy.setEnabled(true);
}
});
}
return null;
}
}.execute();
@ -164,8 +169,11 @@ public class GeneralTunnelPreferenceFragment extends BaseTunnelPreferenceFragmen
@Override
protected void generalClientStandardOrIrc(boolean isStandardOrIrc) {
/*
# TODO: See trac issue #2296
if (!isStandardOrIrc)
portCategory.removePreference(portCategory.findPreference(getString(R.string.TUNNEL_USE_SSL)));
*/
}
@Override
@ -181,7 +189,8 @@ public class GeneralTunnelPreferenceFragment extends BaseTunnelPreferenceFragmen
@Override
protected void generalServerHttpBidirOrStreamr(boolean isStreamr) {
addPreferencesFromResource(R.xml.tunnel_gen_client_port, portCategory);
portCategory.removePreference(portCategory.findPreference(getString(R.string.TUNNEL_USE_SSL)));
// # TODO: See trac issue #2296
//portCategory.removePreference(portCategory.findPreference(getString(R.string.TUNNEL_USE_SSL)));
if (isStreamr)
portCategory.removePreference(portCategory.findPreference(getString(R.string.TUNNEL_LISTEN_PORT)));
@ -197,7 +206,8 @@ public class GeneralTunnelPreferenceFragment extends BaseTunnelPreferenceFragmen
protected void generalServerPortStreamr(boolean isStreamr) {
if (isStreamr) {
portCategory.removePreference(portCategory.findPreference(getString(R.string.TUNNEL_TARGET_HOST)));
portCategory.removePreference(portCategory.findPreference(getString(R.string.TUNNEL_USE_SSL)));
// # TODO: See trac issue #2296
//portCategory.removePreference(portCategory.findPreference(getString(R.string.TUNNEL_USE_SSL)));
}
}

View File

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

View File

@ -182,8 +182,10 @@ public class TunnelUtil extends GeneralHelper {
@Override
protected void generalClientStandardOrIrc(boolean isStandardOrIrc) {
/* # TODO: See trac issue #2296
if (isStandardOrIrc)
ed.putBoolean(res.getString(R.string.TUNNEL_USE_SSL), isSSLEnabled(tunnel));
*/
}
@Override
@ -212,7 +214,8 @@ public class TunnelUtil extends GeneralHelper {
protected void generalServerPortStreamr(boolean isStreamr) {
if (!isStreamr) {
ed.putString(res.getString(R.string.TUNNEL_TARGET_HOST), getTargetHost(tunnel));
ed.putBoolean(res.getString(R.string.TUNNEL_USE_SSL), isSSLEnabled(tunnel));
// # TODO: See trac issue #2296
//ed.putBoolean(res.getString(R.string.TUNNEL_USE_SSL), isSSLEnabled(tunnel));
}
}
@ -414,8 +417,10 @@ public class TunnelUtil extends GeneralHelper {
@Override
protected void generalClientStandardOrIrc(boolean isStandardOrIrc) {
/* # TODO: See trac issue #2296
if (isStandardOrIrc)
cfg.setUseSSL(prefs.getBoolean(res.getString(R.string.TUNNEL_USE_SSL), false));
*/
}
@Override
@ -444,7 +449,8 @@ public class TunnelUtil extends GeneralHelper {
protected void generalServerPortStreamr(boolean isStreamr) {
if (!isStreamr) {
cfg.setTargetHost(prefs.getString(res.getString(R.string.TUNNEL_TARGET_HOST), "127.0.0.1"));
cfg.setUseSSL(prefs.getBoolean(res.getString(R.string.TUNNEL_USE_SSL), false));
// # TODO: See trac issue #2296
//cfg.setUseSSL(prefs.getBoolean(res.getString(R.string.TUNNEL_USE_SSL), false));
}
}

View File

@ -68,6 +68,10 @@ public class ConnectionLimitPreference extends EditTextPreference {
@Override
protected boolean persistString(String value) {
return value != null && persistInt(Integer.valueOf(value));
try {
return value != null && persistInt(Integer.valueOf(value));
} catch (NumberFormatException e) {
return false;
}
}
}

View File

@ -38,6 +38,10 @@ public class IntEditTextPreference extends EditTextPreference {
@Override
protected boolean persistString(String value) {
return value != null && persistInt(Integer.valueOf(value));
try {
return value != null && persistInt(Integer.valueOf(value));
} catch (NumberFormatException e) {
return false;
}
}
}

View File

@ -63,13 +63,13 @@ public class ConsoleContainer extends Fragment {
startActivity(graphs);
}
});
mConsoleMenu.findViewById(R.id.action_peers).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent peers = new Intent(getActivity(), PeersActivity.class);
startActivity(peers);
}
});
// mConsoleMenu.findViewById(R.id.action_peers).setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View view) {
// Intent peers = new Intent(getActivity(), PeersActivity.class);
// startActivity(peers);
// }
// });
mConsoleMenu.findViewById(R.id.action_netdb).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
@ -87,11 +87,6 @@ public class ConsoleContainer extends Fragment {
inflater.inflate(R.menu.activity_main_actions, menu);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
setMenuVisibility();
}
private void setMenuVisibility() {
boolean routerRunning = Util.getRouterContext() != null;
mConsoleMenu.findViewById(R.id.action_logs).setVisibility(routerRunning ? View.VISIBLE : View.GONE);
@ -100,8 +95,8 @@ public class ConsoleContainer extends Fragment {
if (getActivity() != null) {
boolean advanced = PreferenceManager.getDefaultSharedPreferences(getActivity())
.getBoolean("i2pandroid.main.showStats", false);
mConsoleMenu.findViewById(R.id.action_peers).setVisibility(
advanced && routerRunning ? View.VISIBLE : View.GONE);
// mConsoleMenu.findViewById(R.id.action_peers).setVisibility(
// advanced && routerRunning ? View.VISIBLE : View.GONE);
mConsoleMenu.findViewById(R.id.action_netdb).setVisibility(
advanced && routerRunning ? View.VISIBLE : View.GONE);
}

View File

@ -531,7 +531,7 @@ public class MainFragment extends I2PFragmentBase {
public AlphaComparator(RouterContext ctx) {
_ctx = ctx;
xsc = _(ctx, SHARED_CLIENTS);
xsc = _t(ctx, SHARED_CLIENTS);
}
public int compare(Destination lhs, Destination rhs) {
@ -559,12 +559,12 @@ public class MainFragment extends I2PFragmentBase {
if (name == null)
name = d.calculateHash().toBase64().substring(0, 6);
else
name = _(ctx, name);
name = _t(ctx, name);
return name;
}
private String _(RouterContext ctx, String s) {
private String _t(RouterContext ctx, String s) {
if (SHARED_CLIENTS.equals(s))
return getString(R.string.shared_clients);
else
@ -648,9 +648,11 @@ public class MainFragment extends I2PFragmentBase {
private void checkFirstStart() {
I2PActivityBase ab = (I2PActivityBase) getActivity();
boolean firstStart = ab.getPref(PREF_FIRST_START, true);
if (firstStart) {
// Check ab.isFinishing() because DialogFragment.show() will throw IllegalStateException if
// called after ab.onSaveInstanceState().
if (firstStart && !ab.isFinishing()) {
FirstStartDialog dialog = new FirstStartDialog();
dialog.show(getActivity().getSupportFragmentManager(), "firststart");
dialog.show(ab.getSupportFragmentManager(), "firststart");
ab.setPref(PREF_FIRST_START, false);
}
}

View File

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

View File

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

View File

@ -61,7 +61,7 @@ public class AddressbookSettingsActivity extends AppCompatActivity {
private boolean load() {
String res = FileUtil.readTextFile(i2pDir.getAbsolutePath(), -1, true);
if (res.length() > 0) {
if (res != null && res.length() > 0) {
text_content_subscriptions.setText(res);
return true;
}

View File

@ -5,6 +5,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import net.i2p.android.router.util.Util;
import net.i2p.android.router.I2PConstants;
import net.i2p.android.router.service.RouterService;
@ -19,7 +20,12 @@ public class OnBootReceiver extends BroadcastReceiver implements I2PConstants {
if (startOnBoot) {
Intent routerService = new Intent(context, RouterService.class);
context.startService(routerService);
// Ticket #2404
try {
context.startService(routerService);
} catch (IllegalStateException ex) {
Util.e("Error: ", ex);
}
}
}
}

View File

@ -23,7 +23,6 @@ import net.i2p.data.DataHelper;
import net.i2p.router.Job;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
import net.i2p.router.RouterLaunch;
import java.lang.ref.WeakReference;
@ -213,16 +212,18 @@ public class RouterService extends Service {
//Util.d(MARKER + this + " JBigI speed test finished, launching router");
// Launch the router!
RouterLaunch.main(null);
// TODO Store this somewhere instead of relying on global context?
Router r = new Router();
r.runRouter();
synchronized(_stateLock) {
if(_state != State.STARTING) {
return;
}
setState(State.RUNNING);
_statusBar.replace(StatusBar.ICON_RUNNING, R.string.notification_status_running);
_context = Util.getRouterContext();
_context = r.getContext();
if (_context == null) {
throw new IllegalStateException("No contexts. This is usually because the router is either starting up or shutting down.");
throw new IllegalStateException("Router has no context?");
}
_context.router().setKillVMOnEnd(false);
Job loadJob = new LoadClientsJob(RouterService.this, _context, _notif);

View File

@ -39,7 +39,7 @@ public class PeersFragment extends I2PFragmentBase {
wv.getSettings().setLoadsImagesAutomatically(true); // was false
// http://stackoverflow.com/questions/2369310/webview-double-tap-zoom-not-working-on-a-motorola-droid-a855
wv.getSettings().setUseWideViewPort(true);
_wvClient = new I2PWebViewClient();
_wvClient = new I2PWebViewClient(this);
wv.setWebViewClient(_wvClient);
wv.getSettings().setBuiltInZoomControls(true);
return v;

View File

@ -11,9 +11,10 @@ import android.view.ViewGroup;
import com.androidplot.xy.BarFormatter;
import com.androidplot.xy.BarRenderer;
import com.androidplot.xy.BoundaryMode;
import com.androidplot.xy.StepMode;
import com.androidplot.xy.XYGraphWidget;
import com.androidplot.xy.XYPlot;
import com.androidplot.xy.XYSeries;
import com.androidplot.xy.XYStepMode;
import net.i2p.android.router.I2PFragmentBase;
import net.i2p.android.router.R;
@ -120,26 +121,25 @@ public class RateGraphFragment extends I2PFragmentBase {
_ratePlot.addSeries(rateSeries, new BarFormatter(Color.argb(200, 0, 80, 0), Color.argb(200, 0, 80, 0)));
_ratePlot.calculateMinMaxVals();
long maxX = _ratePlot.getCalculatedMaxX().longValue();
long maxX = _ratePlot.getBounds().getMaxX().longValue();
Util.d("Adding plot updater to listener");
_listener.addObserver(_plotUpdater);
// Only one line, so hide the legend
_ratePlot.getLegendWidget().setVisible(false);
_ratePlot.getLegend().setVisible(false);
BarRenderer renderer = (BarRenderer) _ratePlot.getRenderer(BarRenderer.class);
renderer.setBarWidthStyle(BarRenderer.BarWidthStyle.VARIABLE_WIDTH);
renderer.setBarGap(0);
BarRenderer renderer = _ratePlot.getRenderer(BarRenderer.class);
renderer.setBarGroupWidth(BarRenderer.BarGroupWidthMode.FIXED_GAP, 0);
_ratePlot.setDomainUpperBoundary(maxX, BoundaryMode.GROW);
_ratePlot.setDomainStep(XYStepMode.INCREMENT_BY_VAL, 15 * 60 * 1000);
_ratePlot.setTicksPerDomainLabel(4);
_ratePlot.setDomainStep(StepMode.INCREMENT_BY_VAL, 15 * 60 * 1000);
_ratePlot.setLinesPerDomainLabel(4);
_ratePlot.setRangeLowerBoundary(0, BoundaryMode.FIXED);
_ratePlot.setTicksPerRangeLabel(5);
_ratePlot.setLinesPerRangeLabel(5);
_ratePlot.setDomainValueFormat(new Format() {
_ratePlot.getGraph().getLineLabelStyle(XYGraphWidget.Edge.BOTTOM).setFormat(new Format() {
private DateFormat dateFormat = SimpleDateFormat.getTimeInstance(DateFormat.SHORT);
@Override
@ -157,13 +157,13 @@ public class RateGraphFragment extends I2PFragmentBase {
});
final int finalK = _k;
_ratePlot.setRangeValueFormat(new Format() {
_ratePlot.getGraph().getLineLabelStyle(XYGraphWidget.Edge.LEFT).setFormat(new Format() {
@Override
public StringBuffer format(Object obj, @NonNull StringBuffer toAppendTo,
@NonNull FieldPosition pos) {
double val = ((Number) obj).doubleValue();
double maxY = _ratePlot.getCalculatedMaxY().doubleValue();
double maxY = _ratePlot.getBounds().getMaxY().doubleValue();
if (val == 0 || maxY < finalK) {
return new DecimalFormat("0").format(val, toAppendTo, pos);
@ -194,8 +194,8 @@ public class RateGraphFragment extends I2PFragmentBase {
private void updatePlot() {
_ratePlot.calculateMinMaxVals();
double maxY = _ratePlot.getCalculatedMaxY().doubleValue();
_ratePlot.setRangeStep(XYStepMode.INCREMENT_BY_VAL, getRangeStep(maxY, _k));
double maxY = _ratePlot.getBounds().getMaxY().doubleValue();
_ratePlot.setRangeStep(StepMode.INCREMENT_BY_VAL, getRangeStep(maxY, _k));
_ratePlot.redraw();
}

View File

@ -299,7 +299,15 @@ public abstract class Util implements I2PConstants {
public static String getFileDir(Context context) {
// This needs to be changed so that we can have an alternative place
return context.getFilesDir().getAbsolutePath();
File f = context.getFilesDir();
if (f == null) {
// https://code.google.com/p/android/issues/detail?id=8886
// Seems to be a race condition; try again.
// Supposedly only in pre-4.4 devices, but this was observed on a
// Samsung Galaxy Grand Prime (grandprimeve3g), 1024MB RAM, Android 5.1
f = context.getFilesDir();
}
return f.getAbsolutePath();
}
/**

View File

@ -1,10 +1,12 @@
package net.i2p.android.router.web;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.AsyncTask;
import android.support.v4.app.Fragment;
import android.view.Gravity;
import android.view.View;
import android.webkit.HttpAuthHandler;
@ -29,6 +31,7 @@ import java.io.OutputStream;
public class I2PWebViewClient extends WebViewClient {
private Fragment _parentFrag;
private BGLoad _lastTask;
/** save it here so we can dismiss it in onPageFinished() */
private ProgressDialog _lastDialog;
@ -40,6 +43,11 @@ public class I2PWebViewClient extends WebViewClient {
private static final String ERROR_URL = "<p>Unable to load URL: ";
private static final String ERROR_ROUTER = "<p>Your router (or the HTTP proxy) does not appear to be running.</p>";
public I2PWebViewClient(Fragment parentFrag) {
super();
_parentFrag = parentFrag;
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Util.d("Should override? " + url);
@ -102,7 +110,7 @@ public class I2PWebViewClient extends WebViewClient {
///////// API 8
// Otherwise hangs waiting for CSS
view.getSettings().setBlockNetworkLoads(false);
_lastDialog = new ProgressDialog(view.getContext());
_lastDialog = new ProgressDialog(_parentFrag.getContext());
BGLoad task = new BackgroundEepLoad(view, h, _lastDialog);
_lastTask = task;
task.execute(url);
@ -215,10 +223,12 @@ public class I2PWebViewClient extends WebViewClient {
private abstract static class BGLoad extends AsyncTask<String, Integer, Integer> implements DialogInterface.OnCancelListener {
protected final WebView _view;
protected final Context _ctx;
protected final ProgressDialog _dialog;
public BGLoad(WebView view, ProgressDialog dialog) {
_view = view;
_ctx = view.getContext();
if (dialog != null)
dialog.setCancelable(true);
_dialog = dialog;
@ -305,9 +315,9 @@ public class I2PWebViewClient extends WebViewClient {
protected Integer doInBackground(String... urls) {
final String url = urls[0];
Uri uri = Uri.parse(url);
File cacheFile = AppCache.getInstance(_view.getContext()).getCacheFile(uri);
File cacheFile = AppCache.getInstance(_ctx).getCacheFile(uri);
if (cacheFile.exists()) {
final Uri resUri = AppCache.getInstance(_view.getContext()).getCacheUri(uri);
final Uri resUri = AppCache.getInstance(_ctx).getCacheUri(uri);
Util.d("Loading " + url + " from resource cache " + resUri);
_view.post(new Runnable() {
@Override
@ -325,7 +335,7 @@ public class I2PWebViewClient extends WebViewClient {
//EepGetFetcher fetcher = new EepGetFetcher(url);
OutputStream out = null;
try {
out = AppCache.getInstance(_view.getContext()).createCacheFile(uri);
out = AppCache.getInstance(_ctx).createCacheFile(uri);
// write error to stream
EepGetFetcher fetcher = new EepGetFetcher(url, out, true);
fetcher.addStatusListener(this);
@ -338,11 +348,11 @@ public class I2PWebViewClient extends WebViewClient {
if (success) {
// store in cache, get content URL, and load that way
// Set as current base
final Uri content = AppCache.getInstance(_view.getContext()).addCacheFile(uri, true);
final Uri content = AppCache.getInstance(_ctx).addCacheFile(uri, true);
if (content != null) {
Util.d("Stored cache in " + content);
} else {
AppCache.getInstance(_view.getContext()).removeCacheFile(uri);
AppCache.getInstance(_ctx).removeCacheFile(uri);
Util.d("cache create error");
return 0;
}
@ -381,7 +391,7 @@ public class I2PWebViewClient extends WebViewClient {
if (fis != null) try { fis.close(); } catch (IOException ioe) {}
}
}
AppCache.getInstance(_view.getContext()).removeCacheFile(uri);
AppCache.getInstance(_ctx).removeCacheFile(uri);
Util.d("loading error data URL: " + url);
final String finalMsg = msg;
_view.post(new Runnable() {
@ -403,30 +413,35 @@ public class I2PWebViewClient extends WebViewClient {
protected void onProgressUpdate(Integer... progress) {
if (isCancelled())
return;
int prog = progress[0];
if (prog < 0) {
_dialog.setTitle("Contacting...");
_dialog.setMessage(_host);
_dialog.setIndeterminate(true);
_dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
_dialog.setOnCancelListener(this);
_dialog.show();
} else if (prog == 0 && _total > 0) {
_dialog.setTitle("Downloading...");
_dialog.setMessage("...from " + _host);
_dialog.setIndeterminate(false);
_dialog.setMax(_total);
_dialog.setProgress(0);
} else if (_total > 0) {
// so it isn't at 100% while loading images and CSS
_dialog.setProgress(Math.min(prog, _total * 99 / 100));
} else if (prog > 0) {
// ugly, need custom
_dialog.setTitle("Downloading...");
_dialog.setMessage("...from " + _host + ": " + DataHelper.formatSize(prog) + 'B');
//_dialog.setProgress(prog);
} else {
// nothing
try {
int prog = progress[0];
if (prog < 0) {
_dialog.setTitle("Contacting...");
_dialog.setMessage(_host);
_dialog.setIndeterminate(true);
_dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
_dialog.setOnCancelListener(this);
_dialog.show();
} else if (prog == 0 && _total > 0) {
_dialog.setTitle("Downloading...");
_dialog.setMessage("...from " + _host);
_dialog.setIndeterminate(false);
_dialog.setMax(_total);
_dialog.setProgress(0);
} else if (_total > 0) {
// so it isn't at 100% while loading images and CSS
_dialog.setProgress(Math.min(prog, _total * 99 / 100));
} else if (prog > 0) {
// ugly, need custom
_dialog.setTitle("Downloading...");
_dialog.setMessage("...from " + _host + ": " + DataHelper.formatSize(prog) + 'B');
//_dialog.setProgress(prog);
} else {
// nothing
}
} catch (IllegalArgumentException iae) {
// throws IAE - not attached to window manager - perhaps due to screen rotation?
Util.e("Error while updating I2PWebViewClient dialog", iae);
}
}

View File

@ -44,7 +44,7 @@ public class WebFragment extends I2PFragmentBase {
TextView tv = (TextView) v.findViewById(R.id.browser_status);
tv.setText(WARNING);
WebView wv = (WebView) v.findViewById(R.id.browser_webview);
_wvClient = new I2PWebViewClient();
_wvClient = new I2PWebViewClient(this);
wv.setWebViewClient(_wvClient);
wv.getSettings().setBuiltInZoomControls(true);
// http://stackoverflow.com/questions/2369310/webview-double-tap-zoom-not-working-on-a-motorola-droid-a855

View File

@ -67,7 +67,10 @@ public abstract class AbstractWizardModel implements ModelCallbacks {
public void load(Bundle savedValues) {
for (String key : savedValues.keySet()) {
mRootPageList.findByKey(key).resetData(savedValues.getBundle(key));
// Expanded the code to hunt NPE - Ticket #2389
Page tmp = mRootPageList.findByKey(key);
Bundle tmpBundle = savedValues.getBundle(key);
tmp.resetData(tmpBundle);
}
}

View File

@ -31,6 +31,7 @@ public class SingleTextFieldPage extends Page {
protected String mDef = null;
protected String mDesc = "";
protected boolean mNumeric = false;
private String mFeedback;
public SingleTextFieldPage(ModelCallbacks callbacks, String title) {
super(callbacks, title);
@ -81,14 +82,24 @@ public class SingleTextFieldPage extends Page {
// Override these in subclasses to add content verification.
public boolean isValid() {
if (mNumeric) {
try {
//noinspection ResultOfMethodCallIgnored
Integer.parseInt(mData.getString(SIMPLE_DATA_KEY));
} catch (NumberFormatException e) {
mFeedback = "Not a number";
return false;
}
}
mFeedback = "";
return true;
}
public boolean showFeedback() {
return false;
return true;
}
public String getFeedback() {
return "";
return mFeedback;
}
}

View File

@ -46,8 +46,12 @@ public abstract class AbstractWizardActivity extends FragmentActivity implements
// Create the WizardModel before super.onCreate() in case a Fragment
// is created and tries to call e.g. onGetPage()
mWizardModel = onCreateModel();
if (savedInstanceState != null)
mWizardModel.load(savedInstanceState.getBundle("model"));
if (savedInstanceState != null) {
Bundle model = savedInstanceState.getBundle("model");
if (model != null) {
mWizardModel.load(model);
}
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_wizard);

View File

@ -190,6 +190,10 @@ public class I2PB64DestinationFragment extends Fragment {
Util.e("Could not find B64 file", fnfe);
Toast.makeText(getActivity(), "Could not find B64 file.",
Toast.LENGTH_SHORT).show();
} catch (SecurityException se) {
Util.e("Could not open B64 file", se);
Toast.makeText(getActivity(), "Could not open B64 file.",
Toast.LENGTH_SHORT).show();
} finally {
if (br != null)
try {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 287 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 337 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 353 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 504 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 423 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 287 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 246 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 535 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 433 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 283 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 430 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 531 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 650 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 504 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 479 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 641 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 767 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 331 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 640 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 652 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 806 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 290 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 353 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 314 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 272 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 319 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 297 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 234 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 346 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 454 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 346 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 464 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 561 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 444 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 488 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 533 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 198 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 444 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 614 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 464 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 330 B

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