Removed usage data sharing
27
FAQ.md
|
@ -363,33 +363,6 @@ On most devices, NetGuard will keep running in the background with its foregroun
|
|||
On some devices (in particular some Samsung models), where there are lots of applications competing for memory, Android may still stop NetGuard as a last resort.
|
||||
Unfortunately this cannot be fixed from NetGuard, and can be considered a shortcoming of the device and/or as a bug in Android.
|
||||
|
||||
<a name="FAQ39"></a>
|
||||
**(39) Which usage data will be shared?**
|
||||
|
||||
On Android version 5.0 or newer, NetGuard will share anonymized usage data to help improve how NetGuard protects you,
|
||||
by understanding how end users work with NetGuard.
|
||||
|
||||
The following data will be shared when changing application settings:
|
||||
|
||||
* The [SHA256](https://en.wikipedia.org/wiki/SHA-2) hashed [Android ID](https://developer.android.com/reference/android/provider/Settings.Secure.html#ANDROID_ID) (needed to prevent multiple records of the same setting)
|
||||
* The [device name](https://developer.android.com/reference/android/os/Build.html#DEVICE) *
|
||||
* The [Android version](https://developer.android.com/reference/android/os/Build.VERSION_CODES.html) *
|
||||
* The [country](https://developer.android.com/reference/java/util/Locale.html#getCountry()) configured when initializing Android *
|
||||
* The application name, version and [installer](https://developer.android.com/reference/android/content/pm/PackageManager.html#getInstallerPackageName(java.lang.String)) and whether an application is a user installed or a system bundled application
|
||||
* Whether Wi-Fi and mobile connections will be blocked or allowed and which conditions apply (when screen on, roaming, notify on access, addresses, etc)
|
||||
|
||||
This data cannot be used to identify you, so your privacy is guaranteed.
|
||||
Data marked with an asterix (*) is being shared by Google in the [Google Play developer console](https://developer.android.com/distribute/googleplay/developer-console.html) as well.
|
||||
|
||||
Usage data will be queued for sending over an unmetered connection (mostly Wi-Fi) when settings are being changed.
|
||||
Usage data will be anonynized before sending.
|
||||
Sending usage data can optionally be disabled per application.
|
||||
|
||||
You can see the aggregated data [here](https://crowd.netguard.me).
|
||||
|
||||
<br />
|
||||
|
||||
**If you didn't find the answer to your question, you can ask your questions [in this forum](http://forum.xda-developers.com/showthread.php?t=3233012) or contact me directly [by e-mail](mailto:marcel+netguard@faircode.eu)**.
|
||||
|
||||
If you want to request a new feature or want to report a bug, please [create an issue on GitHub](https://github.com/M66B/NetGuard/issues/new).
|
||||
|
||||
|
|
|
@ -16,7 +16,8 @@ Features:
|
|||
* Simple to use
|
||||
* No root required
|
||||
* 100% open source
|
||||
* No tracking and user profiling
|
||||
* No calling home
|
||||
* No tracking or analytics
|
||||
* No advertisements
|
||||
* Actively developed and supported
|
||||
* Android 4.0 and later supported
|
||||
|
@ -183,9 +184,6 @@ Frequently Asked Questions (FAQ)
|
|||
<a name="FAQ38"></a>
|
||||
[**(38) Why did NetGuard stop running?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#FAQ38)
|
||||
|
||||
<a name="FAQ39"></a>
|
||||
[**(39) Which usage data will be shared?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#FAQ39)
|
||||
|
||||
Permissions
|
||||
-----------
|
||||
|
||||
|
|
16
app/app.iml
|
@ -104,14 +104,6 @@
|
|||
<sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/renderscript" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/jni" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/renderscript" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
|
||||
|
@ -120,6 +112,14 @@
|
|||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/renderscript" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/jni" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/renderscript" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/binaries" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
|
||||
|
|
|
@ -117,11 +117,6 @@
|
|||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name=".ServiceJob"
|
||||
android:exported="true"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||
|
||||
<service
|
||||
android:name=".ServiceTile"
|
||||
android:icon="@drawable/ic_security_white_24dp"
|
||||
|
|
|
@ -331,16 +331,13 @@ public class ActivityMain extends AppCompatActivity implements SharedPreferences
|
|||
registerReceiver(packageChangedReceiver, intentFilter);
|
||||
|
||||
// First use
|
||||
boolean submitting = prefs.getBoolean("submitting", false);
|
||||
if (!initialized || !submitting) {
|
||||
if (!initialized) {
|
||||
// Create view
|
||||
LayoutInflater inflater = LayoutInflater.from(this);
|
||||
View view = inflater.inflate(R.layout.first, null, false);
|
||||
|
||||
TextView tvFirst = (TextView) view.findViewById(R.id.tvFirst);
|
||||
TextView tvSubmit = (TextView) view.findViewById(R.id.tvSubmit);
|
||||
tvFirst.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
tvSubmit.setVisibility(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? View.VISIBLE : View.GONE);
|
||||
|
||||
// Show dialog
|
||||
dialogFirst = new AlertDialog.Builder(this)
|
||||
|
@ -349,10 +346,8 @@ public class ActivityMain extends AppCompatActivity implements SharedPreferences
|
|||
.setPositiveButton(R.string.app_agree, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (running) {
|
||||
if (running)
|
||||
prefs.edit().putBoolean("initialized", true).apply();
|
||||
prefs.edit().putBoolean("submitting", true).apply();
|
||||
}
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.app_disagree, new DialogInterface.OnClickListener() {
|
||||
|
|
|
@ -21,10 +21,8 @@ package eu.faircode.netguard;
|
|||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.app.NavUtils;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.text.Editable;
|
||||
|
@ -35,8 +33,6 @@ import android.view.MenuItem;
|
|||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
@ -273,7 +269,6 @@ public class ActivityPro extends AppCompatActivity {
|
|||
TextView tvSpeed = (TextView) findViewById(R.id.tvSpeed);
|
||||
TextView tvTheme = (TextView) findViewById(R.id.tvTheme);
|
||||
TextView tvAll = (TextView) findViewById(R.id.tvAll);
|
||||
CheckBox cbSubmit = (CheckBox) findViewById(R.id.cbSubmit);
|
||||
LinearLayout llChallenge = (LinearLayout) findViewById(R.id.llChallenge);
|
||||
|
||||
btnLog.setVisibility(IAB.isPurchased(SKU_LOG, this) ? View.GONE : View.VISIBLE);
|
||||
|
|
|
@ -1029,10 +1029,6 @@ public class ActivitySettings extends AppCompatActivity implements SharedPrefere
|
|||
xmlExport(getSharedPreferences("notify", Context.MODE_PRIVATE), serializer);
|
||||
serializer.endTag(null, "notify");
|
||||
|
||||
serializer.startTag(null, "submit");
|
||||
xmlExport(getSharedPreferences("submit", Context.MODE_PRIVATE), serializer);
|
||||
serializer.endTag(null, "submit");
|
||||
|
||||
serializer.startTag(null, "filter");
|
||||
filterExport(serializer);
|
||||
serializer.endTag(null, "filter");
|
||||
|
@ -1170,7 +1166,6 @@ public class ActivitySettings extends AppCompatActivity implements SharedPrefere
|
|||
xmlImport(handler.roaming, getSharedPreferences("roaming", Context.MODE_PRIVATE));
|
||||
xmlImport(handler.apply, getSharedPreferences("apply", Context.MODE_PRIVATE));
|
||||
xmlImport(handler.notify, getSharedPreferences("notify", Context.MODE_PRIVATE));
|
||||
xmlImport(handler.submit, getSharedPreferences("submit", Context.MODE_PRIVATE));
|
||||
|
||||
// Upgrade imported settings
|
||||
Receiver.upgrade(true, this);
|
||||
|
@ -1218,7 +1213,6 @@ public class ActivitySettings extends AppCompatActivity implements SharedPrefere
|
|||
public Map<String, Object> roaming = new HashMap<>();
|
||||
public Map<String, Object> apply = new HashMap<>();
|
||||
public Map<String, Object> notify = new HashMap<>();
|
||||
public Map<String, Object> submit = new HashMap<>();
|
||||
private Map<String, Object> current = null;
|
||||
|
||||
public XmlImportHandler(Context context) {
|
||||
|
@ -1257,9 +1251,6 @@ public class ActivitySettings extends AppCompatActivity implements SharedPrefere
|
|||
else if (qName.equals("notify"))
|
||||
current = notify;
|
||||
|
||||
else if (qName.equals("submit"))
|
||||
current = submit;
|
||||
|
||||
else if (qName.equals("filter")) {
|
||||
current = null;
|
||||
Log.i(TAG, "Clearing filters");
|
||||
|
|
|
@ -62,21 +62,9 @@ import android.widget.TextView;
|
|||
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URL;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
|
||||
public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> implements Filterable {
|
||||
private static final String TAG = "NetGuard.Adapter";
|
||||
|
@ -94,10 +82,6 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> im
|
|||
private List<Rule> listAll = new ArrayList<>();
|
||||
private List<Rule> listFiltered = new ArrayList<>();
|
||||
|
||||
private static final String cUrl = "https://crowd.netguard.me/";
|
||||
private static final int cTimeOutMs = 15000;
|
||||
private static final double cConfidence = 0.35;
|
||||
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
public View view;
|
||||
|
||||
|
@ -127,7 +111,6 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> im
|
|||
public CheckBox cbApply;
|
||||
|
||||
public Button btnRelated;
|
||||
public ImageButton ibFetch;
|
||||
public ImageButton ibSettings;
|
||||
public ImageButton ibLaunch;
|
||||
|
||||
|
@ -146,7 +129,6 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> im
|
|||
public ImageButton btnClearAccess;
|
||||
|
||||
public CheckBox cbNotify;
|
||||
public CheckBox cbSubmit;
|
||||
|
||||
public ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
|
@ -178,7 +160,6 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> im
|
|||
cbApply = (CheckBox) itemView.findViewById(R.id.cbApply);
|
||||
|
||||
btnRelated = (Button) itemView.findViewById(R.id.btnRelated);
|
||||
ibFetch = (ImageButton) itemView.findViewById(R.id.ibFetch);
|
||||
ibSettings = (ImageButton) itemView.findViewById(R.id.ibSettings);
|
||||
ibLaunch = (ImageButton) itemView.findViewById(R.id.ibLaunch);
|
||||
|
||||
|
@ -197,7 +178,6 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> im
|
|||
btnClearAccess = (ImageButton) itemView.findViewById(R.id.btnClearAccess);
|
||||
|
||||
cbNotify = (CheckBox) itemView.findViewById(R.id.cbNotify);
|
||||
cbSubmit = (CheckBox) itemView.findViewById(R.id.cbSubmit);
|
||||
|
||||
final View wifiParent = (View) cbWifi.getParent();
|
||||
wifiParent.post(new Runnable() {
|
||||
|
@ -446,135 +426,6 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> im
|
|||
}
|
||||
});
|
||||
|
||||
// Fetch settings
|
||||
holder.ibFetch.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
new AsyncTask<Object, Object, Object>() {
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
holder.ibFetch.setEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doInBackground(Object... args) {
|
||||
HttpsURLConnection urlConnection = null;
|
||||
try {
|
||||
JSONObject json = new JSONObject();
|
||||
|
||||
json.put("type", "fetch");
|
||||
json.put("country", Locale.getDefault().getCountry());
|
||||
json.put("netguard", Util.getSelfVersionCode(context));
|
||||
json.put("fingerprint", Util.getFingerprint(context));
|
||||
|
||||
JSONObject pkg = new JSONObject();
|
||||
pkg.put("name", rule.info.packageName);
|
||||
pkg.put("version_code", rule.info.versionCode);
|
||||
pkg.put("version_name", rule.info.versionName);
|
||||
|
||||
JSONArray pkgs = new JSONArray();
|
||||
pkgs.put(pkg);
|
||||
json.put("package", pkgs);
|
||||
|
||||
urlConnection = (HttpsURLConnection) new URL(cUrl).openConnection();
|
||||
urlConnection.setConnectTimeout(cTimeOutMs);
|
||||
urlConnection.setReadTimeout(cTimeOutMs);
|
||||
urlConnection.setRequestProperty("Accept", "application/json");
|
||||
urlConnection.setRequestProperty("Content-type", "application/json");
|
||||
urlConnection.setRequestMethod("POST");
|
||||
urlConnection.setDoInput(true);
|
||||
urlConnection.setDoOutput(true);
|
||||
|
||||
Log.i(TAG, "Request=" + json.toString());
|
||||
OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
|
||||
out.write(json.toString().getBytes()); // UTF-8
|
||||
out.flush();
|
||||
|
||||
int code = urlConnection.getResponseCode();
|
||||
if (code != HttpsURLConnection.HTTP_OK)
|
||||
throw new IOException("HTTP " + code);
|
||||
|
||||
InputStreamReader isr = new InputStreamReader(urlConnection.getInputStream());
|
||||
String response = Util.readString(isr).toString();
|
||||
Log.i(TAG, "Response=" + response);
|
||||
JSONObject jfetched = new JSONObject(response);
|
||||
JSONArray jpkgs = jfetched.getJSONArray("package");
|
||||
for (int i = 0; i < jpkgs.length(); i++) {
|
||||
JSONObject jpkg = jpkgs.getJSONObject(i);
|
||||
String name = jpkg.getString("name");
|
||||
int wifi = jpkg.getInt("wifi");
|
||||
int wifi_screen = jpkg.getInt("wifi_screen");
|
||||
int other = jpkg.getInt("other");
|
||||
int other_screen = jpkg.getInt("other_screen");
|
||||
int roaming = jpkg.getInt("roaming");
|
||||
int devices = jpkg.getInt("devices");
|
||||
|
||||
double conf_wifi;
|
||||
boolean block_wifi;
|
||||
if (rule.wifi_default) {
|
||||
conf_wifi = confidence(devices - wifi, devices);
|
||||
block_wifi = !(devices - wifi > wifi && conf_wifi > cConfidence);
|
||||
} else {
|
||||
conf_wifi = confidence(wifi, devices);
|
||||
block_wifi = (wifi > devices - wifi && conf_wifi > cConfidence);
|
||||
}
|
||||
|
||||
boolean allow_wifi_screen = rule.screen_wifi_default;
|
||||
if (block_wifi)
|
||||
allow_wifi_screen = (wifi_screen > wifi / 2);
|
||||
|
||||
double conf_other;
|
||||
boolean block_other;
|
||||
if (rule.other_default) {
|
||||
conf_other = confidence(devices - other, devices);
|
||||
block_other = !(devices - other > other && conf_other > cConfidence);
|
||||
} else {
|
||||
conf_other = confidence(other, devices);
|
||||
block_other = (other > devices - other && conf_other > cConfidence);
|
||||
}
|
||||
|
||||
boolean allow_other_screen = rule.screen_other_default;
|
||||
if (block_other)
|
||||
allow_other_screen = (other_screen > other / 2);
|
||||
|
||||
boolean block_roaming = rule.roaming_default;
|
||||
if (!block_other || allow_other_screen)
|
||||
block_roaming = (roaming > (devices - other) / 2);
|
||||
|
||||
Log.i(TAG, "pkg=" + name +
|
||||
" wifi=" + wifi + "/" + wifi_screen + "=" + block_wifi + "/" + allow_wifi_screen + " " + Math.round(100 * conf_wifi) + "%" +
|
||||
" other=" + other + "/" + other_screen + "/" + roaming + "=" + block_other + "/" + allow_other_screen + "/" + block_roaming + " " + Math.round(100 * conf_other) + "%" +
|
||||
" devices=" + devices);
|
||||
|
||||
rule.wifi_blocked = block_wifi;
|
||||
rule.screen_wifi = allow_wifi_screen;
|
||||
rule.other_blocked = block_other;
|
||||
rule.screen_other = allow_other_screen;
|
||||
rule.roaming = block_roaming;
|
||||
}
|
||||
|
||||
} catch (Throwable ex) {
|
||||
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
|
||||
return ex;
|
||||
|
||||
} finally {
|
||||
if (urlConnection != null)
|
||||
urlConnection.disconnect();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Object result) {
|
||||
holder.ibFetch.setEnabled(true);
|
||||
updateRule(rule, true, listAll);
|
||||
}
|
||||
|
||||
}.execute(rule);
|
||||
}
|
||||
});
|
||||
|
||||
// Launch application settings
|
||||
final Intent settings = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||||
settings.setData(Uri.parse("package:" + rule.info.packageName));
|
||||
|
@ -744,8 +595,6 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> im
|
|||
if (IAB.isPurchased(ActivityPro.SKU_FILTER, context)) {
|
||||
DatabaseHelper.getInstance(context).setAccess(id, 0);
|
||||
ServiceSinkhole.reload("allow host", context);
|
||||
if (rule.submit)
|
||||
ServiceJob.submit(rule, version, protocol, daddr, dport, 0, context);
|
||||
} else
|
||||
context.startActivity(new Intent(context, ActivityPro.class));
|
||||
return true;
|
||||
|
@ -754,8 +603,6 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> im
|
|||
if (IAB.isPurchased(ActivityPro.SKU_FILTER, context)) {
|
||||
DatabaseHelper.getInstance(context).setAccess(id, 1);
|
||||
ServiceSinkhole.reload("block host", context);
|
||||
if (rule.submit)
|
||||
ServiceJob.submit(rule, version, protocol, daddr, dport, 1, context);
|
||||
} else
|
||||
context.startActivity(new Intent(context, ActivityPro.class));
|
||||
return true;
|
||||
|
@ -763,8 +610,6 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> im
|
|||
case R.id.menu_reset:
|
||||
DatabaseHelper.getInstance(context).setAccess(id, -1);
|
||||
ServiceSinkhole.reload("reset host", context);
|
||||
if (rule.submit)
|
||||
ServiceJob.submit(rule, version, protocol, daddr, dport, -1, context);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -812,18 +657,6 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> im
|
|||
updateRule(rule, true, listAll);
|
||||
}
|
||||
});
|
||||
|
||||
// Usage data sharing
|
||||
holder.cbSubmit.setVisibility(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? View.VISIBLE : View.GONE);
|
||||
holder.cbSubmit.setOnCheckedChangeListener(null);
|
||||
holder.cbSubmit.setChecked(rule.submit);
|
||||
holder.cbSubmit.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
|
||||
rule.submit = isChecked;
|
||||
updateRule(rule, true, listAll);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateRule(Rule rule, boolean root, List<Rule> listAll) {
|
||||
|
@ -834,8 +667,6 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> im
|
|||
SharedPreferences screen_other = context.getSharedPreferences("screen_other", Context.MODE_PRIVATE);
|
||||
SharedPreferences roaming = context.getSharedPreferences("roaming", Context.MODE_PRIVATE);
|
||||
SharedPreferences notify = context.getSharedPreferences("notify", Context.MODE_PRIVATE);
|
||||
SharedPreferences submit = context.getSharedPreferences("submit", Context.MODE_PRIVATE);
|
||||
SharedPreferences history = context.getSharedPreferences("history", Context.MODE_PRIVATE);
|
||||
|
||||
if (rule.wifi_blocked == rule.wifi_default)
|
||||
wifi.edit().remove(rule.info.packageName).apply();
|
||||
|
@ -872,14 +703,6 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> im
|
|||
else
|
||||
notify.edit().putBoolean(rule.info.packageName, rule.notify).apply();
|
||||
|
||||
if (rule.submit)
|
||||
submit.edit().remove(rule.info.packageName).apply();
|
||||
else
|
||||
submit.edit().putBoolean(rule.info.packageName, rule.submit).apply();
|
||||
|
||||
rule.last_modified = new Date().getTime();
|
||||
history.edit().putLong(rule.info.packageName + ":modified", rule.last_modified).apply();
|
||||
|
||||
rule.updateChanged(context);
|
||||
Log.i(TAG, "Updated " + rule);
|
||||
|
||||
|
@ -910,9 +733,6 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> im
|
|||
NotificationManagerCompat.from(context).cancel(rule.info.applicationInfo.uid);
|
||||
ServiceSinkhole.reload("rule changed", context);
|
||||
}
|
||||
|
||||
if (rule.submit)
|
||||
ServiceJob.submit(rule, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -959,18 +779,6 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> im
|
|||
};
|
||||
}
|
||||
|
||||
private double confidence(int count, int total) {
|
||||
// Agresti-Coull Interval
|
||||
// http://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval#Agresti-Coull_Interval
|
||||
int n = total;
|
||||
double p = count / (float) n;
|
||||
double z = 1.96; // 95%
|
||||
double n1 = n + z * z;
|
||||
double p1 = (1 / n1) * (count + 0.5 * z * z);
|
||||
double ci = z * Math.sqrt((1 / n1) * p1 * (1 - p1));
|
||||
return 1 - ci;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdapterRule.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
return new ViewHolder(LayoutInflater.from(context).inflate(R.layout.rule, parent, false));
|
||||
|
|
|
@ -239,11 +239,6 @@ public class Receiver extends BroadcastReceiver {
|
|||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
|
||||
editor.putBoolean("filter", true); // Mandatory
|
||||
|
||||
if (Util.isPlayStoreInstall(context)) {
|
||||
SharedPreferences apply = context.getSharedPreferences("apply", Context.MODE_PRIVATE);
|
||||
apply.edit().putBoolean(context.getPackageName(), false).apply();
|
||||
}
|
||||
|
||||
editor.putInt("version", newVersion);
|
||||
editor.apply();
|
||||
}
|
||||
|
|
|
@ -71,7 +71,6 @@ public class Rule {
|
|||
|
||||
public boolean apply = true;
|
||||
public boolean notify = true;
|
||||
public boolean submit = true;
|
||||
|
||||
public boolean relateduids = false;
|
||||
public String[] related = null;
|
||||
|
@ -81,8 +80,6 @@ public class Rule {
|
|||
public float totalbytes;
|
||||
|
||||
public boolean changed;
|
||||
public long last_modified;
|
||||
public long last_submitted;
|
||||
|
||||
public boolean expanded = false;
|
||||
|
||||
|
@ -225,8 +222,6 @@ public class Rule {
|
|||
SharedPreferences roaming = context.getSharedPreferences("roaming", Context.MODE_PRIVATE);
|
||||
SharedPreferences apply = context.getSharedPreferences("apply", Context.MODE_PRIVATE);
|
||||
SharedPreferences notify = context.getSharedPreferences("notify", Context.MODE_PRIVATE);
|
||||
SharedPreferences submit = context.getSharedPreferences("submit", Context.MODE_PRIVATE);
|
||||
SharedPreferences history = context.getSharedPreferences("history", Context.MODE_PRIVATE);
|
||||
|
||||
// Get settings
|
||||
boolean default_wifi = prefs.getBoolean("whitelist_wifi", true);
|
||||
|
@ -324,10 +319,7 @@ public class Rule {
|
|||
if (pre_system.containsKey(info.packageName))
|
||||
rule.system = pre_system.get(info.packageName);
|
||||
if (info.applicationInfo.uid == Process.myUid())
|
||||
if (Util.isPlayStoreInstall(context) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||
continue;
|
||||
else
|
||||
rule.system = true;
|
||||
rule.system = true;
|
||||
|
||||
if (all ||
|
||||
((rule.system ? show_system : show_user) &&
|
||||
|
@ -348,13 +340,6 @@ public class Rule {
|
|||
|
||||
rule.apply = apply.getBoolean(info.packageName, true);
|
||||
rule.notify = notify.getBoolean(info.packageName, true);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||
rule.submit = submit.getBoolean(info.packageName, true);
|
||||
else
|
||||
rule.submit = false;
|
||||
|
||||
rule.last_modified = history.getLong(info.packageName + ":modified", 0);
|
||||
rule.last_submitted = history.getLong(info.packageName + ":submitted", 0);
|
||||
|
||||
// Related packages
|
||||
List<String> listPkg = new ArrayList<>();
|
||||
|
|
|
@ -1,253 +0,0 @@
|
|||
package eu.faircode.netguard;
|
||||
|
||||
/*
|
||||
This file is part of NetGuard.
|
||||
|
||||
NetGuard is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
NetGuard is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with NetGuard. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2015-2016 by Marcel Bokhorst (M66B)
|
||||
*/
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.job.JobInfo;
|
||||
import android.app.job.JobParameters;
|
||||
import android.app.job.JobScheduler;
|
||||
import android.app.job.JobService;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.PersistableBundle;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URL;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public class ServiceJob extends JobService {
|
||||
private static int id = 0;
|
||||
private static final String TAG = "NetGuard.Job";
|
||||
|
||||
private static final String cUrl = "https://crowd.netguard.me/";
|
||||
private static final int cTimeOutMs = 15000;
|
||||
|
||||
@Override
|
||||
public boolean onStartJob(JobParameters params) {
|
||||
Log.i(TAG, "Start job=" + params.getJobId());
|
||||
|
||||
new AsyncTask<JobParameters, Object, Object>() {
|
||||
|
||||
@Override
|
||||
protected JobParameters doInBackground(JobParameters... params) {
|
||||
Log.i(TAG, "Executing job=" + params[0].getJobId());
|
||||
|
||||
HttpsURLConnection urlConnection = null;
|
||||
try {
|
||||
String android_id = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
|
||||
JSONObject json = new JSONObject();
|
||||
|
||||
json.put("device", Util.sha256(android_id, ""));
|
||||
json.put("product", Build.DEVICE);
|
||||
json.put("sdk", Build.VERSION.SDK_INT);
|
||||
json.put("country", Locale.getDefault().getCountry());
|
||||
|
||||
json.put("netguard", Util.getSelfVersionCode(ServiceJob.this));
|
||||
try {
|
||||
json.put("store", getPackageManager().getInstallerPackageName(getPackageName()));
|
||||
} catch (Throwable ex) {
|
||||
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
|
||||
json.put("store", null);
|
||||
}
|
||||
|
||||
for (String name : params[0].getExtras().keySet())
|
||||
json.put(name, params[0].getExtras().get(name));
|
||||
|
||||
urlConnection = (HttpsURLConnection) new URL(cUrl).openConnection();
|
||||
urlConnection.setConnectTimeout(cTimeOutMs);
|
||||
urlConnection.setReadTimeout(cTimeOutMs);
|
||||
urlConnection.setRequestProperty("Accept", "application/json");
|
||||
urlConnection.setRequestProperty("Content-type", "application/json");
|
||||
urlConnection.setRequestMethod("POST");
|
||||
urlConnection.setDoInput(true);
|
||||
urlConnection.setDoOutput(true);
|
||||
|
||||
OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
|
||||
out.write(json.toString().getBytes()); // UTF-8
|
||||
out.flush();
|
||||
|
||||
int code = urlConnection.getResponseCode();
|
||||
if (code != HttpsURLConnection.HTTP_OK)
|
||||
throw new IOException("HTTP " + code);
|
||||
|
||||
InputStreamReader isr = new InputStreamReader(urlConnection.getInputStream());
|
||||
Log.i(TAG, "Response=" + Util.readString(isr).toString());
|
||||
|
||||
jobFinished(params[0], false);
|
||||
|
||||
if ("rule".equals(params[0].getExtras().getString("type"))) {
|
||||
SharedPreferences history = getSharedPreferences("history", Context.MODE_PRIVATE);
|
||||
history.edit().putLong(params[0].getExtras().getString("package") + ":submitted", new Date().getTime()).apply();
|
||||
}
|
||||
|
||||
} catch (Throwable ex) {
|
||||
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
|
||||
jobFinished(params[0], true);
|
||||
|
||||
} finally {
|
||||
if (urlConnection != null)
|
||||
urlConnection.disconnect();
|
||||
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}.execute(params);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onStopJob(JobParameters params) {
|
||||
Log.i(TAG, "Stop job=" + params.getJobId());
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void submit(Rule rule, Context context) {
|
||||
PersistableBundle bundle = new PersistableBundle();
|
||||
bundle.putString("type", "rule");
|
||||
|
||||
bundle.putInt("wifi_default", rule.wifi_default ? 1 : 0);
|
||||
bundle.putInt("other_default", rule.other_default ? 1 : 0);
|
||||
bundle.putInt("screen_wifi_default", rule.screen_wifi_default ? 1 : 0);
|
||||
bundle.putInt("screen_other_default", rule.screen_other_default ? 1 : 0);
|
||||
bundle.putInt("roaming_default", rule.roaming_default ? 1 : 0);
|
||||
|
||||
bundle.putInt("wifi_blocked", rule.wifi_blocked ? 1 : 0);
|
||||
bundle.putInt("other_blocked", rule.other_blocked ? 1 : 0);
|
||||
bundle.putInt("screen_wifi", rule.screen_wifi ? 1 : 0);
|
||||
bundle.putInt("screen_other", rule.screen_other ? 1 : 0);
|
||||
bundle.putInt("roaming", rule.roaming ? 1 : 0);
|
||||
|
||||
bundle.putInt("apply", rule.apply ? 1 : 0);
|
||||
bundle.putInt("notify", rule.notify ? 1 : 0);
|
||||
|
||||
submit(rule, bundle, context);
|
||||
}
|
||||
|
||||
public static void submit(Rule rule, int version, int protocol, String daddr, int dport, int blocked, Context context) {
|
||||
PersistableBundle bundle = new PersistableBundle();
|
||||
bundle.putString("type", "host");
|
||||
|
||||
bundle.putInt("version", version);
|
||||
bundle.putInt("protocol", protocol);
|
||||
bundle.putString("daddr", daddr);
|
||||
bundle.putInt("dport", dport);
|
||||
bundle.putInt("blocked", blocked);
|
||||
|
||||
submit(rule, bundle, context);
|
||||
}
|
||||
|
||||
private static void submit(Rule rule, PersistableBundle bundle, Context context) {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
JobScheduler scheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
|
||||
|
||||
// Get english application label
|
||||
String label = null;
|
||||
try {
|
||||
Configuration config = new Configuration();
|
||||
config.setLocale(new Locale("en"));
|
||||
Resources res = pm.getResourcesForApplication(rule.info.packageName);
|
||||
res.updateConfiguration(config, res.getDisplayMetrics());
|
||||
label = res.getString(rule.info.applicationInfo.labelRes);
|
||||
} catch (Throwable ex) {
|
||||
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
|
||||
CharSequence cs = rule.info.applicationInfo.loadLabel(pm);
|
||||
if (cs != null)
|
||||
label = cs.toString();
|
||||
}
|
||||
|
||||
// Add application data
|
||||
bundle.putInt("uid", rule.info.applicationInfo.uid);
|
||||
bundle.putString("package", rule.info.packageName);
|
||||
bundle.putInt("version_code", rule.info.versionCode);
|
||||
bundle.putString("version_name", rule.info.versionName);
|
||||
bundle.putString("label", label);
|
||||
bundle.putInt("system", rule.system ? 1 : 0);
|
||||
try {
|
||||
bundle.putString("installer", pm.getInstallerPackageName(rule.info.packageName));
|
||||
} catch (Throwable ex) {
|
||||
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
|
||||
bundle.putString("installer", null);
|
||||
}
|
||||
|
||||
// Cancel overlapping jobs
|
||||
for (JobInfo pending : scheduler.getAllPendingJobs()) {
|
||||
String type = pending.getExtras().getString("type");
|
||||
if (type != null && type.equals(bundle.getString("type"))) {
|
||||
if (type.equals("rule")) {
|
||||
int uid = pending.getExtras().getInt("uid");
|
||||
if (uid == bundle.getInt("uid")) {
|
||||
Log.i(TAG, "Canceling id=" + pending.getId());
|
||||
scheduler.cancel(pending.getId());
|
||||
}
|
||||
} else if (type.equals("host")) {
|
||||
int uid = pending.getExtras().getInt("uid");
|
||||
int version = pending.getExtras().getInt("version");
|
||||
int protocol = pending.getExtras().getInt("protocol");
|
||||
String daddr = pending.getExtras().getString("daddr");
|
||||
int dport = pending.getExtras().getInt("dport");
|
||||
if (uid == bundle.getInt("uid") &&
|
||||
version == bundle.getInt("version") &&
|
||||
protocol == bundle.getInt("protocol") &&
|
||||
daddr != null && daddr.equals(bundle.getString("daddr")) &&
|
||||
dport == bundle.getInt("dport")) {
|
||||
Log.i(TAG, "Canceling id=" + pending.getId());
|
||||
scheduler.cancel(pending.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule job
|
||||
ComponentName serviceName = new ComponentName(context, ServiceJob.class);
|
||||
JobInfo job = new JobInfo.Builder(++id, serviceName)
|
||||
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
|
||||
.setMinimumLatency(Util.isDebuggable(context) ? 10 * 1000 : 60 * 1000)
|
||||
.setExtras(bundle)
|
||||
.setPersisted(true)
|
||||
.build();
|
||||
if (scheduler.schedule(job) == JobScheduler.RESULT_SUCCESS)
|
||||
Log.i(TAG, "Scheduled job=" + job.getId() + " success");
|
||||
else
|
||||
Log.e(TAG, "Scheduled job=" + job.getId() + " failed");
|
||||
}
|
||||
}
|
|
@ -107,17 +107,6 @@ public class Util {
|
|||
}
|
||||
}
|
||||
|
||||
public static String sha256(String text, String salt) throws NoSuchAlgorithmException, UnsupportedEncodingException {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
byte[] bytes = (text + salt).getBytes("UTF-8");
|
||||
digest.update(bytes, 0, bytes.length);
|
||||
bytes = digest.digest();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : bytes)
|
||||
sb.append(String.format("%02X", b));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static boolean isConnected(Context context) {
|
||||
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
NetworkInfo ni = (cm == null ? null : cm.getActiveNetworkInfo());
|
||||
|
|
Before Width: | Height: | Size: 344 B |
Before Width: | Height: | Size: 353 B |
Before Width: | Height: | Size: 235 B |
Before Width: | Height: | Size: 242 B |
Before Width: | Height: | Size: 404 B |
Before Width: | Height: | Size: 417 B |
Before Width: | Height: | Size: 592 B |
Before Width: | Height: | Size: 610 B |
Before Width: | Height: | Size: 764 B |
Before Width: | Height: | Size: 789 B |
|
@ -1,5 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/fetch_disabled_black" android:state_enabled="false" />
|
||||
<item android:drawable="@drawable/ic_cloud_download_black_24dp" />
|
||||
</selector>
|
|
@ -1,4 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:src="@drawable/ic_cloud_download_black_24dp"
|
||||
android:tint="@color/colorGrayed" />
|
|
@ -1,4 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:src="@drawable/ic_cloud_download_white_24dp"
|
||||
android:tint="@color/colorGrayed" />
|
|
@ -1,5 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/fetch_disabled_white" android:state_enabled="false" />
|
||||
<item android:drawable="@drawable/ic_cloud_download_white_24dp" />
|
||||
</selector>
|
|
@ -44,15 +44,6 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:text="@string/app_first"
|
||||
android:textAppearance="@style/TextMedium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSubmit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dip"
|
||||
android:text="@string/app_submit"
|
||||
android:textAppearance="@style/TextMedium"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</LinearLayout>
|
|
@ -229,16 +229,9 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:text="@string/title_related" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/ibFetch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:src="?attr/iconFetch" />
|
||||
android:text="@string/title_related" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/ibSettings"
|
||||
|
@ -408,13 +401,5 @@
|
|||
android:layout_marginTop="4dp"
|
||||
android:text="@string/title_notify"
|
||||
android:textAppearance="@style/TextSmall" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/cbSubmit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="@string/title_submit"
|
||||
android:textAppearance="@style/TextSmall" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
|
@ -7,7 +7,6 @@
|
|||
<string name="app_first">Great care has been taken to develop and test NetGuard,
|
||||
however it is impossible to guarantee NetGuard will work correctly on every device.
|
||||
\n\nBy using NetGuard, you agree to the <a href="http://www.gnu.org/licenses/gpl.txt">GNU General Public License version 3</a></string>
|
||||
<string name="app_submit">NetGuard will optionally share anonymized usage data to help improve how NetGuard protects you, by understanding how end users work with NetGuard.</string>
|
||||
<string name="app_agree">I agree</string>
|
||||
<string name="app_disagree">I disagree</string>
|
||||
|
||||
|
@ -215,7 +214,6 @@ Your internet traffic is not being sent to a remote VPN server.</string>
|
|||
<string name="title_precedence">Access rules take precedence over other rules</string>
|
||||
<string name="title_options">Options</string>
|
||||
<string name="title_notify">Notify internet access attempts</string>
|
||||
<string name="title_submit">Share settings</string>
|
||||
|
||||
<string name="title_rate">Rate</string>
|
||||
<string name="title_allow">Allow</string>
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
<attr name="expander" format="reference" />
|
||||
<attr name="iconQueue" format="reference" />
|
||||
<attr name="iconMetered" format="reference" />
|
||||
<attr name="iconFetch" format="reference" />
|
||||
<attr name="iconSettings" format="reference" />
|
||||
<attr name="iconDelete" format="reference" />
|
||||
<attr name="iconLaunch" format="reference" />
|
||||
|
@ -18,7 +17,6 @@
|
|||
<item name="expander">@drawable/expander_black</item>
|
||||
<item name="iconQueue">@drawable/ic_hourglass_empty_black_24dp</item>
|
||||
<item name="iconMetered">@drawable/ic_attach_money_black_24dp</item>
|
||||
<item name="iconFetch">@drawable/fetch_black</item>
|
||||
<item name="iconSettings">@drawable/ic_settings_black_24dp</item>
|
||||
<item name="iconDelete">@drawable/ic_delete_black_24dp</item>
|
||||
<item name="iconLaunch">@drawable/ic_launch_black_24dp</item>
|
||||
|
@ -29,7 +27,6 @@
|
|||
<item name="expander">@drawable/expander_white</item>
|
||||
<item name="iconQueue">@drawable/ic_hourglass_empty_white_24dp</item>
|
||||
<item name="iconMetered">@drawable/ic_attach_money_white_24dp</item>
|
||||
<item name="iconFetch">@drawable/fetch_white</item>
|
||||
<item name="iconSettings">@drawable/ic_settings_white_24dp</item>
|
||||
<item name="iconDelete">@drawable/ic_delete_white_24dp</item>
|
||||
<item name="iconLaunch">@drawable/ic_launch_white_24dp</item>
|
||||
|
|
|
@ -28,7 +28,8 @@ Características:
|
|||
• Simple de usar
|
||||
• No requiere root
|
||||
• 100% código abierto
|
||||
• Sin seguimiento o perfilado de usuarios
|
||||
• No calling home
|
||||
• Sin seguimiento o análisis integrado
|
||||
• Sin publicidad
|
||||
• Desarrollado y soportado activamente
|
||||
• Soporta Android 4.0 o superior
|
||||
|
|
|
@ -28,6 +28,7 @@ Caracteristici:
|
|||
• Simplu de utilizat
|
||||
• Nu necesita acces root
|
||||
• 100% software cu sursa deschisa
|
||||
• Nu transmite datele nicaieri
|
||||
• Nu urmareste activitatea si nici nu o analizeaza
|
||||
• Fara reclama
|
||||
• Dezvoltare si asistenta continuua
|
||||
|
|
|
@ -28,7 +28,8 @@ Features:
|
|||
• Simple to use
|
||||
• No root required
|
||||
• 100% open source
|
||||
• No tracking and user profiling
|
||||
• No calling home
|
||||
• No tracking or analytics
|
||||
• No advertisements
|
||||
• Actively developed and supported
|
||||
• Android 4.0 and later supported
|
||||
|
|