Merge branch 'master' into stom79_baseline

# Conflicts:
#	app/src/main/java/org/eu/exodus_privacy/exodusprivacy/MainActivity.java
#	app/src/main/java/org/eu/exodus_privacy/exodusprivacy/fragments/AppListFragment.java
This commit is contained in:
Thomas 2020-10-04 18:07:20 +02:00
commit 555f108853
108 changed files with 3408 additions and 693 deletions

8
.gitignore vendored
View File

@ -8,8 +8,6 @@ gen/
.gradle/ .gradle/
build/ build/
/*/build/ /*/build/
gradlew*
gradle/wrapper/gradle-wrapper.jar
# Local configuration file (sdk path, etc) # Local configuration file (sdk path, etc)
local.properties local.properties
@ -24,4 +22,10 @@ proguard/
.idea .idea
*.iml *.iml
#vim
.*swp
.*swo
.tags
captures/ captures/

View File

@ -1,23 +1,26 @@
language: android language: android
java: oraclejdk8 java: oraclejdk8
cache: false
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
cache:
directories:
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/
- $HOME/.android/build-cache
android: android:
componentes: components:
- tools - tools
- platform-tools - platform-tools
- tools - build-tools-29.0.3
- android-29
- build-tools-27.0.3
- android-27
- extra-m2-repository - extra-m2-repository
- sys-img-armeabi-v7a-android-27 - sys-img-armeabi-v7a-android-28
before_script:
- mkdir "$ANDROID_HOME/licenses" || true
- echo -e "\nd56f5187479451eabf01fb78af6dfcb131a6481e" > "$ANDROID_HOME/licenses/android-sdk-license"
- touch empty.file
- gradle wrapper --gradle-version 4.1 -b empty.file
- rm empty.file
script: script:
- ./gradlew assembleDebug - ./gradlew assembleDebug
- ./gradlew test

View File

@ -3,3 +3,8 @@
Exodus Privacy is an Android application. Exodus Privacy is an Android application.
Exodus Privacy application let you know what trackers are embedded in apps installed on your smartphone. It let you also know the permissions required by any apps on your smartphone. Exodus Privacy application let you know what trackers are embedded in apps installed on your smartphone. It let you also know the permissions required by any apps on your smartphone.
It helps you to take your privacy back! It helps you to take your privacy back!
<a href="https://f-droid.org/packages/org.eu.exodus_privacy.exodusprivacy/" target="_blank">
<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" alt="Get it on F-Droid" height="80"/></a>
<a href="https://play.google.com/store/apps/details?id=org.eu.exodus_privacy.exodusprivacy" target="_blank">
<img src="https://play.google.com/intl/en_us/badges/images/generic/en-play-badge.png" alt="Get it on Google Play" height="80"/></a>

View File

@ -1,14 +1,14 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
android { android {
compileSdkVersion 27 compileSdkVersion 29
buildToolsVersion "27.0.3" buildToolsVersion "29.0.3"
defaultConfig { defaultConfig {
applicationId "org.eu.exodus_privacy.exodusprivacy" applicationId "org.eu.exodus_privacy.exodusprivacy"
minSdkVersion 16 minSdkVersion 17
targetSdkVersion 27 targetSdkVersion 29
versionCode 4 versionCode 10
versionName "1.0.4" versionName "2.1.1"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
} }
buildTypes { buildTypes {
@ -24,19 +24,20 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
dataBinding { buildFeatures {
enabled = true dataBinding = true
} }
} }
dependencies { dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations' exclude group: 'com.android.support', module: 'support-annotations'
}) })
compile 'com.android.support:appcompat-v7:27.1.0' implementation 'androidx.appcompat:appcompat:1.1.0'
compile 'com.android.support.constraint:constraint-layout:1.0.2' implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
compile 'com.android.support:design:27.1.0' implementation 'androidx.recyclerview:recyclerview:1.1.0'
compile 'com.android.support:recyclerview-v7:27.1.0' implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
testCompile 'junit:junit:4.12' implementation 'com.google.android.material:material:1.1.0'
testImplementation 'junit:junit:4.13'
} }

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="org.eu.exodus_privacy.exodusprivacy"> package="org.eu.exodus_privacy.exodusprivacy">
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
@ -11,16 +12,17 @@
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning"
android:largeHeap="true"
> >
<activity android:name=".MainActivity" <activity android:name=".MainActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
> >
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
</application> </application>
</manifest> </manifest>

View File

@ -18,52 +18,83 @@
package org.eu.exodus_privacy.exodusprivacy; package org.eu.exodus_privacy.exodusprivacy;
import android.support.v4.app.FragmentManager; import android.content.pm.PackageInfo;
import android.app.SearchManager; import android.content.pm.PackageManager;
import android.content.Context; import android.content.Context;
import android.databinding.DataBindingUtil; import android.content.Intent;
import android.support.design.widget.Snackbar; import android.net.Uri;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle; import android.os.Bundle;
import android.support.v7.widget.SearchView; import android.provider.Settings;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.widget.SearchView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import com.google.android.material.snackbar.Snackbar;
import org.eu.exodus_privacy.exodusprivacy.adapters.ApplicationListAdapter; import org.eu.exodus_privacy.exodusprivacy.adapters.ApplicationListAdapter;
import org.eu.exodus_privacy.exodusprivacy.adapters.ApplicationViewModel;
import org.eu.exodus_privacy.exodusprivacy.adapters.TrackerListAdapter;
import org.eu.exodus_privacy.exodusprivacy.databinding.MainBinding; import org.eu.exodus_privacy.exodusprivacy.databinding.MainBinding;
import org.eu.exodus_privacy.exodusprivacy.fragments.AppListFragment; import org.eu.exodus_privacy.exodusprivacy.fragments.HomeFragment;
import org.eu.exodus_privacy.exodusprivacy.fragments.ReportFragment; import org.eu.exodus_privacy.exodusprivacy.fragments.ReportFragment;
import org.eu.exodus_privacy.exodusprivacy.fragments.TrackerFragment;
import org.eu.exodus_privacy.exodusprivacy.fragments.Updatable;
import org.eu.exodus_privacy.exodusprivacy.listener.NetworkListener; import org.eu.exodus_privacy.exodusprivacy.listener.NetworkListener;
import org.eu.exodus_privacy.exodusprivacy.manager.DatabaseManager;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity { public class MainActivity extends AppCompatActivity {
AppListFragment appList; private List<Updatable> fragments;
ReportFragment report; private SearchView searchView;
SearchView searchView; private Menu toolbarMenu;
private Menu mMenu; private MenuItem settingsMenuItem;
private String packageName;
private MainBinding binding;
private ApplicationListAdapter.OnAppClickListener onAppClickListener;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
final MainBinding mainBinding = DataBindingUtil.setContentView(this,R.layout.main); binding = DataBindingUtil.setContentView(this,R.layout.main);
final MainBinding mainBinding = binding;
getSupportActionBar().setTitle(R.string.app_title);
fragments = new ArrayList<>();
NetworkListener networkListener = new NetworkListener() { NetworkListener networkListener = new NetworkListener() {
@Override @Override
public void onSuccess() { public void onSuccess() {
runOnUiThread(() -> { runOnUiThread(() -> {
appList.updateComplete(); for(Updatable updatable : fragments){
if(report != null) if(updatable instanceof ReportFragment) {
report.updateComplete(); ApplicationViewModel model = ((ReportFragment) updatable).getModel();
if(model.versionName == null)
model.report = DatabaseManager.getInstance(MainActivity.this).getReportFor(model.packageName, model.versionCode, model.source);
else
model.report = DatabaseManager.getInstance(MainActivity.this).getReportFor(model.packageName,model.versionName,model.source);
if(model.report != null)
model.trackers = DatabaseManager.getInstance(MainActivity.this).getTrackers(model.report.trackers);
}
updatable.onUpdateComplete();
}
}); });
} }
@Override @Override
public void onError(String error) { public void onError(String error) {
runOnUiThread(() -> { runOnUiThread(() -> {
appList.updateComplete(); for(Updatable updatable : fragments){
updatable.onUpdateComplete();
}
Snackbar bar = Snackbar.make(mainBinding.fragmentContainer,error,Snackbar.LENGTH_LONG); Snackbar bar = Snackbar.make(mainBinding.fragmentContainer,error,Snackbar.LENGTH_LONG);
bar.show(); bar.show();
}); });
@ -75,45 +106,72 @@ public class MainActivity extends AppCompatActivity {
} }
}; };
ApplicationListAdapter.OnAppClickListener onAppClickListener = packageInfo -> { TrackerListAdapter.OnTrackerClickListener onTrackerClickListener = id -> {
TrackerFragment tracker = TrackerFragment.newInstance(id);
report = ReportFragment.newInstance(getPackageManager(),packageInfo); tracker.setOnAppClickListener(onAppClickListener);
fragments.add(tracker);
FragmentManager manager = getSupportFragmentManager(); FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction(); FragmentTransaction transaction = manager.beginTransaction();
transaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_right, R.anim.slide_in_left, R.anim.slide_out_left) transaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_right, R.anim.slide_in_left, R.anim.slide_out_left)
.replace(R.id.fragment_container,report) .replace(R.id.fragment_container,tracker)
.addToBackStack(null) .addToBackStack(null)
.commit(); .commit();
searchView.clearFocus();
if (mMenu != null)
(mMenu.findItem(R.id.action_filter)).collapseActionView();
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
assert imm != null;
imm.hideSoftInputFromWindow(mainBinding.fragmentContainer.getWindowToken(), 0);
}; };
appList = AppListFragment.newInstance(networkListener,onAppClickListener); onAppClickListener = vm -> {
try {
PackageManager pm = getPackageManager();
PackageInfo packageInfo = pm.getPackageInfo(vm.packageName, PackageManager.GET_PERMISSIONS);
ReportFragment report = ReportFragment.newInstance(pm,vm,packageInfo,onTrackerClickListener);
fragments.add(report);
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_right, R.anim.slide_in_left, R.anim.slide_out_left)
.replace(R.id.fragment_container,report)
.addToBackStack(null)
.commit();
packageName = packageInfo.packageName;
searchView.clearFocus();
if (toolbarMenu != null)
(toolbarMenu.findItem(R.id.action_filter)).collapseActionView();
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
assert imm != null;
imm.hideSoftInputFromWindow(mainBinding.fragmentContainer.getWindowToken(), 0);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
};
HomeFragment home = new HomeFragment();
fragments.add(home);
home.setNetworkListener(networkListener);
home.setOnAppClickListener(onAppClickListener);
FragmentManager manager = getSupportFragmentManager(); FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction(); FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.fragment_container,appList) transaction.replace(R.id.fragment_container,home)
.commit(); .commit();
home.startRefresh();
} }
@Override @Override
public void onBackPressed() { public void onBackPressed() {
if (getSupportFragmentManager().getBackStackEntryCount() == 0) if (getSupportFragmentManager().getBackStackEntryCount() == 0)
finish(); finish();
else else {
getSupportFragmentManager().popBackStack(); getSupportFragmentManager().popBackStack();
fragments.remove(fragments.size()-1);
}
} }
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
mMenu = menu; toolbarMenu = menu;
MenuInflater inflater = getMenuInflater(); MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main, menu); inflater.inflate(R.menu.main, menu);
searchView = (SearchView) menu.findItem(R.id.action_filter).getActionView(); searchView = (SearchView) menu.findItem(R.id.action_filter).getActionView();
@ -121,16 +179,41 @@ public class MainActivity extends AppCompatActivity {
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override @Override
public boolean onQueryTextSubmit(String query) { public boolean onQueryTextSubmit(String query) {
appList.filter(query); HomeFragment home = (HomeFragment) fragments.get(0);
home.filter(query);
return true; return true;
} }
@Override @Override
public boolean onQueryTextChange(String newText) { public boolean onQueryTextChange(String newText) {
appList.filter(newText); HomeFragment home = (HomeFragment) fragments.get(0);
home.filter(newText);
return true; return true;
} }
}); });
settingsMenuItem = menu.findItem(R.id.action_settings);
Updatable fragment = fragments.get(fragments.size()-1);
if (fragment instanceof ReportFragment)
settingsMenuItem.setVisible(true);
else
settingsMenuItem.setVisible(false);
return true; return true;
} }
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if(item.getItemId() == R.id.action_settings) {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.fromParts("package",packageName,null));
try {
startActivity(intent);
} catch(android.content.ActivityNotFoundException e) {
Snackbar bar = Snackbar.make(binding.fragmentContainer,R.string.no_settings,Snackbar.LENGTH_LONG);
bar.show();
}
return true;
}
return false;
}
} }

View File

@ -0,0 +1,178 @@
package org.eu.exodus_privacy.exodusprivacy;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.View;
import androidx.databinding.BaseObservable;
import androidx.databinding.Bindable;
import org.eu.exodus_privacy.exodusprivacy.objects.Permission;
import org.eu.exodus_privacy.exodusprivacy.objects.ReportDisplay;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
public class ReportViewModel extends BaseObservable {
private ReportDisplay reportDisplay;
public void setReportDisplay(ReportDisplay report){
this.reportDisplay = report;
notifyChange();
}
@Bindable
public String getName() {
return reportDisplay.displayName;
}
@Bindable
public Drawable getLogo() {
return reportDisplay.logo;
}
public int getPermissionNumber() {
return reportDisplay.permissions != null ? reportDisplay.permissions.size() : 0;
}
@Bindable
public String getPermissionNumberStr() {
return String.valueOf(getPermissionNumber());
}
@Bindable
public int getPermissionColor() {
return getColor(getPermissionNumber());
}
@Bindable
public boolean getPermissionVisibility() {
return reportDisplay.permissions != null;
}
@Bindable
public boolean getHasPermissionDangerous() {
for(Permission perm : reportDisplay.permissions) {
if(perm.dangerous)
return true;
}
return false;
}
public int getTrackerNumber() {
return reportDisplay.trackers != null ? reportDisplay.trackers.size() : 0;
}
@Bindable
public String getTrackerNumberStr() {
return String.valueOf(getTrackerNumber());
}
@Bindable
public boolean getTrackerVisibility() {
return reportDisplay.trackers != null;
}
@Bindable
public int getTrackerColor() {
return getColor(getTrackerNumber());
}
public String getCreator(Context context) {
String creator = reportDisplay.creator != null ? reportDisplay.creator : "";
if (reportDisplay.report != null && !reportDisplay.report.downloads.isEmpty()) {
String download = reportDisplay.report.downloads;
download = download.replace("downloads",context.getString(R.string.downloads));
creator += " (" + download + ")";
}
return creator;
}
@Bindable
public boolean getCreatorVisibility() {
return reportDisplay.creator != null && !reportDisplay.creator.isEmpty();
}
@Bindable
public String getInstalledVersion() {
return reportDisplay.versionName != null ? reportDisplay.versionName : String.valueOf(reportDisplay.versionCode);
}
@Bindable
public String getReportVersion() {
if(reportDisplay.report != null) {
if (reportDisplay.versionName != null && !reportDisplay.report.version.equals(reportDisplay.versionName)) {
return reportDisplay.report.version;
} else if (reportDisplay.versionName == null && reportDisplay.report.versionCode != reportDisplay.versionCode) {
return String.valueOf(reportDisplay.report.versionCode);
}
}
return "";
}
@Bindable
public boolean getReportVersionVisibility() {
return !getReportVersion().isEmpty();
}
@Bindable
public boolean getReportVisibility() {
return reportDisplay.report != null;
}
public String getReportDate(Context context) {
String reportDate = "";
if(reportDisplay.report == null)
return reportDate;
DateFormat dateFormat = SimpleDateFormat.getDateInstance(DateFormat.LONG);
reportDate = context.getString(R.string.created_date)+" "+dateFormat.format(reportDisplay.report.creationDate.getTime());
if (reportDisplay.report.creationDate.getTime().compareTo(reportDisplay.report.updateDate.getTime())!=0)
reportDate += " "+context.getString(R.string.and_updated)+" "+dateFormat.format(reportDisplay.report.updateDate.getTime())+".";
return reportDate;
}
public String getCodeSignatureInfo(Context context) {
if(reportDisplay.trackers != null && reportDisplay.trackers.size() > 0)
return context.getString(R.string.code_signature_found);
else if(reportDisplay.trackers != null)
return context.getString(R.string.code_signature_not_found);
else
return "";
}
public String getCodePermissionInfo(Context context) {
if(reportDisplay.permissions != null && reportDisplay.permissions.size() > 0)
return context.getString(R.string.code_permission_found);
else if(reportDisplay.permissions != null)
return context.getString(R.string.code_permission_not_found);
else
return "";
}
private int getColor(int number) {
if (number == 0)
return R.drawable.square_green;
else if(number < 5)
return R.drawable.square_light_yellow;
else
return R.drawable.square_light_red;
}
@Bindable
public String getSource() {
return reportDisplay.source;
}
public String getViewOnStore() {
return reportDisplay.viewOnStore;
}
}

View File

@ -0,0 +1,224 @@
package org.eu.exodus_privacy.exodusprivacy;
import android.annotation.SuppressLint;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Utils {
@SuppressLint("PackageManagerGetSignatures")
public static String getCertificateSHA1Fingerprint(PackageManager pm, String packageName) {
int flags = PackageManager.GET_SIGNATURES;
PackageInfo packageInfo = null;
try {
packageInfo = pm.getPackageInfo(packageName, flags);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
assert packageInfo != null;
Signature[] signatures = packageInfo.signatures;
StringBuilder builder = new StringBuilder();
builder.append(packageName);
for(Signature signature: signatures) {
InputStream input = new ByteArrayInputStream(signature.toByteArray());
CertificateFactory cf = null;
try {
cf = CertificateFactory.getInstance("X509");
} catch (CertificateException e) {
e.printStackTrace();
}
try {
assert cf != null;
X509Certificate c = (X509Certificate) cf.generateCertificate(input);
try {
MessageDigest md = MessageDigest.getInstance("SHA1");
byte[] publicKey = md.digest(c.getEncoded());
builder.append(' ');
builder.append(byte2HexFormatted(publicKey).toUpperCase());
} catch (NoSuchAlgorithmException e1) {
e1.printStackTrace();
}
} catch (CertificateException e) {
e.printStackTrace();
}
}
String hexString = null;
try {
MessageDigest md = MessageDigest.getInstance("SHA1");
byte[] publicKey = md.digest(builder.toString().getBytes());
hexString = byte2HexFormatted(publicKey);
} catch (NoSuchAlgorithmException e1) {
e1.printStackTrace();
}
assert hexString != null;
return hexString.toUpperCase();
}
private static String byte2HexFormatted(byte[] arr) {
StringBuilder str = new StringBuilder(arr.length * 2);
for (byte anArr : arr) {
String h = Integer.toHexString(anArr);
int l = h.length();
if (l == 1) h = "0" + h;
if (l > 2) h = h.substring(l - 2, l);
str.append(h.toUpperCase());
}
return str.toString();
}
/*
Simple and not complete markdownToHtml converter
*/
public static String markdownToHtml(String markdown) {
StringBuilder builder = new StringBuilder();
String[] lines = markdown.split("\r\n");
ArrayList<String> listStarter = new ArrayList<>();
ArrayList<String> formatStarter = new ArrayList<>();
ArrayList<String> closeTags = new ArrayList<>();
for(String line : lines) {
if (line.matches("^#{1,5} .*")) {
int nb = line.indexOf(" ");
String hx = "<h"+nb+">";
String endhx = "</h"+nb+">";
builder.append(hx);
closeTags.add(endhx);
line = line.substring(line.indexOf(" ")+1);
} else if (line.matches("^ *[+\\-*] .*")) {
String starter="";
if (listStarter.size() > 0 && line.startsWith(listStarter.get(listStarter.size()-1))) {
starter = listStarter.get(listStarter.size()-1);
} else {
Pattern pattern = Pattern.compile("^( *[+\\-*] )");
Matcher matcher = pattern.matcher(line);
if (matcher.find()) {
starter = matcher.group(1);
listStarter.add(starter);
builder.append("<ul>\n");
}
}
builder.append("<li> ");
int beginIndex = line.indexOf(starter)+starter.length();
line = line.substring(beginIndex);
closeTags.add("</li>");
} else {
while(!listStarter.isEmpty()) {
listStarter.remove(listStarter.size() - 1);
builder.append("</ul>\n");
}
builder.append("<p>");
closeTags.add("</p>");
}
while(!line.isEmpty()){
Pattern pattern = Pattern.compile("^\\[(.+?)(?=\\]\\()\\]\\((http.+?)(?=\\))\\)");
//Pattern pattern = Pattern.compile("^\\[(.*)\\]\\((http.*)\\)");
Matcher matcher = pattern.matcher(line);
if (matcher.find()) {
builder.append("<a href=\"");
builder.append(matcher.group(2));
builder.append("\">");
builder.append(matcher.group(1));
builder.append("</a>");
line = line.substring(line.indexOf(")")+1);
continue;
}
pattern = Pattern.compile("^(http.*)");
matcher = pattern.matcher(line);
if (matcher.find()) {
builder.append("<a href=\"");
builder.append(matcher.group(1));
builder.append("\">");
builder.append(matcher.group(1));
builder.append("</a>");
line = line.substring(matcher.group(1).length());
continue;
}
pattern = Pattern.compile("^[*_]{2}(.+)[*_]{2}");
matcher = pattern.matcher(line);
if (matcher.find()) {
if(line.startsWith("*")) {
line = line.replaceFirst("\\*\\*", "<b>");
formatStarter.add("**");
}
else {
line = line.replaceFirst("__", "<b>");
formatStarter.add("__");
}
continue;
}
pattern = Pattern.compile("^[*_]{1}(.+)");
matcher = pattern.matcher(line);
if (matcher.find()) {
if(line.startsWith("*")) {
line = line.replaceFirst("\\*", "<i>");
formatStarter.add("*");
}
else {
line = line.replaceFirst("_", "<i>");
formatStarter.add("_");
}
continue;
}
if(formatStarter.size() > 0) {
String checkFormat;
if(line.contains(" "))
checkFormat = line.substring(0,line.indexOf(" "));
else
checkFormat = line;
String lastFormat = formatStarter.get(formatStarter.size()-1);
if (checkFormat.contains(lastFormat)) {
if(lastFormat.length()==2) {
if (lastFormat.contains("*"))
line = line.replaceFirst("\\*\\*", "</b>");
else
line = line.replaceFirst("__", "</b>");
} else {
if (lastFormat.contains("*"))
line = line.replaceFirst("\\*", "</i>");
else
line = line.replaceFirst("_", "</i>");
}
formatStarter.remove(formatStarter.size()-1);
continue;
}
}
if(line.contains(" ")) {
builder.append(line.substring(0, line.indexOf(" ") + 1));
line = line.substring(line.indexOf(" ") + 1);
} else {
builder.append(line);
line = "";
}
}
//close all unclosed tags starting at the end
while(!closeTags.isEmpty()) {
builder.append(closeTags.remove(closeTags.size()-1));
}
builder.append("\n");
}
while(!listStarter.isEmpty()) {
listStarter.remove(listStarter.size() - 1);
builder.append("</ul>\n");
}
return builder.toString();
}
}

View File

@ -19,19 +19,20 @@
package org.eu.exodus_privacy.exodusprivacy.adapters; package org.eu.exodus_privacy.exodusprivacy.adapters;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.databinding.DataBindingUtil;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.RecyclerView;
import org.eu.exodus_privacy.exodusprivacy.R; import org.eu.exodus_privacy.exodusprivacy.R;
import org.eu.exodus_privacy.exodusprivacy.databinding.AppItemBinding; import org.eu.exodus_privacy.exodusprivacy.databinding.AppItemBinding;
import org.eu.exodus_privacy.exodusprivacy.manager.DatabaseManager; import org.eu.exodus_privacy.exodusprivacy.fragments.AppListFragment;
import org.eu.exodus_privacy.exodusprivacy.objects.Report; import org.eu.exodus_privacy.exodusprivacy.objects.Report;
import org.eu.exodus_privacy.exodusprivacy.objects.Tracker; import org.eu.exodus_privacy.exodusprivacy.objects.Tracker;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
@ -41,49 +42,36 @@ import java.util.regex.Pattern;
public class ApplicationListAdapter extends RecyclerView.Adapter { public class ApplicationListAdapter extends RecyclerView.Adapter {
private List<PackageInfo> packages; private List<ApplicationViewModel> applicationViewModels;
private PackageManager packageManager;
private OnAppClickListener onAppClickListener; private OnAppClickListener onAppClickListener;
private static final String gStore = "com.android.vending"; private Object filter = "";
private String filter = ""; private AppListFragment.Type filterType = AppListFragment.Type.NAME;
private final int HIDDEN_APP = 0; private final int HIDDEN_APP = 0;
private final int DISPLAYED_APP = 1; private final int DISPLAYED_APP = 1;
private int displayedApp = 0;
private Comparator<ApplicationViewModel> alphaPackageComparator = new Comparator<ApplicationViewModel>() {
private Comparator<PackageInfo> alphaPackageComparator = new Comparator<PackageInfo>() {
@Override @Override
public int compare(PackageInfo pack1, PackageInfo pack2) { public int compare(ApplicationViewModel app1, ApplicationViewModel app2) {
String pkg1 = packageManager.getApplicationLabel(pack1.applicationInfo).toString(); if(app1.label != null && app2.label != null)
String pkg2 = packageManager.getApplicationLabel(pack2.applicationInfo).toString(); return app1.label.toString().compareToIgnoreCase(app2.label.toString());
return pkg1.compareToIgnoreCase(pkg2); else if(app2.label != null)
return -1;
else if(app1.label != null)
return 1;
else
return 0;
} }
}; };
public ApplicationListAdapter(PackageManager manager, OnAppClickListener listener) { public ApplicationListAdapter(Context context, OnAppClickListener listener) {
applicationViewModels = new ArrayList<>();
onAppClickListener = listener; onAppClickListener = listener;
setPackageManager(manager);
}
private void setInstalledPackages(List<PackageInfo> installedPackages) {
packages = installedPackages;
applyStoreFilter();
Collections.sort(packages, alphaPackageComparator);
notifyDataSetChanged();
}
private void applyStoreFilter() {
List<PackageInfo> toRemove = new ArrayList<>();
for (PackageInfo pkg : packages) {
if (!gStore.equals(packageManager.getInstallerPackageName(pkg.packageName))) {
toRemove.add(pkg);
}
}
packages.removeAll(toRemove);
} }
@Override @Override
public int getItemViewType(int position){ public int getItemViewType(int position){
return (Pattern.compile(Pattern.quote(filter.trim()), Pattern.CASE_INSENSITIVE).matcher(packageManager.getApplicationLabel(packages.get(position).applicationInfo)).find())?DISPLAYED_APP:HIDDEN_APP; return applicationViewModels.get(position).isVisible ? DISPLAYED_APP : HIDDEN_APP;
} }
@NonNull @NonNull
@ -100,12 +88,13 @@ public class ApplicationListAdapter extends RecyclerView.Adapter {
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) { public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
if( viewHolder.getItemViewType() == DISPLAYED_APP) { if( viewHolder.getItemViewType() == DISPLAYED_APP) {
final ApplicationListViewHolder holder = (ApplicationListViewHolder) viewHolder; final ApplicationListViewHolder holder = (ApplicationListViewHolder) viewHolder;
holder.setData(packages.get(position)); ApplicationViewModel vm = applicationViewModels.get(position);
holder.setViewModel(vm);
//noinspection Convert2Lambda //noinspection Convert2Lambda
holder.itemView.setOnClickListener(new View.OnClickListener() { holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
onAppClickListener.onAppClick(holder.packageInfo); onAppClickListener.onAppClick(vm);
} }
}); });
}else { }else {
@ -115,30 +104,30 @@ public class ApplicationListAdapter extends RecyclerView.Adapter {
} }
} }
@Override @Override
public int getItemCount() { public int getItemCount() {
return packages.size(); return applicationViewModels.size();
} }
public void setPackageManager(PackageManager manager) { public void displayAppList(List<ApplicationViewModel> applications) {
packageManager = manager; applicationViewModels = applications;
if(packageManager != null) { Collections.sort(applicationViewModels, alphaPackageComparator);
List<PackageInfo> installedPackages = packageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS); filter(filterType,filter);
setInstalledPackages(installedPackages);
}
} }
class ApplicationEmptyViewHolder extends RecyclerView.ViewHolder{ public int getDisplayedApps() {
return displayedApp;
}
static class ApplicationEmptyViewHolder extends RecyclerView.ViewHolder{
ApplicationEmptyViewHolder(View itemView) { ApplicationEmptyViewHolder(View itemView) {
super(itemView); super(itemView);
} }
} }
static class ApplicationListViewHolder extends RecyclerView.ViewHolder {
class ApplicationListViewHolder extends RecyclerView.ViewHolder { ApplicationViewModel viewModel;
PackageInfo packageInfo;
AppItemBinding appItemBinding; AppItemBinding appItemBinding;
ApplicationListViewHolder(AppItemBinding binding) { ApplicationListViewHolder(AppItemBinding binding) {
@ -146,8 +135,8 @@ public class ApplicationListAdapter extends RecyclerView.Adapter {
appItemBinding = binding; appItemBinding = binding;
} }
public void setData(PackageInfo data) { void setViewModel(ApplicationViewModel vm) {
packageInfo = data; viewModel = vm;
Context context = appItemBinding.getRoot().getContext(); Context context = appItemBinding.getRoot().getContext();
@ -155,49 +144,90 @@ public class ApplicationListAdapter extends RecyclerView.Adapter {
appItemBinding.otherVersion.setVisibility(View.GONE); appItemBinding.otherVersion.setVisibility(View.GONE);
appItemBinding.analysed.setVisibility(View.GONE); appItemBinding.analysed.setVisibility(View.GONE);
appItemBinding.appTrackerNb.setVisibility(View.VISIBLE); appItemBinding.appTrackerNb.setVisibility(View.VISIBLE);
appItemBinding.appTracker.setVisibility(View.VISIBLE);
String versionName = viewModel.versionName;
long versionCode = viewModel.versionCode;
String packageName = packageInfo.packageName; appItemBinding.appLogo.setImageDrawable(viewModel.icon);
String versionName = packageInfo.versionName;
//get logo appItemBinding.appName.setText(viewModel.label);
try { appItemBinding.source.setText(context.getString(R.string.source,viewModel.source));
appItemBinding.appLogo.setImageDrawable(packageManager.getApplicationIcon(packageName));
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
//get name
appItemBinding.appName.setText(packageManager.getApplicationLabel(packageInfo.applicationInfo));
//get permissions long size = viewModel.requestedPermissions != null ? viewModel.requestedPermissions.length : 0;
if(packageInfo.requestedPermissions != null) { appItemBinding.appPermissionNb.setText(String.valueOf(size));
appItemBinding.appPermissionNb.setText(context.getString(R.string.permissions) + " " + String.valueOf(data.requestedPermissions.length)); if(size == 0)
} else { appItemBinding.appPermissionNb.setBackgroundResource(R.drawable.square_green);
appItemBinding.appPermissionNb.setText(context.getString(R.string.permissions) + " " + String.valueOf(0)); else if (size < 5)
} appItemBinding.appPermissionNb.setBackgroundResource(R.drawable.square_light_yellow);
//get reports else
Report report = DatabaseManager.getInstance(context).getReportFor(packageName, versionName); appItemBinding.appPermissionNb.setBackgroundResource(R.drawable.square_light_red);
Report report = viewModel.report;
if(report != null) { if(report != null) {
Set<Tracker> trackers = DatabaseManager.getInstance(context).getTrackers(report.trackers); Set<Tracker> trackers = viewModel.trackers;
appItemBinding.appTrackerNb.setText(context.getString(R.string.trackers) + " " + trackers.size());
if(!report.version.equals(data.versionName)) { size = trackers.size();
appItemBinding.appTrackerNb.setText(String.valueOf(size));
if(size == 0)
appItemBinding.appTrackerNb.setBackgroundResource(R.drawable.square_green);
else if (size < 5)
appItemBinding.appTrackerNb.setBackgroundResource(R.drawable.square_light_yellow);
else
appItemBinding.appTrackerNb.setBackgroundResource(R.drawable.square_light_red);
if(versionName != null && !report.version.equals(viewModel.versionName)) {
String string = context.getString(R.string.tested,versionName, report.version);
appItemBinding.otherVersion.setText(string);
appItemBinding.otherVersion.setVisibility(View.VISIBLE);
} else if (versionName == null && report.versionCode != versionCode) {
String string = context.getString(R.string.tested,String.valueOf(versionCode),String.valueOf(report.versionCode));
appItemBinding.otherVersion.setText(string);
appItemBinding.otherVersion.setVisibility(View.VISIBLE); appItemBinding.otherVersion.setVisibility(View.VISIBLE);
} }
} else { } else {
appItemBinding.appTrackerNb.setVisibility(View.GONE); appItemBinding.appTrackerNb.setVisibility(View.GONE);
appItemBinding.appTracker.setVisibility(View.GONE);
appItemBinding.analysed.setVisibility(View.VISIBLE); appItemBinding.analysed.setVisibility(View.VISIBLE);
} }
} }
} }
public interface OnAppClickListener { public interface OnAppClickListener {
void onAppClick(PackageInfo packageInfo); void onAppClick(ApplicationViewModel vm);
} }
public void filter(AppListFragment.Type type, Object filterObject) {
displayedApp = 0;
if (type.equals(AppListFragment.Type.NAME)) {
filter = filterObject;
filterType = type;
String filterStr = (String) filterObject;
public void filter(String text) { Pattern p = Pattern.compile(Pattern.quote(filterStr.trim()), Pattern.CASE_INSENSITIVE);
filter = text; for (ApplicationViewModel app : applicationViewModels) {
app.isVisible = p.matcher(app.label).find();
if(app.isVisible)
displayedApp++;
}
} else if(type.equals(AppListFragment.Type.TRACKER)) {
filter = filterObject;
filterType = type;
Long filterLng = (Long) filterObject;
for (ApplicationViewModel app : applicationViewModels) {
app.isVisible = false;
if (app.trackers != null) {
for (Tracker tracker : app.trackers) {
if (tracker.id == filterLng) {
app.isVisible = true;
displayedApp++;
break;
}
}
}
}
}
notifyDataSetChanged(); notifyDataSetChanged();
} }
} }

View File

@ -0,0 +1,29 @@
package org.eu.exodus_privacy.exodusprivacy.adapters;
import android.graphics.drawable.Drawable;
import androidx.annotation.Nullable;
import org.eu.exodus_privacy.exodusprivacy.objects.Report;
import org.eu.exodus_privacy.exodusprivacy.objects.Tracker;
import java.util.Set;
/**
* This class holds the data needed to display an application cell in the RecyclerView
*/
public class ApplicationViewModel {
public String packageName;
public String versionName;
public int versionCode;
public String[] requestedPermissions;
public @Nullable
Report report;
public Set<Tracker> trackers;
public @Nullable
Drawable icon;
public CharSequence label;
public String installerPackageName;
public boolean isVisible;
public String source;
}

View File

@ -1,12 +1,13 @@
package org.eu.exodus_privacy.exodusprivacy.adapters; package org.eu.exodus_privacy.exodusprivacy.adapters;
import android.databinding.DataBindingUtil;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.RecyclerView;
import org.eu.exodus_privacy.exodusprivacy.R; import org.eu.exodus_privacy.exodusprivacy.R;
import org.eu.exodus_privacy.exodusprivacy.databinding.PermissionItemBinding; import org.eu.exodus_privacy.exodusprivacy.databinding.PermissionItemBinding;
import org.eu.exodus_privacy.exodusprivacy.objects.Permission; import org.eu.exodus_privacy.exodusprivacy.objects.Permission;
@ -39,7 +40,7 @@ public class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAd
@Override @Override
public int getItemCount() { public int getItemCount() {
if(permissionList == null || permissionList.size() == 0) if(permissionList == null || permissionList.size() == 0)
return 1; return 0;
else else
return permissionList.size(); return permissionList.size();
} }
@ -60,8 +61,21 @@ public class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAd
void setupData(Permission permission) { void setupData(Permission permission) {
if(permission != null) { if(permission != null) {
permissionItemBinding.permissionName.setText(permission.name); if(permission.name != null) {
permissionItemBinding.permissionShort.setText(permission.name);
permissionItemBinding.permissionShort.setVisibility(View.VISIBLE);
}
else
permissionItemBinding.permissionShort.setVisibility(View.GONE);
permissionItemBinding.permissionName.setText(permission.fullName.substring(permission.fullName.lastIndexOf(".")+1));
permissionItemBinding.permissionDescription.setText(permission.description); permissionItemBinding.permissionDescription.setText(permission.description);
if(permission.icon != null)
permissionItemBinding.icon.setImageDrawable(permission.icon);
if(!permission.dangerous)
permissionItemBinding.dangerous.setVisibility(View.GONE);
else
permissionItemBinding.dangerous.setVisibility(View.VISIBLE);
manageExpanded(permission); manageExpanded(permission);
permissionItemBinding.mainLayout.setOnClickListener((View.OnClickListener) v -> { permissionItemBinding.mainLayout.setOnClickListener((View.OnClickListener) v -> {
if( permission.description != null && permission.description.trim().length() > 0) { if( permission.description != null && permission.description.trim().length() > 0) {
@ -74,6 +88,9 @@ public class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAd
else { else {
permissionItemBinding.permissionName.setText(R.string.no_permissions); permissionItemBinding.permissionName.setText(R.string.no_permissions);
permissionItemBinding.arrow.setText(" "); permissionItemBinding.arrow.setText(" ");
permissionItemBinding.permissionShort.setVisibility(View.GONE);
permissionItemBinding.dangerous.setVisibility(View.GONE);
permissionItemBinding.permissionDescription.setVisibility(View.GONE);
} }
} }
@ -86,7 +103,7 @@ public class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAd
if( permission.description != null && permission.description.trim().length() > 0 ) if( permission.description != null && permission.description.trim().length() > 0 )
permissionItemBinding.arrow.setText(""); permissionItemBinding.arrow.setText("");
else else
permissionItemBinding.arrow.setText(""); permissionItemBinding.arrow.setText("");
permissionItemBinding.permissionDescription.setVisibility(View.GONE); permissionItemBinding.permissionDescription.setVisibility(View.GONE);
} }

View File

@ -1,26 +1,35 @@
package org.eu.exodus_privacy.exodusprivacy.adapters; package org.eu.exodus_privacy.exodusprivacy.adapters;
import android.databinding.DataBindingUtil; import android.content.Intent;
import android.databinding.ViewDataBinding; import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ViewDataBinding;
import androidx.recyclerview.widget.RecyclerView;
import org.eu.exodus_privacy.exodusprivacy.R; import org.eu.exodus_privacy.exodusprivacy.R;
import org.eu.exodus_privacy.exodusprivacy.databinding.TrackerItemBinding; import org.eu.exodus_privacy.exodusprivacy.databinding.TrackerItemBinding;
import org.eu.exodus_privacy.exodusprivacy.objects.Tracker; import org.eu.exodus_privacy.exodusprivacy.objects.Tracker;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set; import java.util.Set;
public class TrackerListAdapter extends android.support.v7.widget.RecyclerView.Adapter<TrackerListAdapter.TrackerListViewHolder>{ public class TrackerListAdapter extends RecyclerView.Adapter<TrackerListAdapter.TrackerListViewHolder>{
private Set<Tracker> trackersList; private List<Tracker> trackersList;
private OnTrackerClickListener trackerClickListener;
private int layout; private int layout;
public TrackerListAdapter(Set<Tracker> trackerList, int resource) { public TrackerListAdapter(Set<Tracker> trackerList, int resource, OnTrackerClickListener listener) {
setTrackers(trackerList); setTrackers(trackerList);
layout = resource; layout = resource;
trackerClickListener = listener;
} }
@NonNull @NonNull
@ -35,7 +44,7 @@ public class TrackerListAdapter extends android.support.v7.widget.RecyclerView.A
if(trackersList == null || trackersList.size() == 0) if(trackersList == null || trackersList.size() == 0)
holder.setupData(null); holder.setupData(null);
else else
holder.setupData((Tracker) trackersList.toArray()[position]); holder.setupData(trackersList.get(position));
} }
@Override @Override
@ -46,8 +55,13 @@ public class TrackerListAdapter extends android.support.v7.widget.RecyclerView.A
return trackersList.size(); return trackersList.size();
} }
private Comparator<Tracker> alphaTrackerComparator = (track1, track2) -> track1.name.compareToIgnoreCase(track2.name);
public void setTrackers(Set<Tracker> trackers) { public void setTrackers(Set<Tracker> trackers) {
trackersList = trackers; if(trackers != null) {
trackersList = new ArrayList<>(trackers);
Collections.sort(trackersList, alphaTrackerComparator);
}
} }
class TrackerListViewHolder extends RecyclerView.ViewHolder { class TrackerListViewHolder extends RecyclerView.ViewHolder {
@ -62,13 +76,21 @@ public class TrackerListAdapter extends android.support.v7.widget.RecyclerView.A
void setupData(Tracker tracker) { void setupData(Tracker tracker) {
if(viewDataBinding instanceof TrackerItemBinding) { if(viewDataBinding instanceof TrackerItemBinding) {
TrackerItemBinding binding = (TrackerItemBinding) viewDataBinding; TrackerItemBinding binding = (TrackerItemBinding) viewDataBinding;
if(tracker != null) if(tracker != null) {
binding.trackerName.setText(tracker.name); binding.trackerName.setText(tracker.name + "");
binding.getRoot().setOnClickListener(v -> {
trackerClickListener.onTrackerClick(tracker.id);
});
}
else else
binding.trackerName.setText(R.string.no_trackers); binding.trackerName.setText(R.string.no_trackers);
} }
} }
} }
public interface OnTrackerClickListener{
public void onTrackerClick(long trackerId);
}
} }

View File

@ -1,178 +1,104 @@
/*
* Copyright (C) 2018 Anthony Chomienne, anthony@mob-dev.fr
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.eu.exodus_privacy.exodusprivacy.fragments; package org.eu.exodus_privacy.exodusprivacy.fragments;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.databinding.DataBindingUtil;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import org.eu.exodus_privacy.exodusprivacy.R; import org.eu.exodus_privacy.exodusprivacy.R;
import org.eu.exodus_privacy.exodusprivacy.adapters.ApplicationListAdapter; import org.eu.exodus_privacy.exodusprivacy.adapters.ApplicationListAdapter;
import org.eu.exodus_privacy.exodusprivacy.adapters.ApplicationViewModel;
import org.eu.exodus_privacy.exodusprivacy.databinding.ApplistBinding; import org.eu.exodus_privacy.exodusprivacy.databinding.ApplistBinding;
import org.eu.exodus_privacy.exodusprivacy.listener.NetworkListener;
import org.eu.exodus_privacy.exodusprivacy.manager.NetworkManager;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class AppListFragment extends Fragment { public class AppListFragment extends Fragment {
private PackageManager packageManager;
private NetworkListener networkListener;
private ApplicationListAdapter.OnAppClickListener onAppClickListener;
private boolean startupRefresh;
private ApplistBinding applistBinding; private ApplistBinding applistBinding;
private List<ApplicationViewModel> applications;
private ApplicationListAdapter adapter; private ApplicationListAdapter adapter;
private ApplicationListAdapter.OnAppClickListener onAppClickListener;
public static AppListFragment newInstance(NetworkListener networkListener, ApplicationListAdapter.OnAppClickListener appClickListener) { private Type filterType = Type.NAME;
AppListFragment fragment = new AppListFragment(); private Object filterObject = "";
fragment.setNetworkListener(networkListener); private boolean scrollbarEnabled = true;
fragment.setOnAppClickListener(appClickListener);
fragment.startupRefresh = true;
return fragment;
}
@Override @Override
public void onCreate(Bundle savedInstanceState) { public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreate(savedInstanceState); //create binding
} applistBinding = DataBindingUtil.inflate(inflater, R.layout.applist,container,false);
//init variables
@Override if (applications == null)
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { applications = new ArrayList<>();
applistBinding = DataBindingUtil.inflate(inflater,R.layout.applist,container,false); Context context = applistBinding.getRoot().getContext();
//configure list
applistBinding.appList.setLayoutManager(new LinearLayoutManager(context));
applistBinding.appList.setVerticalScrollBarEnabled(scrollbarEnabled);
adapter = new ApplicationListAdapter(context, onAppClickListener);
adapter.displayAppList(applications);
adapter.filter(filterType,filterObject);
applistBinding.appList.setAdapter(adapter);
return applistBinding.getRoot(); return applistBinding.getRoot();
} }
@Override public void setOnAppClickListener(ApplicationListAdapter.OnAppClickListener listener){
public void onActivityCreated(@Nullable Bundle savedInstanceState) { onAppClickListener = listener;
super.onActivityCreated(savedInstanceState);
if(applistBinding == null)
return;
Context context = applistBinding.getRoot().getContext();
applistBinding.swipeRefresh.setOnRefreshListener(() -> startRefresh());
if (packageManager == null)
packageManager = context.getPackageManager();
applistBinding.appList.setLayoutManager(new LinearLayoutManager(context));
if (packageManager != null) {
if(startupRefresh) {
startRefresh();
startupRefresh = false;
}
applistBinding.noPackageManager.setVisibility(View.GONE);
applistBinding.noAppFound.setVisibility(View.GONE);
adapter = new ApplicationListAdapter(packageManager, onAppClickListener);
if(adapter.getItemCount() == 0) {
applistBinding.noAppFound.setVisibility(View.VISIBLE);
} else {
applistBinding.appList.setAdapter(adapter);
}
} else {
applistBinding.noPackageManager.setVisibility(View.VISIBLE);
}
} }
public void startRefresh(){ public void setApplications(List<ApplicationViewModel> applicationList){
if(applistBinding == null) applications = applicationList;
return; if(adapter != null)
applistBinding.layoutProgress.setVisibility(View.VISIBLE); adapter.displayAppList(applications);
applistBinding.swipeRefresh.setRefreshing(true);
List<PackageInfo> packageInstalled = packageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS);
ArrayList<String> packageList = new ArrayList<>();
for(PackageInfo pkgInfo : packageInstalled)
packageList.add(pkgInfo.packageName);
NetworkManager.getInstance().getReports(applistBinding.getRoot().getContext(),networkListener,packageList);
} }
public void updateComplete() { public void setFilter(Type type, Object filter){
if(applistBinding != null) { filterType = type;
applistBinding.layoutProgress.setVisibility(View.GONE); filterObject = filter;
applistBinding.swipeRefresh.setRefreshing(false); if(adapter != null)
if(packageManager != null && applistBinding.appList.getAdapter() != null) { adapter.filter(type,filterObject);
((ApplicationListAdapter) applistBinding.appList.getAdapter()).setPackageManager(packageManager);
}
}
} }
public void setNetworkListener(NetworkListener listener) { public void disableScrollBar() {
this.networkListener = new NetworkListener() { scrollbarEnabled = false;
@Override if (applistBinding != null)
public void onSuccess() { applistBinding.appList.setVerticalScrollBarEnabled(false);
listener.onSuccess();
}
@Override
public void onError(String error) {
listener.onError(error);
}
public void onProgress(int resourceId, int progress, int maxProgress) {
updateProgress(resourceId, progress, maxProgress);
}
};
} }
private void updateProgress(int resourceId, int progress, int maxProgress) { public void enableScrollBar() {
Activity activity = getActivity(); scrollbarEnabled = true;
if(activity == null) if (applistBinding != null)
return; applistBinding.appList.setVerticalScrollBarEnabled(true);
activity.runOnUiThread(() -> {
if (applistBinding == null)
return;
if(maxProgress > 0)
applistBinding.statusProgress.setText(getString(resourceId)+" "+progress+"/"+maxProgress);
else
applistBinding.statusProgress.setText(getString(resourceId));
applistBinding.progress.setMax(maxProgress);
applistBinding.progress.setProgress(progress);
});
} }
public void setOnAppClickListener(ApplicationListAdapter.OnAppClickListener onAppClickListener) { public int getTotalApps() {
this.onAppClickListener = onAppClickListener; if (adapter == null)
return 0;
return adapter.getItemCount();
} }
@Override public int getDisplayedApps() {
public void onAttach(Context context) { if (adapter == null)
super.onAttach(context); return 0;
packageManager = context.getPackageManager(); return adapter.getDisplayedApps();
} }
@Override public enum Type {
public void onDetach() { NAME,
super.onDetach(); TRACKER
packageManager = null;
} }
public void filter(String filter){ public enum Order {
adapter.filter(filter); NAME_ASC,
NAME_DSC,
TRACKER_ASC,
TRACKER_DSC,
PERMISSIONS_ASC,
PERMISSIONS_DSC
} }
} }

View File

@ -0,0 +1,138 @@
package org.eu.exodus_privacy.exodusprivacy.fragments;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import org.eu.exodus_privacy.exodusprivacy.Utils;
import org.eu.exodus_privacy.exodusprivacy.adapters.ApplicationViewModel;
import org.eu.exodus_privacy.exodusprivacy.manager.DatabaseManager;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
class ComputeAppListTask extends AsyncTask<Void, Void, List<ApplicationViewModel>> {
interface Listener {
void onAppsComputed(List<ApplicationViewModel> apps);
}
private static final String gStore = "com.android.vending";
private static final String fdroid = "ord.fdroid.fdroid";
private WeakReference<PackageManager> packageManagerRef;
private WeakReference<DatabaseManager> databaseManagerRef;
private WeakReference<Listener> listenerRef;
ComputeAppListTask(WeakReference<PackageManager> packageManagerRef,
WeakReference<DatabaseManager> databaseManagerRef,
WeakReference<Listener> listenerRef) {
this.packageManagerRef = packageManagerRef;
this.databaseManagerRef = databaseManagerRef;
this.listenerRef = listenerRef;
}
protected List<ApplicationViewModel> doInBackground(Void... params) {
PackageManager packageManager = packageManagerRef.get();
DatabaseManager databaseManager = databaseManagerRef.get();
List<ApplicationViewModel> vms = new ArrayList<>();
if(packageManager != null && databaseManager != null) {
List<PackageInfo> installedPackages = packageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS);
vms = applyStoreFilter(installedPackages, databaseManager, packageManager);
convertPackagesToViewModels(vms, databaseManager, packageManager);
}
return vms;
}
@Override
protected void onPostExecute(List<ApplicationViewModel> vms) {
Listener listener = listenerRef.get();
if(listener != null) {
listener.onAppsComputed(vms);
}
}
private void convertPackagesToViewModels(List<ApplicationViewModel> appsToBuild,
DatabaseManager databaseManager,
PackageManager packageManager) {
for (ApplicationViewModel vm : appsToBuild) {
try {
PackageInfo pi = packageManager.getPackageInfo(vm.packageName, PackageManager.GET_PERMISSIONS);
buildViewModelFromPackageInfo(vm, pi, databaseManager, packageManager);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
}
private void buildViewModelFromPackageInfo(ApplicationViewModel vm, PackageInfo pi,
DatabaseManager databaseManager,
PackageManager packageManager) {
vm.versionName = pi.versionName;
vm.packageName = pi.packageName;
vm.versionCode = pi.versionCode;
vm.requestedPermissions = pi.requestedPermissions;
if (vm.versionName != null)
vm.report = databaseManager.getReportFor(vm.packageName, vm.versionName, vm.source);
else {
vm.report = databaseManager.getReportFor(vm.packageName, vm.versionCode, vm.source);
}
if (vm.report != null) {
vm.trackers = databaseManager.getTrackers(vm.report.trackers);
}
try {
vm.icon = packageManager.getApplicationIcon(vm.packageName);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
vm.label = packageManager.getApplicationLabel(pi.applicationInfo);
vm.installerPackageName = packageManager.getInstallerPackageName(vm.packageName);
vm.isVisible = true;
}
private List<ApplicationViewModel> applyStoreFilter(List<PackageInfo> packageInfos,
DatabaseManager databaseManager,
PackageManager packageManager) {
List<ApplicationViewModel> result = new ArrayList<>();
for (PackageInfo packageInfo : packageInfos) {
String packageName = packageInfo.packageName;
String installerPackageName = packageManager.getInstallerPackageName(packageName);
ApplicationViewModel vm = new ApplicationViewModel();
vm.packageName = packageName;
if (!gStore.equals(installerPackageName) && !fdroid.equals(installerPackageName)) {
String auid = Utils.getCertificateSHA1Fingerprint(packageManager, packageName);
Map<String,String> sources = databaseManager.getSources(packageName);
for(Map.Entry<String,String> entry : sources.entrySet()) {
if(entry.getValue().equalsIgnoreCase(auid)) {
vm.source = entry.getKey();
break;
}
}
} else if (gStore.equals(installerPackageName)) {
vm.source = "google";
} else {
vm.source = "fdroid";
}
ApplicationInfo appInfo = null;
try {
appInfo = packageManager.getApplicationInfo(packageName,0);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
if(vm.source != null && appInfo != null && appInfo.enabled)
result.add(vm);
}
return result;
}
}

View File

@ -0,0 +1,183 @@
package org.eu.exodus_privacy.exodusprivacy.fragments;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import org.eu.exodus_privacy.exodusprivacy.R;
import org.eu.exodus_privacy.exodusprivacy.adapters.ApplicationListAdapter;
import org.eu.exodus_privacy.exodusprivacy.adapters.ApplicationViewModel;
import org.eu.exodus_privacy.exodusprivacy.databinding.HomeBinding;
import org.eu.exodus_privacy.exodusprivacy.listener.NetworkListener;
import org.eu.exodus_privacy.exodusprivacy.manager.DatabaseManager;
import org.eu.exodus_privacy.exodusprivacy.manager.NetworkManager;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
public class HomeFragment extends Fragment implements ComputeAppListTask.Listener, Updatable {
private @Nullable
PackageManager packageManager;
private NetworkListener networkListener;
private ApplicationListAdapter.OnAppClickListener onAppClickListener;
private boolean startupRefresh = true;
private HomeBinding homeBinding;
private List<ApplicationViewModel> applications;
private AppListFragment appListFragment;
private boolean startRefreshAsked;
private boolean refreshInProgress;
private int lastResource=0;
private int lastProgress=0;
private int lastMaxProgress=0;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if(applications == null)
applications = new ArrayList<>();
homeBinding = DataBindingUtil.inflate(inflater, R.layout.home,container,false);
appListFragment = new AppListFragment();
appListFragment.setOnAppClickListener(onAppClickListener);
FragmentManager fragmentManager = getChildFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.app_list_container,appListFragment);
transaction.commit();
Context context = homeBinding.getRoot().getContext();
packageManager = context.getPackageManager();
homeBinding.swipeRefresh.setOnRefreshListener(this::startRefresh);
if(packageManager != null) {
homeBinding.noPackageManager.setVisibility(View.GONE);
onAppsComputed(applications);
if(applications.isEmpty())
displayAppListAsync();
if(startRefreshAsked)
startRefresh();
else if (refreshInProgress) {
homeBinding.layoutProgress.setVisibility(View.VISIBLE);
homeBinding.swipeRefresh.setRefreshing(true);
updateProgress(lastResource,lastProgress,lastMaxProgress);
}
} else {
homeBinding.noPackageManager.setVisibility(View.VISIBLE);
}
return homeBinding.getRoot();
}
public void startRefresh(){
if(packageManager != null) {
refreshInProgress = true;
homeBinding.layoutProgress.setVisibility(View.VISIBLE);
homeBinding.swipeRefresh.setRefreshing(true);
List<PackageInfo> packageInstalled = packageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS);
ArrayList<String> packageList = new ArrayList<>();
for (PackageInfo pkgInfo : packageInstalled)
packageList.add(pkgInfo.packageName);
NetworkManager.getInstance().getReports(homeBinding.getRoot().getContext(), networkListener, packageList);
startRefreshAsked = false;
} else {
startRefreshAsked = true;
}
}
@Override
public void onUpdateComplete() {
refreshInProgress = false;
homeBinding.layoutProgress.setVisibility(View.GONE);
homeBinding.swipeRefresh.setRefreshing(false);
displayAppListAsync();
}
public void setNetworkListener(NetworkListener listener) {
this.networkListener = new NetworkListener() {
@Override
public void onSuccess() {
listener.onSuccess();
}
@Override
public void onError(String error) {
listener.onError(error);
}
public void onProgress(int resourceId, int progress, int maxProgress) {
updateProgress(resourceId, progress, maxProgress);
}
};
}
private void updateProgress(int resourceId, int progress, int maxProgress) {
lastResource = resourceId;
lastProgress = progress;
lastMaxProgress = maxProgress;
if(lastResource == 0)
return;
Activity activity = getActivity();
if(activity == null)
return;
activity.runOnUiThread(() -> {
if (homeBinding == null)
return;
if(maxProgress > 0)
homeBinding.statusProgress.setText(activity.getString(resourceId)+" "+progress+"/"+maxProgress);//fixme
else
homeBinding.statusProgress.setText(activity.getString(resourceId));
homeBinding.progress.setMax(maxProgress);
homeBinding.progress.setProgress(progress);
});
}
public void setOnAppClickListener(ApplicationListAdapter.OnAppClickListener onAppClickListener) {
this.onAppClickListener = onAppClickListener;
if(appListFragment != null)
appListFragment.setOnAppClickListener(onAppClickListener);
}
public void filter(String filter){
appListFragment.setFilter(AppListFragment.Type.NAME,filter);
}
private void displayAppListAsync() {
homeBinding.noAppFound.setVisibility(View.GONE);
if (applications.isEmpty()) {
homeBinding.retrieveApp.setVisibility(View.VISIBLE);
homeBinding.logo.setVisibility(View.VISIBLE);
}
new ComputeAppListTask(
new WeakReference<>(packageManager),
new WeakReference<>(DatabaseManager.getInstance(getActivity())),
new WeakReference<>(this)
).execute();
}
@Override
public void onAppsComputed(List<ApplicationViewModel> apps) {
this.applications = apps;
homeBinding.retrieveApp.setVisibility(View.GONE);
homeBinding.logo.setVisibility(View.GONE);
homeBinding.noAppFound.setVisibility(apps.isEmpty() ? View.VISIBLE : View.GONE);
appListFragment.setApplications(apps);
if(!apps.isEmpty()) {
if(startupRefresh) {
startRefresh();
startupRefresh = false;
}
}
}
}

View File

@ -20,171 +20,154 @@
package org.eu.exodus_privacy.exodusprivacy.fragments; package org.eu.exodus_privacy.exodusprivacy.fragments;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo; import android.net.Uri;
import android.databinding.DataBindingUtil;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.Fragment; import android.text.method.LinkMovementMethod;
import android.support.v7.widget.LinearLayoutManager;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import org.eu.exodus_privacy.exodusprivacy.R; import org.eu.exodus_privacy.exodusprivacy.R;
import org.eu.exodus_privacy.exodusprivacy.ReportViewModel;
import org.eu.exodus_privacy.exodusprivacy.adapters.ApplicationViewModel;
import org.eu.exodus_privacy.exodusprivacy.adapters.PermissionListAdapter; import org.eu.exodus_privacy.exodusprivacy.adapters.PermissionListAdapter;
import org.eu.exodus_privacy.exodusprivacy.adapters.TrackerListAdapter; import org.eu.exodus_privacy.exodusprivacy.adapters.TrackerListAdapter;
import org.eu.exodus_privacy.exodusprivacy.databinding.ReportBinding; import org.eu.exodus_privacy.exodusprivacy.databinding.ReportBinding;
import org.eu.exodus_privacy.exodusprivacy.manager.DatabaseManager; import org.eu.exodus_privacy.exodusprivacy.objects.ReportDisplay;
import org.eu.exodus_privacy.exodusprivacy.objects.Permission; public class ReportFragment extends Fragment implements Updatable {
import org.eu.exodus_privacy.exodusprivacy.objects.Report;
import org.eu.exodus_privacy.exodusprivacy.objects.Tracker;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class ReportFragment extends Fragment {
private PackageManager packageManager; private PackageManager packageManager;
private PackageInfo packageInfo; private PackageInfo packageInfo = null;
private ReportBinding reportBinding; private ReportBinding reportBinding;
private TrackerListAdapter.OnTrackerClickListener trackerClickListener;
private ApplicationViewModel model;
public static ReportFragment newInstance(PackageManager packageManager, PackageInfo packageInfo) { public static ReportFragment newInstance(PackageManager packageManager,ApplicationViewModel model, PackageInfo packageInfo, TrackerListAdapter.OnTrackerClickListener trackerClickListener) {
ReportFragment fragment = new ReportFragment(); ReportFragment fragment = new ReportFragment();
fragment.setPackageManager(packageManager); fragment.setPackageManager(packageManager);
fragment.setPackageInfo(packageInfo); fragment.setPackageInfo(packageInfo);
fragment.setApplicationViewModel(model);
fragment.setOnTrackerClickListener(trackerClickListener);
return fragment; return fragment;
} }
private void setApplicationViewModel(ApplicationViewModel model) {
this.model = model;
}
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
if(savedInstanceState != null && packageInfo == null) {
packageInfo = savedInstanceState.getParcelable("PackageInfo");
}
setHasOptionsMenu(true); setHasOptionsMenu(true);
} }
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
outState.putParcelable("PackageInfo", packageInfo);
super.onSaveInstanceState(outState);
}
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
reportBinding = DataBindingUtil.inflate(inflater,R.layout.report,container,false); reportBinding = DataBindingUtil.inflate(inflater,R.layout.report,container,false);
updateComplete(); onUpdateComplete();
return reportBinding.getRoot(); return reportBinding.getRoot();
} }
public void updateComplete() { @Override
public void onUpdateComplete() {
if(model != null)
onUpdateComplete(model);
}
public void onUpdateComplete(ApplicationViewModel model) {
Context context = reportBinding.getRoot().getContext(); Context context = reportBinding.getRoot().getContext();
String packageName = packageInfo.packageName;
String versionName = packageInfo.versionName;
//setup logo ReportDisplay reportDisplay = ReportDisplay.buildReportDisplay(context,model,packageManager,packageInfo);
try { ReportViewModel viewModel = new ReportViewModel();
reportBinding.logo.setImageDrawable(packageManager.getApplicationIcon(packageName)); viewModel.setReportDisplay(reportDisplay);
} catch (PackageManager.NameNotFoundException e) { reportBinding.setReportInfo(viewModel);
e.printStackTrace();
}
//setup name
reportBinding.name.setText(packageManager.getApplicationLabel(packageInfo.applicationInfo));
//setup permissions number
String permissions_text;
if (packageInfo.requestedPermissions != null && packageInfo.requestedPermissions.length > 0)
permissions_text = context.getString(R.string.permissions) + " " + String.valueOf(packageInfo.requestedPermissions.length);
else
permissions_text = context.getString(R.string.permissions);
reportBinding.permissionsTitle.setText(permissions_text);
//setup permissions list
List<Permission> requestedPermissions = null;
if (packageInfo.requestedPermissions != null && packageInfo.requestedPermissions.length > 0) {
requestedPermissions = new ArrayList<>();
for(int i = 0; i < packageInfo.requestedPermissions.length; i++) {
Permission permission = new Permission();
permission.fullName = packageInfo.requestedPermissions[i];
try {
PermissionInfo permissionInfo = packageManager.getPermissionInfo(permission.fullName,PackageManager.GET_META_DATA);
if(permissionInfo.loadDescription(packageManager) != null)
permission.description = permissionInfo.loadDescription(packageManager).toString();
if(permissionInfo.loadLabel(packageManager) != null)
permission.name = permissionInfo.loadLabel(packageManager).toString();
requestedPermissions.add(permission);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
}
reportBinding.permissions.setLayoutManager(new LinearLayoutManager(context)); reportBinding.permissions.setLayoutManager(new LinearLayoutManager(context));
PermissionListAdapter permissionAdapter = new PermissionListAdapter(requestedPermissions); PermissionListAdapter permissionAdapter = new PermissionListAdapter(reportDisplay.permissions);
reportBinding.permissions.setNestedScrollingEnabled(false); reportBinding.permissions.setNestedScrollingEnabled(false);
reportBinding.permissions.setAdapter(permissionAdapter); reportBinding.permissions.setAdapter(permissionAdapter);
reportBinding.analysed.setVisibility(View.GONE);
reportBinding.trackerLayout.setVisibility(View.VISIBLE);
//get trackers
Report report = DatabaseManager.getInstance(context).getReportFor(packageName,versionName);
Set<Tracker> trackers = null;
if(report != null) {
trackers = DatabaseManager.getInstance(context).getTrackers(report.trackers);
} else {
reportBinding.analysed.setVisibility(View.VISIBLE);
reportBinding.trackerLayout.setVisibility(View.GONE);
}
//setup trackers report
String trackers_text;
if(trackers != null && trackers.size() > 0)
trackers_text = context.getString(R.string.trackers)+" "+String.valueOf(trackers.size());
else
trackers_text = context.getString(R.string.trackers);
reportBinding.trackersTitle.setText(trackers_text);
//setup trackers lists //setup trackers lists
reportBinding.trackers.setLayoutManager(new LinearLayoutManager(context)); reportBinding.trackers.setLayoutManager(new LinearLayoutManager(context));
TrackerListAdapter trackerAdapter = new TrackerListAdapter(trackers,R.layout.tracker_item); TrackerListAdapter trackerAdapter = new TrackerListAdapter(reportDisplay.trackers,R.layout.tracker_item, trackerClickListener);
reportBinding.trackers.setNestedScrollingEnabled(false); reportBinding.trackers.setNestedScrollingEnabled(false);
reportBinding.trackers.setAdapter(trackerAdapter); reportBinding.trackers.setAdapter(trackerAdapter);
//setup creator reportBinding.reportDate.setText(viewModel.getReportDate(context));
if(report != null) reportBinding.creatorValue.setText(viewModel.getCreator(context));
reportBinding.creator.setText(DatabaseManager.getInstance(context).getCreator(report.appId)); reportBinding.codeSignature.setText(viewModel.getCodeSignatureInfo(context));
else reportBinding.codePermission.setText(viewModel.getCodePermissionInfo(context));
reportBinding.creator.setVisibility(View.GONE);
//setup installed reportBinding.trackerExplanation.setText(getText(R.string.tracker_infos));
String installed_str = context.getString(R.string.installed) +" "+ versionName; reportBinding.trackerExplanation.setMovementMethod(LinkMovementMethod.getInstance());
reportBinding.installedVersion.setText(installed_str); reportBinding.trackerExplanation.setClickable(true);
//setup reportversion reportBinding.permissionExplanationDangerous.setText(getText(R.string.permission_infos_dangerous));
reportBinding.reportVersion.setVisibility(View.VISIBLE); reportBinding.permissionExplanationDangerous.setMovementMethod(LinkMovementMethod.getInstance());
if(report != null && !report.version.equals(versionName)) { reportBinding.permissionExplanationDangerous.setClickable(true);
String report_str = context.getString(R.string.report_version)+" "+report.version;
reportBinding.reportVersion.setText(report_str); reportBinding.permissionExplanation.setText(getText(R.string.permission_infos));
reportBinding.permissionExplanation.setMovementMethod(LinkMovementMethod.getInstance());
reportBinding.permissionExplanation.setClickable(true);
reportBinding.viewStore.setOnClickListener(v -> {
Intent intent = new Intent(Intent.ACTION_VIEW);
if(reportDisplay.source.contains("google"))
intent.setData(Uri.parse("https://play.google.com/store/apps/details?id="+reportDisplay.packageName));
else
intent.setData(Uri.parse("https://f-droid.org/packages/"+reportDisplay.packageName));
startActivity(intent);
});
if(reportDisplay.report != null) {
reportBinding.reportUrl.setOnClickListener(v -> {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("https://reports.exodus-privacy.eu.org/reports/" + reportDisplay.report.id + "/"));
startActivity(intent);
});
} }
else
reportBinding.reportVersion.setVisibility(View.GONE);
//setup report url
if(report != null)
reportBinding.reportUrl.setText("https://reports.exodus-privacy.eu.org/reports/"+report.id+"/");
} }
public void setPackageManager(PackageManager packageManager) { private void setPackageManager(PackageManager packageManager) {
this.packageManager = packageManager; this.packageManager = packageManager;
} }
public void setPackageInfo(PackageInfo packageInfo) { private void setPackageInfo(PackageInfo packageInfo) {
this.packageInfo = packageInfo; this.packageInfo = packageInfo;
} }
private void setOnTrackerClickListener(TrackerListAdapter.OnTrackerClickListener listener) {
trackerClickListener = listener;
}
@Override @Override
public void onPrepareOptionsMenu(Menu menu) { public void onPrepareOptionsMenu(Menu menu) {
MenuItem item = menu.findItem(R.id.action_filter); MenuItem item = menu.findItem(R.id.action_filter);
item.setVisible(false); item.setVisible(false);
} }
public ApplicationViewModel getModel() {
return model;
}
} }

View File

@ -0,0 +1,155 @@
package org.eu.exodus_privacy.exodusprivacy.fragments;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import org.eu.exodus_privacy.exodusprivacy.R;
import org.eu.exodus_privacy.exodusprivacy.Utils;
import org.eu.exodus_privacy.exodusprivacy.adapters.ApplicationListAdapter;
import org.eu.exodus_privacy.exodusprivacy.adapters.ApplicationViewModel;
import org.eu.exodus_privacy.exodusprivacy.databinding.TrackerBinding;
import org.eu.exodus_privacy.exodusprivacy.manager.DatabaseManager;
import org.eu.exodus_privacy.exodusprivacy.objects.Tracker;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
public class TrackerFragment extends Fragment implements ComputeAppListTask.Listener, Updatable {
private TrackerBinding trackerBinding;
private long trackerId;
private PackageManager packageManager;
private List<ApplicationViewModel> applications;
private AppListFragment appListFragment;
private ApplicationListAdapter.OnAppClickListener onAppClickListener;
public static TrackerFragment newInstance(long trackerId) {
TrackerFragment fragment = new TrackerFragment();
fragment.setTrackerId(trackerId);
return fragment;
}
private void setTrackerId(long id) {
trackerId = id;
}
@Override
public void onUpdateComplete() {
Context context = trackerBinding.getRoot().getContext();
Tracker tracker = DatabaseManager.getInstance(context).getTracker(trackerId);
trackerBinding.name.setText(tracker.name);
trackerBinding.codeDetection.setText(tracker.codeSignature);
trackerBinding.networkDetection.setText(tracker.networkSignature);
trackerBinding.description.setText(Html.fromHtml(Utils.markdownToHtml(tracker.description)));
trackerBinding.description.setMovementMethod(LinkMovementMethod.getInstance());
trackerBinding.description.setClickable(true);
trackerBinding.trackerUrl.setOnClickListener(v -> {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(tracker.website));
startActivity(intent);
});
displayAppListAsync();
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
trackerBinding = DataBindingUtil.inflate(inflater, R.layout.tracker,container,false);
if (applications == null)
applications = new ArrayList<>();
appListFragment = new AppListFragment();
appListFragment.setFilter(AppListFragment.Type.TRACKER,trackerId);
appListFragment.disableScrollBar();
appListFragment.setOnAppClickListener(onAppClickListener);
FragmentManager manager = getChildFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.applications,appListFragment);
transaction.commit();
Context context = trackerBinding.getRoot().getContext();
packageManager = context.getPackageManager();
onUpdateComplete();
return trackerBinding.getRoot();
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
MenuItem item = menu.findItem(R.id.action_filter);
item.setVisible(false);
item = menu.findItem(R.id.action_settings);
item.setVisible(false);
}
private void displayAppListAsync() {
trackerBinding.noAppFound.setVisibility(View.GONE);
trackerBinding.trackerPresence.setVisibility(View.GONE);
//todo
trackerBinding.trackerPresenceNb.setVisibility(View.GONE);
//todo
trackerBinding.trackerPresenceTitle.setVisibility(View.GONE);
if (applications.isEmpty()) {
trackerBinding.retrieveApp.setVisibility(View.VISIBLE);
}
new ComputeAppListTask(
new WeakReference<>(packageManager),
new WeakReference<>(DatabaseManager.getInstance(getActivity())),
new WeakReference<>(this)
).execute();
}
@Override
public void onAppsComputed(List<ApplicationViewModel> apps) {
this.applications = apps;
trackerBinding.retrieveApp.setVisibility(View.GONE);
trackerBinding.noAppFound.setVisibility(apps.isEmpty() ? View.VISIBLE : View.GONE);
trackerBinding.trackerPresence.setVisibility(View.VISIBLE);
trackerBinding.trackerPresenceNb.setVisibility(View.VISIBLE);
trackerBinding.trackerPresenceTitle.setVisibility(View.VISIBLE);
appListFragment.setApplications(apps);
int total = appListFragment.getTotalApps();
int displayedApps = appListFragment.getDisplayedApps();
int percent = displayedApps*100/total;
if(percent >=50)
trackerBinding.trackerPresenceNb.setBackgroundResource(R.drawable.square_red);
else if(percent >=33)
trackerBinding.trackerPresenceNb.setBackgroundResource(R.drawable.square_dark_orange);
else if(percent >=20)
trackerBinding.trackerPresenceNb.setBackgroundResource(R.drawable.square_yellow);
else
trackerBinding.trackerPresenceNb.setBackgroundResource(R.drawable.square_light_blue);
trackerBinding.trackerPresenceNb.setText(percent+"%");
Context context = trackerBinding.getRoot().getContext();
String presence = context.getResources().getString(R.string.tracker_presence,displayedApps);
trackerBinding.trackerPresence.setText(presence);
trackerBinding.trackerPresenceTitle.setText(R.string.tracker_presence_in);
}
public void setOnAppClickListener(ApplicationListAdapter.OnAppClickListener listener) {
onAppClickListener = listener;
}
}

View File

@ -0,0 +1,5 @@
package org.eu.exodus_privacy.exodusprivacy.fragments;
public interface Updatable {
void onUpdateComplete();
}

View File

@ -29,8 +29,10 @@ import org.eu.exodus_privacy.exodusprivacy.objects.Report;
import org.eu.exodus_privacy.exodusprivacy.objects.Tracker; import org.eu.exodus_privacy.exodusprivacy.objects.Tracker;
import java.util.Calendar; import java.util.Calendar;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
public class DatabaseManager extends SQLiteOpenHelper { public class DatabaseManager extends SQLiteOpenHelper {
@ -44,14 +46,14 @@ public class DatabaseManager extends SQLiteOpenHelper {
public static DatabaseManager getInstance(Context context) { public static DatabaseManager getInstance(Context context) {
if(instance == null) if(instance == null)
instance = new DatabaseManager(context,"Exodus.db",null,1); instance = new DatabaseManager(context,"Exodus.db",null,3);
return instance; return instance;
} }
@Override @Override
public void onCreate(SQLiteDatabase db) { public void onCreate(SQLiteDatabase db) {
db.execSQL("Create Table if not exists applications (id INTEGER primary key autoincrement, package TEXT, name TEXT, creator TEXT);"); db.execSQL("Create Table if not exists applications (id INTEGER primary key autoincrement, package TEXT, name TEXT, creator TEXT, sources TEXT);");
db.execSQL("Create Table if not exists reports (id INTEGER primary key, creation INTEGER, updateat INTEGER, downloads TEXT, version TEXT, version_code INTEGER, app_id INTEGER, foreign key(app_id) references applications(id));"); db.execSQL("Create Table if not exists reports (id INTEGER primary key, creation INTEGER, updateat INTEGER, downloads TEXT, version TEXT, version_code INTEGER, app_id INTEGER, source TEXT, foreign key(app_id) references applications(id));");
db.execSQL("Create Table if not exists trackers (id INTEGER primary key, name TEXT, creation_date INTEGER, code_signature TEXT, network_signature TEXT, website TEXT, description TEXT);"); db.execSQL("Create Table if not exists trackers (id INTEGER primary key, name TEXT, creation_date INTEGER, code_signature TEXT, network_signature TEXT, website TEXT, description TEXT);");
db.execSQL("Create Table if not exists trackers_reports (id INTEGER primary key autoincrement, tracker_id INTEGER, report_id INTEGER, foreign key(tracker_id) references trackers(id), foreign key(report_id) references reports(id));"); db.execSQL("Create Table if not exists trackers_reports (id INTEGER primary key autoincrement, tracker_id INTEGER, report_id INTEGER, foreign key(tracker_id) references trackers(id), foreign key(report_id) references reports(id));");
@ -59,7 +61,36 @@ public class DatabaseManager extends SQLiteOpenHelper {
@Override @Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// do nothing if(oldVersion <= 1) {
db.execSQL("Alter Table applications add column auid TEXT");
}
if (oldVersion <= 2) {
try {
db.beginTransaction();
db.execSQL("Alter Table reports add column source TEXT");
db.execSQL("Alter Table applications rename to old_apps");
db.execSQL("Create Table if not exists applications (id INTEGER primary key autoincrement, package TEXT, name TEXT, creator TEXT, sources TEXT);");
Cursor cursor = db.query("old_apps",null,null,null,null,null,null);
while (cursor.moveToNext()){
ContentValues values = new ContentValues();
values.put("package",cursor.getString(1));
values.put("name",cursor.getString(2));
values.put("creator",cursor.getString(3));
String sources = "unknown:"+cursor.getString(4)+"|";
values.put("sources",sources);
db.insert("applications",null,values);
}
cursor.close();
db.execSQL("Drop Table old_apps");
db.setTransactionSuccessful();
} catch (Exception e) {
e.printStackTrace();
} finally {
db.endTransaction();
}
}
} }
private boolean existReport(SQLiteDatabase db, long reportId) { private boolean existReport(SQLiteDatabase db, long reportId) {
@ -119,6 +150,7 @@ public class DatabaseManager extends SQLiteOpenHelper {
values.put("package", application.packageName); values.put("package", application.packageName);
values.put("name",application.name); values.put("name",application.name);
values.put("creator",application.creator); values.put("creator",application.creator);
values.put("sources",buildSourcesStr(application.sources));
if(!existApplication(db, application.packageName)) { if(!existApplication(db, application.packageName)) {
db.insert("applications", null, values); db.insert("applications", null, values);
@ -134,8 +166,8 @@ public class DatabaseManager extends SQLiteOpenHelper {
Cursor cursor = db.query("applications",columns,where,whereArgs,null,null,null); Cursor cursor = db.query("applications",columns,where,whereArgs,null,null,null);
if(cursor.moveToFirst()) { if(cursor.moveToFirst()) {
application.id = cursor.getLong(0); application.id = cursor.getLong(0);
cursor.close();
} }
cursor.close();
for(Report report : application.reports) { for(Report report : application.reports) {
insertOrUpdateReport(db,report,application.id); insertOrUpdateReport(db,report,application.id);
@ -151,6 +183,7 @@ public class DatabaseManager extends SQLiteOpenHelper {
values.put("version",report.version); values.put("version",report.version);
values.put("version_code",report.versionCode); values.put("version_code",report.versionCode);
values.put("app_id",appId); values.put("app_id",appId);
values.put("source",report.source);
if(!existReport(db,report.id)) { if(!existReport(db,report.id)) {
values.put("id",report.id); values.put("id",report.id);
@ -180,36 +213,91 @@ public class DatabaseManager extends SQLiteOpenHelper {
db.insert("trackers_reports",null,values); db.insert("trackers_reports",null,values);
} }
public Report getReportFor(String packageName, String version) { public Report getReportFor(String packageName, String version, String source) {
SQLiteDatabase db = getReadableDatabase();
String[] columns = {"id"}; String[] columns = {"id"};
String where = "package = ?"; String where = "package = ?";
String[] whereArgs = {packageName}; String[] whereArgs = {packageName};
Cursor cursor = getReadableDatabase().query("applications",columns,where,whereArgs,null,null,null); Cursor cursor = db.query("applications",columns,where,whereArgs,null,null,null);
if(cursor.moveToFirst()) { if(cursor.moveToFirst()) {
long appId = cursor.getLong(0); long appId = cursor.getLong(0);
cursor.close(); cursor.close();
where = "app_id = ? and version = ?"; where = "app_id = ? and version = ? and source = ?";
whereArgs = new String[2]; whereArgs = new String[3];
whereArgs[0] = String.valueOf(appId); whereArgs[0] = String.valueOf(appId);
whereArgs[1] = version; whereArgs[1] = version;
whereArgs[2] = source;
String order = "id ASC"; String order = "id ASC";
cursor = getReadableDatabase().query("reports",columns,where,whereArgs,null,null,order); cursor = db.query("reports",columns,where,whereArgs,null,null,order);
long reportId; long reportId;
if(cursor.moveToFirst()) { if(cursor.moveToFirst()) {
reportId = cursor.getLong(0); reportId = cursor.getLong(0);
cursor.close();
} else { } else {
cursor.close();
columns = new String[2]; columns = new String[2];
columns[0] = "id"; columns[0] = "id";
columns[1] = "creation"; columns[1] = "creation";
where = "app_id = ?"; where = "app_id = ? and source = ?";
whereArgs = new String[1]; whereArgs = new String[2];
whereArgs[0] = String.valueOf(appId); whereArgs[0] = String.valueOf(appId);
whereArgs[1] = source;
order = "creation DESC"; order = "creation DESC";
//search a recent reports //search a recent reports
cursor = getReadableDatabase().query("reports",columns,where,whereArgs,null,null,order); cursor = db.query("reports",columns,where,whereArgs,null,null,order);
if(cursor.moveToFirst()) { if(cursor.moveToFirst()) {
reportId = cursor.getLong(0); reportId = cursor.getLong(0);
cursor.close();
} else { } else {
cursor.close();
return null;
}
}
return getReport(reportId);
} else {
cursor.close();
return null;
}
}
public Report getReportFor(String packageName, long version, String source) {
SQLiteDatabase db = getReadableDatabase();
String[] columns = {"id"};
String where = "package = ?";
String[] whereArgs = {packageName};
Cursor cursor = db.query("applications",columns,where,whereArgs,null,null,null);
if(cursor.moveToFirst()) {
long appId = cursor.getLong(0);
cursor.close();
where = "app_id = ? and version_code = ? and source = ?";
whereArgs = new String[3];
whereArgs[0] = String.valueOf(appId);
whereArgs[1] = String.valueOf(version);
whereArgs[2] = source;
String order = "id ASC";
cursor = db.query("reports",columns,where,whereArgs,null,null,order);
long reportId;
if(cursor.moveToFirst()) {
reportId = cursor.getLong(0);
cursor.close();
} else {
cursor.close();
columns = new String[2];
columns[0] = "id";
columns[1] = "creation";
where = "app_id = ? and source = ?";
whereArgs = new String[2];
whereArgs[0] = String.valueOf(appId);
whereArgs[1] = source;
order = "creation DESC";
//search a recent reports
cursor = db.query("reports",columns,where,whereArgs,null,null,order);
if(cursor.moveToFirst()) {
reportId = cursor.getLong(0);
cursor.close();
} else {
cursor.close();
return null; return null;
} }
} }
@ -222,12 +310,15 @@ public class DatabaseManager extends SQLiteOpenHelper {
} }
private Report getReport(long reportId) { private Report getReport(long reportId) {
SQLiteDatabase db = getReadableDatabase();
String where = "id = ?"; String where = "id = ?";
String[] whereArgs = {String.valueOf(reportId)}; String[] whereArgs = {String.valueOf(reportId)};
Cursor cursor = getReadableDatabase().query("reports",null,where,whereArgs,null,null,null); Cursor cursor = db.query("reports",null,where,whereArgs,null,null,null);
//get report //get report
if(!cursor.moveToFirst()) if(!cursor.moveToFirst()) {
cursor.close();
return null; return null;
}
Report report = new Report(); Report report = new Report();
int col = 0; int col = 0;
@ -235,19 +326,23 @@ public class DatabaseManager extends SQLiteOpenHelper {
long creation = cursor.getLong(col++); long creation = cursor.getLong(col++);
report.creationDate = Calendar.getInstance(); report.creationDate = Calendar.getInstance();
report.creationDate.setTimeInMillis(creation); report.creationDate.setTimeInMillis(creation);
report.creationDate.set(Calendar.MILLISECOND,0);
long update = cursor.getLong(col++); long update = cursor.getLong(col++);
report.updateDate = Calendar.getInstance(); report.updateDate = Calendar.getInstance();
report.updateDate.setTimeInMillis(update); report.updateDate.setTimeInMillis(update);
report.updateDate.set(Calendar.MILLISECOND,0);
report.downloads = cursor.getString(col++); report.downloads = cursor.getString(col++);
report.version = cursor.getString(col++); report.version = cursor.getString(col++);
report.versionCode = cursor.getLong(col++); report.versionCode = cursor.getLong(col++);
report.appId = cursor.getLong(col); report.appId = cursor.getLong(col++);
report.source = cursor.getString(col);
cursor.close();
report.trackers = new HashSet<>(); report.trackers = new HashSet<>();
where = "report_id = ?"; where = "report_id = ?";
String[] columns = {"tracker_id"}; String[] columns = {"tracker_id"};
String order = "tracker_id DESC"; String order = "tracker_id DESC";
cursor = getReadableDatabase().query("trackers_reports",columns,where,whereArgs,null,null,order); cursor = db.query("trackers_reports",columns,where,whereArgs,null,null,order);
//get trackersIds //get trackersIds
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
report.trackers.add(cursor.getLong(0)); report.trackers.add(cursor.getLong(0));
@ -270,10 +365,11 @@ public class DatabaseManager extends SQLiteOpenHelper {
return creator; return creator;
} }
private Tracker getTracker(long trackerId) { public Tracker getTracker(long trackerId) {
SQLiteDatabase db = getReadableDatabase();
String where = "id = ?"; String where = "id = ?";
String[] whereArgs = {String.valueOf(trackerId)}; String[] whereArgs = {String.valueOf(trackerId)};
Cursor cursor = getReadableDatabase().query("trackers",null,where,whereArgs,null,null,null,null); Cursor cursor = db.query("trackers",null,where,whereArgs,null,null,null,null);
Tracker tracker = null; Tracker tracker = null;
if(cursor.moveToFirst()) if(cursor.moveToFirst())
{ {
@ -309,4 +405,40 @@ public class DatabaseManager extends SQLiteOpenHelper {
insertOrUpdateTracker(db,tracker); insertOrUpdateTracker(db,tracker);
} }
} }
public Map<String,String> getSources(String packageName) {
String where = "package = ?";
String[] whereArgs = {packageName};
String[] columns = {"sources"};
Cursor cursor = getReadableDatabase().query("applications",columns,where,whereArgs,null,null,null,null);
String sourcesStr="";
if(cursor.moveToFirst())
{
sourcesStr = cursor.getString(0);
}
cursor.close();
return extractSources(sourcesStr);
}
private String buildSourcesStr(Map<String,String> sources) {
StringBuilder sourceStr = new StringBuilder();
for(Map.Entry<String,String> entry : sources.entrySet()) {
sourceStr.append(entry.getKey()).append(":").append(entry.getValue()).append("|");
}
return sourceStr.toString();
}
private Map<String, String> extractSources(String sourcesStr) {
Map<String,String> sources = new HashMap<>();
String[] sourceList = sourcesStr.split("\\|");
for(String sourceItem : sourceList){
if(!sourceItem.isEmpty()) {
String[] data = sourceItem.split(":");
if(data.length == 2)
sources.put(data[0], data[1]);
}
}
return sources;
}
} }

View File

@ -22,12 +22,6 @@ import android.content.Context;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.NetworkInfo; import android.net.NetworkInfo;
import android.os.Bundle; import android.os.Bundle;
import org.eu.exodus_privacy.exodusprivacy.listener.NetworkListener;
import org.eu.exodus_privacy.exodusprivacy.R;
import org.eu.exodus_privacy.exodusprivacy.objects.Application;
import org.eu.exodus_privacy.exodusprivacy.objects.Report;
import org.eu.exodus_privacy.exodusprivacy.objects.Tracker;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
@ -42,14 +36,27 @@ import java.text.DateFormat;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.Set;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.concurrent.Semaphore; import java.util.concurrent.Semaphore;
import org.eu.exodus_privacy.exodusprivacy.R;
import org.eu.exodus_privacy.exodusprivacy.listener.NetworkListener;
import org.eu.exodus_privacy.exodusprivacy.objects.Application;
import org.eu.exodus_privacy.exodusprivacy.objects.Report;
import org.eu.exodus_privacy.exodusprivacy.objects.Tracker;
/* /*
Singleton that handle all network connection Singleton that handle all network connection
*/ */
@ -195,8 +202,14 @@ public class NetworkManager {
JSONObject tracker = trackers.getJSONObject(trackerId); JSONObject tracker = trackers.getJSONObject(trackerId);
Tracker track = parseTracker(tracker,trackerId); Tracker track = parseTracker(tracker,trackerId);
trackersList.add(track); trackersList.add(track);
if (trackersList.size() == 20) {
DatabaseManager.getInstance(mes.context).insertOrUpdateTrackers(trackersList);
trackersList.clear();
}
} }
DatabaseManager.getInstance(mes.context).insertOrUpdateTrackers(trackersList); if(!trackersList.isEmpty())
DatabaseManager.getInstance(mes.context).insertOrUpdateTrackers(trackersList);
trackersList.clear();
} catch (JSONException e) { } catch (JSONException e) {
mes.listener.onError(mes.context.getString(R.string.json_error)); mes.listener.onError(mes.context.getString(R.string.json_error));
} }
@ -207,7 +220,7 @@ public class NetworkManager {
mes.listener.onProgress(R.string.get_reports_connection,0,0); mes.listener.onProgress(R.string.get_reports_connection,0,0);
URL url; URL url;
try { try {
url = new URL(apiUrl+"applications"); url = new URL(apiUrl+"applications?option=short");
} catch (Exception e){ } catch (Exception e){
e.printStackTrace(); e.printStackTrace();
return; return;
@ -218,45 +231,64 @@ public class NetworkManager {
if(object != null) { if(object != null) {
List<String> handles = new ArrayList<>(); Map<String,Map<String,String>> handles = new HashMap<>();
ArrayList<String> packages = mes.args.getStringArrayList("packages");
if (packages == null)
return;
try { try {
JSONArray applications = object.getJSONArray("applications"); JSONArray applications = object.getJSONArray("applications");
//manage handles map (handle,UAID)
for(int i = 0; i<applications.length(); i++) { for(int i = 0; i<applications.length(); i++) {
JSONObject app = applications.getJSONObject(i); JSONObject app = applications.getJSONObject(i);
String handle = app.getString("handle"); String handle = app.getString("handle");
handles.add(handle); String auid = app.getString("app_uid");
String source = app.getString("source");
Map<String,String> sources = handles.get(handle);
if(sources == null)
sources = new HashMap<>();
sources.put(source,auid);
if (packages.contains(handle))
handles.put(handle,sources);
} }
//remove app not analyzed by Exodus
packages.retainAll(handles.keySet());
// Add some random packages to avoid tracking
Random rand = new Random(Thread.currentThread().getId());
int alea = rand.nextInt(120) % 10 + 11;
for(int i = 0 ; i < alea; i++) {
int val = rand.nextInt(applications.length());
JSONObject app = applications.getJSONObject(val);
String handle = app.getString("handle");
handles.put(handle,new HashMap<>());
packages.add(handle);
}
//shuffle the list
Collections.shuffle(packages);
object.remove("applications");
} catch (JSONException e) { } catch (JSONException e) {
e.printStackTrace();
mes.listener.onError(mes.context.getString(R.string.json_error)); mes.listener.onError(mes.context.getString(R.string.json_error));
} }
getReports(mes,handles); object = null;
getReports(mes,handles,packages);
} }
mes.listener.onSuccess(); mes.listener.onSuccess();
} }
private void getReports(Message mes, List<String> handles) { private void getReports(Message mes, Map<String, Map<String,String>> handles, ArrayList<String> packages) {
ArrayList<String> packages = mes.args.getStringArrayList("packages");
if(packages == null)
return;
packages.retainAll(handles);
// Add some random packages to avoid tracking
Random rand = new Random(Thread.currentThread().getId());
int alea = rand.nextInt(120) % 10 + 11;
for(int i = 0 ; i < alea; i++) {
int val = rand.nextInt(handles.size());
packages.add(handles.get(val));
}
for(int i = 0; i < packages.size(); i++) { for(int i = 0; i < packages.size(); i++) {
mes.listener.onProgress(R.string.parse_application,i+1,packages.size()); mes.listener.onProgress(R.string.parse_application,i+1,packages.size());
getReport(mes,packages.get(i)); getReport(mes,packages.get(i),handles.get(packages.get(i)));
} }
} }
private void getReport(Message mes, String handle) { private void getReport(Message mes, String handle, Map<String,String> sources) {
URL url; URL url;
try { try {
url = new URL(apiUrl+"search/"+handle); url = new URL(apiUrl+"search/"+handle);
@ -272,6 +304,7 @@ public class NetworkManager {
ArrayList<String> packages = mes.args.getStringArrayList("packages"); ArrayList<String> packages = mes.args.getStringArrayList("packages");
if(packages != null && packages.contains(handle)) { if(packages != null && packages.contains(handle)) {
Application app = parseApplication(application, handle); Application app = parseApplication(application, handle);
app.sources = sources;
DatabaseManager.getInstance(mes.context).insertOrUpdateApplication(app); DatabaseManager.getInstance(mes.context).insertOrUpdateApplication(app);
} }
} catch (JSONException e) { } catch (JSONException e) {
@ -285,6 +318,7 @@ public class NetworkManager {
application.packageName = packageName; application.packageName = packageName;
application.creator = object.getString("creator"); application.creator = object.getString("creator");
application.name = object.getString("name"); application.name = object.getString("name");
//parse Report //parse Report
application.reports = new HashSet<>(); application.reports = new HashSet<>();
JSONArray reports = object.getJSONArray("reports"); JSONArray reports = object.getJSONArray("reports");
@ -300,6 +334,7 @@ public class NetworkManager {
report.id = object.getLong("id"); report.id = object.getLong("id");
report.downloads = object.getString("downloads"); report.downloads = object.getString("downloads");
report.version = object.getString("version"); report.version = object.getString("version");
report.source = object.getString("source");
if(!object.getString("version_code").isEmpty()) if(!object.getString("version_code").isEmpty())
report.versionCode = Long.parseLong(object.getString("version_code")); report.versionCode = Long.parseLong(object.getString("version_code"));
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault()); DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault());
@ -307,10 +342,12 @@ public class NetworkManager {
report.updateDate = Calendar.getInstance(); report.updateDate = Calendar.getInstance();
report.updateDate.setTimeZone(TimeZone.getTimeZone("UTC")); report.updateDate.setTimeZone(TimeZone.getTimeZone("UTC"));
report.updateDate.setTime(dateFormat.parse(object.getString("updated_at"))); report.updateDate.setTime(dateFormat.parse(object.getString("updated_at")));
report.updateDate.set(Calendar.MILLISECOND,0);
report.creationDate = Calendar.getInstance(); report.creationDate = Calendar.getInstance();
report.creationDate.setTimeZone(TimeZone.getTimeZone("UTC")); report.creationDate.setTimeZone(TimeZone.getTimeZone("UTC"));
report.creationDate.setTime(dateFormat.parse(object.getString("creation_date"))); report.creationDate.setTime(dateFormat.parse(object.getString("creation_date")));
report.creationDate.set(Calendar.MILLISECOND,0);
} catch (ParseException e) { } catch (ParseException e) {
e.printStackTrace(); e.printStackTrace();
} }

View File

@ -18,6 +18,8 @@
package org.eu.exodus_privacy.exodusprivacy.objects; package org.eu.exodus_privacy.exodusprivacy.objects;
import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
public class Application { public class Application {
@ -26,6 +28,7 @@ public class Application {
public String name; public String name;
public String creator; public String creator;
public Set<Report> reports; public Set<Report> reports;
public Map<String,String> sources;
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
@ -35,13 +38,13 @@ public class Application {
Application that = (Application) o; Application that = (Application) o;
if (id != that.id) return false; if (id != that.id) return false;
return packageName.equals(that.packageName); return packageName != null ? packageName.equals(that.packageName) : that.packageName == null;
} }
@Override @Override
public int hashCode() { public int hashCode() {
int result = (int) (id ^ (id >>> 32)); int result = (int) (id ^ (id >>> 32));
result = 31 * result + packageName.hashCode(); result = 31 * result + (packageName != null ? packageName.hashCode() : 0);
return result; return result;
} }
} }

View File

@ -1,8 +1,12 @@
package org.eu.exodus_privacy.exodusprivacy.objects; package org.eu.exodus_privacy.exodusprivacy.objects;
import android.graphics.drawable.Drawable;
public class Permission { public class Permission {
public String name; public String name;
public String fullName; public String fullName;
public String description; public String description;
public Drawable icon;
public boolean dangerous;
public boolean expanded = false; public boolean expanded = false;
} }

View File

@ -30,6 +30,7 @@ public class Report {
public long versionCode; public long versionCode;
public Set<Long> trackers; public Set<Long> trackers;
public long appId; public long appId;
public String source;
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {

View File

@ -0,0 +1,87 @@
package org.eu.exodus_privacy.exodusprivacy.objects;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.graphics.drawable.Drawable;
import android.os.Build;
import org.eu.exodus_privacy.exodusprivacy.R;
import org.eu.exodus_privacy.exodusprivacy.adapters.ApplicationViewModel;
import org.eu.exodus_privacy.exodusprivacy.manager.DatabaseManager;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class ReportDisplay {
public Report report;
public String packageName;
public String versionName;
public String displayName;
public String creator;
public long versionCode;
public Drawable logo;
public List<Permission> permissions;
public Set<Tracker> trackers;
public String source;
public String viewOnStore;
private ReportDisplay(){
}
public static ReportDisplay buildReportDisplay(Context context, ApplicationViewModel model, PackageManager manager, PackageInfo info) {
ReportDisplay reportDisplay = new ReportDisplay();
reportDisplay.packageName = model.packageName;
reportDisplay.versionName = model.versionName;
reportDisplay.versionCode = model.versionCode;
reportDisplay.displayName = model.label.toString();
reportDisplay.report = model.report;
reportDisplay.source = context.getString(R.string.source,model.source);
reportDisplay.viewOnStore = context.getString(model.source.equals("google")? R.string.view_on_google_play : R.string.view_on_fdroid);
reportDisplay.trackers = model.trackers;
if (reportDisplay.report != null)
reportDisplay.creator = DatabaseManager.getInstance(context).getCreator(reportDisplay.report.appId);
List<Permission> requestedPermissions= new ArrayList<>();
if (info.requestedPermissions != null) {
for(int i = 0; i < info.requestedPermissions.length; i++) {
Permission permission = new Permission();
permission.fullName = info.requestedPermissions[i];
try {
PermissionInfo permissionInfo = manager.getPermissionInfo(permission.fullName,PackageManager.GET_META_DATA);
if(permissionInfo.loadDescription(manager) != null)
permission.description = permissionInfo.loadDescription(manager).toString();
if(permissionInfo.loadLabel(manager) != null)
permission.name = permissionInfo.loadLabel(manager).toString();
permission.dangerous = permissionInfo.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS;
if(permission.fullName.equals(Manifest.permission.WRITE_SETTINGS) || permission.fullName.equals(Manifest.permission.SYSTEM_ALERT_WINDOW)) //Special permissions
permission.dangerous = true;
if (permissionInfo.group != null) {
PermissionGroupInfo permissionGroupInfo = manager.getPermissionGroupInfo(permissionInfo.group, PackageManager.GET_META_DATA);
if(permissionGroupInfo.loadIcon(manager) != null)
permission.icon = permissionGroupInfo.loadIcon(manager);
}
} catch (PackageManager.NameNotFoundException e) {
e.getLocalizedMessage();
}
requestedPermissions.add(permission);
}
}
reportDisplay.permissions = requestedPermissions;
reportDisplay.logo = model.icon;
return reportDisplay;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 448 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 492 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 744 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 967 B

View File

@ -0,0 +1,6 @@
<vector android:height="24dp" android:viewportHeight="16"
android:viewportWidth="4" android:width="6dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#E61718" android:fillType="evenOdd"
android:pathData="M0.18,1.5C0.18,0.6716 0.8516,0 1.68,0C2.5084,0 3.18,0.6716 3.18,1.5L3.18,8.39C3.18,9.2184 2.5084,9.89 1.68,9.89C0.8516,9.89 0.18,9.2184 0.18,8.39L0.18,1.5ZM1.72,15.01C0.7701,15.01 0,14.2399 0,13.29C0,12.3401 0.7701,11.57 1.72,11.57C2.6699,11.57 3.44,12.3401 3.44,13.29C3.44,14.2399 2.6699,15.01 1.72,15.01Z"
android:strokeColor="#00000000" android:strokeWidth="1"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector android:height="100dp" android:viewportHeight="54"
android:viewportWidth="54" android:width="100dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillAlpha="1" android:fillColor="#684971"
android:pathData="M2.1737,27a24.8263,24.5976 0,1 0,49.6526 0a24.8263,24.5976 0,1 0,-49.6526 0z"
android:strokeColor="#00000000" android:strokeWidth="0.0297731"/>
<path android:fillAlpha="1" android:fillColor="#ffffff"
android:pathData="m22.6353,26.1025c-4.3263,-1.5231 -6.4894,-3.8295 -6.4894,-6.9192 -0,-2.4369 1.1474,-4.3843 3.4423,-5.8421 2.2949,-1.4578 5.1443,-2.1867 8.5482,-2.1867 3.0964,0 5.556,0.5059 7.3788,1.5176 1.8227,1.0118 2.7341,2.2248 2.7341,3.6391 -0,0.7398 -0.2855,1.3871 -0.8565,1.9419 -0.571,0.5549 -1.2298,0.8323 -1.9765,0.8323 -1.2298,0 -2.24,-0.8486 -3.0306,-2.5457 -1.098,-2.3499 -2.7451,-3.5248 -4.9412,-3.5249 -1.7349,0 -3.1624,0.5657 -4.2823,1.6972 -1.12,1.1315 -1.68,2.7089 -1.68,4.7325 -0,3.9818 2.0753,5.9727 6.2259,5.9727 0.4392,0 0.9443,-0.0435 1.5153,-0.1305 0.9882,-0.1305 1.7568,-0.1958 2.3059,-0.1958 1.3396,0 2.0094,0.3808 2.0094,1.1423 -0,0.8486 -0.6808,1.2729 -2.0424,1.2729 -0.4832,0 -1.2079,-0.0761 -2.1741,-0.2285 -0.7247,-0.1305 -1.2847,-0.1958 -1.68,-0.1958 -4.3922,0 -6.5882,2.2194 -6.5882,6.6581 -0,2.1541 0.5819,3.8893 1.7459,5.2057 1.1639,1.3164 2.789,1.9746 4.8753,1.9746 2.6133,0 4.3482,-1.3381 5.2047,-4.0144 0.4392,-1.4143 0.9278,-2.3934 1.4659,-2.9374 0.538,-0.544 1.2572,-0.8159 2.1576,-0.8159 0.7466,0 1.4219,0.2665 2.0259,0.7996 0.6039,0.5331 0.9059,1.213 0.9059,2.0399 -0,1.98 -1.12,3.6173 -3.36,4.912 -2.24,1.2946 -4.9522,1.9419 -8.1365,1.9419 -3.4918,0 -6.5937,-0.8268 -9.3059,-2.4805 -2.7122,-1.6536 -4.0682,-3.8621 -4.0682,-6.6254 -0,-3.4596 2.6902,-6.0053 8.0706,-7.6372z"
android:strokeAlpha="1" android:strokeColor="#00000000"
android:strokeLineCap="butt" android:strokeLineJoin="miter" android:strokeWidth="0.03275258"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
</vector>

View File

@ -0,0 +1,4 @@
<vector android:height="24dp" android:viewportHeight="20"
android:viewportWidth="20" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFFFF" android:pathData="M15.95,10.78c0.03,-0.25 0.05,-0.51 0.05,-0.78s-0.02,-0.53 -0.06,-0.78l1.69,-1.32c0.15,-0.12 0.19,-0.34 0.1,-0.51l-1.6,-2.77c-0.1,-0.18 -0.31,-0.24 -0.49,-0.18l-1.99,0.8c-0.42,-0.32 -0.86,-0.58 -1.35,-0.78L12,2.34c-0.03,-0.2 -0.2,-0.34 -0.4,-0.34H8.4c-0.2,0 -0.36,0.14 -0.39,0.34l-0.3,2.12c-0.49,0.2 -0.94,0.47 -1.35,0.78l-1.99,-0.8c-0.18,-0.07 -0.39,0 -0.49,0.18l-1.6,2.77c-0.1,0.18 -0.06,0.39 0.1,0.51l1.69,1.32c-0.04,0.25 -0.07,0.52 -0.07,0.78s0.02,0.53 0.06,0.78L2.37,12.1c-0.15,0.12 -0.19,0.34 -0.1,0.51l1.6,2.77c0.1,0.18 0.31,0.24 0.49,0.18l1.99,-0.8c0.42,0.32 0.86,0.58 1.35,0.78l0.3,2.12c0.04,0.2 0.2,0.34 0.4,0.34h3.2c0.2,0 0.37,-0.14 0.39,-0.34l0.3,-2.12c0.49,-0.2 0.94,-0.47 1.35,-0.78l1.99,0.8c0.18,0.07 0.39,0 0.49,-0.18l1.6,-2.77c0.1,-0.18 0.06,-0.39 -0.1,-0.51l-1.67,-1.32zM10,13c-1.65,0 -3,-1.35 -3,-3s1.35,-3 3,-3 3,1.35 3,3 -1.35,3 -3,3z"/>
</vector>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/colorDarkOrange"/>
<corners android:radius="5dp"/>
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
</shape>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/colorGreen"/>
<corners android:radius="5dp"/>
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
</shape>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/colorLightBlue"/>
<corners android:radius="5dp"/>
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
</shape>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/colorLightRed"/>
<corners android:radius="5dp"/>
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
</shape>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/colorLightYellow"/>
<corners android:radius="5dp"/>
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
</shape>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/colorPrimary"/>
<corners android:radius="5dp"/>
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
</shape>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/colorRed"/>
<corners android:radius="5dp"/>
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
</shape>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/colorYellow"/>
<corners android:radius="5dp"/>
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
</shape>

View File

@ -10,6 +10,8 @@
<RelativeLayout <RelativeLayout
android:id="@+id/base_info" android:id="@+id/base_info"
android:layout_marginTop="10dp"
android:layout_marginBottom="5dp"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<ImageView <ImageView
@ -29,15 +31,44 @@
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<TextView <LinearLayout
android:id="@+id/app_tracker_nb" android:orientation="horizontal"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" /> android:layout_height="wrap_content">
<TextView <TextView
android:id="@+id/app_permission_nb" android:textAlignment="center"
android:background="@drawable/square_green"
android:id="@+id/app_tracker_nb"
android:layout_width="30dp"
android:layout_height="wrap_content" />
<TextView
android:layout_marginLeft="5dp"
android:layout_marginStart="5dp"
android:text="@string/trackers"
android:id="@+id/app_tracker"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" /> android:layout_height="wrap_content"
android:layout_marginTop="5dp"
>
<TextView
android:textAlignment="center"
android:background="@drawable/square_green"
android:id="@+id/app_permission_nb"
android:layout_width="30dp"
android:layout_height="wrap_content" />
<TextView
android:layout_marginLeft="5dp"
android:layout_marginStart="5dp"
android:text="@string/permissions"
android:id="@+id/app_permission"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout> </LinearLayout>
<TextView <TextView
android:layout_centerInParent="true" android:layout_centerInParent="true"
@ -52,6 +83,12 @@
</RelativeLayout> </RelativeLayout>
<TextView <TextView
android:layout_below="@+id/base_info" android:layout_below="@+id/base_info"
android:id="@+id/source"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<TextView
android:layout_below="@+id/source"
android:id="@+id/other_version" android:id="@+id/other_version"
android:text="@string/tested" android:text="@string/tested"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -1,76 +1,13 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout <layout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
tools:context="org.eu.exodus_privacy.exodusprivacy.MainActivity" tools:context="org.eu.exodus_privacy.exodusprivacy.MainActivity"
> >
<data/> <data/>
<android.support.constraint.ConstraintLayout <androidx.recyclerview.widget.RecyclerView
android:id="@+id/app_list"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
> android:scrollbars="vertical"/>
<LinearLayout
android:visibility="gone"
android:id="@+id/layout_progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_margin="10dp"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:layout_marginBottom="5dp"
android:id="@+id/status_progress"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ProgressBar
android:id="@+id/progress"
style="@android:style/Widget.ProgressBar.Horizontal"
android:progressDrawable="@drawable/progressbar"
android:layout_width="match_parent"
android:layout_height="5dp" />
</LinearLayout>
<TextView
android:id="@+id/no_package_manager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/no_package_manager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/no_app_found"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/no_app_found"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/layout_progress"
>
<android.support.v7.widget.RecyclerView
android:id="@+id/app_list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v4.widget.SwipeRefreshLayout>
</android.support.constraint.ConstraintLayout>
</layout> </layout>

View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="org.eu.exodus_privacy.exodusprivacy.MainActivity"
>
<data/>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<LinearLayout
android:visibility="gone"
android:id="@+id/layout_progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_margin="10dp"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:layout_marginBottom="5dp"
android:id="@+id/status_progress"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ProgressBar
android:id="@+id/progress"
style="@android:style/Widget.ProgressBar.Horizontal"
android:progressDrawable="@drawable/progressbar"
android:layout_width="match_parent"
android:layout_height="5dp"
android:layout_marginBottom="10dp"
/>
</LinearLayout>
<TextView
android:id="@+id/no_package_manager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/no_package_manager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/no_app_found"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/no_app_found"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/logo"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:src="@drawable/ic_logo_purple"
android:layout_width="200dp"
android:layout_height="200dp" />
<ProgressBar
android:id="@+id/retrieve_app"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/logo" />
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/layout_progress"
>
<FrameLayout
android:id="@+id/app_list_container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@ -8,12 +8,8 @@
> >
<data/> <data/>
<FrameLayout <FrameLayout
android:layout_marginRight="10dp"
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp" android:layout_marginStart="10dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:id="@+id/fragment_container" android:id="@+id/fragment_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"

View File

@ -1,19 +1,56 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout <layout xmlns:android="http://schemas.android.com/apk/res/android">
xmlns:android="http://schemas.android.com/apk/res/android"
>
<data/> <data/>
<LinearLayout <LinearLayout
android:id="@+id/main_layout" android:id="@+id/main_layout"
android:gravity="center"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
<LinearLayout android:layout_marginTop="10dp"
android:gravity="center" android:layout_marginBottom="10dp"
android:orientation="horizontal" >
<RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
>
<ImageView
android:id="@+id/icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_alignParentTop="true"
android:layout_alignParentStart="true"
/>
<ImageView
android:id="@+id/dangerous"
android:src="@drawable/ic_danger"
android:layout_width="4dp"
android:layout_height="18dp"
android:layout_toEndOf="@id/icon"
android:layout_marginTop="3dp"
android:layout_alignParentTop="true"
/>
<TextView
android:id="@+id/permission_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_toEndOf="@id/dangerous"
android:layout_toStartOf="@id/arrow"
android:layout_alignParentTop="true"
/>
<TextView
android:id="@+id/permission_short"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="5dp"
android:layout_toEndOf="@id/dangerous"
android:layout_toStartOf="@id/arrow"
android:layout_below="@id/permission_name"
android:textStyle="italic"
/>
<TextView <TextView
android:text="▼" android:text="▼"
android:id="@+id/arrow" android:id="@+id/arrow"
@ -21,20 +58,18 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:layout_marginRight="10dp"
android:layout_marginStart="5dp" android:layout_marginStart="5dp"
android:layout_marginLeft="5dp" android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
/> />
<TextView </RelativeLayout>
android:id="@+id/permission_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
<TextView <TextView
android:paddingLeft="20dp"
android:paddingStart="20dp"
android:id="@+id/permission_description" android:id="@+id/permission_description"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"/> android:layout_height="wrap_content"
android:layout_marginStart="66dp"
android:layout_marginEnd="20dp"
android:textStyle="italic"
/>
</LinearLayout> </LinearLayout>
</layout> </layout>

View File

@ -1,130 +1,444 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout <layout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
> >
<data/> <data>
<ScrollView <import type="androidx.core.content.ContextCompat"/>
<import type="android.view.View"/>
<variable
name="reportInfo"
type="org.eu.exodus_privacy.exodusprivacy.ReportViewModel" />
</data>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent" <androidx.constraintlayout.widget.ConstraintLayout
android:layout_height="wrap_content">
<LinearLayout
android:orientation="horizontal"
android:id="@+id/base_info"
android:layout_alignParentTop="true"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="match_parent">
<ImageView <ImageView
android:id="@+id/logo" android:id="@+id/logo"
android:layout_width="100dp" android:layout_width="100dp"
android:layout_height="100dp" android:layout_height="100dp"
/> android:layout_marginTop="20dp"
android:padding="10dp"
android:src="@{reportInfo.logo}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<LinearLayout <TextView
android:layout_marginLeft="2dp" android:id="@+id/name"
android:layout_marginStart="2dp" android:layout_width="match_parent"
android:orientation="vertical" android:layout_height="0dp"
android:text="@{reportInfo.name}"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
app:layout_constraintTop_toBottomOf="@id/logo"
app:layout_constraintStart_toStartOf="parent"
android:textSize="30sp"
android:textColor="@color/colorPurple"
android:textStyle="bold"
android:textAlignment="center"/>
<View
android:id="@+id/dummy"
android:layout_marginTop="15dp"
app:layout_constraintTop_toBottomOf="@id/name"
android:layout_width="match_parent"
android:layout_height="0dp"/>
<TextView
android:id="@+id/trackers_nb"
android:layout_width="40dp"
android:layout_height="0dp"
android:text="@{reportInfo.trackerNumberStr}"
android:layout_marginStart="20dp"
android:layout_marginTop="15dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/dummy"
android:background="@{ContextCompat.getDrawable(context,reportInfo.trackerColor)}"
android:visibility="@{reportInfo.trackerVisibility ? View.VISIBLE : View.GONE}"
android:textColor="@color/textColorWhite"
android:textStyle="bold"
android:textAlignment="center"
android:textSize="22sp"/>
<TextView
android:id="@+id/trackers_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/trackers"
android:visibility="@{reportInfo.trackerVisibility ? View.VISIBLE : View.GONE}"
android:layout_marginStart="10dp"
android:layout_marginEnd="20dp"
app:layout_constraintTop_toTopOf="@id/trackers_nb"
app:layout_constraintStart_toEndOf="@id/trackers_nb"
app:layout_constraintEnd_toEndOf="parent"
android:textStyle="bold"
android:textSize="22sp"
android:textColor="@color/textColorDark"/>
<TextView
android:id="@+id/permissions_nb"
android:layout_width="40dp"
android:layout_height="0dp"
android:text="@{reportInfo.permissionNumberStr}"
android:layout_marginStart="20dp"
android:layout_marginTop="15dp"
app:layout_constraintTop_toBottomOf="@id/trackers_nb"
app:layout_constraintStart_toStartOf="parent"
android:background="@{ContextCompat.getDrawable(context,reportInfo.permissionColor)}"
android:visibility="@{reportInfo.permissionVisibility ? View.VISIBLE : View.GONE}"
android:textColor="@color/textColorWhite"
android:textStyle="bold"
android:textAlignment="center"
android:textSize="22sp"/>
<TextView
android:id="@+id/permissions_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/permissions"
android:visibility="@{reportInfo.permissionVisibility ? View.VISIBLE : View.GONE}"
android:layout_marginStart="10dp"
android:layout_marginEnd="20dp"
android:layout_marginTop="15dp"
app:layout_constraintTop_toBottomOf="@id/trackers_nb"
app:layout_constraintStart_toEndOf="@id/permissions_nb"
app:layout_constraintEnd_toEndOf="parent"
android:textStyle="bold"
android:textSize="22sp"
android:textColor="@color/textColorDark"/>
<TextView
android:id="@+id/installed_version"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/installed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/permissions_nb"
android:layout_marginTop="30dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:textSize="16sp"
android:textColor="@color/textColorDark"
/>
<TextView
android:id="@+id/installed_version_value"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@{reportInfo.installedVersion}"
app:layout_constraintStart_toEndOf="@id/installed_version"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/permissions_nb"
android:layout_marginTop="30dp"
android:layout_marginStart="5dp"
android:layout_marginEnd="20dp"
android:textSize="16sp"
android:textStyle="bold"
android:textColor="@color/textColorDark"/>
<TextView
android:id="@+id/report_version"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/report_version"
android:visibility="@{reportInfo.reportVersionVisibility ? View.VISIBLE : View.GONE}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/installed_version_value"
android:layout_marginTop="5dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:textSize="16sp"
android:textColor="@color/textColorDark"/>
<TextView
android:id="@+id/report_version_value"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@{reportInfo.reportVersion}"
android:visibility="@{reportInfo.reportVersionVisibility ? View.VISIBLE : View.GONE}"
app:layout_constraintStart_toEndOf="@id/report_version"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/installed_version_value"
android:layout_marginTop="5dp"
android:layout_marginStart="5dp"
android:layout_marginEnd="20dp"
android:textSize="16sp"
android:textStyle="bold"
android:textColor="@color/textColorDark"/>
<TextView
android:id="@+id/source"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@{reportInfo.source}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/report_version_value"
android:layout_marginTop="5dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:textSize="16sp"
android:textColor="@color/textColorDark"/>
<TextView
android:id="@+id/creator"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/created_by"
android:visibility="@{reportInfo.creatorVisibility ? View.VISIBLE : View.GONE}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/source"
android:layout_marginTop="5dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:textSize="16sp"
android:textColor="@color/textColorDark"/>
<TextView
android:id="@+id/creator_value"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:visibility="@{reportInfo.creatorVisibility ? View.VISIBLE : View.GONE}"
app:layout_constraintStart_toEndOf="@id/creator"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/source"
android:layout_marginTop="5dp"
android:layout_marginStart="5dp"
android:layout_marginEnd="20dp"
android:textSize="16sp"
android:textColor="@color/textColorDark"/>
<TextView
android:id="@+id/report_date"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:visibility="@{reportInfo.reportVisibility ? View.VISIBLE : View.GONE}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/creator_value"
android:layout_marginTop="5dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:textSize="16sp"
android:textColor="@color/textColorDarkLight"/>
<TextView
android:id="@+id/report_url"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
> android:autoLink="web"
<TextView android:text="@string/view_on_exodus"
android:id="@+id/name" android:visibility="@{reportInfo.reportVisibility ? View.VISIBLE : View.GONE}"
android:text="test" app:layout_constraintEnd_toEndOf="parent"
android:textAppearance="@style/TextAppearance.AppCompat.Large" android:textAlignment="textEnd"
android:layout_width="match_parent" app:layout_constraintTop_toBottomOf="@id/report_date"
android:layout_height="wrap_content" /> android:layout_marginTop="5dp"
<TextView android:layout_marginStart="20dp"
android:text="Creator" android:layout_marginEnd="20dp"
android:id="@+id/creator" android:textSize="16sp"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textColor="@color/colorPurple"/>
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:text="installed version"
android:id="@+id/installed_version"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:text="Report version"
android:id="@+id/report_version"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/report_url"
android:autoLink="web"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/analysed"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/analysed"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_below="@id/base_info"
android:layout_alignParentBottom="true"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<LinearLayout <TextView
android:id="@+id/tracker_layout" android:id="@+id/view_store"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:layout_weight="50" android:autoLink="web"
android:orientation="vertical"> android:text="@{reportInfo.viewOnStore}"
android:textAlignment="textEnd"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/report_url"
android:layout_marginTop="5dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:textSize="16sp"
android:textColor="@color/colorPurple"/>
<TextView <TextView
android:id="@+id/trackers_title" android:id="@+id/trackers_nb_list"
android:layout_width="match_parent" android:layout_width="40dp"
android:layout_height="wrap_content" android:layout_height="0dp"
android:background="@color/colorPrimary" android:text="@{reportInfo.trackerNumberStr}"
android:text="@string/trackers" android:layout_marginStart="20dp"
android:textAppearance="@style/TextAppearance.AppCompat.Large" android:layout_marginTop="30dp"
android:textColor="@android:color/white" /> app:layout_constraintTop_toBottomOf="@id/analysed"
app:layout_constraintStart_toStartOf="parent"
android:background="@{ContextCompat.getDrawable(context,reportInfo.trackerColor)}"
android:visibility="@{reportInfo.trackerVisibility ? View.VISIBLE : View.GONE}"
android:textStyle="bold"
android:textAlignment="center"
android:textSize="22sp"
android:textColor="@color/textColorWhite"
/>
<android.support.v7.widget.RecyclerView <TextView
android:id="@+id/trackers" android:id="@+id/trackers_title_list"
android:layout_marginTop="5dp" android:layout_width="0dp"
android:layout_marginBottom="5dp" android:layout_height="wrap_content"
android:layout_width="match_parent" android:text="@string/trackers"
android:layout_height="wrap_content" /> android:visibility="@{reportInfo.trackerVisibility ? View.VISIBLE : View.GONE}"
</LinearLayout> android:layout_marginStart="10dp"
android:layout_marginEnd="20dp"
android:layout_marginTop="30dp"
app:layout_constraintTop_toBottomOf="@id/analysed"
app:layout_constraintStart_toEndOf="@id/trackers_nb_list"
app:layout_constraintEnd_toEndOf="parent"
android:textStyle="bold"
android:textSize="22sp"
android:textColor="@color/textColorDark"/>
<LinearLayout <TextView
android:orientation="vertical" android:id="@+id/code_signature"
android:layout_weight="50" android:layout_width="0dp"
android:layout_height="wrap_content"
android:visibility="@{reportInfo.trackerVisibility ? View.VISIBLE : View.GONE}"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
app:layout_constraintTop_toBottomOf="@id/trackers_nb_list"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/trackers"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="wrap_content"
<TextView android:visibility="@{reportInfo.trackerVisibility ? View.VISIBLE : View.GONE}"
android:background="@color/colorPrimary" android:layout_marginTop="5dp"
android:id="@+id/permissions_title" android:layout_marginBottom="5dp"
android:text="@string/permissions" android:layout_marginStart="20dp"
android:textAppearance="@style/TextAppearance.AppCompat.Large" android:layout_marginEnd="20dp"
android:layout_width="match_parent" app:layout_constraintTop_toBottomOf="@id/code_signature"
android:layout_height="wrap_content" app:layout_constraintStart_toStartOf="parent"
android:textColor="@android:color/white" app:layout_constraintEnd_toEndOf="parent"
/> />
<android.support.v7.widget.RecyclerView <TextView
android:id="@+id/permissions" android:id="@+id/tracker_explanation"
android:layout_marginTop="5dp" android:layout_width="0dp"
android:layout_marginBottom="5dp" android:layout_height="wrap_content"
android:layout_width="match_parent" android:visibility="@{reportInfo.trackerVisibility ? View.VISIBLE : View.GONE}"
android:layout_height="wrap_content" android:text="@string/tracker_infos"
/> android:layout_marginTop="5dp"
</LinearLayout> android:layout_marginBottom="5dp"
</LinearLayout> android:layout_marginStart="20dp"
</RelativeLayout> android:layout_marginEnd="20dp"
</ScrollView> app:layout_constraintTop_toBottomOf="@id/trackers"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
/>
<TextView
android:id="@+id/permissions_nb_list"
android:layout_width="40dp"
android:layout_height="0dp"
android:text="@{reportInfo.permissionNumberStr}"
android:visibility="@{reportInfo.permissionVisibility ? View.VISIBLE : View.GONE}"
android:layout_marginStart="20dp"
android:layout_marginTop="30dp"
app:layout_constraintTop_toBottomOf="@id/tracker_explanation"
app:layout_constraintStart_toStartOf="parent"
android:background="@{ContextCompat.getDrawable(context,reportInfo.permissionColor)}"
android:textStyle="bold"
android:textAlignment="center"
android:textSize="22sp"
android:textColor="@color/textColorWhite"/>
<TextView
android:id="@+id/permissions_title_list"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/permissions"
android:visibility="@{reportInfo.permissionVisibility ? View.VISIBLE : View.GONE}"
android:layout_marginStart="10dp"
android:layout_marginEnd="20dp"
android:layout_marginTop="30dp"
app:layout_constraintTop_toBottomOf="@id/tracker_explanation"
app:layout_constraintStart_toEndOf="@id/permissions_nb_list"
app:layout_constraintEnd_toEndOf="parent"
android:textStyle="bold"
android:textSize="22sp"
android:textColor="@color/textColorDark"/>
<TextView
android:id="@+id/code_permission"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:visibility="@{reportInfo.permissionVisibility ? View.VISIBLE : View.GONE}"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
app:layout_constraintTop_toBottomOf="@id/permissions_nb_list"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/permissions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:visibility="@{reportInfo.permissionVisibility ? View.VISIBLE : View.GONE}"
app:layout_constraintBottom_toTopOf="@id/permission_explanation_dangerous"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/code_permission" />
<TextView
android:id="@+id/permission_explanation_dangerous"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:visibility="@{reportInfo.hasPermissionDangerous ? View.VISIBLE : View.GONE}"
android:text="@string/permission_infos_dangerous"
android:layout_marginTop="5dp"
android:layout_marginBottom="10dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
app:layout_constraintBottom_toTopOf="@id/permission_explanation"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
/>
<TextView
android:id="@+id/permission_explanation"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:visibility="@{reportInfo.permissionVisibility ? View.VISIBLE : View.GONE}"
android:text="@string/permission_infos"
android:layout_marginTop="5dp"
android:layout_marginBottom="10dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
/>
<TextView
android:id="@+id/analysed"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/analysed"
android:visibility="@{reportInfo.trackerVisibility ? View.GONE : View.VISIBLE}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/view_store"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:layout_marginTop="5dp"
android:textAlignment="center"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</layout> </layout>

View File

@ -0,0 +1,213 @@
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
>
<data>
</data>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:textSize="30sp"
android:textColor="@color/colorPurple"
android:textStyle="bold"
android:textAlignment="center"/>
<TextView
android:id="@+id/tracker_presence"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="15dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/name"
android:textStyle="bold"
android:textSize="14sp"
android:textColor="@color/textColorDark"/>
<TextView
android:id="@+id/tracker_presence_nb"
android:layout_width="40dp"
android:layout_height="0dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="20dp"
app:layout_constraintTop_toTopOf="@id/tracker_presence"
app:layout_constraintStart_toEndOf="@id/tracker_presence"
android:textColor="@color/textColorDark"
android:textStyle="bold"
android:textAlignment="center"
android:textSize="14sp"/>
<TextView
android:id="@+id/dummy"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="@id/tracker_presence"
app:layout_constraintStart_toEndOf="@id/tracker_presence_nb"
app:layout_constraintEnd_toEndOf="parent"/>
<TextView
android:id="@+id/tracker_url"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:text="@string/tracker_web_page"
app:layout_constraintEnd_toEndOf="parent"
android:textAlignment="textEnd"
app:layout_constraintTop_toBottomOf="@id/tracker_presence"
android:layout_marginTop="5dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:textSize="16sp"
android:textColor="@color/colorPurple"/>
<TextView
android:id="@+id/description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:layout_marginBottom="5dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
app:layout_constraintTop_toBottomOf="@id/tracker_url"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
/>
<TextView
android:id="@+id/detection_rules"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/detection_rules"
android:layout_marginStart="10dp"
android:layout_marginEnd="20dp"
android:layout_marginTop="30dp"
app:layout_constraintTop_toBottomOf="@id/description"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:textStyle="bold"
android:textSize="22sp"
android:textColor="@color/textColorDark"/>
<TextView
android:id="@+id/code_detection_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/code_signature"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
app:layout_constraintTop_toBottomOf="@id/detection_rules"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
/>
<TextView
android:id="@+id/code_detection"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
app:layout_constraintTop_toBottomOf="@id/code_detection_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:textColor="@color/textDetection"
/>
<TextView
android:id="@+id/network_detection_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/network_signature"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
app:layout_constraintTop_toBottomOf="@id/code_detection"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
/>
<TextView
android:id="@+id/network_detection"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
app:layout_constraintTop_toBottomOf="@id/network_detection_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:textColor="@color/textDetection"
/>
<TextView
android:id="@+id/tracker_presence_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginEnd="20dp"
android:layout_marginTop="30dp"
app:layout_constraintTop_toBottomOf="@id/network_detection"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:textStyle="bold"
android:textSize="22sp"
android:textColor="@color/textColorDark"/>
<TextView
android:id="@+id/no_app_found"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/no_app_found_tracker"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tracker_presence_title"
/>
<ProgressBar
android:id="@+id/retrieve_app"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tracker_presence_title"/>
<FrameLayout
android:id="@+id/applications"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tracker_presence_title" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</layout>

View File

@ -3,25 +3,11 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
> >
<data/> <data/>
<LinearLayout <TextView
android:gravity="center" android:id="@+id/tracker_name"
android:orientation="horizontal" android:layout_marginTop="5dp"
android:layout_width="match_parent" android:layout_marginBottom="5dp"
android:layout_height="wrap_content"> android:textColor="@color/textColorDark"
<TextView android:layout_width="wrap_content"
android:text="●" android:layout_height="wrap_content"/>
android:textColor="@android:color/black"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:layout_marginRight="10dp"
android:layout_marginStart="5dp"
android:layout_marginLeft="5dp"
/>
<TextView
android:id="@+id/tracker_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</layout> </layout>

View File

@ -5,6 +5,11 @@
android:id="@+id/action_filter" android:id="@+id/action_filter"
android:icon="@drawable/ic_search" android:icon="@drawable/ic_search"
android:title="@string/menu_action_filter" android:title="@string/menu_action_filter"
app:actionViewClass="android.support.v7.widget.SearchView" app:actionViewClass="android.widget.SearchView"
app:showAsAction="ifRoom|collapseActionView" /> app:showAsAction="ifRoom|collapseActionView" />
</menu> <item
android:id="@+id/action_settings"
android:icon="@drawable/ic_settings"
android:title="@string/menu_action_settings"
app:showAsAction="ifRoom" />
</menu>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="108dp"
android:layout_height="108dp">
<background android:drawable="@mipmap/ic_launcher_background" />
<foreground>
<inset
android:drawable="@mipmap/ic_launcher_foreground"
android:inset="16%" />
</foreground>
</adaptive-icon>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size
android:width="108dp"
android:height="108dp" />
<solid android:color="#684971" />
</shape>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportHeight="54"
android:viewportWidth="54">
<path
android:fillColor="#fff"
android:pathData="m22.6353,26.1025c-4.3263,-1.5231 -6.4894,-3.8295 -6.4894,-6.9192 -0,-2.4369 1.1474,-4.3843 3.4423,-5.8421 2.2949,-1.4578 5.1443,-2.1867 8.5482,-2.1867 3.0964,0 5.556,0.5059 7.3788,1.5176 1.8227,1.0118 2.7341,2.2248 2.7341,3.6391 -0,0.7398 -0.2855,1.3871 -0.8565,1.9419 -0.571,0.5549 -1.2298,0.8323 -1.9765,0.8323 -1.2298,0 -2.24,-0.8486 -3.0306,-2.5457 -1.098,-2.3499 -2.7451,-3.5248 -4.9412,-3.5249 -1.7349,0 -3.1624,0.5657 -4.2823,1.6972 -1.12,1.1315 -1.68,2.7089 -1.68,4.7325 -0,3.9818 2.0753,5.9727 6.2259,5.9727 0.4392,0 0.9443,-0.0435 1.5153,-0.1305 0.9882,-0.1305 1.7568,-0.1958 2.3059,-0.1958 1.3396,0 2.0094,0.3808 2.0094,1.1423 -0,0.8486 -0.6808,1.2729 -2.0424,1.2729 -0.4832,0 -1.2079,-0.0761 -2.1741,-0.2285 -0.7247,-0.1305 -1.2847,-0.1958 -1.68,-0.1958 -4.3922,0 -6.5882,2.2194 -6.5882,6.6581 -0,2.1541 0.5819,3.8893 1.7459,5.2057 1.1639,1.3164 2.789,1.9746 4.8753,1.9746 2.6133,0 4.3482,-1.3381 5.2047,-4.0144 0.4392,-1.4143 0.9278,-2.3934 1.4659,-2.9374 0.538,-0.544 1.2572,-0.8159 2.1576,-0.8159 0.7466,0 1.4219,0.2665 2.0259,0.7996 0.6039,0.5331 0.9059,1.213 0.9059,2.0399 -0,1.98 -1.12,3.6173 -3.36,4.912 -2.24,1.2946 -4.9522,1.9419 -8.1365,1.9419 -3.4918,0 -6.5937,-0.8268 -9.3059,-2.4805 -2.7122,-1.6536 -4.0682,-3.8621 -4.0682,-6.6254 -0,-3.4596 2.6902,-6.0053 8.0706,-7.6372z" />
</vector>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="108dp"
android:layout_height="108dp">
<background android:drawable="@mipmap/ic_launcher_background" />
<foreground>
<inset
android:drawable="@mipmap/ic_launcher_foreground"
android:inset="16%" />
</foreground>
</adaptive-icon>

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_logo">شعار التطبيق</string>
<string name="trackers_pct">المتعقِّبون:</string>
<string name="permissions_pct">التصريحات:</string>
<string name="trackers">المتعقِّبون</string>
<string name="permissions">التصريحات</string>
<string name="network_error">الشبكة غير متوفرة</string>
<string name="not_connected">لا يوجد اتصال بشبكة الانترنت</string>
<string name="json_error">خطأ JSON</string>
<string name="installed">النسخة المثبتة:</string>
<string name="report_version">النسخة المُجرَّبة:</string>
<string name="no_trackers">لم نعثر على أي متعقِّب قمنا بتجريبه</string>
<string name="no_permissions">هذا التطبيق لا يتطلب أية تصريحات</string>
<string name="tested">%1$s%2$sليس هناك أي تقرير عن النسخة المثبتة، إنّ البيانات المعروضة مستمدة مِن نسخة أخرى قديمة لنفس التطبيق</string>
<string name="analysed">لم يتم بعد فحص هذا التطبيق بواسطة Exodus Privacy.</string>
<string name="no_package_manager">يبدو أنّ نظام جهازك لا يسمح بالوصول إلى قائمة التطبيقات المثبة.</string>
<string name="no_app_found">يبدو أنه ليس لديك أي تطبيقات مثبتة من المصدر الذي نقوم باختباره (متجر Google Play أو F-Droid).</string>
<string name="get_reports_connection">جلب التطبيقات: في انتظار الاتصال بالخادم</string>
<string name="get_reports">جارٍ جلب التطبيقات</string>
<string name="parse_application">فحص التطبيقات:</string>
<string name="parse_trackers">فحص المتعقّبون:</string>
<string name="get_trackers_connection">جلب المتعقّبين: في انتظار الاتصال بالخادم</string>
<string name="get_trackers">جارٍ جلب المُتعقّبين</string>
<string name="no_settings">ليس لدى هذا التطبيق إعدادات</string>
<string name="logo">شعار تطبيق %1</string>
<string name="created_by">أنشأه</string>
<string name="created_date">تم إنشاء التقرير في</string>
<string name="and_updated">وتحديثه في</string>
<string name="view_on_exodus">رؤيته على εxodus ←</string>
<string name="view_on_google_play">رؤيته على متجر غوغل للتطبيقات ←</string>
<string name="view_on_fdroid">رؤيته على F-Droid ←</string>
<string name="downloads">تنزيلات</string>
<string name="code_signature_found">لقد وجدنا توقيع الرمز البرمجي للمتعقبين التاليين في التطبيق:</string>
<string name="code_signature_not_found">لم نجد توقيع الرمز البرمجي لأي متتبع نعرفه في التطبيق.</string>
<string name="code_permission_found">لقد وجدنا التصريحات التالية في التطبيق:</string>
<string name="code_permission_not_found">هذا التطبيق لا يطلب صلاحيات.</string>
<string name="tracker_infos">المتعقب هو جزء من برنامج يهدف إلى جمع البيانات عنك أو عن استخداماتك. <a href="https://reports.exodus-privacy.eu.org/en/info/trackers/">تعرف على المزيد…</a></string>
<string name="permission_infos">التصريحات هي إجراءات يمكن أن يقوم بها التطبيق على هاتفك. <a href="https://reports.exodus-privacy.eu.org/en/info/permissions/">اعرف المزيد…</a></string>
<string name="permission_infos_dangerous">تشير الأيقونة <font color="red"><b>!</b></font> إلى درجات \'خطير\' أو \'خاص\' حسب تصنيف <a href="https://developer.android.com/guide/topics/permissions/overview">درجات الأمان لدى غوغل.</a></string>
<string name="tracker_web_page">صفحة الويب للمتعقب ←</string>
<string name="detection_rules">قواعد الكشف</string>
<string name="code_signature">قاعدة كشف الرمز:</string>
<string name="network_signature">القاعدة الشبكية للإكتشاف:</string>
<string name="tracker_presence">موجود في %d من تطبيقاتك</string>
<string name="tracker_presence_in">موجود في:</string>
<string name="no_app_found_tracker">يبدو أن هذا المتعقب غير موجود في تطبيقاتك</string>
<string name="source">المصدر: %s</string>
<!-- Menu -->
<string name="menu_action_filter">عامل التصفية</string>
<string name="menu_action_settings">إعدادات التطبيق</string>
</resources>

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_logo">Logo de l\'aplicació</string>
<string name="trackers_pct">Rastrejadors:</string>
<string name="permissions_pct">Permisos:</string>
<string name="trackers">Rastrejadors</string>
<string name="permissions">Permisos</string>
<string name="network_error">Xarxa no disponible</string>
<string name="not_connected">No hi ha connexió a Internet</string>
<string name="json_error">Error JSON</string>
<string name="installed">Versió instal·lada:</string>
<string name="report_version">Versió que s\'ha provat:</string>
<string name="no_trackers">L\'aplicació podria contenir rastrejadors que encara no ho coneixem.</string>
<string name="no_permissions">Aquesta aplicació no requereix cap permís.</string>
<string name="tested">No hi ha cap informe per a la versió instal·lada (%1$s), la informació mostrada es basa en un altre versió (%2$s)</string>
<string name="analysed">Aquesta aplicació no ha estat analitzada encara per Exodus Privacy.</string>
<string name="no_package_manager">Sembla que el sistema no permet laccés a la llista daplicacions instal·lades.</string>
<string name="no_app_found">Sembla que no teniu cap aplicació instal·lada des de la font que provem (Google Play Store o F-Droid).</string>
<string name="get_reports_connection">S\'estan obtenint les aplicacions: s\'està esperant la connexió del servidor </string>
<string name="get_reports">S\'estan obtenint les aplicacions</string>
<string name="parse_application">S\'estan processant les aplicacions:</string>
<string name="parse_trackers">S\'estan processant els rastrejadors:</string>
<string name="get_trackers_connection">S\'estan obtenint els rastrejadors: s\'està esperant la connexió del servidor</string>
<string name="get_trackers">S\'estan obtenint els rastrejadors</string>
<string name="no_settings">Aquesta aplicació no té configuració</string>
<string name="logo">Logo de l\'aplicació %1</string>
<string name="created_by">Creat per</string>
<string name="created_date">S\'ha creat aquest informe el </string>
<string name="and_updated">i actualitzat el</string>
<string name="view_on_exodus">Vegueu al εxodus ➤</string>
<string name="view_on_google_play">Vegueu al Google Play ➤</string>
<string name="view_on_fdroid">Vegeu a F-Droid ➤</string>
<string name="downloads">baixades</string>
<string name="code_signature_found">Hem trobat la signatura del codi dels següents rastrejadors a l\'aplicació:</string>
<string name="code_signature_not_found">No hem trobat cap signatura de codi de cap rastrejador que coneguem a l\'aplicació.</string>
<string name="code_permission_found">Hem trobat els permisos següents a l\'aplicació:</string>
<string name="code_permission_not_found">Aquesta aplicació no requereix cap permís.</string>
<string name="tracker_infos">Un rastrejador és una peça de programari destinat a recollir dades sobre tu o el teu comportament. <a href="https://reports.exodus-privacy.eu.org/en/info/trackers/">Més informació…</a></string>
<string name="permission_infos">Els permisos són accions que l\'aplicació pot fer al telèfon. <a href="https://reports.exodus-privacy.eu.org/en/info/permissions/">Més informació…</a></string>
<string name="permission_infos_dangerous">La icona <font color="red"><b>!</b></font> indica nivell «perillós» o «especial» segons <a href="https://developer.android.com/guide/topics/permissions/overview">els nivells de protecció de Google.</a></string>
<string name="tracker_web_page">Pàgina web del rastrejador ➤</string>
<string name="detection_rules">Regles de detecció</string>
<string name="code_signature">Regla de detecció de codi:</string>
<string name="network_signature">Regla de detecció de xarxa:</string>
<string name="tracker_presence">Present en %d de les vostres aplicacions</string>
<string name="tracker_presence_in">Present en:</string>
<string name="no_app_found_tracker">Aquest rastrejador no sembla estar present en les vostres aplicacions</string>
<string name="source">Font: %s</string>
<!-- Menu -->
<string name="menu_action_filter">Filtre</string>
<string name="menu_action_settings">Paràmetres de l\'aplicació</string>
</resources>

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_logo">App Logo</string>
<string name="trackers_pct">Tracker:</string>
<string name="permissions_pct">Berechtigungen:</string>
<string name="trackers">Tracker</string>
<string name="permissions">Berechtigungen</string>
<string name="network_error">Netzwerk nicht verfügbar</string>
<string name="not_connected">Keine Verbindung</string>
<string name="json_error">JSON Fehler</string>
<string name="installed">Installierte Version: </string>
<string name="report_version">Getestete Version: </string>
<string name="no_trackers">Diese Anwendung könnte Tracker, die uns unbekannt sind, enthalten.</string>
<string name="no_permissions">Diese App braucht keine Berechtigungen</string>
<string name="tested">Es gibt keinen Bericht für die installierte Version (%1$s), die gezeigte Informationen basieren auf einer anderen Version (%2$s)</string>
<string name="analysed">Diese App wurde von Exodus Privacy noch nicht analysiert.</string>
<string name="no_package_manager">Ihr System scheint den Zugriff auf die Appliste nicht zu erlauben.</string>
<string name="no_app_found">Es scheint, dass keine App aus den Quellen, die wir prüfen (Google Play Store und F-Droid), installiert ist.</string>
<string name="get_reports_connection">App erhalten: Warte auf Antwort des Servers</string>
<string name="get_reports">App erhalten</string>
<string name="parse_application">Untersuchung der App:</string>
<string name="parse_trackers">Untersuchung der Tracker:</string>
<string name="get_trackers_connection">Tracker erhalten: Warte auf Antwort des Servers</string>
<string name="get_trackers">Tracker erhalten</string>
<string name="no_settings">Diese App hat keine Einstellungen</string>
<string name="logo">%1 Anwendungslogo</string>
<string name="created_by">Erstellt von</string>
<string name="created_date">Bericht erstellt am</string>
<string name="and_updated">und zuletzt aktualisiert am</string>
<string name="view_on_exodus">Siehe auf εxodus ➤</string>
<string name="view_on_google_play">Siehe auf Google Play ➤</string>
<string name="view_on_fdroid">Siehe auf F-Droid ➤</string>
<string name="downloads">downloads</string>
<string name="code_signature_found">Wir haben die Code-Signatur der folgenden Tracker in der Anwendung gefunden:</string>
<string name="code_signature_not_found">Wir haben keine Code-Signatur irgendeines Trackers in der App gefunden.</string>
<string name="code_permission_found">Wir haben die folgenden Berechtigungen in der Anwendung gefunden:</string>
<string name="code_permission_not_found">Diese App erfordert keine Berechtigungen.</string>
<string name="tracker_infos">Ein Tracker ist eine Software, die Daten über Sie oder Ihre Nutzung erfassen soll. <a href="https://reports.exodus-privacy.eu.org/en/info/trackers/">Erfahre mehr…</a></string>
<string name="permission_infos">Berechtigungen sind Aktionen, die die Anwendung auf Ihrem Telefon ausführen kann. <a href="https://reports.exodus-privacy.eu.org/en/info/permissions/">Mehr erfahren…</a></string>
<string name="permission_infos_dangerous">Das Symbol <font color="red"><b>!</b></font> zeigt eine \'Gefährliche\' oder \'Spezial\' Stufe nach <a href="https://developer.android.com/guide/topics/permissions/overview">Googles Schutzstufen an.</a></string>
<string name="tracker_web_page">Tracker-Webseite ➤</string>
<string name="detection_rules">Erkennungsregeln</string>
<string name="code_signature">Code-Erkennungsregel:</string>
<string name="network_signature">Netzwerkerkennungsregel:</string>
<string name="tracker_presence">Vorhanden in %d der installierten Apps</string>
<string name="tracker_presence_in">Vorhanden in:</string>
<string name="no_app_found_tracker">Dieser Tracker scheint in den installierten Apps nicht vorhanden zu sein</string>
<string name="source">Quelle: %s</string>
<!-- Menu -->
<string name="menu_action_filter">Filter</string>
<string name="menu_action_settings">Anwendungs-Einstellungen</string>
</resources>

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_logo">Λογότυπο Εφαρμογής</string>
<string name="trackers_pct">Trackers:</string>
<string name="permissions_pct">Άδειες:</string>
<string name="trackers">Trackers</string>
<string name="permissions">Άδειες</string>
<string name="network_error">Το δίκτυο δεν είναι διαθέσιμο</string>
<string name="not_connected">Χωρίς σύνδεση στο Διαδίκτυο</string>
<string name="json_error">Σφάλμα JSON</string>
<string name="installed">Εγκατεστημένη Έκδοση: </string>
<string name="report_version">Δοκιμαστική έκδοση: </string>
<string name="no_trackers">Η εφαρμογή μπορεί να περιέχει tracker(s) που δεν γνωρίζουμε ακόμα.</string>
<string name="no_permissions">Αυτή η εφαρμογή δεν απαιτεί δικαιώματα.</string>
<string name="tested">Δεν υπάρχει αναφορά για την εγκατεστημένη έκδοση (%1$s), οι πληροφορίες που εμφανίζονται βασίζονται σε άλλη (%2$s) έκδοση</string>
<string name="analysed">Αυτή η εφαρμογή δεν έχει αναλυθεί ακόμη από το Exodus Privacy.</string>
<string name="no_package_manager">Φαίνεται ότι το σύστημα σας δεν επιτρέπει την πρόσβαση στη λίστα των εγκατεστημένων εφαρμογών.</string>
<string name="no_app_found">Φαίνεται ότι δεν έχετε εγκαταστήσει εφαρμογές, από την πηγές που δοκιμάζουμε (Google Play store ή F-Droid).</string>
<string name="get_reports_connection">Λήψη αναφοράς εφαρμογών: Αναμονή για σύνδεση διακομιστή</string>
<string name="get_reports">Λήψη αναφοράς εφαρμογών</string>
<string name="parse_application">Επεξεργασία Εφαρμογών:</string>
<string name="parse_trackers">Επεξεργασία των Trackers:</string>
<string name="get_trackers_connection">Λήψη αναφοράς των trackers: Αναμονή για σύνδεση διακομιστή</string>
<string name="get_trackers">Λήψη αναφοράς των trackers</string>
<string name="no_settings">Αυτή η εφαρμογή δεν έχει ρυθμίσεις</string>
<string name="logo">%1 Λογότυπο Εφαρμογής</string>
<string name="created_by">Δημιουργήθηκε από</string>
<string name="created_date">Η αναφορά δημιουργήθηκε στις</string>
<string name="and_updated">και ενημερώθηκε στις</string>
<string name="view_on_exodus">Δείτε στο εxodus ➤</string>
<string name="view_on_google_play">Δείτε στο Google Play ➤</string>
<string name="view_on_fdroid">Δείτε στο F-Droid ➤</string>
<string name="downloads">λήψεις</string>
<string name="code_signature_found">Εχουμε βρει υπογραφές κώδικα των παρακάτω trackers στην εφαρμογή:</string>
<string name="code_signature_not_found">Δεν έχουμε βρει υπογραφή κώδικα σε κάποιο tracker που γνωρίζουμε στην εφαρμογή.</string>
<string name="code_permission_found">Βρήκαμε τα ακόλουθα δικαιώματα στην εφαρμογή:</string>
<string name="code_permission_not_found">Αυτή η εφαρμογή δεν απαιτεί δικαιώματα.</string>
<string name="tracker_infos">Το tracker είναι ένα λογισμικό που προορίζεται για να συλλέγει δεδομένα για εσάς, ή για τις ενέργειες που κάνετε. <a href="https://reports.exodus-privacy.eu.org/en/info/trackers/">Μάθετε περισσότερα…</a></string>
<string name="permission_infos">Οι άδειες, είναι οι ενέργειες που μπορεί εκτελέσει μια εφαρμογή στο τηλέφωνό σας. <a href="https://reports.exodus-privacy.eu.org/en/info/permissions/">Μάθετε περισσότερα…</a></string>
<string name="permission_infos_dangerous">Το εικονίδιο <font color="red"><b>!</b></font> υποδεικνύει ένα «Επικίνδυνο» ή «Ειδικό» επίπεδο, σύμφωνα με τα <a href="https://developer.android.com/guide/topics/permissions/overview">Επίπεδα προστασίας της Google.</a></string>
<string name="tracker_web_page">Ιστοσελίδα του Tracker ➤</string>
<string name="detection_rules">Κανόνες Ανίχνευσης</string>
<string name="code_signature">Κανόνας ανίχνευσης κώδικα:</string>
<string name="network_signature">Κανόνας ανίχνευσης δικτύου:</string>
<string name="tracker_presence">Παρών σε %d από τις εφαρμογές σας</string>
<string name="tracker_presence_in">Παρών σε:</string>
<string name="no_app_found_tracker">Αυτό το tracker φαίνεται να μην υπάρχει στις εφαρμογές σας</string>
<string name="source">Πηγή: %s</string>
<!-- Menu -->
<string name="menu_action_filter">Φίλτρο</string>
<string name="menu_action_settings">Ρυθμίσεις Εφαρμογής</string>
</resources>

View File

@ -1,26 +1,52 @@
<resources> <resources>
<string name="app_name">Exodus Privacy</string>
<string name="app_logo">Application Logo</string> <string name="app_logo">Application Logo</string>
<string name="trackers">Pisteurs :</string> <string name="trackers_pct">Pisteurs :</string>
<string name="permissions">Autorisations :</string> <string name="permissions_pct">Autorisations :</string>
<string name="trackers">Pisteurs</string>
<string name="permissions">Autorisations</string>
<string name="network_error">Réseau indisponible</string> <string name="network_error">Réseau indisponible</string>
<string name="not_connected">Aucune connexion Internet</string> <string name="not_connected">Aucune connexion Internet</string>
<string name="json_error">Erreur JSON</string> <string name="json_error">Erreur JSON</string>
<string name="installed">Version installée : </string> <string name="installed">Version installée : </string>
<string name="report_version">Version testée : </string> <string name="report_version">Version testée : </string>
<string name="no_trackers">Aucun pisteur testé n\'a été trouvé</string> <string name="no_trackers">Lapplication pourrait contenir un ou plusieurs pisteurs que nous navons pas encore identifiés.</string>
<string name="no_permissions">Aucune autorisation n\'est demandée par cette application</string> <string name="no_permissions">Cette application ne demande aucun autorisation.</string>
<string name="tested">Il n\'y a pas de rapport pour la version installée, les informations affichées sont basées sur une autre version (vraisemblablement plus ancienne)</string> <string name="tested">Il n\'y a pas de rapport pour la version installée (%1$s), les informations affichées sont basées sur une autre version (%2$s)</string>
<string name="analysed">Cette application n\'a pas encore été analysée par Exodus Privacy.</string> <string name="analysed">Cette application n\'a pas encore été analysée par Exodus Privacy.</string>
<string name="no_package_manager">Votre système semble ne pas donner accès aux applications installées.</string> <string name="no_package_manager">Votre système semble ne pas donner accès aux applications installées.</string>
<string name="no_app_found">Vous semblez n\'avoir aucune application installée par la source que nous recherchons (Google Play).</string> <string name="no_app_found">Vous semblez n\'avoir aucune application installée par la source que nous recherchons (Google Play ou F-Droid).</string>
<string name="get_reports_connection">Récupération des applications : en attente de connexion au serveur</string> <string name="get_reports_connection">Récupération des applications : en attente de connexion au serveur</string>
<string name="get_reports">Récupération des applications</string> <string name="get_reports">Récupération des applications</string>
<string name="parse_application">Traitement des applications :</string> <string name="parse_application">Traitement des applications :</string>
<string name="parse_trackers">Traitement des pisteurs :</string> <string name="parse_trackers">Traitement des pisteurs :</string>
<string name="get_trackers_connection">Récupération des pisteurs : en attente de connexion au serveur</string> <string name="get_trackers_connection">Récupération des pisteurs : en attente de connexion au serveur</string>
<string name="get_trackers">Récupération des pisteurs</string> <string name="get_trackers">Récupération des pisteurs</string>
<string name="no_settings">Cette app n\'a pas de paramêtre</string>
<string name="logo">Logo de l\'application %1</string>
<string name="created_by">Crée par</string>
<string name="created_date">Rapport créé le</string>
<string name="and_updated">et mis à jour le</string>
<string name="view_on_exodus">Voir sur εxodus ➤</string>
<string name="view_on_google_play">Voir sur Google Play ➤</string>
<string name="view_on_fdroid">Voir sur F-Droid ➤</string>
<string name="downloads">téléchargements</string>
<string name="code_signature_found">Nous avons trouvé la signature des pisteurs suivants dans cette application : </string>
<string name="code_signature_not_found">Nous navons pas trouvé la signature de pisteurs que nous connaissons dans lapplication. </string>
<string name="code_permission_found">Nous avons trouvé les permissions suivantes dans cette application:</string>
<string name="code_permission_not_found">Cette application ne demande aucune autorisation.</string>
<string name="tracker_infos">Un pisteur est une partie du logiciel dédiée à la collecte de données sur vous et vos usages. <a href="https://reports.exodus-privacy.eu.org/fr/info/trackers/">En savoir plus…</a></string>
<string name="permission_infos">Les permissions sont les actions que l\'application peut effectuer sur votre téléphone. <a href="https://reports.exodus-privacy.eu.org/fr/info/permissions/">En savoir plus…</a></string>
<string name="permission_infos_dangerous">L\'icône <font color="red"><b>!</b></font> indique un niveau \'Dangereux\' ou \'Spécial\' d\'après les <a href="https://developer.android.com/guide/topics/permissions/overview">niveaux de protection de Google.</a></string>
<string name="tracker_web_page">Page web du pisteur ➤</string>
<string name="detection_rules">Règles de détection</string>
<string name="code_signature">Règle de détection(code):</string>
<string name="network_signature">Règle de détection(réseau):</string>
<string name="tracker_presence">Présent dans %d de vos applications</string>
<string name="tracker_presence_in">Présent dans:</string>
<string name="no_app_found_tracker">Ce pisteur ne semble pas être présent dans vos applications</string>
<string name="source">Source: %s</string>
<!-- Menu --> <!-- Menu -->
<string name="menu_action_filter">Filtrer</string> <string name="menu_action_filter">Filtrer</string>
<string name="menu_action_settings">Paramètres de l\'application</string>
</resources> </resources>

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_logo">Logo applicazione</string>
<string name="trackers_pct">Tracciatori:</string>
<string name="permissions_pct">Autorizzazioni:</string>
<string name="trackers">Tracciatori</string>
<string name="permissions">Autorizzazioni</string>
<string name="network_error">Rete non disponibile</string>
<string name="not_connected">Nessuna connessione internet</string>
<string name="json_error">Errore JSON</string>
<string name="installed">Versione installata: </string>
<string name="report_version">Versione testata: </string>
<string name="no_trackers">Nessun tracciatore testato trovato</string>
<string name="no_permissions">Nessuna autorizzazione richiesta dall\'applicazione</string>
<string name="tested">Non ci sono report per la versione installata (%1$s), le informazioni mostrate sono basate su un\'altra (%2$s) versione</string>
<string name="analysed">Questa app non è ancora stata analizzata da Exodus Privacy.</string>
<string name="no_package_manager">Sembra che il tuo sistema non permetta l\'accesso all\'elenco di app installate.</string>
<string name="no_app_found">Sembra che tu non abbia nessuna app installata dalla fonte che testiamo (Google Play store o F-Droid).</string>
<string name="get_reports_connection">Rilevazione applicazioni: in attesa della connessione al server</string>
<string name="get_reports">Rilevazione applicazioni</string>
<string name="parse_application">Elaborazione applicazioni:</string>
<string name="parse_trackers">Elaborazione tracciatori:</string>
<string name="get_trackers_connection">Rilevazione tracciatori: in attesa della connessione al server</string>
<string name="get_trackers">Rilevazione tracker</string>
<string name="no_settings">Questa app non ha impostazioni</string>
<string name="logo">%1 Logo dell\'applicazione</string>
<string name="created_by">Creato da</string>
<string name="created_date">Report creato il</string>
<string name="and_updated">ae aggiornato il</string>
<string name="view_on_exodus">Vedi su εxodus ➤</string>
<string name="view_on_google_play">Vedi su Google Play ➤</string>
<string name="view_on_fdroid">Vedi su F-Droid ➤</string>
<string name="downloads">scaricamenti</string>
<string name="code_signature_found">Abbiamo trovato i codici di firma dei seguenti tracker nell\'applicazione:</string>
<string name="code_signature_not_found">Non abbiamo trovato il codice di firma di alcun tracker che conosciamo nell\'applicazione.</string>
<string name="code_permission_found">Abbiamo trovato i permessi seguenti nell\'applicazione:</string>
<string name="code_permission_not_found">Questa applicazione non richiede alcun permesso.</string>
<string name="tracker_infos">Un tracker è un software destinato a raccogliere dati su di te o sui tuoi utilizzi. <a href="https://reports.exodus-privacy.eu.org/en/info/trackers/">Per saperne di più…</a></string>
<string name="permission_infos">Le autorizzazioni sono azioni che l\'applicazione può fare sul tuo telefono. <a href="https://reports.exodus-privacy.eu.org/en/info/permissions/">Per saperne di più…</a></string>
<string name="permission_infos_dangerous">L\'icona <font color="red"><b>!</b></font> indica un livello \'Pericoloso\' o \'Speciale\' in base ai <a href="https://developer.android.com/guide/topics/permissions/overview">livelli di protezione di Google.</a></string>
<string name="tracker_web_page">Pagina web del tracker ➤</string>
<string name="detection_rules">Regole di rilevamento</string>
<string name="code_signature">Regola di rilevamento (codice):</string>
<string name="network_signature">Regola di rilevamento (rete):</string>
<string name="tracker_presence">Presente in %d applicazioni</string>
<string name="tracker_presence_in">Presente in:</string>
<string name="no_app_found_tracker">Questo tracker non sembra essere presente nelle tue applicazioni</string>
<string name="source">Fonte: %s</string>
<!-- Menu -->
<string name="menu_action_filter">Filtra</string>
<string name="menu_action_settings">Impostazioni dell\'applicazione</string>
</resources>

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_logo">Логотип приложения</string>
<string name="trackers_pct">Трекеры:</string>
<string name="permissions_pct">Разрешения:</string>
<string name="trackers">трекеров</string>
<string name="permissions">разрешений</string>
<string name="network_error">Сеть недоступна</string>
<string name="not_connected">Нет подключения к интернету</string>
<string name="json_error">Ошибка JSON</string>
<string name="installed">Установленная версия: </string>
<string name="report_version">Изученная версия: </string>
<string name="no_trackers">Не найдено ни одного известного трекера</string>
<string name="no_permissions">Это приложение не запрашивает никаких разрешений</string>
<string name="tested">Данные по установленной версии (%1$s) отсутствуют, показана информация на основе изученной (%2$s) версии</string>
<string name="analysed">Это приложение ещё не было изучено Exodus Privacy.</string>
<string name="no_package_manager">Похоже, ваша система не разрешает доступ к списку установленных приложений.</string>
<string name="no_app_found">Похоже, у вас нет приложений, установленных из источника, который мы тестируем (магазин Google Play или F-Droid).</string>
<string name="get_reports_connection">Получение приложений: ожидание подключения к серверу</string>
<string name="get_reports">Получение приложений</string>
<string name="parse_application">Обработка приложений:</string>
<string name="parse_trackers">Обработка трекеров:</string>
<string name="get_trackers_connection">Получение трекеров: ожидание подключения к серверу</string>
<string name="get_trackers">Получение трекеров</string>
<string name="no_settings">Это приложение не имеет настроек</string>
<string name="logo">Логотип приложения %1</string>
<string name="created_by">Создано</string>
<string name="created_date">Отчёт создан</string>
<string name="and_updated">и обновлено</string>
<string name="view_on_exodus">εxodus ➤</string>
<string name="view_on_google_play">Google Play ➤</string>
<string name="view_on_fdroid">F-Droid ➤</string>
<string name="downloads">загрузок</string>
<string name="code_signature_found">В приложении обнаружены сигнатуры следующих трекеров:</string>
<string name="code_signature_not_found">В приложении не обнаружено сигнатур ни одного известного трекера.</string>
<string name="code_permission_found">В приложении обнаружены следующее разрешения:</string>
<string name="code_permission_not_found">Приложение не требует никаких разрешений.</string>
<string name="tracker_infos">Трекер — это часть приложения, предназначенная для сбора данных о вас или ваших действиях. <a href="https://reports.exodus-privacy.eu.org/en/info/trackers/">Подробнее…</a></string>
<string name="permission_infos">Разрешения — это действия, которые приложение сможет выполнять на вашем устройстве. <a href="https://reports.exodus-privacy.eu.org/en/info/permissions/">Подробнее…</a></string>
<string name="permission_infos_dangerous">Значок <font color="red"><b>!</b></font> обозначает уровень угрозы «Опасность» или «Особый» в соответствии с <a href="https://developer.android.com/guide/topics/permissions/overview">уровнем защиты Google</a>.</string>
<string name="tracker_web_page">Сайт трекера ➤</string>
<string name="detection_rules">Определяющие правила</string>
<string name="code_signature">Правило обнаружения кода:</string>
<string name="network_signature">Правило определения сети:</string>
<string name="tracker_presence">Присутствует в %d приложениях</string>
<string name="tracker_presence_in">Присутствует в:</string>
<string name="no_app_found_tracker">Этот трекер не присутствует в ваших приложениях</string>
<string name="source">Источник: %s</string>
<!-- Menu -->
<string name="menu_action_filter">Фильтр</string>
<string name="menu_action_settings">Настройки приложения</string>
</resources>

View File

@ -3,4 +3,19 @@
<color name="colorPrimary">#684971</color> <color name="colorPrimary">#684971</color>
<color name="colorPrimaryDark">#3d2b43</color> <color name="colorPrimaryDark">#3d2b43</color>
<color name="colorAccent">#684971</color> <color name="colorAccent">#684971</color>
<color name="colorGreen">#6fc384</color>
<color name="colorLightRed">#e46772</color>
<color name="colorLightYellow">#ffdb66</color>
<color name="colorLightBlue">#17a2b8</color>
<color name="colorYellow">#ffc70f</color>
<color name="colorDarkOrange">#ff8c00</color>
<color name="colorRed">#e61718</color>
<color name="colorPurple">#684971</color>
<color name="textColorDark">#343A40</color>
<color name="textColorDarkLight">#6C757D</color>
<color name="textColorWhite">#FFFFFF</color>
<color name="textDetection">#E83E8C</color>
</resources> </resources>

View File

@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="exodus">9c6106a229bc5f34b5802e5861bcb87d1626617d</string> <string name="exodus">9c6106a229bc5f34b5802e5861bcb87d1626617d</string>
<string name="app_title">εxodus</string>
<string name="app_name">Exodus</string>
</resources> </resources>

View File

@ -1,26 +1,53 @@
<resources> <resources>
<string name="app_name">Exodus Privacy</string>
<string name="app_logo">Application Logo</string> <string name="app_logo">Application Logo</string>
<string name="trackers">Trackers:</string> <string name="trackers_pct">Trackers:</string>
<string name="permissions">Permissions:</string> <string name="permissions_pct">Permissions:</string>
<string name="trackers">Trackers</string>
<string name="permissions">Permissions</string>
<string name="network_error">Network Unavailable</string> <string name="network_error">Network Unavailable</string>
<string name="not_connected">No Internet Connection</string> <string name="not_connected">No Internet Connection</string>
<string name="json_error">JSON Error</string> <string name="json_error">JSON Error</string>
<string name="installed">Installed Version: </string> <string name="installed">Installed Version: </string>
<string name="report_version">Tested Version: </string> <string name="report_version">Tested Version: </string>
<string name="no_trackers">No tested trackers were found</string> <string name="no_trackers">The application could contain tracker(s) we do not know yet.</string>
<string name="no_permissions">No permissions are requested by this application</string> <string name="no_permissions">This application doesn\'t require any permissions.</string>
<string name="tested">There\'s no report for the installed version, informations displayed are based on another (presumably older) version</string> <string name="tested">There\'s no report for the installed version (%1$s), the information displayed is based on another (%2$s) version</string>
<string name="analysed">This app hasn\'t been analysed by Exodus Privacy yet.</string> <string name="analysed">This app hasn\'t been analysed by Exodus Privacy yet.</string>
<string name="no_package_manager">Your system seems doesn\'t provide access to installed apps.</string> <string name="no_package_manager">It appears that your system doesn\'t allow access to the list of installed apps.</string>
<string name="no_app_found">You seems not having any apps installed by source we search for (Google Play).</string> <string name="no_app_found">It appears that you don\'t have any apps installed from the source we test (Google Play store or F-Droid).</string>
<string name="get_reports_connection">Getting applications: Waiting for server connection</string> <string name="get_reports_connection">Getting applications: Waiting for server connection</string>
<string name="get_reports">Getting applications</string> <string name="get_reports">Getting applications</string>
<string name="parse_application">Processing Applications:</string> <string name="parse_application">Processing Applications:</string>
<string name="parse_trackers">Processing Trackers:</string> <string name="parse_trackers">Processing Trackers:</string>
<string name="get_trackers_connection">Getting trackers: Waiting for server connection</string> <string name="get_trackers_connection">Getting trackers: Waiting for server connection</string>
<string name="get_trackers">Getting trackers</string> <string name="get_trackers">Getting trackers</string>
<string name="no_settings">This app doesn\'t have settings</string>
<string name="logo">%1 Application Logo</string>
<string name="created_by">Created By</string>
<string name="created_date">Report created on</string>
<string name="and_updated">and updated on</string>
<string name="view_on_exodus">See on εxodus ➤</string>
<string name="view_on_google_play">See on Google Play ➤</string>
<string name="view_on_fdroid">See on F-Droid ➤</string>
<string name="downloads">downloads</string>
<string name="code_signature_found">We have found code signature of the following trackers in the application:</string>
<string name="code_signature_not_found">We have not found code signature of any tracker we know in the application.</string>
<string name="code_permission_found">We have found the following permissions in the application:</string>
<string name="code_permission_not_found">This application doesn\'t require any permissions.</string>
<string name="tracker_infos">A tracker is a piece of software meant to collect data about you or your usages. <a href="https://reports.exodus-privacy.eu.org/en/info/trackers/">Learn more…</a></string>
<string name="permission_infos">Permissions are actions the application can do on your phone. <a href="https://reports.exodus-privacy.eu.org/en/info/permissions/">Learn more…</a></string>
<string name="permission_infos_dangerous">The icon <font color="red"><b>!</b></font> indicates a \'Dangerous\' or \'Special\' level according to <a href="https://developer.android.com/guide/topics/permissions/overview">Google\'s protection levels.</a></string>
<string name="tracker_web_page">Tracker web page ➤</string>
<string name="detection_rules">Detection Rules</string>
<string name="code_signature">Code detection rule:</string>
<string name="network_signature">Network detection rule:</string>
<string name="tracker_presence">Present in %d of your applications</string>
<string name="tracker_presence_in">Present in:</string>
<string name="no_app_found_tracker">This tracker seems not be present in your applications</string>
<string name="source">Source: %s</string>
<!-- Menu --> <!-- Menu -->
<string name="menu_action_filter">Filter</string> <string name="menu_action_filter">Filter</string>
<string name="menu_action_settings">Application Settings</string>
</resources> </resources>

View File

@ -6,9 +6,10 @@ buildscript {
maven { maven {
url "https://maven.google.com" url "https://maven.google.com"
} }
google()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.0.1' classpath 'com.android.tools.build:gradle:4.0.0'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files

View File

@ -0,0 +1,7 @@
يساعدك Exodus Privacy على معرفة أي مِن المتعقبين والتصريحات المدمجة في التطبيقات المثبتة على جهازك.
يقوم التطبيق بتحميل التقارير من Exodus Privacy (https://exodus-privacy.eu.org/) ويعرضها عليك تطبيقًا تلو الآخر
بإمكان التطبيق اكتشاف التطبيقات المثبتة من متجر Google Play فقط.
الرمز المصدري: https://github.com/Exodus-Privacy/exodus-android-app/

View File

@ -0,0 +1 @@
يُظهِر المتعقّبِين والتصريحات التي تطلبها التطبيقات المثبتة الأخرى

View File

@ -0,0 +1,7 @@
Exodus Privacy us ajuda a saber quins rastrejadors i permisos estan incrustats en les aplicacions instal·lades al vostre dispositiu.
The app downloads reports from Exodus Privacy (https://exodus-privacy.eu.org/) and shows them to you app by app
L'aplicació només pot detectar les aplicacions instal·lades des de la botiga Google Play.
Codi font: https://github.com/Exodus-Privacy/exodus-android-app/

View File

@ -0,0 +1 @@
Mostra els rastrejadors i els permisos de les aplicacions instal·lades

View File

@ -0,0 +1,7 @@
Exodus Privacy hilft Ihnen zu wissen, welche Tracker und Berechtigungen in Apps eingebettet sind, die auf Ihrem Gerät installiert sind.
Die App lädt Berichte von Exodus Privacy (https://exodus-privacy.eu.org/) herunter und zeigt sie für jede App
Die App kann nur installierte Apps aus dem Google Play Store erkennen.
Quellcode: https://github.com/Exodus-Privacy/exodus-android-app/

View File

@ -0,0 +1 @@
Tracker und Berechtigungen anderer installierter Apps anzeigen

View File

@ -0,0 +1,7 @@
Το Exodus Privacy σάς βοηθά να γνωρίζετε ποια trackers και ποια δικαιώματα, βρίσκονται μέσα στις εφαρμογές που είναι εγκατεστημένες στην συσκευή σας.
Η εφαρμογή κατεβάζει αναφορές από το Exodus Privacy (https://exodus-privacy.eu.org/) και τις εμφανίζει σε κάθε μία απο τις εφαρμογές σας
Η εφαρμογή μπορεί να εντοπίσει μόνο εφαρμογές που έχουν εγκατασταθεί από το Google Play store.
Πηγαίος Κώδικας: https://github.com/Exodus-Privacy/exodus-android-app/

View File

@ -0,0 +1 @@
Εμφάνιση ιχνηλατών και αδειών από άλλες εγκατεστημένες εφαρμογές

View File

@ -0,0 +1,4 @@
- speedup navigation (thanks to borsini)
- add shortcut to apps settings
- add version identifier when app version doesn't match (thanks to tdelmas)
- bug fix

View File

@ -0,0 +1,2 @@
- rewrite of report page
- bug fix

View File

@ -0,0 +1,4 @@
- Add Trackers Page
- Add F-Droid detection Support
- bug fix
- New Translations (Italian, Russian, Greek, Arabic, German, and Catalan)

View File

@ -0,0 +1,7 @@
Exodus Privacy helps you to know which trackers and permissions are embedded in apps installed on your device.
The app downloads reports from Exodus Privacy (https://exodus-privacy.eu.org/) and shows them to you app by app
The app can only detect apps installed from the Google Play store.
Source Code: https://github.com/Exodus-Privacy/exodus-android-app/

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

View File

@ -0,0 +1 @@
Show trackers and permissions from other installed apps

View File

@ -0,0 +1,4 @@
- Amélioration de la vitesse de navigation (grâce à borsini)
- Ajout d'un raccourci vers les paramètres des applications
- Ajout du numéro de version lorsque la version de l'application ne correspond pas (grâce à tdelmas)
- Correction de bug

View File

@ -0,0 +1,2 @@
- Refonte de la page des rapports
- Correction de bug

View File

@ -0,0 +1,4 @@
- Ajout de la page des pisteurs
- Ajout de la détection pour F-Droid
- Correction de bug
- Nouvelles Traductions (Italien, Russe, Grec , Arabe, Allemand et Catalan)

View File

@ -0,0 +1,7 @@
Exodus Privacy vous aide à savoir quels sont les pisteurs et permissions embarqués dans les applications installées sur votre smartphone.
L'application télécharge les rapports d'Exodus Privacy (https://exodus-privacy.eu.org/) et vous les montre application par application.
L'application ne peut détecter que les applications installées depuis le Google Play Store.
Code source: https://github.com/Exodus-Privacy/exodus-android-app/

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

View File

@ -0,0 +1 @@
Montre les pisteurs et permissions des applications installées

View File

@ -0,0 +1,7 @@
Exodus Privacy ti aiuta a sapere quali tracker e permessi sono incorporati nelle applicazioni installate sul tuo smartphone.
L'app scarica i report da Exodus Privacy (https://exodus-privacy.eu.org/) e li mostra all'app per app
L'applicazione può rilevare solo le applicazioni installate dal Google Play store.
Codice sorgente: https://github.com/Exodus-Privacy/exodus-android-app/

Some files were not shown because too many files have changed in this diff Show More