re-do the AndroidSAMSecureSession so it doesn't require me to ask permission to draw over other apps. Permission to draw over other apps gives apps the power to take screenshots, so it's a special permission on Android.
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,2 +1,4 @@
|
||||
etc/docker.signing.properties
|
||||
signing.properties
|
||||
signing.properties.idea
|
||||
.idea/
|
||||
.gradle/
|
||||
|
@ -7,7 +7,7 @@ repositories {
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
defaultConfig {
|
||||
versionCode 4745266
|
||||
versionCode 4745267
|
||||
versionName "$I2P_VERSION"
|
||||
minSdkVersion 14
|
||||
targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION as String)
|
||||
|
@ -55,6 +55,10 @@
|
||||
<action android:name="net.i2p.android.router.START_I2P" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="net.i2p.android.router.service.APPROVE_SAM" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
|
||||
<!-- Addressbook filters -->
|
||||
<intent-filter>
|
||||
|
@ -19,6 +19,7 @@ import net.i2p.android.router.MainFragment;
|
||||
import net.i2p.android.router.R;
|
||||
import net.i2p.android.router.SettingsActivity;
|
||||
import net.i2p.android.router.addressbook.AddressbookContainer;
|
||||
import net.i2p.android.router.service.AndroidSAMSecureSession;
|
||||
import net.i2p.android.router.service.RouterService;
|
||||
import net.i2p.android.router.service.State;
|
||||
import net.i2p.android.router.util.Connectivity;
|
||||
@ -134,6 +135,7 @@ public class I2PActivity extends I2PActivityBase implements
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
setIntent(intent);
|
||||
handleIntents();
|
||||
}
|
||||
|
||||
@ -143,17 +145,29 @@ public class I2PActivity extends I2PActivityBase implements
|
||||
|
||||
Intent intent = getIntent();
|
||||
String action = intent.getAction();
|
||||
Util.d("handleIntent: intent=" + intent.toString());
|
||||
|
||||
if (action == null)
|
||||
return;
|
||||
|
||||
Util.d("handleIntent: action=" + action);
|
||||
|
||||
Bundle extra = intent.getExtras();
|
||||
if (extra != null)
|
||||
Util.d("handleIntent extra=" + extra.toString());
|
||||
|
||||
switch (action) {
|
||||
case "net.i2p.android.router.START_I2P":
|
||||
if (mViewPager.getCurrentItem() != 0)
|
||||
mViewPager.setCurrentItem(0, false);
|
||||
autoStart();
|
||||
break;
|
||||
|
||||
case "net.i2p.android.router.service.APPROVE_SAM":
|
||||
Util.w("Affirmed SAM Connection");
|
||||
String ID = extra.getString("ID");
|
||||
Util.d("SAM ID was: " + ID);
|
||||
AndroidSAMSecureSession.affirmResult(ID);
|
||||
break;
|
||||
case Intent.ACTION_PICK:
|
||||
mViewPager.setFixedPage(2, R.string.select_an_address);
|
||||
break;
|
||||
|
@ -1,23 +1,25 @@
|
||||
package net.i2p.android.router.service;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.res.AssetManager;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.support.annotation.RequiresApi;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.widget.Toast;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
|
||||
import net.i2p.android.I2PActivity;
|
||||
import net.i2p.android.I2PActivityBase;
|
||||
import net.i2p.android.router.R;
|
||||
import net.i2p.android.router.util.Notifications;
|
||||
import net.i2p.android.router.util.Util;
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.sam.*;
|
||||
import net.i2p.sam.SAMException;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
@ -26,79 +28,110 @@ import java.util.Properties;
|
||||
*
|
||||
* @since 1.8.0
|
||||
*/
|
||||
public class AndroidSAMSecureSession implements SAMSecureSessionInterface {
|
||||
public class AndroidSAMSecureSession extends AppCompatActivity implements SAMSecureSessionInterface {
|
||||
private static final String URI_I2P_ANDROID = "net.i2p.android";
|
||||
private final Context mCtx;
|
||||
private final RouterService _routerService;
|
||||
private final StatusBar _statusBar;
|
||||
static private Map<String, Integer> results = new HashMap<>();
|
||||
|
||||
public AndroidSAMSecureSession(Context ctx) {
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
public static void affirmResult(String clientId) {
|
||||
Util.d("Affirmed result for: " + clientId);
|
||||
results.put(clientId, 1);
|
||||
}
|
||||
|
||||
public AndroidSAMSecureSession(Context ctx, RouterService rCtx, StatusBar statusBar) {
|
||||
mCtx = ctx;
|
||||
_routerService = rCtx;
|
||||
_statusBar = statusBar;
|
||||
}
|
||||
|
||||
private class SAMSecureRunnable implements Runnable {
|
||||
private int result = -1;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext());
|
||||
|
||||
builder.setTitle(mCtx.getResources().getString(R.string.settings_confirm_sam));
|
||||
builder.setMessage(mCtx.getResources().getString(R.string.settings_confirm_sam));
|
||||
|
||||
builder.setPositiveButton(mCtx.getResources().getString(R.string.settings_confirm_allow_sam), new DialogInterface.OnClickListener() {
|
||||
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
result = 1;
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
builder.setNegativeButton(mCtx.getResources().getString(R.string.settings_confirm_deny_sam), new DialogInterface.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
result = 0;
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
AlertDialog alert = builder.create();
|
||||
alert.show();
|
||||
}
|
||||
|
||||
private synchronized void waitForResult() {
|
||||
private void waitForResult(String clientId) {
|
||||
for (int i=0;i<60;i++) {
|
||||
if (result != - 1)
|
||||
break;
|
||||
Util.i("Waiting on user to approve SAM connection");
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
Util.e("SAMSecureSession Error", e);
|
||||
}
|
||||
Integer result = results.get(clientId);
|
||||
if (result == null)
|
||||
continue;
|
||||
if (result != -1)
|
||||
break;
|
||||
Util.d("Waiting on user to approve SAM connection for: "+clientId);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isResult() {
|
||||
if (result == 0)
|
||||
private boolean isResult(String clientId) {
|
||||
waitForResult(clientId);
|
||||
final Integer finResult = results.get(clientId);
|
||||
if (finResult == null)
|
||||
return false;
|
||||
if (result == 1)
|
||||
_routerService.updateStatus();
|
||||
if (finResult == 0) {
|
||||
Util.w("SAM connection cancelled by user request");
|
||||
return false;
|
||||
}
|
||||
if (finResult == 1) {
|
||||
Util.w("SAM connection allowed by user action");
|
||||
return true;
|
||||
}
|
||||
Util.w("SAM connection denied by timeout.");
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean checkResult() {
|
||||
waitForResult();
|
||||
return isResult();
|
||||
}
|
||||
}
|
||||
public Context getApplicationContext() {
|
||||
return mCtx.getApplicationContext();
|
||||
private boolean checkResult(String clientId) {
|
||||
Intent intent = new Intent("net.i2p.android.router.service.APPROVE_SAM", null, mCtx, I2PActivity.class );
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setPackage(URI_I2P_ANDROID);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putBoolean("approveSAMConnection", true);
|
||||
bundle.putString("ID", clientId);
|
||||
intent.putExtras(bundle);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(
|
||||
mCtx, 7656,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT, bundle);
|
||||
String dlgText = mCtx.getString(R.string.settings_confirm_sam) + "\n";//""</br>";
|
||||
dlgText += mCtx.getString(R.string.settings_confirm_sam_id) + clientId + "\n";//""</br>";
|
||||
dlgText += mCtx.getString(R.string.settings_confirm_allow_sam) + "\n";//""</br>";
|
||||
dlgText += mCtx.getString(R.string.settings_confirm_deny_sam) + "\n";//""</br>";
|
||||
|
||||
|
||||
_statusBar.replaceIntent(StatusBar.ICON_ACTIVE, dlgText, pendingIntent);
|
||||
return isResult(clientId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean approveOrDenySecureSession(Properties i2cpProps, Properties props) throws SAMException {
|
||||
Handler handler = new Handler(Looper.getMainLooper());
|
||||
SAMSecureRunnable ssr = new SAMSecureRunnable();
|
||||
handler.post(ssr);
|
||||
return ssr.checkResult();
|
||||
String ID = props.getProperty("USER");
|
||||
if (ID == null)
|
||||
ID = i2cpProps.getProperty("inbound.nickname");
|
||||
if (ID == null)
|
||||
ID = i2cpProps.getProperty("outbound.nickname");
|
||||
if (ID == null)
|
||||
ID = props.getProperty("ID");
|
||||
if (ID == null) {
|
||||
MessageDigest messageDigest = null;
|
||||
try {
|
||||
messageDigest = MessageDigest.getInstance("SHA-256");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
ID = "No_ID_Present";
|
||||
}
|
||||
if (messageDigest != null) {
|
||||
String combinedProps = i2cpProps.toString() + props.toString();
|
||||
messageDigest.update(combinedProps.getBytes());
|
||||
ID = messageDigest.digest().toString();
|
||||
}else{
|
||||
ID = "No_ID_Present";
|
||||
}
|
||||
}
|
||||
return checkResult(ID);
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +1,23 @@
|
||||
package net.i2p.android.router.service;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.content.Context;
|
||||
|
||||
//import net.i2p.BOB.BOB;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.addressbook.DaemonThread;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Looper;
|
||||
import android.preference.PreferenceManager;
|
||||
import net.i2p.android.apps.NewsFetcher;
|
||||
import net.i2p.android.router.service.AndroidSAMSecureSession;
|
||||
import net.i2p.android.router.util.Notifications;
|
||||
import net.i2p.android.router.util.Util;
|
||||
import net.i2p.i2ptunnel.TunnelControllerGroup;
|
||||
import net.i2p.router.Job;
|
||||
import net.i2p.router.JobImpl;
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.startup.RouterAppManager;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.sam.SAMBridge;
|
||||
import net.i2p.sam.SAMSecureSessionInterface;
|
||||
@ -45,19 +49,41 @@ import java.util.Properties;
|
||||
class LoadClientsJob extends JobImpl {
|
||||
|
||||
private final Context mCtx;
|
||||
private final RouterService _routerService;
|
||||
private final Notifications _notif;
|
||||
private DaemonThread _addressbook;
|
||||
public SAMBridge SAM_BRIDGE;
|
||||
private final StatusBar _statusBar;
|
||||
// private BOB _bob;
|
||||
|
||||
/** this is the delay to load (and start) the clients. */
|
||||
private static final long LOAD_DELAY = 60 * 1000;
|
||||
|
||||
public LoadClientsJob(Context ctx, RouterContext rCtx, Notifications notif, StatusBar status) {
|
||||
super(rCtx);
|
||||
mCtx = ctx;
|
||||
_routerService = null;
|
||||
_notif = notif;
|
||||
getTiming().setStartAfter(getContext().clock().now() + LOAD_DELAY);
|
||||
_statusBar = status;
|
||||
}
|
||||
|
||||
public LoadClientsJob(Context ctx, RouterContext rCtx, RouterService rSvc, Notifications notif, StatusBar status) {
|
||||
super(rCtx);
|
||||
mCtx = ctx;
|
||||
_routerService = rSvc;
|
||||
_notif = notif;
|
||||
getTiming().setStartAfter(getContext().clock().now() + LOAD_DELAY);
|
||||
_statusBar = status;
|
||||
}
|
||||
|
||||
public LoadClientsJob(Context ctx, RouterContext rCtx, Notifications notif) {
|
||||
super(rCtx);
|
||||
mCtx = ctx;
|
||||
_routerService = null;
|
||||
_notif = notif;
|
||||
getTiming().setStartAfter(getContext().clock().now() + LOAD_DELAY);
|
||||
_statusBar = null;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
@ -153,7 +179,9 @@ class LoadClientsJob extends JobImpl {
|
||||
Util.d("Starting SAM");
|
||||
try {
|
||||
Util.i("Starting the SAM API");
|
||||
SAMSecureSessionInterface _secureSession = new AndroidSAMSecureSession(mCtx);
|
||||
Looper.prepare();
|
||||
AndroidSAMSecureSession _androidSecureSession = new AndroidSAMSecureSession(mCtx, _routerService, _statusBar);
|
||||
SAMSecureSessionInterface _secureSession = _androidSecureSession;
|
||||
SAM_BRIDGE = new SAMBridge("127.0.0.1",
|
||||
7656,
|
||||
false,
|
||||
|
@ -236,7 +236,7 @@ public class RouterService extends Service {
|
||||
throw new IllegalStateException("Router has no context?");
|
||||
}
|
||||
_context.router().setKillVMOnEnd(false);
|
||||
Job loadJob = new LoadClientsJob(RouterService.this, _context, _notif);
|
||||
Job loadJob = new LoadClientsJob(RouterService.this, _context, RouterService.this, _notif, _statusBar);
|
||||
_context.jobQueue().addJob(loadJob);
|
||||
_context.addShutdownTask(new ShutdownHook());
|
||||
_context.addFinalShutdownTask(new FinalShutdownHook());
|
||||
@ -262,6 +262,16 @@ public class RouterService extends Service {
|
||||
private String _currTitle;
|
||||
private boolean _hadTunnels;
|
||||
|
||||
public void updateStatus() {
|
||||
RouterContext ctx = _context;
|
||||
if(ctx != null && (_state == State.RUNNING || _state == State.ACTIVE || _state == State.GRACEFUL_SHUTDOWN)) {
|
||||
Router router = ctx.router();
|
||||
if(router.isAlive()) {
|
||||
updateStatus(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateStatus(RouterContext ctx) {
|
||||
int active = ctx.commSystem().countActivePeers();
|
||||
int known = Math.max(ctx.netDb().getKnownRouters() - 1, 0);
|
||||
|
@ -13,6 +13,7 @@ import android.widget.Toast;
|
||||
|
||||
import net.i2p.android.I2PActivity;
|
||||
import net.i2p.android.router.R;
|
||||
import net.i2p.android.router.util.Notifications;
|
||||
|
||||
class StatusBar {
|
||||
|
||||
@ -45,37 +46,69 @@ class StatusBar {
|
||||
int icon = ICON_STARTING;
|
||||
// won't be shown if replace() is called
|
||||
String text = mCtx.getString(R.string.notification_status_starting);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
mNotifyBuilder = new NotificationCompat.Builder(mCtx);
|
||||
} else {
|
||||
mNotifyBuilder = new NotificationCompat.Builder(mCtx, NOTIFICATION_CHANNEL_ID);
|
||||
mNotifyBuilder = notifyBuilder(icon, text);
|
||||
}
|
||||
|
||||
mNotifyBuilder.setContentText(text);
|
||||
mNotifyBuilder.setSmallIcon(icon);
|
||||
mNotifyBuilder.setColor(mCtx.getResources().getColor(R.color.primary_light));
|
||||
mNotifyBuilder.setOngoing(true);
|
||||
mNotifyBuilder.setPriority(NotificationManager.IMPORTANCE_LOW);
|
||||
mNotifyBuilder.setCategory(Notification.CATEGORY_SERVICE);
|
||||
private NotificationCompat.Builder notifyBuilder(int icon, String text) {
|
||||
NotificationCompat.Builder tNotifyBuilder;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
tNotifyBuilder = new NotificationCompat.Builder(mCtx);
|
||||
} else {
|
||||
tNotifyBuilder = new NotificationCompat.Builder(mCtx, NOTIFICATION_CHANNEL_ID);
|
||||
}
|
||||
tNotifyBuilder.setContentText(text);
|
||||
tNotifyBuilder.setSmallIcon(icon);
|
||||
tNotifyBuilder.setColor(mCtx.getResources().getColor(R.color.primary_light));
|
||||
tNotifyBuilder.setOngoing(true);
|
||||
tNotifyBuilder.setPriority(NotificationManager.IMPORTANCE_LOW);
|
||||
tNotifyBuilder.setCategory(Notification.CATEGORY_SERVICE);
|
||||
|
||||
PendingIntent pi = this.pendingIntent();
|
||||
tNotifyBuilder.setContentIntent(pi);
|
||||
return tNotifyBuilder;
|
||||
}
|
||||
|
||||
private PendingIntent pendingIntent() {
|
||||
Intent intent = new Intent(mCtx, I2PActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
PendingIntent pi = PendingIntent.getActivity(mCtx, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
mNotifyBuilder.setContentIntent(pi);
|
||||
return pi;
|
||||
}
|
||||
|
||||
public void replace(int icon, int textResource) {
|
||||
PendingIntent pi = pendingIntent();
|
||||
mNotifyBuilder.setContentIntent(pi);
|
||||
replace(icon, mCtx.getString(textResource));
|
||||
}
|
||||
|
||||
public void replace(int icon, String title) {
|
||||
PendingIntent pi = pendingIntent();
|
||||
mNotifyBuilder.setContentIntent(pi);
|
||||
mNotifyBuilder.setSmallIcon(icon)
|
||||
.setStyle(null)
|
||||
.setTicker(title);
|
||||
update(title);
|
||||
}
|
||||
|
||||
public void replaceIntent(int icon, String text, PendingIntent pi) {
|
||||
mNotifyBuilder.setContentIntent(pi);
|
||||
mNotifyBuilder.setPriority(NotificationCompat.PRIORITY_MAX);
|
||||
mNotifyBuilder.setAutoCancel(true);
|
||||
mNotifyBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(text));
|
||||
mNotifyBuilder.setSmallIcon(icon).setContentText(text);
|
||||
update(null, text);
|
||||
}
|
||||
|
||||
public void replaceIntent(int icon, int textResource, PendingIntent pi) {
|
||||
String text = mCtx.getString(textResource);
|
||||
mNotifyBuilder.setContentIntent(pi);
|
||||
mNotifyBuilder.setPriority(NotificationCompat.PRIORITY_MAX);
|
||||
mNotifyBuilder.setAutoCancel(true);
|
||||
mNotifyBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(text));
|
||||
mNotifyBuilder.setSmallIcon(icon).setContentText(text);
|
||||
update(null, text);
|
||||
}
|
||||
|
||||
public void update(String title) {
|
||||
update(title, null);
|
||||
}
|
||||
@ -87,8 +120,10 @@ class StatusBar {
|
||||
}
|
||||
|
||||
public void update(String title, String text) {
|
||||
mNotifyBuilder.setContentTitle(title)
|
||||
.setContentText(text);
|
||||
if (title != null)
|
||||
mNotifyBuilder.setContentTitle(title);
|
||||
if (text != null)
|
||||
mNotifyBuilder.setContentText(text);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
mNotificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_LOW);
|
||||
mNotificationManager.createNotificationChannel(mNotificationChannel);
|
||||
|
@ -172,4 +172,28 @@
|
||||
<item>ECDSA-P521</item>
|
||||
<item>Ed25519</item>
|
||||
</string-array>
|
||||
<string-array name="speed_increments">
|
||||
<item>100 KB/s</item>
|
||||
<item>200 KB/s</item>
|
||||
<item>300 KB/s</item>
|
||||
<item>400 KB/s</item>
|
||||
<item>500 KB/s</item>
|
||||
<item>600 KB/s</item>
|
||||
<item>700 KB/s</item>
|
||||
<item>800 KB/s</item>
|
||||
<item>900 KB/s</item>
|
||||
<item>1000 KB/s</item>
|
||||
</string-array>
|
||||
<string-array name="speed_values">
|
||||
<item>100</item>
|
||||
<item>200</item>
|
||||
<item>300</item>
|
||||
<item>400</item>
|
||||
<item>500</item>
|
||||
<item>600</item>
|
||||
<item>700</item>
|
||||
<item>800</item>
|
||||
<item>900</item>
|
||||
<item>1000</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
@ -189,8 +189,9 @@
|
||||
<string name="settings_label_sam">SAM interface</string>
|
||||
<string name="settings_desc_sam">Allow third-party apps to create tunnels using SAM (requires router restart)</string>
|
||||
<string name="settings_confirm_sam">An application is trying to make a SAM connection.</string>
|
||||
<string name="settings_confirm_allow_sam">Allow</string>
|
||||
<string name="settings_confirm_deny_sam">Deny</string>
|
||||
<string name="settings_confirm_sam_id">Connection Name/ID:</string>
|
||||
<string name="settings_confirm_allow_sam">Tap to allow</string>
|
||||
<string name="settings_confirm_deny_sam">Ignore to deny</string>
|
||||
<string name="settings_label_exploratory_pool">Exploratory pool</string>
|
||||
<string name="settings_desc_exploratory_pool">Tunnel parameters</string>
|
||||
<string name="settings_label_expl_inbound">Inbound tunnels</string>
|
||||
|
@ -17,22 +17,20 @@
|
||||
<!--
|
||||
i2np.bandwidth.inboundKBytesPerSecond=100
|
||||
-->
|
||||
<com.pavelsikun.seekbarpreference.SeekBarPreference
|
||||
<DropDownPreference
|
||||
android:key="@string/PROP_INBOUND_BANDWIDTH"
|
||||
android:title="@string/settings_desc_bw_inbound"
|
||||
app:msbp_defaultValue="100"
|
||||
app:msbp_maxValue="1000"
|
||||
app:msbp_measurementUnit="KB/s"
|
||||
android:entries="@array/speed_increments"
|
||||
android:entryValues="@array/speed_values"
|
||||
/>
|
||||
<!--
|
||||
i2np.bandwidth.outboundKBytesPerSecond=30
|
||||
-->
|
||||
<com.pavelsikun.seekbarpreference.SeekBarPreference
|
||||
<DropDownPreference
|
||||
android:key="@string/PROP_OUTBOUND_BANDWIDTH"
|
||||
android:title="@string/settings_desc_bw_outbound"
|
||||
app:msbp_defaultValue="100"
|
||||
app:msbp_maxValue="1000"
|
||||
app:msbp_measurementUnit="KB/s"
|
||||
android:title="@string/settings_desc_bw_inbound"
|
||||
android:entries="@array/speed_increments"
|
||||
android:entryValues="@array/speed_values"
|
||||
/>
|
||||
</PreferenceCategory>
|
||||
|
||||
|
@ -17,4 +17,4 @@ POM_DEVELOPER_EMAIL=hankhill19580@gmail.com
|
||||
ANDROID_BUILD_TARGET_SDK_VERSION=30
|
||||
ANDROID_BUILD_SDK_VERSION=28
|
||||
|
||||
I2P_VERSION=1.7.0-21
|
||||
I2P_VERSION=1.8.0-1
|
||||
|
Reference in New Issue
Block a user