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
|
@ -8,8 +8,6 @@ gen/
|
|||
.gradle/
|
||||
build/
|
||||
/*/build/
|
||||
gradlew*
|
||||
gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Local configuration file (sdk path, etc)
|
||||
local.properties
|
||||
|
@ -24,4 +22,10 @@ proguard/
|
|||
.idea
|
||||
*.iml
|
||||
|
||||
#vim
|
||||
.*swp
|
||||
.*swo
|
||||
.tags
|
||||
|
||||
|
||||
captures/
|
||||
|
|
29
.travis.yml
|
@ -1,23 +1,26 @@
|
|||
language: android
|
||||
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:
|
||||
componentes:
|
||||
components:
|
||||
- tools
|
||||
- platform-tools
|
||||
- tools
|
||||
|
||||
- build-tools-27.0.3
|
||||
- android-27
|
||||
- build-tools-29.0.3
|
||||
- android-29
|
||||
- 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:
|
||||
- ./gradlew assembleDebug
|
||||
- ./gradlew test
|
||||
|
|
|
@ -3,3 +3,8 @@
|
|||
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.
|
||||
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>
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 27
|
||||
buildToolsVersion "27.0.3"
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion "29.0.3"
|
||||
defaultConfig {
|
||||
applicationId "org.eu.exodus_privacy.exodusprivacy"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 27
|
||||
versionCode 4
|
||||
versionName "1.0.4"
|
||||
minSdkVersion 17
|
||||
targetSdkVersion 29
|
||||
versionCode 10
|
||||
versionName "2.1.1"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
buildTypes {
|
||||
|
@ -24,19 +24,20 @@ android {
|
|||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
dataBinding {
|
||||
enabled = true
|
||||
buildFeatures {
|
||||
dataBinding = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile fileTree(include: ['*.jar'], dir: 'libs')
|
||||
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
|
||||
exclude group: 'com.android.support', module: 'support-annotations'
|
||||
})
|
||||
compile 'com.android.support:appcompat-v7:27.1.0'
|
||||
compile 'com.android.support.constraint:constraint-layout:1.0.2'
|
||||
compile 'com.android.support:design:27.1.0'
|
||||
compile 'com.android.support:recyclerview-v7:27.1.0'
|
||||
testCompile 'junit:junit:4.12'
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
||||
implementation 'com.google.android.material:material:1.1.0'
|
||||
testImplementation 'junit:junit:4.13'
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="org.eu.exodus_privacy.exodusprivacy">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
@ -11,13 +12,14 @@
|
|||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme"
|
||||
tools:ignore="GoogleAppIndexingWarning"
|
||||
android:largeHeap="true"
|
||||
>
|
||||
<activity android:name=".MainActivity"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||
>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
|
|
@ -18,52 +18,83 @@
|
|||
|
||||
package org.eu.exodus_privacy.exodusprivacy;
|
||||
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.app.SearchManager;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.Context;
|
||||
import android.databinding.DataBindingUtil;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.widget.SearchView;
|
||||
import android.provider.Settings;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MenuInflater;
|
||||
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.ApplicationViewModel;
|
||||
import org.eu.exodus_privacy.exodusprivacy.adapters.TrackerListAdapter;
|
||||
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.TrackerFragment;
|
||||
import org.eu.exodus_privacy.exodusprivacy.fragments.Updatable;
|
||||
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 {
|
||||
|
||||
AppListFragment appList;
|
||||
ReportFragment report;
|
||||
SearchView searchView;
|
||||
private Menu mMenu;
|
||||
private List<Updatable> fragments;
|
||||
private SearchView searchView;
|
||||
private Menu toolbarMenu;
|
||||
private MenuItem settingsMenuItem;
|
||||
private String packageName;
|
||||
private MainBinding binding;
|
||||
private ApplicationListAdapter.OnAppClickListener onAppClickListener;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle 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() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
runOnUiThread(() -> {
|
||||
appList.updateComplete();
|
||||
if(report != null)
|
||||
report.updateComplete();
|
||||
|
||||
for(Updatable updatable : fragments){
|
||||
if(updatable instanceof ReportFragment) {
|
||||
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
|
||||
public void onError(String error) {
|
||||
runOnUiThread(() -> {
|
||||
appList.updateComplete();
|
||||
for(Updatable updatable : fragments){
|
||||
updatable.onUpdateComplete();
|
||||
}
|
||||
Snackbar bar = Snackbar.make(mainBinding.fragmentContainer,error,Snackbar.LENGTH_LONG);
|
||||
bar.show();
|
||||
});
|
||||
|
@ -75,45 +106,72 @@ public class MainActivity extends AppCompatActivity {
|
|||
}
|
||||
};
|
||||
|
||||
ApplicationListAdapter.OnAppClickListener onAppClickListener = packageInfo -> {
|
||||
TrackerListAdapter.OnTrackerClickListener onTrackerClickListener = id -> {
|
||||
TrackerFragment tracker = TrackerFragment.newInstance(id);
|
||||
tracker.setOnAppClickListener(onAppClickListener);
|
||||
fragments.add(tracker);
|
||||
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,tracker)
|
||||
.addToBackStack(null)
|
||||
.commit();
|
||||
};
|
||||
|
||||
report = ReportFragment.newInstance(getPackageManager(),packageInfo);
|
||||
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 (mMenu != null)
|
||||
(mMenu.findItem(R.id.action_filter)).collapseActionView();
|
||||
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();
|
||||
}
|
||||
};
|
||||
|
||||
appList = AppListFragment.newInstance(networkListener,onAppClickListener);
|
||||
HomeFragment home = new HomeFragment();
|
||||
fragments.add(home);
|
||||
home.setNetworkListener(networkListener);
|
||||
home.setOnAppClickListener(onAppClickListener);
|
||||
|
||||
|
||||
FragmentManager manager = getSupportFragmentManager();
|
||||
FragmentTransaction transaction = manager.beginTransaction();
|
||||
transaction.replace(R.id.fragment_container,appList)
|
||||
transaction.replace(R.id.fragment_container,home)
|
||||
.commit();
|
||||
home.startRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (getSupportFragmentManager().getBackStackEntryCount() == 0)
|
||||
finish();
|
||||
else
|
||||
else {
|
||||
getSupportFragmentManager().popBackStack();
|
||||
fragments.remove(fragments.size()-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
mMenu = menu;
|
||||
toolbarMenu = menu;
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.main, menu);
|
||||
searchView = (SearchView) menu.findItem(R.id.action_filter).getActionView();
|
||||
|
@ -121,16 +179,41 @@ public class MainActivity extends AppCompatActivity {
|
|||
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
appList.filter(query);
|
||||
HomeFragment home = (HomeFragment) fragments.get(0);
|
||||
home.filter(query);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
appList.filter(newText);
|
||||
HomeFragment home = (HomeFragment) fragments.get(0);
|
||||
home.filter(newText);
|
||||
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;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -19,19 +19,20 @@
|
|||
package org.eu.exodus_privacy.exodusprivacy.adapters;
|
||||
|
||||
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.View;
|
||||
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.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.Tracker;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
|
@ -41,49 +42,36 @@ import java.util.regex.Pattern;
|
|||
|
||||
public class ApplicationListAdapter extends RecyclerView.Adapter {
|
||||
|
||||
private List<PackageInfo> packages;
|
||||
private PackageManager packageManager;
|
||||
private List<ApplicationViewModel> applicationViewModels;
|
||||
private OnAppClickListener onAppClickListener;
|
||||
private static final String gStore = "com.android.vending";
|
||||
private String filter = "";
|
||||
private Object filter = "";
|
||||
private AppListFragment.Type filterType = AppListFragment.Type.NAME;
|
||||
private final int HIDDEN_APP = 0;
|
||||
private final int DISPLAYED_APP = 1;
|
||||
private int displayedApp = 0;
|
||||
|
||||
|
||||
private Comparator<PackageInfo> alphaPackageComparator = new Comparator<PackageInfo>() {
|
||||
private Comparator<ApplicationViewModel> alphaPackageComparator = new Comparator<ApplicationViewModel>() {
|
||||
@Override
|
||||
public int compare(PackageInfo pack1, PackageInfo pack2) {
|
||||
String pkg1 = packageManager.getApplicationLabel(pack1.applicationInfo).toString();
|
||||
String pkg2 = packageManager.getApplicationLabel(pack2.applicationInfo).toString();
|
||||
return pkg1.compareToIgnoreCase(pkg2);
|
||||
public int compare(ApplicationViewModel app1, ApplicationViewModel app2) {
|
||||
if(app1.label != null && app2.label != null)
|
||||
return app1.label.toString().compareToIgnoreCase(app2.label.toString());
|
||||
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;
|
||||
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
|
||||
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
|
||||
|
@ -100,12 +88,13 @@ public class ApplicationListAdapter extends RecyclerView.Adapter {
|
|||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
|
||||
if( viewHolder.getItemViewType() == DISPLAYED_APP) {
|
||||
final ApplicationListViewHolder holder = (ApplicationListViewHolder) viewHolder;
|
||||
holder.setData(packages.get(position));
|
||||
ApplicationViewModel vm = applicationViewModels.get(position);
|
||||
holder.setViewModel(vm);
|
||||
//noinspection Convert2Lambda
|
||||
holder.itemView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
onAppClickListener.onAppClick(holder.packageInfo);
|
||||
onAppClickListener.onAppClick(vm);
|
||||
}
|
||||
});
|
||||
}else {
|
||||
|
@ -115,30 +104,30 @@ public class ApplicationListAdapter extends RecyclerView.Adapter {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return packages.size();
|
||||
return applicationViewModels.size();
|
||||
}
|
||||
|
||||
public void setPackageManager(PackageManager manager) {
|
||||
packageManager = manager;
|
||||
if(packageManager != null) {
|
||||
List<PackageInfo> installedPackages = packageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS);
|
||||
setInstalledPackages(installedPackages);
|
||||
}
|
||||
public void displayAppList(List<ApplicationViewModel> applications) {
|
||||
applicationViewModels = applications;
|
||||
Collections.sort(applicationViewModels, alphaPackageComparator);
|
||||
filter(filterType,filter);
|
||||
}
|
||||
|
||||
class ApplicationEmptyViewHolder extends RecyclerView.ViewHolder{
|
||||
public int getDisplayedApps() {
|
||||
return displayedApp;
|
||||
}
|
||||
|
||||
static class ApplicationEmptyViewHolder extends RecyclerView.ViewHolder{
|
||||
ApplicationEmptyViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
}
|
||||
}
|
||||
|
||||
static class ApplicationListViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
class ApplicationListViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
PackageInfo packageInfo;
|
||||
ApplicationViewModel viewModel;
|
||||
AppItemBinding appItemBinding;
|
||||
|
||||
ApplicationListViewHolder(AppItemBinding binding) {
|
||||
|
@ -146,8 +135,8 @@ public class ApplicationListAdapter extends RecyclerView.Adapter {
|
|||
appItemBinding = binding;
|
||||
}
|
||||
|
||||
public void setData(PackageInfo data) {
|
||||
packageInfo = data;
|
||||
void setViewModel(ApplicationViewModel vm) {
|
||||
viewModel = vm;
|
||||
|
||||
Context context = appItemBinding.getRoot().getContext();
|
||||
|
||||
|
@ -155,49 +144,90 @@ public class ApplicationListAdapter extends RecyclerView.Adapter {
|
|||
appItemBinding.otherVersion.setVisibility(View.GONE);
|
||||
appItemBinding.analysed.setVisibility(View.GONE);
|
||||
appItemBinding.appTrackerNb.setVisibility(View.VISIBLE);
|
||||
appItemBinding.appTracker.setVisibility(View.VISIBLE);
|
||||
|
||||
String versionName = viewModel.versionName;
|
||||
long versionCode = viewModel.versionCode;
|
||||
|
||||
String packageName = packageInfo.packageName;
|
||||
String versionName = packageInfo.versionName;
|
||||
appItemBinding.appLogo.setImageDrawable(viewModel.icon);
|
||||
|
||||
//get logo
|
||||
try {
|
||||
appItemBinding.appLogo.setImageDrawable(packageManager.getApplicationIcon(packageName));
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
//get name
|
||||
appItemBinding.appName.setText(packageManager.getApplicationLabel(packageInfo.applicationInfo));
|
||||
appItemBinding.appName.setText(viewModel.label);
|
||||
appItemBinding.source.setText(context.getString(R.string.source,viewModel.source));
|
||||
|
||||
//get permissions
|
||||
if(packageInfo.requestedPermissions != null) {
|
||||
appItemBinding.appPermissionNb.setText(context.getString(R.string.permissions) + " " + String.valueOf(data.requestedPermissions.length));
|
||||
} else {
|
||||
appItemBinding.appPermissionNb.setText(context.getString(R.string.permissions) + " " + String.valueOf(0));
|
||||
}
|
||||
//get reports
|
||||
Report report = DatabaseManager.getInstance(context).getReportFor(packageName, versionName);
|
||||
long size = viewModel.requestedPermissions != null ? viewModel.requestedPermissions.length : 0;
|
||||
appItemBinding.appPermissionNb.setText(String.valueOf(size));
|
||||
if(size == 0)
|
||||
appItemBinding.appPermissionNb.setBackgroundResource(R.drawable.square_green);
|
||||
else if (size < 5)
|
||||
appItemBinding.appPermissionNb.setBackgroundResource(R.drawable.square_light_yellow);
|
||||
else
|
||||
appItemBinding.appPermissionNb.setBackgroundResource(R.drawable.square_light_red);
|
||||
|
||||
Report report = viewModel.report;
|
||||
if(report != null) {
|
||||
Set<Tracker> trackers = DatabaseManager.getInstance(context).getTrackers(report.trackers);
|
||||
appItemBinding.appTrackerNb.setText(context.getString(R.string.trackers) + " " + trackers.size());
|
||||
if(!report.version.equals(data.versionName)) {
|
||||
Set<Tracker> trackers = viewModel.trackers;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
} else {
|
||||
appItemBinding.appTrackerNb.setVisibility(View.GONE);
|
||||
appItemBinding.appTracker.setVisibility(View.GONE);
|
||||
appItemBinding.analysed.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
filter = text;
|
||||
Pattern p = Pattern.compile(Pattern.quote(filterStr.trim()), Pattern.CASE_INSENSITIVE);
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -1,12 +1,13 @@
|
|||
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.View;
|
||||
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.databinding.PermissionItemBinding;
|
||||
import org.eu.exodus_privacy.exodusprivacy.objects.Permission;
|
||||
|
@ -39,7 +40,7 @@ public class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAd
|
|||
@Override
|
||||
public int getItemCount() {
|
||||
if(permissionList == null || permissionList.size() == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
else
|
||||
return permissionList.size();
|
||||
}
|
||||
|
@ -60,8 +61,21 @@ public class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAd
|
|||
|
||||
void setupData(Permission permission) {
|
||||
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);
|
||||
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);
|
||||
permissionItemBinding.mainLayout.setOnClickListener((View.OnClickListener) v -> {
|
||||
if( permission.description != null && permission.description.trim().length() > 0) {
|
||||
|
@ -74,6 +88,9 @@ public class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAd
|
|||
else {
|
||||
permissionItemBinding.permissionName.setText(R.string.no_permissions);
|
||||
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 )
|
||||
permissionItemBinding.arrow.setText("▶");
|
||||
else
|
||||
permissionItemBinding.arrow.setText("■");
|
||||
permissionItemBinding.arrow.setText("");
|
||||
permissionItemBinding.permissionDescription.setVisibility(View.GONE);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,26 +1,35 @@
|
|||
package org.eu.exodus_privacy.exodusprivacy.adapters;
|
||||
|
||||
import android.databinding.DataBindingUtil;
|
||||
import android.databinding.ViewDataBinding;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.view.LayoutInflater;
|
||||
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.databinding.TrackerItemBinding;
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
public TrackerListAdapter(Set<Tracker> trackerList, int resource) {
|
||||
public TrackerListAdapter(Set<Tracker> trackerList, int resource, OnTrackerClickListener listener) {
|
||||
setTrackers(trackerList);
|
||||
layout = resource;
|
||||
trackerClickListener = listener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
@ -35,7 +44,7 @@ public class TrackerListAdapter extends android.support.v7.widget.RecyclerView.A
|
|||
if(trackersList == null || trackersList.size() == 0)
|
||||
holder.setupData(null);
|
||||
else
|
||||
holder.setupData((Tracker) trackersList.toArray()[position]);
|
||||
holder.setupData(trackersList.get(position));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -46,8 +55,13 @@ public class TrackerListAdapter extends android.support.v7.widget.RecyclerView.A
|
|||
return trackersList.size();
|
||||
}
|
||||
|
||||
private Comparator<Tracker> alphaTrackerComparator = (track1, track2) -> track1.name.compareToIgnoreCase(track2.name);
|
||||
|
||||
public void setTrackers(Set<Tracker> trackers) {
|
||||
trackersList = trackers;
|
||||
if(trackers != null) {
|
||||
trackersList = new ArrayList<>(trackers);
|
||||
Collections.sort(trackersList, alphaTrackerComparator);
|
||||
}
|
||||
}
|
||||
|
||||
class TrackerListViewHolder extends RecyclerView.ViewHolder {
|
||||
|
@ -62,13 +76,21 @@ public class TrackerListAdapter extends android.support.v7.widget.RecyclerView.A
|
|||
void setupData(Tracker tracker) {
|
||||
if(viewDataBinding instanceof TrackerItemBinding) {
|
||||
TrackerItemBinding binding = (TrackerItemBinding) viewDataBinding;
|
||||
if(tracker != null)
|
||||
binding.trackerName.setText(tracker.name);
|
||||
if(tracker != null) {
|
||||
binding.trackerName.setText(tracker.name + " ➤");
|
||||
binding.getRoot().setOnClickListener(v -> {
|
||||
trackerClickListener.onTrackerClick(tracker.id);
|
||||
});
|
||||
}
|
||||
else
|
||||
binding.trackerName.setText(R.string.no_trackers);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnTrackerClickListener{
|
||||
public void onTrackerClick(long trackerId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.databinding.DataBindingUtil;
|
||||
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.View;
|
||||
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.adapters.ApplicationListAdapter;
|
||||
import org.eu.exodus_privacy.exodusprivacy.adapters.ApplicationViewModel;
|
||||
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.List;
|
||||
|
||||
public class AppListFragment extends Fragment {
|
||||
|
||||
|
||||
private PackageManager packageManager;
|
||||
private NetworkListener networkListener;
|
||||
private ApplicationListAdapter.OnAppClickListener onAppClickListener;
|
||||
private boolean startupRefresh;
|
||||
private ApplistBinding applistBinding;
|
||||
private List<ApplicationViewModel> applications;
|
||||
private ApplicationListAdapter adapter;
|
||||
|
||||
public static AppListFragment newInstance(NetworkListener networkListener, ApplicationListAdapter.OnAppClickListener appClickListener) {
|
||||
AppListFragment fragment = new AppListFragment();
|
||||
fragment.setNetworkListener(networkListener);
|
||||
fragment.setOnAppClickListener(appClickListener);
|
||||
fragment.startupRefresh = true;
|
||||
return fragment;
|
||||
}
|
||||
private ApplicationListAdapter.OnAppClickListener onAppClickListener;
|
||||
private Type filterType = Type.NAME;
|
||||
private Object filterObject = "";
|
||||
private boolean scrollbarEnabled = true;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
//create binding
|
||||
applistBinding = DataBindingUtil.inflate(inflater, R.layout.applist,container,false);
|
||||
//init variables
|
||||
if (applications == null)
|
||||
applications = new ArrayList<>();
|
||||
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();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
||||
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 setOnAppClickListener(ApplicationListAdapter.OnAppClickListener listener){
|
||||
onAppClickListener = listener;
|
||||
}
|
||||
|
||||
public void startRefresh(){
|
||||
if(applistBinding == null)
|
||||
return;
|
||||
applistBinding.layoutProgress.setVisibility(View.VISIBLE);
|
||||
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 setApplications(List<ApplicationViewModel> applicationList){
|
||||
applications = applicationList;
|
||||
if(adapter != null)
|
||||
adapter.displayAppList(applications);
|
||||
}
|
||||
|
||||
public void updateComplete() {
|
||||
if(applistBinding != null) {
|
||||
applistBinding.layoutProgress.setVisibility(View.GONE);
|
||||
applistBinding.swipeRefresh.setRefreshing(false);
|
||||
if(packageManager != null && applistBinding.appList.getAdapter() != null) {
|
||||
((ApplicationListAdapter) applistBinding.appList.getAdapter()).setPackageManager(packageManager);
|
||||
}
|
||||
}
|
||||
public void setFilter(Type type, Object filter){
|
||||
filterType = type;
|
||||
filterObject = filter;
|
||||
if(adapter != null)
|
||||
adapter.filter(type,filterObject);
|
||||
}
|
||||
|
||||
public void setNetworkListener(NetworkListener listener) {
|
||||
this.networkListener = new NetworkListener() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
listener.onSuccess();
|
||||
public void disableScrollBar() {
|
||||
scrollbarEnabled = false;
|
||||
if (applistBinding != null)
|
||||
applistBinding.appList.setVerticalScrollBarEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String error) {
|
||||
listener.onError(error);
|
||||
public void enableScrollBar() {
|
||||
scrollbarEnabled = true;
|
||||
if (applistBinding != null)
|
||||
applistBinding.appList.setVerticalScrollBarEnabled(true);
|
||||
}
|
||||
|
||||
public void onProgress(int resourceId, int progress, int maxProgress) {
|
||||
updateProgress(resourceId, progress, maxProgress);
|
||||
}
|
||||
};
|
||||
public int getTotalApps() {
|
||||
if (adapter == null)
|
||||
return 0;
|
||||
return adapter.getItemCount();
|
||||
}
|
||||
|
||||
private void updateProgress(int resourceId, int progress, int maxProgress) {
|
||||
Activity activity = getActivity();
|
||||
if(activity == null)
|
||||
return;
|
||||
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 int getDisplayedApps() {
|
||||
if (adapter == null)
|
||||
return 0;
|
||||
return adapter.getDisplayedApps();
|
||||
}
|
||||
|
||||
public void setOnAppClickListener(ApplicationListAdapter.OnAppClickListener onAppClickListener) {
|
||||
this.onAppClickListener = onAppClickListener;
|
||||
public enum Type {
|
||||
NAME,
|
||||
TRACKER
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
packageManager = context.getPackageManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
super.onDetach();
|
||||
packageManager = null;
|
||||
}
|
||||
|
||||
public void filter(String filter){
|
||||
adapter.filter(filter);
|
||||
public enum Order {
|
||||
NAME_ASC,
|
||||
NAME_DSC,
|
||||
TRACKER_ASC,
|
||||
TRACKER_DSC,
|
||||
PERMISSIONS_ASC,
|
||||
PERMISSIONS_DSC
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,171 +20,154 @@
|
|||
package org.eu.exodus_privacy.exodusprivacy.fragments;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PermissionInfo;
|
||||
import android.databinding.DataBindingUtil;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
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.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.ReportViewModel;
|
||||
import org.eu.exodus_privacy.exodusprivacy.adapters.ApplicationViewModel;
|
||||
import org.eu.exodus_privacy.exodusprivacy.adapters.PermissionListAdapter;
|
||||
import org.eu.exodus_privacy.exodusprivacy.adapters.TrackerListAdapter;
|
||||
import org.eu.exodus_privacy.exodusprivacy.databinding.ReportBinding;
|
||||
import org.eu.exodus_privacy.exodusprivacy.manager.DatabaseManager;
|
||||
import org.eu.exodus_privacy.exodusprivacy.objects.Permission;
|
||||
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 {
|
||||
import org.eu.exodus_privacy.exodusprivacy.objects.ReportDisplay;
|
||||
public class ReportFragment extends Fragment implements Updatable {
|
||||
|
||||
private PackageManager packageManager;
|
||||
private PackageInfo packageInfo;
|
||||
private PackageInfo packageInfo = null;
|
||||
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();
|
||||
fragment.setPackageManager(packageManager);
|
||||
fragment.setPackageInfo(packageInfo);
|
||||
fragment.setApplicationViewModel(model);
|
||||
fragment.setOnTrackerClickListener(trackerClickListener);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
private void setApplicationViewModel(ApplicationViewModel model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if(savedInstanceState != null && packageInfo == null) {
|
||||
packageInfo = savedInstanceState.getParcelable("PackageInfo");
|
||||
}
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
outState.putParcelable("PackageInfo", packageInfo);
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
reportBinding = DataBindingUtil.inflate(inflater,R.layout.report,container,false);
|
||||
updateComplete();
|
||||
onUpdateComplete();
|
||||
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();
|
||||
String packageName = packageInfo.packageName;
|
||||
String versionName = packageInfo.versionName;
|
||||
|
||||
//setup logo
|
||||
try {
|
||||
reportBinding.logo.setImageDrawable(packageManager.getApplicationIcon(packageName));
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
ReportDisplay reportDisplay = ReportDisplay.buildReportDisplay(context,model,packageManager,packageInfo);
|
||||
ReportViewModel viewModel = new ReportViewModel();
|
||||
viewModel.setReportDisplay(reportDisplay);
|
||||
reportBinding.setReportInfo(viewModel);
|
||||
|
||||
//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));
|
||||
PermissionListAdapter permissionAdapter = new PermissionListAdapter(requestedPermissions);
|
||||
PermissionListAdapter permissionAdapter = new PermissionListAdapter(reportDisplay.permissions);
|
||||
reportBinding.permissions.setNestedScrollingEnabled(false);
|
||||
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
|
||||
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.setAdapter(trackerAdapter);
|
||||
|
||||
//setup creator
|
||||
if(report != null)
|
||||
reportBinding.creator.setText(DatabaseManager.getInstance(context).getCreator(report.appId));
|
||||
reportBinding.reportDate.setText(viewModel.getReportDate(context));
|
||||
reportBinding.creatorValue.setText(viewModel.getCreator(context));
|
||||
reportBinding.codeSignature.setText(viewModel.getCodeSignatureInfo(context));
|
||||
reportBinding.codePermission.setText(viewModel.getCodePermissionInfo(context));
|
||||
|
||||
reportBinding.trackerExplanation.setText(getText(R.string.tracker_infos));
|
||||
reportBinding.trackerExplanation.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
reportBinding.trackerExplanation.setClickable(true);
|
||||
|
||||
reportBinding.permissionExplanationDangerous.setText(getText(R.string.permission_infos_dangerous));
|
||||
reportBinding.permissionExplanationDangerous.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
reportBinding.permissionExplanationDangerous.setClickable(true);
|
||||
|
||||
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
|
||||
reportBinding.creator.setVisibility(View.GONE);
|
||||
intent.setData(Uri.parse("https://f-droid.org/packages/"+reportDisplay.packageName));
|
||||
startActivity(intent);
|
||||
});
|
||||
|
||||
//setup installed
|
||||
String installed_str = context.getString(R.string.installed) +" "+ versionName;
|
||||
reportBinding.installedVersion.setText(installed_str);
|
||||
|
||||
//setup reportversion
|
||||
reportBinding.reportVersion.setVisibility(View.VISIBLE);
|
||||
if(report != null && !report.version.equals(versionName)) {
|
||||
String report_str = context.getString(R.string.report_version)+" "+report.version;
|
||||
reportBinding.reportVersion.setText(report_str);
|
||||
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;
|
||||
}
|
||||
|
||||
public void setPackageInfo(PackageInfo packageInfo) {
|
||||
private void setPackageInfo(PackageInfo packageInfo) {
|
||||
this.packageInfo = packageInfo;
|
||||
}
|
||||
|
||||
private void setOnTrackerClickListener(TrackerListAdapter.OnTrackerClickListener listener) {
|
||||
trackerClickListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepareOptionsMenu(Menu menu) {
|
||||
MenuItem item = menu.findItem(R.id.action_filter);
|
||||
item.setVisible(false);
|
||||
}
|
||||
|
||||
public ApplicationViewModel getModel() {
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package org.eu.exodus_privacy.exodusprivacy.fragments;
|
||||
|
||||
public interface Updatable {
|
||||
void onUpdateComplete();
|
||||
}
|
|
@ -29,8 +29,10 @@ import org.eu.exodus_privacy.exodusprivacy.objects.Report;
|
|||
import org.eu.exodus_privacy.exodusprivacy.objects.Tracker;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class DatabaseManager extends SQLiteOpenHelper {
|
||||
|
@ -44,14 +46,14 @@ public class DatabaseManager extends SQLiteOpenHelper {
|
|||
|
||||
public static DatabaseManager getInstance(Context context) {
|
||||
if(instance == null)
|
||||
instance = new DatabaseManager(context,"Exodus.db",null,1);
|
||||
instance = new DatabaseManager(context,"Exodus.db",null,3);
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
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 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 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, 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_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
|
||||
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) {
|
||||
|
@ -119,6 +150,7 @@ public class DatabaseManager extends SQLiteOpenHelper {
|
|||
values.put("package", application.packageName);
|
||||
values.put("name",application.name);
|
||||
values.put("creator",application.creator);
|
||||
values.put("sources",buildSourcesStr(application.sources));
|
||||
|
||||
if(!existApplication(db, application.packageName)) {
|
||||
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);
|
||||
if(cursor.moveToFirst()) {
|
||||
application.id = cursor.getLong(0);
|
||||
cursor.close();
|
||||
}
|
||||
cursor.close();
|
||||
|
||||
for(Report report : application.reports) {
|
||||
insertOrUpdateReport(db,report,application.id);
|
||||
|
@ -151,6 +183,7 @@ public class DatabaseManager extends SQLiteOpenHelper {
|
|||
values.put("version",report.version);
|
||||
values.put("version_code",report.versionCode);
|
||||
values.put("app_id",appId);
|
||||
values.put("source",report.source);
|
||||
|
||||
if(!existReport(db,report.id)) {
|
||||
values.put("id",report.id);
|
||||
|
@ -180,36 +213,91 @@ public class DatabaseManager extends SQLiteOpenHelper {
|
|||
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 where = "package = ?";
|
||||
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()) {
|
||||
long appId = cursor.getLong(0);
|
||||
cursor.close();
|
||||
where = "app_id = ? and version = ?";
|
||||
whereArgs = new String[2];
|
||||
where = "app_id = ? and version = ? and source = ?";
|
||||
whereArgs = new String[3];
|
||||
whereArgs[0] = String.valueOf(appId);
|
||||
whereArgs[1] = version;
|
||||
whereArgs[2] = source;
|
||||
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;
|
||||
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 = ?";
|
||||
whereArgs = new String[1];
|
||||
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 = getReadableDatabase().query("reports",columns,where,whereArgs,null,null,order);
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
|
@ -222,12 +310,15 @@ public class DatabaseManager extends SQLiteOpenHelper {
|
|||
}
|
||||
|
||||
private Report getReport(long reportId) {
|
||||
SQLiteDatabase db = getReadableDatabase();
|
||||
String where = "id = ?";
|
||||
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
|
||||
if(!cursor.moveToFirst())
|
||||
if(!cursor.moveToFirst()) {
|
||||
cursor.close();
|
||||
return null;
|
||||
}
|
||||
|
||||
Report report = new Report();
|
||||
int col = 0;
|
||||
|
@ -235,19 +326,23 @@ public class DatabaseManager extends SQLiteOpenHelper {
|
|||
long creation = cursor.getLong(col++);
|
||||
report.creationDate = Calendar.getInstance();
|
||||
report.creationDate.setTimeInMillis(creation);
|
||||
report.creationDate.set(Calendar.MILLISECOND,0);
|
||||
long update = cursor.getLong(col++);
|
||||
report.updateDate = Calendar.getInstance();
|
||||
report.updateDate.setTimeInMillis(update);
|
||||
report.updateDate.set(Calendar.MILLISECOND,0);
|
||||
report.downloads = cursor.getString(col++);
|
||||
report.version = cursor.getString(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<>();
|
||||
where = "report_id = ?";
|
||||
String[] columns = {"tracker_id"};
|
||||
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
|
||||
while (cursor.moveToNext()) {
|
||||
report.trackers.add(cursor.getLong(0));
|
||||
|
@ -270,10 +365,11 @@ public class DatabaseManager extends SQLiteOpenHelper {
|
|||
return creator;
|
||||
}
|
||||
|
||||
private Tracker getTracker(long trackerId) {
|
||||
public Tracker getTracker(long trackerId) {
|
||||
SQLiteDatabase db = getReadableDatabase();
|
||||
String where = "id = ?";
|
||||
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;
|
||||
if(cursor.moveToFirst())
|
||||
{
|
||||
|
@ -309,4 +405,40 @@ public class DatabaseManager extends SQLiteOpenHelper {
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,12 +22,6 @@ import android.content.Context;
|
|||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
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.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
@ -42,14 +36,27 @@ import java.text.DateFormat;
|
|||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
|
||||
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
|
||||
*/
|
||||
|
@ -195,8 +202,14 @@ public class NetworkManager {
|
|||
JSONObject tracker = trackers.getJSONObject(trackerId);
|
||||
Tracker track = parseTracker(tracker,trackerId);
|
||||
trackersList.add(track);
|
||||
}
|
||||
if (trackersList.size() == 20) {
|
||||
DatabaseManager.getInstance(mes.context).insertOrUpdateTrackers(trackersList);
|
||||
trackersList.clear();
|
||||
}
|
||||
}
|
||||
if(!trackersList.isEmpty())
|
||||
DatabaseManager.getInstance(mes.context).insertOrUpdateTrackers(trackersList);
|
||||
trackersList.clear();
|
||||
} catch (JSONException e) {
|
||||
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);
|
||||
URL url;
|
||||
try {
|
||||
url = new URL(apiUrl+"applications");
|
||||
url = new URL(apiUrl+"applications?option=short");
|
||||
} catch (Exception e){
|
||||
e.printStackTrace();
|
||||
return;
|
||||
|
@ -218,45 +231,64 @@ public class NetworkManager {
|
|||
|
||||
|
||||
if(object != null) {
|
||||
List<String> handles = new ArrayList<>();
|
||||
try {
|
||||
JSONArray applications = object.getJSONArray("applications");
|
||||
for(int i = 0; i<applications.length(); i++) {
|
||||
JSONObject app = applications.getJSONObject(i);
|
||||
String handle = app.getString("handle");
|
||||
handles.add(handle);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
mes.listener.onError(mes.context.getString(R.string.json_error));
|
||||
}
|
||||
getReports(mes,handles);
|
||||
}
|
||||
mes.listener.onSuccess();
|
||||
}
|
||||
|
||||
private void getReports(Message mes, List<String> handles) {
|
||||
Map<String,Map<String,String>> handles = new HashMap<>();
|
||||
ArrayList<String> packages = mes.args.getStringArrayList("packages");
|
||||
if (packages == null)
|
||||
return;
|
||||
|
||||
packages.retainAll(handles);
|
||||
try {
|
||||
JSONArray applications = object.getJSONArray("applications");
|
||||
|
||||
//manage handles map (handle,UAID)
|
||||
for(int i = 0; i<applications.length(); i++) {
|
||||
JSONObject app = applications.getJSONObject(i);
|
||||
String handle = app.getString("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(handles.size());
|
||||
packages.add(handles.get(val));
|
||||
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) {
|
||||
e.printStackTrace();
|
||||
mes.listener.onError(mes.context.getString(R.string.json_error));
|
||||
}
|
||||
object = null;
|
||||
getReports(mes,handles,packages);
|
||||
}
|
||||
mes.listener.onSuccess();
|
||||
}
|
||||
|
||||
|
||||
private void getReports(Message mes, Map<String, Map<String,String>> handles, ArrayList<String> packages) {
|
||||
for(int i = 0; i < packages.size(); i++) {
|
||||
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;
|
||||
try {
|
||||
url = new URL(apiUrl+"search/"+handle);
|
||||
|
@ -272,6 +304,7 @@ public class NetworkManager {
|
|||
ArrayList<String> packages = mes.args.getStringArrayList("packages");
|
||||
if(packages != null && packages.contains(handle)) {
|
||||
Application app = parseApplication(application, handle);
|
||||
app.sources = sources;
|
||||
DatabaseManager.getInstance(mes.context).insertOrUpdateApplication(app);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
|
@ -285,6 +318,7 @@ public class NetworkManager {
|
|||
application.packageName = packageName;
|
||||
application.creator = object.getString("creator");
|
||||
application.name = object.getString("name");
|
||||
|
||||
//parse Report
|
||||
application.reports = new HashSet<>();
|
||||
JSONArray reports = object.getJSONArray("reports");
|
||||
|
@ -300,6 +334,7 @@ public class NetworkManager {
|
|||
report.id = object.getLong("id");
|
||||
report.downloads = object.getString("downloads");
|
||||
report.version = object.getString("version");
|
||||
report.source = object.getString("source");
|
||||
if(!object.getString("version_code").isEmpty())
|
||||
report.versionCode = Long.parseLong(object.getString("version_code"));
|
||||
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.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
report.updateDate.setTime(dateFormat.parse(object.getString("updated_at")));
|
||||
report.updateDate.set(Calendar.MILLISECOND,0);
|
||||
|
||||
report.creationDate = Calendar.getInstance();
|
||||
report.creationDate.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
report.creationDate.setTime(dateFormat.parse(object.getString("creation_date")));
|
||||
report.creationDate.set(Calendar.MILLISECOND,0);
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
package org.eu.exodus_privacy.exodusprivacy.objects;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
public class Application {
|
||||
|
@ -26,6 +28,7 @@ public class Application {
|
|||
public String name;
|
||||
public String creator;
|
||||
public Set<Report> reports;
|
||||
public Map<String,String> sources;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
@ -35,13 +38,13 @@ public class Application {
|
|||
Application that = (Application) o;
|
||||
|
||||
if (id != that.id) return false;
|
||||
return packageName.equals(that.packageName);
|
||||
return packageName != null ? packageName.equals(that.packageName) : that.packageName == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = (int) (id ^ (id >>> 32));
|
||||
result = 31 * result + packageName.hashCode();
|
||||
result = 31 * result + (packageName != null ? packageName.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
package org.eu.exodus_privacy.exodusprivacy.objects;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
public class Permission {
|
||||
public String name;
|
||||
public String fullName;
|
||||
public String description;
|
||||
public Drawable icon;
|
||||
public boolean dangerous;
|
||||
public boolean expanded = false;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ public class Report {
|
|||
public long versionCode;
|
||||
public Set<Long> trackers;
|
||||
public long appId;
|
||||
public String source;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 448 B |
Before Width: | Height: | Size: 384 B |
Before Width: | Height: | Size: 265 B |
Before Width: | Height: | Size: 492 B |
Before Width: | Height: | Size: 744 B |
Before Width: | Height: | Size: 967 B |
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
<RelativeLayout
|
||||
android:id="@+id/base_info"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<ImageView
|
||||
|
@ -29,15 +31,44 @@
|
|||
android:layout_alignParentEnd="true"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
<TextView
|
||||
android:textAlignment="center"
|
||||
android:background="@drawable/square_green"
|
||||
android:id="@+id/app_tracker_nb"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="wrap_content" />
|
||||
<TextView
|
||||
android:id="@+id/app_permission_nb"
|
||||
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_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>
|
||||
<TextView
|
||||
android:layout_centerInParent="true"
|
||||
|
@ -52,6 +83,12 @@
|
|||
</RelativeLayout>
|
||||
<TextView
|
||||
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:text="@string/tested"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -1,76 +1,13 @@
|
|||
<?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/>
|
||||
<android.support.constraint.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" />
|
||||
</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
|
||||
<androidx.recyclerview.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>
|
||||
android:layout_height="match_parent"
|
||||
android:scrollbars="vertical"/>
|
||||
</layout>
|
|
@ -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>
|
|
@ -8,12 +8,8 @@
|
|||
>
|
||||
<data/>
|
||||
<FrameLayout
|
||||
android:layout_marginRight="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:id="@+id/fragment_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
|
|
@ -1,19 +1,56 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<data/>
|
||||
<LinearLayout
|
||||
android:id="@+id/main_layout"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<LinearLayout
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
>
|
||||
<RelativeLayout
|
||||
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
|
||||
android:text="▼"
|
||||
android:id="@+id/arrow"
|
||||
|
@ -21,20 +58,18 @@
|
|||
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"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentTop="true"
|
||||
/>
|
||||
</RelativeLayout>
|
||||
<TextView
|
||||
android:id="@+id/permission_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
<TextView
|
||||
android:paddingLeft="20dp"
|
||||
android:paddingStart="20dp"
|
||||
android:id="@+id/permission_description"
|
||||
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>
|
||||
</layout>
|
|
@ -1,130 +1,444 @@
|
|||
<?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/>
|
||||
<ScrollView
|
||||
<data>
|
||||
<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_height="match_parent">
|
||||
<RelativeLayout
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
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_height="wrap_content">
|
||||
android:layout_height="match_parent">
|
||||
<ImageView
|
||||
android:id="@+id/logo"
|
||||
android:layout_width="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
|
||||
android:layout_marginLeft="2dp"
|
||||
android:layout_marginStart="2dp"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
<TextView
|
||||
android:id="@+id/name"
|
||||
android:text="test"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Large"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
<TextView
|
||||
android:text="Creator"
|
||||
android:id="@+id/creator"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
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="wrap_content" />
|
||||
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:text="installed version"
|
||||
android:id="@+id/installed_version"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
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:text="Report version"
|
||||
android:id="@+id/report_version"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
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:autoLink="web"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
android:layout_height="wrap_content"
|
||||
android:autoLink="web"
|
||||
android:text="@string/view_on_exodus"
|
||||
android:visibility="@{reportInfo.reportVisibility ? View.VISIBLE : View.GONE}"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:textAlignment="textEnd"
|
||||
app:layout_constraintTop_toBottomOf="@id/report_date"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@color/colorPurple"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/view_store"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:autoLink="web"
|
||||
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
|
||||
android:id="@+id/trackers_nb_list"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="0dp"
|
||||
android:text="@{reportInfo.trackerNumberStr}"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginTop="30dp"
|
||||
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"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/trackers_title_list"
|
||||
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"
|
||||
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"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/code_signature"
|
||||
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_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/code_signature"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tracker_explanation"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="@{reportInfo.trackerVisibility ? View.VISIBLE : View.GONE}"
|
||||
android:text="@string/tracker_infos"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
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: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
|
||||
android:id="@+id/tracker_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="50"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/trackers_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/colorPrimary"
|
||||
android:text="@string/trackers"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Large"
|
||||
android:textColor="@android:color/white" />
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/trackers"
|
||||
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:layout_marginBottom="5dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_weight="50"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<TextView
|
||||
android:background="@color/colorPrimary"
|
||||
android:id="@+id/permissions_title"
|
||||
android:text="@string/permissions"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Large"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@android:color/white"
|
||||
android:textAlignment="center"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
/>
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/permissions"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
</ScrollView>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</layout>
|
|
@ -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>
|
|
@ -3,25 +3,11 @@
|
|||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
>
|
||||
<data/>
|
||||
<LinearLayout
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<TextView
|
||||
android:text="●"
|
||||
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_marginTop="5dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:textColor="@color/textColorDark"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
</layout>
|
|
@ -5,6 +5,11 @@
|
|||
android:id="@+id/action_filter"
|
||||
android:icon="@drawable/ic_search"
|
||||
android:title="@string/menu_action_filter"
|
||||
app:actionViewClass="android.support.v7.widget.SearchView"
|
||||
app:actionViewClass="android.widget.SearchView"
|
||||
app:showAsAction="ifRoom|collapseActionView" />
|
||||
<item
|
||||
android:id="@+id/action_settings"
|
||||
android:icon="@drawable/ic_settings"
|
||||
android:title="@string/menu_action_settings"
|
||||
app:showAsAction="ifRoom" />
|
||||
</menu>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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 l’accés a la llista d’aplicacions 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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -1,26 +1,52 @@
|
|||
<resources>
|
||||
<string name="app_name">Exodus Privacy</string>
|
||||
<string name="app_logo">Application Logo</string>
|
||||
<string name="trackers">Pisteurs :</string>
|
||||
<string name="permissions">Autorisations :</string>
|
||||
<string name="trackers_pct">Pisteurs :</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="not_connected">Aucune connexion Internet</string>
|
||||
<string name="json_error">Erreur JSON</string>
|
||||
<string name="installed">Version installé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_permissions">Aucune autorisation n\'est demandée par cette application</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="no_trackers">L’application pourrait contenir un ou plusieurs pisteurs que nous n’avons pas encore identifiés.</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 (%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="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">Récupération des applications</string>
|
||||
<string name="parse_application">Traitement des applications :</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">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 n’avons pas trouvé la signature de pisteurs que nous connaissons dans l’application. </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 -->
|
||||
<string name="menu_action_filter">Filtrer</string>
|
||||
<string name="menu_action_settings">Paramètres de l\'application</string>
|
||||
</resources>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -3,4 +3,19 @@
|
|||
<color name="colorPrimary">#684971</color>
|
||||
<color name="colorPrimaryDark">#3d2b43</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>
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="exodus">9c6106a229bc5f34b5802e5861bcb87d1626617d</string>
|
||||
<string name="app_title">εxodus</string>
|
||||
<string name="app_name">Exodus</string>
|
||||
</resources>
|
|
@ -1,26 +1,53 @@
|
|||
<resources>
|
||||
<string name="app_name">Exodus Privacy</string>
|
||||
<string name="app_logo">Application Logo</string>
|
||||
<string name="trackers">Trackers:</string>
|
||||
<string name="permissions">Permissions:</string>
|
||||
<string name="trackers_pct">Trackers:</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="not_connected">No Internet Connection</string>
|
||||
<string name="json_error">JSON Error</string>
|
||||
<string name="installed">Installed Version: </string>
|
||||
<string name="report_version">Tested Version: </string>
|
||||
<string name="no_trackers">No tested trackers were found</string>
|
||||
<string name="no_permissions">No permissions are requested by this application</string>
|
||||
<string name="tested">There\'s no report for the installed version, informations displayed are based on another (presumably older) version</string>
|
||||
<string name="no_trackers">The application could contain tracker(s) we do not know yet.</string>
|
||||
<string name="no_permissions">This application doesn\'t require any permissions.</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="no_package_manager">Your system seems doesn\'t provide access to 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_package_manager">It appears that your system doesn\'t allow access to the list of installed apps.</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">Getting applications</string>
|
||||
<string name="parse_application">Processing Applications:</string>
|
||||
<string name="parse_trackers">Processing Trackers:</string>
|
||||
<string name="get_trackers_connection">Getting trackers: Waiting for server connection</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 -->
|
||||
<string name="menu_action_filter">Filter</string>
|
||||
<string name="menu_action_settings">Application Settings</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -6,9 +6,10 @@ buildscript {
|
|||
maven {
|
||||
url "https://maven.google.com"
|
||||
}
|
||||
google()
|
||||
}
|
||||
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
|
||||
// in the individual module build.gradle files
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
يساعدك Exodus Privacy على معرفة أي مِن المتعقبين والتصريحات المدمجة في التطبيقات المثبتة على جهازك.
|
||||
|
||||
يقوم التطبيق بتحميل التقارير من Exodus Privacy (https://exodus-privacy.eu.org/) ويعرضها عليك تطبيقًا تلو الآخر
|
||||
|
||||
بإمكان التطبيق اكتشاف التطبيقات المثبتة من متجر Google Play فقط.
|
||||
|
||||
الرمز المصدري: https://github.com/Exodus-Privacy/exodus-android-app/
|
|
@ -0,0 +1 @@
|
|||
يُظهِر المتعقّبِين والتصريحات التي تطلبها التطبيقات المثبتة الأخرى
|
|
@ -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/
|
|
@ -0,0 +1 @@
|
|||
Mostra els rastrejadors i els permisos de les aplicacions instal·lades
|
|
@ -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/
|
|
@ -0,0 +1 @@
|
|||
Tracker und Berechtigungen anderer installierter Apps anzeigen
|
|
@ -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/
|
|
@ -0,0 +1 @@
|
|||
Εμφάνιση ιχνηλατών και αδειών από άλλες εγκατεστημένες εφαρμογές
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
|||
- rewrite of report page
|
||||
- bug fix
|
|
@ -0,0 +1,4 @@
|
|||
- Add Trackers Page
|
||||
- Add F-Droid detection Support
|
||||
- bug fix
|
||||
- New Translations (Italian, Russian, Greek, Arabic, German, and Catalan)
|
|
@ -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/
|
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 194 KiB |
After Width: | Height: | Size: 129 KiB |
After Width: | Height: | Size: 123 KiB |
After Width: | Height: | Size: 134 KiB |
After Width: | Height: | Size: 160 KiB |
After Width: | Height: | Size: 158 KiB |
After Width: | Height: | Size: 158 KiB |
|
@ -0,0 +1 @@
|
|||
Show trackers and permissions from other installed apps
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
|||
- Refonte de la page des rapports
|
||||
- Correction de bug
|
|
@ -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)
|
|
@ -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/
|
After Width: | Height: | Size: 206 KiB |
After Width: | Height: | Size: 124 KiB |
After Width: | Height: | Size: 130 KiB |
After Width: | Height: | Size: 157 KiB |
After Width: | Height: | Size: 165 KiB |
After Width: | Height: | Size: 157 KiB |
After Width: | Height: | Size: 159 KiB |
|
@ -0,0 +1 @@
|
|||
Montre les pisteurs et permissions des applications installées
|
|
@ -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/
|