diff --git a/app/src/main/java/eu/faircode/netguard/ActivityMain.java b/app/src/main/java/eu/faircode/netguard/ActivityMain.java index 17588500..a251857c 100644 --- a/app/src/main/java/eu/faircode/netguard/ActivityMain.java +++ b/app/src/main/java/eu/faircode/netguard/ActivityMain.java @@ -326,6 +326,7 @@ public class ActivityMain extends AppCompatActivity implements SharedPreferences menu.findItem(R.id.menu_whitelist_wifi).setChecked(prefs.getBoolean("whitelist_wifi", true)); menu.findItem(R.id.menu_whitelist_other).setChecked(prefs.getBoolean("whitelist_other", true)); + menu.findItem(R.id.menu_whitelist_roaming).setChecked(prefs.getBoolean("whitelist_roaming", true)); menu.findItem(R.id.menu_system).setChecked(prefs.getBoolean("manage_system", false)); menu.findItem(R.id.menu_export).setEnabled(getIntentCreateDocument().resolveActivity(getPackageManager()) != null); menu.findItem(R.id.menu_import).setEnabled(getIntentOpenDocument().resolveActivity(getPackageManager()) != null); @@ -354,6 +355,10 @@ public class ActivityMain extends AppCompatActivity implements SharedPreferences menu_whitelist_other(prefs); return true; + case R.id.menu_whitelist_roaming: + menu_whitelist_roaming(prefs); + return true; + case R.id.menu_system: menu_system(prefs); return true; @@ -408,6 +413,12 @@ public class ActivityMain extends AppCompatActivity implements SharedPreferences SinkholeService.reload("other", this); } + private void menu_whitelist_roaming(SharedPreferences prefs) { + prefs.edit().putBoolean("whitelist_roaming", !prefs.getBoolean("whitelist_roaming", true)).apply(); + updateApplicationList(); + SinkholeService.reload("other", this); + } + private void menu_system(SharedPreferences prefs) { prefs.edit().putBoolean("manage_system", !prefs.getBoolean("manage_system", true)).apply(); updateApplicationList(); diff --git a/app/src/main/java/eu/faircode/netguard/Rule.java b/app/src/main/java/eu/faircode/netguard/Rule.java index fb73a94a..636c5a47 100644 --- a/app/src/main/java/eu/faircode/netguard/Rule.java +++ b/app/src/main/java/eu/faircode/netguard/Rule.java @@ -27,6 +27,7 @@ public class Rule implements Comparable { public boolean wifi_blocked; public boolean other_blocked; public boolean unused; + public boolean roaming; public boolean changed; public Intent intent; public boolean attributes = false; @@ -51,13 +52,15 @@ public class Rule implements Comparable { SharedPreferences wifi = context.getSharedPreferences("wifi", Context.MODE_PRIVATE); SharedPreferences other = context.getSharedPreferences("other", Context.MODE_PRIVATE); SharedPreferences unused = context.getSharedPreferences("unused", Context.MODE_PRIVATE); + SharedPreferences roaming = context.getSharedPreferences("roaming", Context.MODE_PRIVATE); // Get settings boolean whitelist_wifi = prefs.getBoolean("whitelist_wifi", true); boolean whitelist_other = prefs.getBoolean("whitelist_other", true); + boolean whitelist_roaming = prefs.getBoolean("whitelist_roaming", true); boolean manage_system = prefs.getBoolean("manage_system", false); - // Get predifined rules + // Get predefined rules Map predefined = new HashMap<>(); try { XmlResourceParser xml = context.getResources().getXml(R.xml.predefined); @@ -80,18 +83,18 @@ public class Rule implements Comparable { for (PackageInfo info : context.getPackageManager().getInstalledPackages(0)) { boolean system = ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0); if (!system || manage_system || all) { + boolean isPredefined = predefined.containsKey(info.packageName); Rule rule = new Rule(info, context); rule.system = system; rule.wifi_blocked = (system && !manage_system ? false : - wifi.getBoolean(info.packageName, predefined.containsKey(info.packageName) - ? predefined.get(info.packageName) - : whitelist_wifi)); + wifi.getBoolean(info.packageName, isPredefined ? predefined.get(info.packageName) : whitelist_wifi)); rule.other_blocked = (system && !manage_system ? false : - other.getBoolean(info.packageName, predefined.containsKey(info.packageName) - ? predefined.get(info.packageName) - : whitelist_other)); + other.getBoolean(info.packageName, isPredefined ? predefined.get(info.packageName) : whitelist_other)); rule.unused = unused.getBoolean(info.packageName, false); - rule.changed = (rule.wifi_blocked != whitelist_wifi || rule.other_blocked != whitelist_other); + rule.roaming = roaming.getBoolean(info.packageName, isPredefined ? predefined.get(info.packageName) : whitelist_roaming); + rule.changed = (rule.wifi_blocked != whitelist_wifi || + rule.other_blocked != whitelist_other || + (!rule.other_blocked || rule.unused) && rule.roaming != whitelist_roaming); listRules.add(rule); } } diff --git a/app/src/main/java/eu/faircode/netguard/RuleAdapter.java b/app/src/main/java/eu/faircode/netguard/RuleAdapter.java index 6405124e..ec84c825 100644 --- a/app/src/main/java/eu/faircode/netguard/RuleAdapter.java +++ b/app/src/main/java/eu/faircode/netguard/RuleAdapter.java @@ -38,6 +38,7 @@ public class RuleAdapter extends RecyclerView.Adapter im public static class ViewHolder extends RecyclerView.ViewHolder { public View view; + public LinearLayout llApplication; public ImageView ivIcon; public ImageView ivExpander; @@ -45,26 +46,36 @@ public class RuleAdapter extends RecyclerView.Adapter im public TextView tvPackage; public CheckBox cbWifi; public CheckBox cbOther; + public LinearLayout llAttributes; public ImageView ivUsing; + public TextView tvRoaming; + public LinearLayout llConfiguration; public CheckBox cbUsing; + public CheckBox cbRoaming; public Button btnLaunch; public ViewHolder(View itemView) { super(itemView); view = itemView; + llApplication = (LinearLayout) itemView.findViewById(R.id.llApplication); ivIcon = (ImageView) itemView.findViewById(R.id.ivIcon); ivExpander = (ImageView) itemView.findViewById(R.id.ivExpander); tvName = (TextView) itemView.findViewById(R.id.tvName); - tvPackage = (TextView) itemView.findViewById(R.id.tvPackage); + cbWifi = (CheckBox) itemView.findViewById(R.id.cbWifi); cbOther = (CheckBox) itemView.findViewById(R.id.cbOther); + llAttributes = (LinearLayout) itemView.findViewById(R.id.llAttributes); ivUsing = (ImageView) itemView.findViewById(R.id.ivUsing); + tvRoaming = (TextView) itemView.findViewById(R.id.tvRoaming); + llConfiguration = (LinearLayout) itemView.findViewById(R.id.llConfiguration); + tvPackage = (TextView) itemView.findViewById(R.id.tvPackage); cbUsing = (CheckBox) itemView.findViewById(R.id.cbUsing); + cbRoaming = (CheckBox) itemView.findViewById(R.id.cbRoaming); btnLaunch = (Button) itemView.findViewById(R.id.btnLaunch); } } @@ -134,9 +145,7 @@ public class RuleAdapter extends RecyclerView.Adapter im } }; - int color = rule.system ? colorAccent : colorText; - if (rule.disabled) - color = Color.argb(100, Color.red(color), Color.green(color), Color.blue(color)); + holder.llApplication.setOnClickListener(llListener); if (rule.info.applicationInfo.icon == 0) Picasso.with(context).load(android.R.drawable.sym_def_app_icon).into(holder.ivIcon); @@ -146,11 +155,12 @@ public class RuleAdapter extends RecyclerView.Adapter im } holder.ivExpander.setImageResource(rule.attributes ? android.R.drawable.arrow_up_float : android.R.drawable.arrow_down_float); - holder.llApplication.setOnClickListener(llListener); holder.tvName.setText(rule.name); + + int color = rule.system ? colorAccent : colorText; + if (rule.disabled) + color = Color.argb(100, Color.red(color), Color.green(color), Color.blue(color)); holder.tvName.setTextColor(color); - holder.tvPackage.setText(rule.info.packageName); - holder.tvPackage.setTextColor(color); holder.cbWifi.setOnCheckedChangeListener(null); holder.cbWifi.setChecked(rule.wifi_blocked); @@ -162,8 +172,11 @@ public class RuleAdapter extends RecyclerView.Adapter im holder.llAttributes.setOnClickListener(llListener); holder.ivUsing.setVisibility(rule.unused && (rule.wifi_blocked || rule.other_blocked) ? View.VISIBLE : View.INVISIBLE); + holder.tvRoaming.setVisibility(rule.roaming && (!rule.other_blocked || rule.unused) ? View.VISIBLE : View.INVISIBLE); holder.llConfiguration.setVisibility(rule.attributes ? View.VISIBLE : View.GONE); + holder.tvPackage.setText(rule.info.packageName); + holder.cbUsing.setOnCheckedChangeListener(null); holder.cbUsing.setChecked(rule.unused); holder.cbUsing.setEnabled(rule.wifi_blocked || rule.other_blocked); @@ -189,6 +202,32 @@ public class RuleAdapter extends RecyclerView.Adapter im } }); + holder.cbRoaming.setOnCheckedChangeListener(null); + holder.cbRoaming.setChecked(rule.roaming); + holder.cbRoaming.setEnabled(!rule.other_blocked || rule.unused); + + holder.cbRoaming.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + // Update rule + rule.roaming = isChecked; + + // Store rule + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + SharedPreferences roaming = context.getSharedPreferences("roaming", Context.MODE_PRIVATE); + if (rule.roaming == prefs.getBoolean("whitelist_roaming", true)) + roaming.edit().remove(rule.info.packageName).apply(); + else + roaming.edit().putBoolean(rule.info.packageName, rule.roaming).apply(); + + // Update UI + notifyItemChanged(position); + + // Apply updated rule + SinkholeService.reload(null, context); + } + }); + holder.btnLaunch.setEnabled(rule.intent != null); holder.btnLaunch.setOnClickListener(new View.OnClickListener() { @Override diff --git a/app/src/main/java/eu/faircode/netguard/SinkholeService.java b/app/src/main/java/eu/faircode/netguard/SinkholeService.java index 0f003eff..a232e22b 100644 --- a/app/src/main/java/eu/faircode/netguard/SinkholeService.java +++ b/app/src/main/java/eu/faircode/netguard/SinkholeService.java @@ -25,6 +25,7 @@ import java.nio.ByteOrder; public class SinkholeService extends VpnService { private static final String TAG = "NetGuard.Service"; + private boolean last_roaming; private ParcelFileDescriptor vpn = null; private boolean debug = false; private Thread thread = null; @@ -52,6 +53,7 @@ public class SinkholeService extends VpnService { switch (cmd) { case start: if (enabled && vpn == null) { + last_roaming = Util.isRoaming(SinkholeService.this); vpn = startVPN(); startDebug(vpn); removeDisabledNotification(); @@ -93,6 +95,10 @@ public class SinkholeService extends VpnService { boolean wifi = Util.isWifiActive(this); Log.i(TAG, "wifi=" + wifi); + // Check if Wi-Fi + boolean roaming = Util.isRoaming(this); + Log.i(TAG, "roaming=" + roaming); + // Check if interactive boolean interactive = Util.isInteractive(this); Log.i(TAG, "interactive=" + interactive); @@ -106,8 +112,9 @@ public class SinkholeService extends VpnService { builder.addRoute("0:0:0:0:0:0:0:0", 0); // Add list of allowed applications - for (Rule rule : Rule.getRules(true, TAG, this)) - if (!(wifi ? rule.wifi_blocked : rule.other_blocked) || (rule.unused && interactive)) { + for (Rule rule : Rule.getRules(true, TAG, this)) { + boolean blocked = (wifi ? rule.wifi_blocked : rule.other_blocked); + if ((!blocked || (rule.unused && interactive)) && (wifi || !(rule.roaming && roaming))) { Log.i(TAG, "Allowing " + rule.info.packageName); try { builder.addDisallowedApplication(rule.info.packageName); @@ -115,6 +122,7 @@ public class SinkholeService extends VpnService { Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); } } + } // Build configure intent Intent configure = new Intent(this, ActivityMain.class); @@ -228,8 +236,15 @@ public class SinkholeService extends VpnService { public void onReceive(Context context, Intent intent) { Log.i(TAG, "Received " + intent); Util.logExtras(TAG, intent); - if (intent.hasExtra(ConnectivityManager.EXTRA_NETWORK_TYPE) && - intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, ConnectivityManager.TYPE_DUMMY) == ConnectivityManager.TYPE_WIFI) + + if (last_roaming != Util.isRoaming(SinkholeService.this)) { + last_roaming = !last_roaming; + Log.i(TAG, "New state roaming=" + last_roaming); + reload(null, SinkholeService.this); + + } else if (intent.hasExtra(ConnectivityManager.EXTRA_NETWORK_TYPE) && + intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, ConnectivityManager.TYPE_DUMMY) == + ConnectivityManager.TYPE_WIFI) reload(null, SinkholeService.this); } }; diff --git a/app/src/main/res/layout/rule.xml b/app/src/main/res/layout/rule.xml index bbaf44a7..497e34d0 100644 --- a/app/src/main/res/layout/rule.xml +++ b/app/src/main/res/layout/rule.xml @@ -60,16 +60,26 @@ + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:layout_marginStart="12dp" + android:orientation="vertical"> + + @@ -77,7 +87,7 @@ android:id="@+id/llConfiguration" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_marginStart="8dp" + android:layout_marginStart="4dp" android:orientation="vertical" android:paddingStart="?android:attr/listPreferredItemHeightSmall" android:paddingTop="0dp" @@ -100,6 +110,14 @@ android:text="@string/title_using" android:textAppearance="@android:style/TextAppearance.Material.Small" /> + +