From f0939d00698e755a122f2a998e64b31e67707b05 Mon Sep 17 00:00:00 2001 From: M66B Date: Mon, 5 Sep 2022 22:41:30 +0200 Subject: [PATCH] Added option to connect account via VPN only --- .../eu/faircode/email/FragmentAccount.java | 8 ++++++++ .../java/eu/faircode/email/FragmentPop.java | 8 ++++++++ .../eu/faircode/email/ServiceSynchronize.java | 20 +++++++++---------- .../email/TupleAccountNetworkState.java | 11 ++++++---- app/src/main/res/layout/fragment_account.xml | 13 ++++++++++-- app/src/main/res/layout/fragment_pop.xml | 13 ++++++++++-- 6 files changed, 55 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/eu/faircode/email/FragmentAccount.java b/app/src/main/java/eu/faircode/email/FragmentAccount.java index 47b8df3877..9a43d1d1e5 100644 --- a/app/src/main/java/eu/faircode/email/FragmentAccount.java +++ b/app/src/main/java/eu/faircode/email/FragmentAccount.java @@ -128,6 +128,7 @@ public class FragmentAccount extends FragmentBase { private RadioGroup rgDate; private CheckBox cbUnicode; private CheckBox cbUnmetered; + private CheckBox cbVpnOnly; private Button btnCheck; private ContentLoadingProgressBar pbCheck; @@ -240,6 +241,7 @@ public class FragmentAccount extends FragmentBase { rgDate = view.findViewById(R.id.rgDate); cbUnicode = view.findViewById(R.id.cbUnicode); cbUnmetered = view.findViewById(R.id.cbUnmeteredOnly); + cbVpnOnly = view.findViewById(R.id.cbVpnOnly); btnCheck = view.findViewById(R.id.btnCheck); 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("unicode", cbUnicode.isChecked()); args.putBoolean("unmetered", cbUnmetered.isChecked()); + args.putBoolean("vpn_only", cbVpnOnly.isChecked()); args.putSerializable("drafts", drafts); args.putSerializable("sent", sent); @@ -982,6 +985,7 @@ public class FragmentAccount extends FragmentBase { boolean use_received = args.getBoolean("use_received"); boolean unicode = args.getBoolean("unicode"); boolean unmetered = args.getBoolean("unmetered"); + boolean vpn_only = args.getBoolean("vpn_only"); EntityFolder drafts = (EntityFolder) args.getSerializable("drafts"); EntityFolder sent = (EntityFolder) args.getSerializable("sent"); @@ -1094,6 +1098,8 @@ public class FragmentAccount extends FragmentBase { return true; if (unmetered != jconditions.optBoolean("unmetered")) return true; + if (vpn_only != jconditions.optBoolean("vpn_only")) + return true; if (account.error != null && account.synchronize) return true; @@ -1238,6 +1244,7 @@ public class FragmentAccount extends FragmentBase { account.unicode = unicode; jconditions.put("unmetered", unmetered); + jconditions.put("vpn_only", vpn_only); account.conditions = jconditions.toString(); if (!update) @@ -1600,6 +1607,7 @@ public class FragmentAccount extends FragmentBase { cbIgnoreSize.setChecked(account == null ? false : account.ignore_size); cbUnicode.setChecked(account == null ? false : account.unicode); cbUnmetered.setChecked(jcondition.optBoolean("unmetered")); + cbVpnOnly.setChecked(jcondition.optBoolean("vpn_only")); if (account != null && account.use_date) rgDate.check(R.id.radio_date_header); diff --git a/app/src/main/java/eu/faircode/email/FragmentPop.java b/app/src/main/java/eu/faircode/email/FragmentPop.java index b9980ca9f7..b4253a6e52 100644 --- a/app/src/main/java/eu/faircode/email/FragmentPop.java +++ b/app/src/main/java/eu/faircode/email/FragmentPop.java @@ -98,6 +98,7 @@ public class FragmentPop extends FragmentBase { private EditText etMax; private EditText etInterval; private CheckBox cbUnmetered; + private CheckBox cbVpnOnly; private ArrayAdapter adapterSwipe; private Spinner spLeft; @@ -165,6 +166,7 @@ public class FragmentPop extends FragmentBase { etMax = view.findViewById(R.id.etMax); etInterval = view.findViewById(R.id.etInterval); cbUnmetered = view.findViewById(R.id.cbUnmeteredOnly); + cbVpnOnly = view.findViewById(R.id.cbVpnOnly); spLeft = view.findViewById(R.id.spLeft); spRight = view.findViewById(R.id.spRight); @@ -332,6 +334,7 @@ public class FragmentPop extends FragmentBase { args.putString("max", etMax.getText().toString()); args.putString("interval", etInterval.getText().toString()); args.putBoolean("unmetered", cbUnmetered.isChecked()); + args.putBoolean("vpn_only", cbVpnOnly.isChecked()); args.putLong("left", ((EntityFolder) spLeft.getSelectedItem()).id); args.putLong("right", ((EntityFolder) spRight.getSelectedItem()).id); @@ -384,6 +387,7 @@ public class FragmentPop extends FragmentBase { String max = args.getString("max"); String interval = args.getString("interval"); boolean unmetered = args.getBoolean("unmetered"); + boolean vpn_only = args.getBoolean("vpn_only"); long left = args.getLong("left"); long right = args.getLong("right"); @@ -476,6 +480,8 @@ public class FragmentPop extends FragmentBase { return true; if (unmetered != jconditions.optBoolean("unmetered")) return true; + if (vpn_only != jconditions.optBoolean("vpn_only")) + return true; if (!Objects.equals(account.swipe_left, left)) return true; @@ -557,6 +563,7 @@ public class FragmentPop extends FragmentBase { account.poll_interval = poll_interval; jconditions.put("unmetered", unmetered); + jconditions.put("vpn_only", vpn_only); account.conditions = jconditions.toString(); account.swipe_left = left; @@ -755,6 +762,7 @@ public class FragmentPop extends FragmentBase { etInterval.setText(account == null ? "" : Long.toString(account.poll_interval)); cbUnmetered.setChecked(jcondition.optBoolean("unmetered")); + cbVpnOnly.setChecked(jcondition.optBoolean("vpn_only")); cbIdentity.setChecked(account == null); List folders = getSwipeActions(); diff --git a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java index 5b80eaffad..ce7f45f873 100644 --- a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java +++ b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java @@ -297,7 +297,7 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences } if (current.accountState.synchronize) operations += current.accountState.operations; - if (current.accountState.operations > 0 && current.canConnect()) + if (current.accountState.operations > 0 && current.canConnect(ServiceSynchronize.this)) runFts = false; long account = current.command.getLong("account", -1); @@ -313,11 +313,11 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences int index = accountStates.indexOf(current); if (index < 0) { - if (current.canRun()) { + if (current.canRun(ServiceSynchronize.this)) { EntityLog.log(ServiceSynchronize.this, EntityLog.Type.Scheduling, "### new " + current + " force=" + force + - " start=" + current.canRun() + + " start=" + current.canRun(ServiceSynchronize.this) + " sync=" + current.accountState.isEnabled(current.enabled) + " enabled=" + current.accountState.synchronize + " ondemand=" + current.accountState.ondemand + @@ -347,15 +347,15 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences // Some networks disallow email server connections: // - reload on network type change when disconnected if (reload || - prev.canRun() != current.canRun() || + prev.canRun(ServiceSynchronize.this) != current.canRun(ServiceSynchronize.this) || !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, "### changed " + current + " reload=" + reload + " force=" + force + - " stop=" + prev.canRun() + - " start=" + current.canRun() + + " stop=" + prev.canRun(ServiceSynchronize.this) + + " start=" + current.canRun(ServiceSynchronize.this) + " sync=" + sync + " enabled=" + current.accountState.isEnabled(current.enabled) + " should=" + current.accountState.shouldRun(current.enabled) + @@ -367,15 +367,15 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences " tbd=" + current.accountState.tbd + " state=" + current.accountState.state + " active=" + prev.networkState.getActive() + "/" + current.networkState.getActive()); - if (prev.canRun()) { + if (prev.canRun(ServiceSynchronize.this)) { event = true; stop(prev); } - if (current.canRun()) { + if (current.canRun(ServiceSynchronize.this)) { event = true; 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"); EntityLog.log(ServiceSynchronize.this, "### died " + current); event = true; diff --git a/app/src/main/java/eu/faircode/email/TupleAccountNetworkState.java b/app/src/main/java/eu/faircode/email/TupleAccountNetworkState.java index 2b6dddf742..2ba25b03f4 100644 --- a/app/src/main/java/eu/faircode/email/TupleAccountNetworkState.java +++ b/app/src/main/java/eu/faircode/email/TupleAccountNetworkState.java @@ -19,6 +19,7 @@ package eu.faircode.email; Copyright 2018-2022 by Marcel Bokhorst (M66B) */ +import android.content.Context; import android.os.Bundle; import android.text.TextUtils; @@ -61,13 +62,15 @@ public class TupleAccountNetworkState { this.enabled = false; } - public boolean canConnect() { + public boolean canConnect(Context context) { 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() { - if (!canConnect()) + public boolean canRun(Context context) { + if (!canConnect(context)) return false; return (this.networkState.isSuitable() && this.accountState.shouldRun(enabled)); diff --git a/app/src/main/res/layout/fragment_account.xml b/app/src/main/res/layout/fragment_account.xml index 74753f9c51..085351b78c 100644 --- a/app/src/main/res/layout/fragment_account.xml +++ b/app/src/main/res/layout/fragment_account.xml @@ -715,6 +715,15 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/cbUnicode" /> + +