mirror of https://github.com/M66B/NetGuard.git
Fixed crash on sort on name, removed dependency on Picasso, require Android 5.1
This commit is contained in:
parent
d485ffa50e
commit
eb7334814c
4
FAQ.md
4
FAQ.md
|
@ -48,7 +48,7 @@ However, NetGuard supports a [SOCKS5 proxy](https://en.wikipedia.org/wiki/SOCKS)
|
|||
<a name="FAQ3"></a>
|
||||
**(3) Can I use NetGuard on any Android version?**
|
||||
|
||||
No, the minimum required Android version is 5.0 (<a href= "https://developer.android.com/about/versions/android-5.0.html">LOLLIPOP</a>)
|
||||
No, the minimum required Android version is 5.1 (<a href= "https://developer.android.com/about/versions/android-5.1.html">LOLLIPOP</a>)
|
||||
|
||||
<a name="FAQ4"></a>
|
||||
**(4) Will NetGuard use extra battery power?**
|
||||
|
@ -160,7 +160,7 @@ and is incorrectly attributed to NetGuard instead to the Google Play™ store ap
|
|||
<a name="FAQ18"></a>
|
||||
**(18) Why can't I find NetGuard in the Google Play™ store app?**
|
||||
|
||||
NetGuard requires at least Android 5.0, so it is not available in the Google Play™ store app on devices running prior Android versions.
|
||||
NetGuard requires at least Android 5.1, so it is not available in the Google Play™ store app on devices running prior Android versions.
|
||||
|
||||
<a name="FAQ19"></a>
|
||||
**(19) Why does application XYZ still have internet access?**
|
||||
|
|
|
@ -19,7 +19,7 @@ Features:
|
|||
* No calling home
|
||||
* No tracking or analytics
|
||||
* Actively developed and supported
|
||||
* Android 5.0 and later supported
|
||||
* Android 5.1 and later supported
|
||||
* IPv4/IPv6 TCP/UDP supported
|
||||
* Tethering supported
|
||||
* Multiple device users supported
|
||||
|
@ -44,7 +44,7 @@ There is no other no-root firewall offering all these features.
|
|||
|
||||
Requirements:
|
||||
|
||||
* Android 5.0 or later
|
||||
* Android 5.1 or later
|
||||
* A [compatible device](#compatibility)
|
||||
|
||||
Downloads:
|
||||
|
@ -314,7 +314,6 @@ Attribution
|
|||
|
||||
NetGuard uses:
|
||||
|
||||
* [Picasso](http://square.github.io/picasso/)
|
||||
* [Android Support Library](https://developer.android.com/tools/support-library/index.html)
|
||||
|
||||
License
|
||||
|
|
|
@ -6,10 +6,10 @@ android {
|
|||
|
||||
defaultConfig {
|
||||
applicationId = "eu.faircode.netguard"
|
||||
versionName = "2.149"
|
||||
versionName = "2.150"
|
||||
minSdkVersion 22
|
||||
targetSdkVersion 27
|
||||
versionCode = 2017110501
|
||||
versionCode = 2017110502
|
||||
archivesBaseName = "NetGuard-v$versionName"
|
||||
|
||||
externalNativeBuild {
|
||||
|
@ -58,9 +58,6 @@ dependencies {
|
|||
// https://firebase.google.com/docs/android/setup
|
||||
implementation 'com.google.firebase:firebase-core:11.4.+'
|
||||
implementation 'com.google.firebase:firebase-ads:11.4.+'
|
||||
|
||||
// https://mvnrepository.com/artifact/com.squareup.picasso/picasso
|
||||
implementation 'com.squareup.picasso:picasso:2.5.+'
|
||||
}
|
||||
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
|
|
|
@ -23,12 +23,11 @@ import android.content.Context;
|
|||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
|
@ -45,13 +44,12 @@ import android.widget.CursorAdapter;
|
|||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class AdapterLog extends CursorAdapter {
|
||||
private static String TAG = "NetGuard.Log";
|
||||
|
@ -80,6 +78,8 @@ public class AdapterLog extends CursorAdapter {
|
|||
private InetAddress vpn4 = null;
|
||||
private InetAddress vpn6 = null;
|
||||
|
||||
private ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
|
||||
|
||||
public AdapterLog(Context context, Cursor cursor, boolean resolve, boolean organization) {
|
||||
super(context, cursor, 0);
|
||||
this.resolve = resolve;
|
||||
|
@ -222,34 +222,44 @@ public class AdapterLog extends CursorAdapter {
|
|||
if (info == null)
|
||||
ivIcon.setImageDrawable(null);
|
||||
else {
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
|
||||
Icon icon;
|
||||
if (info.icon == 0)
|
||||
icon = Icon.createWithResource(context, android.R.drawable.sym_def_app_icon);
|
||||
else
|
||||
icon = Icon.createWithResource(info.packageName, info.icon);
|
||||
try {
|
||||
icon.loadDrawableAsync(context, new Icon.OnDrawableLoadedListener() {
|
||||
@Override
|
||||
public void onDrawableLoaded(Drawable drawable) {
|
||||
if (info.icon <= 0)
|
||||
ivIcon.setImageResource(android.R.drawable.sym_def_app_icon);
|
||||
else {
|
||||
ivIcon.setHasTransientState(true);
|
||||
final ApplicationInfo finalInfo = info;
|
||||
executor.submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Resources res = context.getPackageManager().getResourcesForApplication(finalInfo.packageName);
|
||||
Drawable drawable = res.getDrawable(finalInfo.icon, null);
|
||||
|
||||
final Drawable scaledDrawable;
|
||||
if (drawable instanceof BitmapDrawable) {
|
||||
Bitmap original = ((BitmapDrawable) drawable).getBitmap();
|
||||
Bitmap scaled = Bitmap.createScaledBitmap(original, iconSize, iconSize, false);
|
||||
ivIcon.setImageDrawable(new BitmapDrawable(context.getResources(), scaled));
|
||||
Bitmap scaled = Util.decodeSampledBitmapFromResource(res, finalInfo.icon, iconSize, iconSize);
|
||||
scaledDrawable = new BitmapDrawable(context.getResources(), scaled);
|
||||
} else
|
||||
ivIcon.setImageDrawable(drawable);
|
||||
scaledDrawable = drawable;
|
||||
|
||||
new Handler(context.getMainLooper()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ivIcon.setImageDrawable(scaledDrawable);
|
||||
ivIcon.setHasTransientState(false);
|
||||
}
|
||||
});
|
||||
} catch (Throwable ex) {
|
||||
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
|
||||
new Handler(context.getMainLooper()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ivIcon.setImageDrawable(null);
|
||||
ivIcon.setHasTransientState(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, new Handler(context.getMainLooper()));
|
||||
} catch (RejectedExecutionException ex) {
|
||||
Log.w(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
|
||||
}
|
||||
} else {
|
||||
if (info.icon == 0)
|
||||
Picasso.with(context).load(android.R.drawable.sym_def_app_icon).into(ivIcon);
|
||||
else {
|
||||
Uri uri = Uri.parse("android.resource://" + info.packageName + "/" + info.icon);
|
||||
Picasso.with(context).load(uri).resize(iconSize, iconSize).into(ivIcon);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
|
@ -71,11 +72,11 @@ import android.widget.PopupMenu;
|
|||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
|
||||
public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> implements Filterable {
|
||||
|
@ -96,6 +97,8 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> im
|
|||
private List<Rule> listAll = new ArrayList<>();
|
||||
private List<Rule> listFiltered = new ArrayList<>();
|
||||
|
||||
private ExecutorService executor = Executors.newCachedThreadPool();
|
||||
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
public View view;
|
||||
|
||||
|
@ -154,6 +157,8 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> im
|
|||
public ImageButton btnClearAccess;
|
||||
public CheckBox cbNotify;
|
||||
|
||||
public IconLoader iconLoader = null;
|
||||
|
||||
public ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
view = itemView;
|
||||
|
@ -339,38 +344,11 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> im
|
|||
holder.ivExpander.setImageLevel(rule.expanded ? 1 : 0);
|
||||
|
||||
// Show application icon
|
||||
if (rule.info.applicationInfo == null)
|
||||
holder.ivIcon.setImageDrawable(null);
|
||||
if (rule.info.applicationInfo.icon <= 0)
|
||||
holder.ivIcon.setImageResource(android.R.drawable.sym_def_app_icon);
|
||||
else {
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
|
||||
final Icon icon;
|
||||
if (rule.info.applicationInfo.icon == 0)
|
||||
icon = Icon.createWithResource(context, android.R.drawable.sym_def_app_icon);
|
||||
else
|
||||
icon = Icon.createWithResource(rule.info.packageName, rule.info.applicationInfo.icon);
|
||||
try {
|
||||
icon.loadDrawableAsync(context, new Icon.OnDrawableLoadedListener() {
|
||||
@Override
|
||||
public void onDrawableLoaded(Drawable drawable) {
|
||||
if (drawable instanceof BitmapDrawable) {
|
||||
Bitmap original = ((BitmapDrawable) drawable).getBitmap();
|
||||
Bitmap scaled = Bitmap.createScaledBitmap(original, iconSize, iconSize, false);
|
||||
holder.ivIcon.setImageDrawable(new BitmapDrawable(context.getResources(), scaled));
|
||||
} else
|
||||
holder.ivIcon.setImageDrawable(drawable);
|
||||
}
|
||||
}, new Handler(context.getMainLooper()));
|
||||
} catch (RejectedExecutionException ex) {
|
||||
Log.w(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
|
||||
}
|
||||
} else {
|
||||
if (rule.info.applicationInfo.icon == 0)
|
||||
Picasso.with(context).load(android.R.drawable.sym_def_app_icon).into(holder.ivIcon);
|
||||
else {
|
||||
Uri uri = Uri.parse("android.resource://" + rule.info.packageName + "/" + rule.info.applicationInfo.icon);
|
||||
Picasso.with(context).load(uri).resize(iconSize, iconSize).into(holder.ivIcon);
|
||||
}
|
||||
}
|
||||
holder.iconLoader = new IconLoader(holder, rule);
|
||||
executor.submit(holder.iconLoader);
|
||||
}
|
||||
|
||||
// Show application label
|
||||
|
@ -857,6 +835,9 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> im
|
|||
public void onViewRecycled(ViewHolder holder) {
|
||||
super.onViewRecycled(holder);
|
||||
|
||||
if (holder.iconLoader != null)
|
||||
holder.iconLoader.cancel();
|
||||
|
||||
CursorAdapter adapter = (CursorAdapter) holder.lvAccess.getAdapter();
|
||||
if (adapter != null) {
|
||||
Log.i(TAG, "Closing access cursor");
|
||||
|
@ -1017,4 +998,57 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> im
|
|||
public int getItemCount() {
|
||||
return listFiltered.size();
|
||||
}
|
||||
|
||||
private class IconLoader implements Runnable {
|
||||
private ViewHolder holder;
|
||||
private Rule rule;
|
||||
private boolean cancelled = false;
|
||||
|
||||
public IconLoader(ViewHolder holder, Rule rule) {
|
||||
this.holder = holder;
|
||||
this.rule = rule;
|
||||
holder.ivIcon.setHasTransientState(true);
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
if (!cancelled)
|
||||
Log.i(TAG, "Cancelling icon loader");
|
||||
cancelled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (cancelled)
|
||||
throw new InterruptedException();
|
||||
|
||||
Resources res = context.getPackageManager().getResourcesForApplication(rule.info.packageName);
|
||||
Drawable drawable = res.getDrawable(rule.info.applicationInfo.icon, null);
|
||||
|
||||
final Drawable scaledDrawable;
|
||||
if (drawable instanceof BitmapDrawable) {
|
||||
Bitmap scaled = Util.decodeSampledBitmapFromResource(res, rule.info.applicationInfo.icon, iconSize, iconSize);
|
||||
scaledDrawable = new BitmapDrawable(context.getResources(), scaled);
|
||||
} else
|
||||
scaledDrawable = drawable;
|
||||
|
||||
new Handler(context.getMainLooper()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
holder.ivIcon.setImageDrawable(scaledDrawable);
|
||||
holder.ivIcon.setHasTransientState(false);
|
||||
}
|
||||
});
|
||||
} catch (Throwable ex) {
|
||||
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
|
||||
new Handler(context.getMainLooper()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
holder.ivIcon.setImageDrawable(null);
|
||||
holder.ivIcon.setHasTransientState(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -419,36 +419,38 @@ public class Rule {
|
|||
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
|
||||
}
|
||||
|
||||
final Collator collator = Collator.getInstance(Locale.getDefault());
|
||||
collator.setStrength(Collator.SECONDARY); // Case insensitive, process accents etc
|
||||
|
||||
// Sort rule list
|
||||
String sort = prefs.getString("sort", "name");
|
||||
if ("uid".equals(sort))
|
||||
Collections.sort(listRules, new Comparator<Rule>() {
|
||||
@Override
|
||||
public int compare(Rule rule, Rule other) {
|
||||
if (rule.info.applicationInfo.uid < other.info.applicationInfo.uid)
|
||||
return -1;
|
||||
else if (rule.info.applicationInfo.uid > other.info.applicationInfo.uid)
|
||||
return 1;
|
||||
else {
|
||||
int i = collator.compare(rule.name, other.name);
|
||||
return (i == 0 ? rule.info.packageName.compareTo(other.info.packageName) : i);
|
||||
if (!service) {
|
||||
final Collator collator = Collator.getInstance(Locale.getDefault());
|
||||
collator.setStrength(Collator.SECONDARY); // Case insensitive, process accents etc
|
||||
|
||||
String sort = prefs.getString("sort", "name");
|
||||
if ("uid".equals(sort))
|
||||
Collections.sort(listRules, new Comparator<Rule>() {
|
||||
@Override
|
||||
public int compare(Rule rule, Rule other) {
|
||||
if (rule.info.applicationInfo.uid < other.info.applicationInfo.uid)
|
||||
return -1;
|
||||
else if (rule.info.applicationInfo.uid > other.info.applicationInfo.uid)
|
||||
return 1;
|
||||
else {
|
||||
int i = collator.compare(rule.name, other.name);
|
||||
return (i == 0 ? rule.info.packageName.compareTo(other.info.packageName) : i);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
else
|
||||
Collections.sort(listRules, new Comparator<Rule>() {
|
||||
@Override
|
||||
public int compare(Rule rule, Rule other) {
|
||||
if (all || rule.changed == other.changed) {
|
||||
int i = collator.compare(rule.name, other.name);
|
||||
return (i == 0 ? rule.info.packageName.compareTo(other.info.packageName) : i);
|
||||
});
|
||||
else
|
||||
Collections.sort(listRules, new Comparator<Rule>() {
|
||||
@Override
|
||||
public int compare(Rule rule, Rule other) {
|
||||
if (all || rule.changed == other.changed) {
|
||||
int i = collator.compare(rule.name, other.name);
|
||||
return (i == 0 ? rule.info.packageName.compareTo(other.info.packageName) : i);
|
||||
}
|
||||
return (rule.changed ? -1 : 1);
|
||||
}
|
||||
return (rule.changed ? -1 : 1);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return listRules;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,10 @@ import android.content.SharedPreferences;
|
|||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.Network;
|
||||
|
@ -476,6 +479,35 @@ public class Util {
|
|||
return Math.round(dips * context.getResources().getDisplayMetrics().density + 0.5f);
|
||||
}
|
||||
|
||||
private static int calculateInSampleSize(
|
||||
BitmapFactory.Options options, int reqWidth, int reqHeight) {
|
||||
int height = options.outHeight;
|
||||
int width = options.outWidth;
|
||||
int inSampleSize = 1;
|
||||
|
||||
if (height > reqHeight || width > reqWidth) {
|
||||
int halfHeight = height / 2;
|
||||
int halfWidth = width / 2;
|
||||
|
||||
while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth)
|
||||
inSampleSize *= 2;
|
||||
}
|
||||
|
||||
return inSampleSize;
|
||||
}
|
||||
|
||||
public static Bitmap decodeSampledBitmapFromResource(
|
||||
Resources resources, int resourceId, int reqWidth, int reqHeight) {
|
||||
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeResource(resources, resourceId, options);
|
||||
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
|
||||
options.inJustDecodeBounds = false;
|
||||
|
||||
return BitmapFactory.decodeResource(resources, resourceId, options);
|
||||
}
|
||||
|
||||
public static String getProtocolName(int protocol, int version, boolean brief) {
|
||||
// https://en.wikipedia.org/wiki/List_of_IP_protocol_numbers
|
||||
String p = null;
|
||||
|
|
Loading…
Reference in New Issue