Added option to connect account via VPN only

pull/209/head
M66B 2 years ago
parent 7677b34c4e
commit f0939d0069

@ -128,6 +128,7 @@ public class FragmentAccount extends FragmentBase {
private RadioGroup rgDate; private RadioGroup rgDate;
private CheckBox cbUnicode; private CheckBox cbUnicode;
private CheckBox cbUnmetered; private CheckBox cbUnmetered;
private CheckBox cbVpnOnly;
private Button btnCheck; private Button btnCheck;
private ContentLoadingProgressBar pbCheck; private ContentLoadingProgressBar pbCheck;
@ -240,6 +241,7 @@ public class FragmentAccount extends FragmentBase {
rgDate = view.findViewById(R.id.rgDate); rgDate = view.findViewById(R.id.rgDate);
cbUnicode = view.findViewById(R.id.cbUnicode); cbUnicode = view.findViewById(R.id.cbUnicode);
cbUnmetered = view.findViewById(R.id.cbUnmeteredOnly); cbUnmetered = view.findViewById(R.id.cbUnmeteredOnly);
cbVpnOnly = view.findViewById(R.id.cbVpnOnly);
btnCheck = view.findViewById(R.id.btnCheck); btnCheck = view.findViewById(R.id.btnCheck);
pbCheck = view.findViewById(R.id.pbCheck); pbCheck = view.findViewById(R.id.pbCheck);
@ -909,6 +911,7 @@ public class FragmentAccount extends FragmentBase {
args.putBoolean("use_received", rgDate.getCheckedRadioButtonId() == R.id.radio_received_header); args.putBoolean("use_received", rgDate.getCheckedRadioButtonId() == R.id.radio_received_header);
args.putBoolean("unicode", cbUnicode.isChecked()); args.putBoolean("unicode", cbUnicode.isChecked());
args.putBoolean("unmetered", cbUnmetered.isChecked()); args.putBoolean("unmetered", cbUnmetered.isChecked());
args.putBoolean("vpn_only", cbVpnOnly.isChecked());
args.putSerializable("drafts", drafts); args.putSerializable("drafts", drafts);
args.putSerializable("sent", sent); args.putSerializable("sent", sent);
@ -982,6 +985,7 @@ public class FragmentAccount extends FragmentBase {
boolean use_received = args.getBoolean("use_received"); boolean use_received = args.getBoolean("use_received");
boolean unicode = args.getBoolean("unicode"); boolean unicode = args.getBoolean("unicode");
boolean unmetered = args.getBoolean("unmetered"); boolean unmetered = args.getBoolean("unmetered");
boolean vpn_only = args.getBoolean("vpn_only");
EntityFolder drafts = (EntityFolder) args.getSerializable("drafts"); EntityFolder drafts = (EntityFolder) args.getSerializable("drafts");
EntityFolder sent = (EntityFolder) args.getSerializable("sent"); EntityFolder sent = (EntityFolder) args.getSerializable("sent");
@ -1094,6 +1098,8 @@ public class FragmentAccount extends FragmentBase {
return true; return true;
if (unmetered != jconditions.optBoolean("unmetered")) if (unmetered != jconditions.optBoolean("unmetered"))
return true; return true;
if (vpn_only != jconditions.optBoolean("vpn_only"))
return true;
if (account.error != null && account.synchronize) if (account.error != null && account.synchronize)
return true; return true;
@ -1238,6 +1244,7 @@ public class FragmentAccount extends FragmentBase {
account.unicode = unicode; account.unicode = unicode;
jconditions.put("unmetered", unmetered); jconditions.put("unmetered", unmetered);
jconditions.put("vpn_only", vpn_only);
account.conditions = jconditions.toString(); account.conditions = jconditions.toString();
if (!update) if (!update)
@ -1600,6 +1607,7 @@ public class FragmentAccount extends FragmentBase {
cbIgnoreSize.setChecked(account == null ? false : account.ignore_size); cbIgnoreSize.setChecked(account == null ? false : account.ignore_size);
cbUnicode.setChecked(account == null ? false : account.unicode); cbUnicode.setChecked(account == null ? false : account.unicode);
cbUnmetered.setChecked(jcondition.optBoolean("unmetered")); cbUnmetered.setChecked(jcondition.optBoolean("unmetered"));
cbVpnOnly.setChecked(jcondition.optBoolean("vpn_only"));
if (account != null && account.use_date) if (account != null && account.use_date)
rgDate.check(R.id.radio_date_header); rgDate.check(R.id.radio_date_header);

@ -98,6 +98,7 @@ public class FragmentPop extends FragmentBase {
private EditText etMax; private EditText etMax;
private EditText etInterval; private EditText etInterval;
private CheckBox cbUnmetered; private CheckBox cbUnmetered;
private CheckBox cbVpnOnly;
private ArrayAdapter<EntityFolder> adapterSwipe; private ArrayAdapter<EntityFolder> adapterSwipe;
private Spinner spLeft; private Spinner spLeft;
@ -165,6 +166,7 @@ public class FragmentPop extends FragmentBase {
etMax = view.findViewById(R.id.etMax); etMax = view.findViewById(R.id.etMax);
etInterval = view.findViewById(R.id.etInterval); etInterval = view.findViewById(R.id.etInterval);
cbUnmetered = view.findViewById(R.id.cbUnmeteredOnly); cbUnmetered = view.findViewById(R.id.cbUnmeteredOnly);
cbVpnOnly = view.findViewById(R.id.cbVpnOnly);
spLeft = view.findViewById(R.id.spLeft); spLeft = view.findViewById(R.id.spLeft);
spRight = view.findViewById(R.id.spRight); spRight = view.findViewById(R.id.spRight);
@ -332,6 +334,7 @@ public class FragmentPop extends FragmentBase {
args.putString("max", etMax.getText().toString()); args.putString("max", etMax.getText().toString());
args.putString("interval", etInterval.getText().toString()); args.putString("interval", etInterval.getText().toString());
args.putBoolean("unmetered", cbUnmetered.isChecked()); args.putBoolean("unmetered", cbUnmetered.isChecked());
args.putBoolean("vpn_only", cbVpnOnly.isChecked());
args.putLong("left", ((EntityFolder) spLeft.getSelectedItem()).id); args.putLong("left", ((EntityFolder) spLeft.getSelectedItem()).id);
args.putLong("right", ((EntityFolder) spRight.getSelectedItem()).id); args.putLong("right", ((EntityFolder) spRight.getSelectedItem()).id);
@ -384,6 +387,7 @@ public class FragmentPop extends FragmentBase {
String max = args.getString("max"); String max = args.getString("max");
String interval = args.getString("interval"); String interval = args.getString("interval");
boolean unmetered = args.getBoolean("unmetered"); boolean unmetered = args.getBoolean("unmetered");
boolean vpn_only = args.getBoolean("vpn_only");
long left = args.getLong("left"); long left = args.getLong("left");
long right = args.getLong("right"); long right = args.getLong("right");
@ -476,6 +480,8 @@ public class FragmentPop extends FragmentBase {
return true; return true;
if (unmetered != jconditions.optBoolean("unmetered")) if (unmetered != jconditions.optBoolean("unmetered"))
return true; return true;
if (vpn_only != jconditions.optBoolean("vpn_only"))
return true;
if (!Objects.equals(account.swipe_left, left)) if (!Objects.equals(account.swipe_left, left))
return true; return true;
@ -557,6 +563,7 @@ public class FragmentPop extends FragmentBase {
account.poll_interval = poll_interval; account.poll_interval = poll_interval;
jconditions.put("unmetered", unmetered); jconditions.put("unmetered", unmetered);
jconditions.put("vpn_only", vpn_only);
account.conditions = jconditions.toString(); account.conditions = jconditions.toString();
account.swipe_left = left; account.swipe_left = left;
@ -755,6 +762,7 @@ public class FragmentPop extends FragmentBase {
etInterval.setText(account == null ? "" : Long.toString(account.poll_interval)); etInterval.setText(account == null ? "" : Long.toString(account.poll_interval));
cbUnmetered.setChecked(jcondition.optBoolean("unmetered")); cbUnmetered.setChecked(jcondition.optBoolean("unmetered"));
cbVpnOnly.setChecked(jcondition.optBoolean("vpn_only"));
cbIdentity.setChecked(account == null); cbIdentity.setChecked(account == null);
List<EntityFolder> folders = getSwipeActions(); List<EntityFolder> folders = getSwipeActions();

@ -297,7 +297,7 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences
} }
if (current.accountState.synchronize) if (current.accountState.synchronize)
operations += current.accountState.operations; operations += current.accountState.operations;
if (current.accountState.operations > 0 && current.canConnect()) if (current.accountState.operations > 0 && current.canConnect(ServiceSynchronize.this))
runFts = false; runFts = false;
long account = current.command.getLong("account", -1); long account = current.command.getLong("account", -1);
@ -313,11 +313,11 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences
int index = accountStates.indexOf(current); int index = accountStates.indexOf(current);
if (index < 0) { if (index < 0) {
if (current.canRun()) { if (current.canRun(ServiceSynchronize.this)) {
EntityLog.log(ServiceSynchronize.this, EntityLog.Type.Scheduling, EntityLog.log(ServiceSynchronize.this, EntityLog.Type.Scheduling,
"### new " + current + "### new " + current +
" force=" + force + " force=" + force +
" start=" + current.canRun() + " start=" + current.canRun(ServiceSynchronize.this) +
" sync=" + current.accountState.isEnabled(current.enabled) + " sync=" + current.accountState.isEnabled(current.enabled) +
" enabled=" + current.accountState.synchronize + " enabled=" + current.accountState.synchronize +
" ondemand=" + current.accountState.ondemand + " ondemand=" + current.accountState.ondemand +
@ -347,15 +347,15 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences
// Some networks disallow email server connections: // Some networks disallow email server connections:
// - reload on network type change when disconnected // - reload on network type change when disconnected
if (reload || if (reload ||
prev.canRun() != current.canRun() || prev.canRun(ServiceSynchronize.this) != current.canRun(ServiceSynchronize.this) ||
!prev.accountState.equals(current.accountState)) { !prev.accountState.equals(current.accountState)) {
if (prev.canRun() || current.canRun()) if (prev.canRun(ServiceSynchronize.this) || current.canRun(ServiceSynchronize.this))
EntityLog.log(ServiceSynchronize.this, EntityLog.Type.Scheduling, EntityLog.log(ServiceSynchronize.this, EntityLog.Type.Scheduling,
"### changed " + current + "### changed " + current +
" reload=" + reload + " reload=" + reload +
" force=" + force + " force=" + force +
" stop=" + prev.canRun() + " stop=" + prev.canRun(ServiceSynchronize.this) +
" start=" + current.canRun() + " start=" + current.canRun(ServiceSynchronize.this) +
" sync=" + sync + " sync=" + sync +
" enabled=" + current.accountState.isEnabled(current.enabled) + " enabled=" + current.accountState.isEnabled(current.enabled) +
" should=" + current.accountState.shouldRun(current.enabled) + " should=" + current.accountState.shouldRun(current.enabled) +
@ -367,15 +367,15 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences
" tbd=" + current.accountState.tbd + " tbd=" + current.accountState.tbd +
" state=" + current.accountState.state + " state=" + current.accountState.state +
" active=" + prev.networkState.getActive() + "/" + current.networkState.getActive()); " active=" + prev.networkState.getActive() + "/" + current.networkState.getActive());
if (prev.canRun()) { if (prev.canRun(ServiceSynchronize.this)) {
event = true; event = true;
stop(prev); stop(prev);
} }
if (current.canRun()) { if (current.canRun(ServiceSynchronize.this)) {
event = true; event = true;
start(current, current.accountState.isEnabled(current.enabled) || sync, force); start(current, current.accountState.isEnabled(current.enabled) || sync, force);
} }
} else if (current.canRun() && !state.isAlive()) { } else if (current.canRun(ServiceSynchronize.this) && !state.isAlive()) {
Log.e(current + " died"); Log.e(current + " died");
EntityLog.log(ServiceSynchronize.this, "### died " + current); EntityLog.log(ServiceSynchronize.this, "### died " + current);
event = true; event = true;

@ -19,6 +19,7 @@ package eu.faircode.email;
Copyright 2018-2022 by Marcel Bokhorst (M66B) Copyright 2018-2022 by Marcel Bokhorst (M66B)
*/ */
import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.text.TextUtils; import android.text.TextUtils;
@ -61,13 +62,15 @@ public class TupleAccountNetworkState {
this.enabled = false; this.enabled = false;
} }
public boolean canConnect() { public boolean canConnect(Context context) {
boolean unmetered = jconditions.optBoolean("unmetered"); boolean unmetered = jconditions.optBoolean("unmetered");
return (!unmetered || this.networkState.isUnmetered()); boolean vpn_only = jconditions.optBoolean("vpn_only");
return (!unmetered || this.networkState.isUnmetered()) &&
(!vpn_only || ConnectionHelper.vpnActive(context));
} }
public boolean canRun() { public boolean canRun(Context context) {
if (!canConnect()) if (!canConnect(context))
return false; return false;
return (this.networkState.isSuitable() && this.accountState.shouldRun(enabled)); return (this.networkState.isSuitable() && this.accountState.shouldRun(enabled));

@ -715,6 +715,15 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbUnicode" /> app:layout_constraintTop_toBottomOf="@id/cbUnicode" />
<CheckBox
android:id="@+id/cbVpnOnly"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/title_advanced_vpn_only"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbUnmeteredOnly" />
<!-- check --> <!-- check -->
<Button <Button
@ -727,7 +736,7 @@
android:tag="disable" android:tag="disable"
android:text="@string/title_check" android:text="@string/title_check"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbUnmeteredOnly" /> app:layout_constraintTop_toBottomOf="@id/cbVpnOnly" />
<eu.faircode.email.ContentLoadingProgressBar <eu.faircode.email.ContentLoadingProgressBar
android:id="@+id/pbCheck" android:id="@+id/pbCheck"
@ -1115,7 +1124,7 @@
cbAutoSeen, cbAutoSeen,
tvInterval,etInterval,tvIntervalRemark,cbNoop,tvNoopRemark, tvInterval,etInterval,tvIntervalRemark,cbNoop,tvNoopRemark,
cbPartialFetch,tvPartialFetchRemark,cbIgnoreSize,rgDate,tvDateRemark, cbPartialFetch,tvPartialFetchRemark,cbIgnoreSize,rgDate,tvDateRemark,
cbUnicode,cbUnmeteredOnly" /> cbUnicode,cbUnmeteredOnly,cbVpnOnly" />
<androidx.constraintlayout.widget.Group <androidx.constraintlayout.widget.Group
android:id="@+id/grpFolders" android:id="@+id/grpFolders"

@ -226,10 +226,10 @@
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:drawableEnd="@drawable/twotone_open_in_new_12" android:drawableEnd="@drawable/twotone_open_in_new_12"
android:drawablePadding="6dp" android:drawablePadding="6dp"
app:drawableTint="?android:attr/textColorLink"
android:text="@string/title_password_storage" android:text="@string/title_password_storage"
android:textAppearance="@style/TextAppearance.AppCompat.Small" android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textColor="?android:attr/textColorLink" android:textColor="?android:attr/textColorLink"
app:drawableTint="?android:attr/textColorLink"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvCaseSensitive" /> app:layout_constraintTop_toBottomOf="@id/tvCaseSensitive" />
@ -484,6 +484,15 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvIntervalRemark" /> app:layout_constraintTop_toBottomOf="@id/tvIntervalRemark" />
<CheckBox
android:id="@+id/cbVpnOnly"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/title_advanced_vpn_only"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbUnmeteredOnly" />
<TextView <TextView
android:id="@+id/tvLeft" android:id="@+id/tvLeft"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -492,7 +501,7 @@
android:text="@string/title_account_left" android:text="@string/title_account_left"
android:textAppearance="@style/TextAppearance.AppCompat.Small" android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/cbUnmeteredOnly" /> app:layout_constraintTop_toBottomOf="@+id/cbVpnOnly" />
<Spinner <Spinner
android:id="@+id/spLeft" android:id="@+id/spLeft"

Loading…
Cancel
Save