Compare commits

...

14 Commits

Author SHA1 Message Date
Thomas 90848c678a Create flavor for fdroid & google as not the same permission is used. 2021-01-11 11:38:08 +01:00
Thomas 6b5631bb43 Merge branch 'master' of https://github.com/stom79/exodus-android-app 2021-01-08 09:01:59 +01:00
Thomas 2f531d7d24 Avoid to have a remaining refresh bar when going back to main screen. 2021-01-04 18:32:28 +01:00
Thomas e3f2325d00 Some fixes + Android 11 permission for scanning app. 2021-01-04 18:06:51 +01:00
Thomas 3d5b4fd04f Merge branch 'stom79_baseline' 2020-12-16 08:45:03 +01:00
Thomas 7d4d2533ac Remove previous feature that copied id into clipboard. 2020-12-16 08:42:09 +01:00
Thomas c40afdf348 Merge branch 'stom79_baseline'
# Conflicts:
#	app/src/main/java/org/eu/exodus_privacy/exodusprivacy/CheckAppActivity.java
2020-12-16 08:22:49 +01:00
Thomas 20f2e8412f Allow the refresh for tracking list 2020-12-15 18:21:10 +01:00
Thomas 9d330bf07f Improve layout 2020-12-15 17:24:46 +01:00
Thomas cae158d489 Some improvements 2020-12-15 16:53:36 +01:00
Thomas efe9a88209 Allow to navigate through viewpager 2020-12-15 16:31:06 +01:00
Thomas 3bf596f1cf Prepare views + percent 2020-12-15 13:59:19 +01:00
Thomas 7e6e1fcff6 Menu + transitions 2020-12-15 10:00:26 +01:00
Thomas 2a091043c6 Prepare new layout with bottom menu + hide top bar when scrolling 2020-12-15 09:20:44 +01:00
49 changed files with 833 additions and 249 deletions

View File

@ -28,20 +28,56 @@ android {
}
productFlavors {
exodus {
exodus_google {
applicationId "org.eu.exodus_privacy.exodusprivacy"
buildConfigField "boolean", "amal", "false"
}
amal {
amal_google {
applicationId "app.fedilab.amal"
buildConfigField "boolean", "amal", "true"
}
fedilab_google {
applicationId "app.fedilab.exodusprivacy"
buildConfigField "boolean", "amal", "false"
}
exodus_fdroid {
applicationId "org.eu.exodus_privacy.exodusprivacy"
buildConfigField "boolean", "amal", "false"
}
amal_fdroid {
applicationId "app.fedilab.amal"
buildConfigField "boolean", "amal", "true"
}
fedilab_fdroid {
applicationId "app.fedilab.exodusprivacy"
buildConfigField "boolean", "amal", "false"
}
}
sourceSets {
exodus {
exodus_google {
res.srcDirs = ['src/main/res', 'src/exodus/res']
manifest.srcFile 'src/google/AndroidManifest.xml'
}
amal {
fedilab_google {
res.srcDirs = ['src/main/res', 'src/fedilab/res']
manifest.srcFile 'src/google/AndroidManifest.xml'
}
amal_google {
res.srcDirs = ['src/main/res', 'src/amal/res']
manifest.srcFile 'src/google/AndroidManifest.xml'
}
exodus_fdroid {
res.srcDirs = ['src/main/res', 'src/exodus/res']
manifest.srcFile 'src/fdroid/AndroidManifest.xml'
}
fedilab_fdroid {
res.srcDirs = ['src/main/res', 'src/fedilab/res']
manifest.srcFile 'src/fdroid/AndroidManifest.xml'
}
amal_fdroid {
res.srcDirs = ['src/main/res', 'src/amal/res']
manifest.srcFile 'src/fdroid/AndroidManifest.xml'
}
}

View File

@ -3,7 +3,7 @@
<color name="colorPrimary">#005e8b</color>
<color name="colorPrimaryDark">#3d2b43</color>
<color name="colorAccent">#007bff</color>
<color name="percent">#77005e8b</color>
<color name="colorGreen">#6fc384</color>
<color name="colorLightRed">#e46772</color>
<color name="colorLightYellow">#ffdb66</color>

View File

@ -3,6 +3,7 @@
<color name="colorPrimary">#684971</color>
<color name="colorPrimaryDark">#3d2b43</color>
<color name="colorAccent">#3d2b43</color>
<color name="percent">#77684971</color>
<color name="colorGreen">#6fc384</color>
<color name="colorLightRed">#e46772</color>

View File

@ -0,0 +1,9 @@
<?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.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,27 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<group
android:scaleX="1.48"
android:scaleY="1.48"
android:translateX="14.04"
android:translateY="14.04">
<path
android:fillAlpha="1"
android:fillColor="#1976D2"
android:pathData="M2.1737,27a24.8263,24.5976 0,1 0,49.6526 0a24.8263,24.5976 0,1 0,-49.6526 0z"
android:strokeWidth="0.0297731"
android:strokeColor="#00000000" />
<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:strokeWidth="0.03275258"
android:strokeAlpha="1"
android:strokeColor="#00000000"
android:strokeLineCap="butt"
android:strokeLineJoin="miter" />
</group>
</vector>

View File

@ -0,0 +1,21 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="100dp"
android:height="100dp"
android:viewportWidth="54"
android:viewportHeight="54">
<path
android:fillAlpha="1"
android:fillColor="#1976D2"
android:pathData="M2.1737,27a24.8263,24.5976 0,1 0,49.6526 0a24.8263,24.5976 0,1 0,-49.6526 0z"
android:strokeWidth="0.0297731"
android:strokeColor="#00000000" />
<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:strokeWidth="0.03275258"
android:strokeAlpha="1"
android:strokeColor="#00000000"
android:strokeLineCap="butt"
android:strokeLineJoin="miter" />
</vector>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#1976D2</color>
<color name="colorPrimaryDark">#0D47A1</color>
<color name="colorAccent">#64B5F6</color>
<color name="percent">#771976D2</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>

View File

@ -0,0 +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 (test)</string>
</resources>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#1976D2</color>
</resources>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.eu.exodus_privacy.exodusprivacy">
<queries>
<intent>
<action android:name="android.intent.action.MAIN" />
</intent>
</queries>
</manifest>

View File

@ -3,8 +3,10 @@
xmlns:tools="http://schemas.android.com/tools"
package="org.eu.exodus_privacy.exodusprivacy">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<application
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
@ -14,6 +16,7 @@
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning"
android:largeHeap="true">
<activity
android:name=".MainActivity"
android:configChanges="keyboardHidden|orientation|screenSize">

View File

@ -48,8 +48,6 @@ public class CheckAppActivity extends AppCompatActivity implements NetworkListen
private static final Pattern fdroidRegex = Pattern.compile("https?://f-droid\\.org/([\\w-]+/)?packages/([\\w.-]+)");
private static final Pattern googleRegex = Pattern.compile("https?://play\\.google\\.com/store/apps/details\\?id=([\\w.-]+)");
private String app_id;
ArrayList<Updatable> fragments;
AppCheckActivityBinding binding;
TrackerListAdapter.OnTrackerClickListener onTrackerClickListener = id -> {
@ -62,6 +60,7 @@ public class CheckAppActivity extends AppCompatActivity implements NetworkListen
.addToBackStack(null)
.commit();
};
private String app_id;
private TrackerListAdapter.OnTrackerClickListener trackerClickListener;
@Override
@ -105,10 +104,11 @@ public class CheckAppActivity extends AppCompatActivity implements NetworkListen
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(CheckAppActivity.this);
dialogBuilder.setTitle(getString(R.string.app_not_analyzed_title));
dialogBuilder.setMessage(getString(R.string.app_not_analyzed));
//noinspection RedundantSuppression
dialogBuilder.setPositiveButton(R.string.submit, (dialog, id) -> {
Uri uri;
//noinspection ConstantConditions
if (BuildConfig.FLAVOR.equals("exodus")) {
if (!BuildConfig.amal) {
uri = Uri.parse("https://reports.exodus-privacy.eu.org/analysis/submit/#" + app_id);
} else {
uri = Uri.parse("https://exodus.phm.education.gouv.fr/analysis/submit/#" + app_id);

View File

@ -33,12 +33,18 @@ import android.view.inputmethod.InputMethodManager;
import android.widget.ImageView;
import android.widget.PopupMenu;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.SearchView;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.fragment.app.FragmentTransaction;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.android.material.snackbar.Snackbar;
import org.eu.exodus_privacy.exodusprivacy.adapters.ApplicationListAdapter;
@ -47,6 +53,7 @@ import org.eu.exodus_privacy.exodusprivacy.adapters.TrackerListAdapter;
import org.eu.exodus_privacy.exodusprivacy.databinding.MainBinding;
import org.eu.exodus_privacy.exodusprivacy.fragments.ComputeAppList;
import org.eu.exodus_privacy.exodusprivacy.fragments.HomeFragment;
import org.eu.exodus_privacy.exodusprivacy.fragments.MyTrackersFragment;
import org.eu.exodus_privacy.exodusprivacy.fragments.ReportFragment;
import org.eu.exodus_privacy.exodusprivacy.fragments.TrackerFragment;
import org.eu.exodus_privacy.exodusprivacy.fragments.Updatable;
@ -65,8 +72,20 @@ public class MainActivity extends AppCompatActivity {
private Menu toolbarMenu;
private String packageName;
private MainBinding binding;
private final BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= item -> {
int itemId = item.getItemId();
if (itemId == R.id.navigation_apps) {
binding.viewpager.setCurrentItem(0);
} else if (itemId == R.id.navigation_analytics) {
binding.viewpager.setCurrentItem(1);
}
return true;
};
private ApplicationListAdapter.OnAppClickListener onAppClickListener;
private TrackerListAdapter.OnTrackerClickListener onTrackerClickListener;
private String previousQuery = "";
private HomeFragment home;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -102,7 +121,7 @@ public class MainActivity extends AppCompatActivity {
for (Updatable updatable : fragments) {
updatable.onUpdateComplete();
}
Snackbar bar = Snackbar.make(mainBinding.fragmentContainer, error, Snackbar.LENGTH_LONG);
Snackbar bar = Snackbar.make(mainBinding.viewpager, error, Snackbar.LENGTH_LONG);
bar.show();
});
}
@ -113,20 +132,56 @@ public class MainActivity extends AppCompatActivity {
}
};
TrackerListAdapter.OnTrackerClickListener onTrackerClickListener = id -> {
setSupportActionBar(binding.toolbar);
binding.navView.inflateMenu(R.menu.bottom_nav_menu);
binding.navView.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
binding.viewpager.setOffscreenPageLimit(2);
binding.viewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
MenuItem item = binding.navView.getMenu().getItem(position);
binding.navView.setSelectedItemId(item.getItemId());
if (binding.fragmentContainer.getVisibility() == View.VISIBLE) {
while (fragments.size() > 0) {
getSupportFragmentManager().popBackStack();
fragments.remove(fragments.size() - 1);
}
binding.fragmentContainer.setVisibility(View.GONE);
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
onTrackerClickListener = id -> {
TrackerFragment tracker = TrackerFragment.newInstance(id);
tracker.setOnAppClickListener(onAppClickListener);
fragments.add(tracker);
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
binding.fragmentContainer.setVisibility(View.VISIBLE);
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();
};
onAppClickListener = (vm) -> {
try {
PackageManager pm = getPackageManager();
PackageInfo packageInfo = pm.getPackageInfo(vm.packageName, PackageManager.GET_PERMISSIONS);
ReportFragment report = ReportFragment.newInstance(pm, vm, packageInfo, onTrackerClickListener);
@ -137,7 +192,7 @@ public class MainActivity extends AppCompatActivity {
.replace(R.id.fragment_container, report)
.addToBackStack(null)
.commit();
binding.fragmentContainer.setVisibility(View.VISIBLE);
packageName = packageInfo.packageName;
searchView.clearFocus();
@ -145,22 +200,18 @@ public class MainActivity extends AppCompatActivity {
(toolbarMenu.findItem(R.id.action_filter)).collapseActionView();
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
assert imm != null;
imm.hideSoftInputFromWindow(mainBinding.fragmentContainer.getWindowToken(), 0);
imm.hideSoftInputFromWindow(mainBinding.viewpager.getWindowToken(), 0);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
};
HomeFragment home = new 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, home)
.commit();
PagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
binding.viewpager.setAdapter(mPagerAdapter);
home.startRefresh();
}
@ -215,12 +266,15 @@ public class MainActivity extends AppCompatActivity {
}
});
MenuItem settingsMenuItem = menu.findItem(R.id.action_settings);
Updatable fragment = fragments.get(fragments.size() - 1);
settingsMenuItem.setVisible(fragment instanceof ReportFragment);
if (fragments.size() > 0) {
Updatable fragment = fragments.get(fragments.size() - 1);
settingsMenuItem.setVisible(fragment instanceof ReportFragment);
} else {
settingsMenuItem.setVisible(false);
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_settings) {
@ -229,13 +283,13 @@ public class MainActivity extends AppCompatActivity {
try {
startActivity(intent);
} catch (android.content.ActivityNotFoundException e) {
Snackbar bar = Snackbar.make(binding.fragmentContainer, R.string.no_settings, Snackbar.LENGTH_LONG);
Snackbar bar = Snackbar.make(binding.viewpager, R.string.no_settings, Snackbar.LENGTH_LONG);
bar.show();
}
return true;
} else if (item.getItemId() == R.id.action_filter_options) {
View menuItemView = findViewById(R.id.action_filter_options);
PopupMenu popup = new PopupMenu(binding.fragmentContainer.getContext(), menuItemView);
PopupMenu popup = new PopupMenu(binding.viewpager.getContext(), menuItemView);
popup.getMenuInflater()
.inflate(R.menu.popup_menu_filter, popup.getMenu());
MenuItem filterByNameMI = popup.getMenu().findItem(R.id.filter_by_name);
@ -282,4 +336,30 @@ public class MainActivity extends AppCompatActivity {
return false;
}
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
ScreenSlidePagerAdapter(FragmentManager fm) {
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
}
@NonNull
@Override
public Fragment getItem(final int position) {
//noinspection SwitchStatementWithTooFewBranches
switch (position) {
case 1:
MyTrackersFragment myTrackersFragment = new MyTrackersFragment();
myTrackersFragment.setOnTrackerClickListener(onTrackerClickListener);
return myTrackersFragment;
default:
return home;
}
}
@Override
public int getCount() {
return 2;
}
}
}

View File

@ -30,7 +30,7 @@ public class Utils {
public static final String LAST_REFRESH = "last_refresh";
public static String getDomain() {
return BuildConfig.FLAVOR.compareTo("exodus") == 0 ? "reports.exodus-privacy.eu.org" : "exodus.phm.education.gouv.fr";
return BuildConfig.amal ? "exodus.phm.education.gouv.fr" : "reports.exodus-privacy.eu.org";
}
@SuppressLint("PackageManagerGetSignatures")

View File

@ -0,0 +1,109 @@
package org.eu.exodus_privacy.exodusprivacy.adapters;
/*
* Copyright (C) 2020 Thomas Schneider
*
* 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.
*/
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import org.eu.exodus_privacy.exodusprivacy.R;
import org.eu.exodus_privacy.exodusprivacy.databinding.MyTrackerItemBinding;
import org.eu.exodus_privacy.exodusprivacy.objects.MyTracker;
import java.util.List;
public class MyTrackersListAdapter extends RecyclerView.Adapter<MyTrackersListAdapter.TrackerListViewHolder> {
private final TrackerClickListener trackerClickListener;
private final List<MyTracker> myTrackers;
private final int max, installedApps;
private int viewWidth = 0;
public MyTrackersListAdapter(List<MyTracker> mTrackers, TrackerClickListener listener, int maxValue, int appInstalled) {
myTrackers = mTrackers;
trackerClickListener = listener;
max = maxValue;
installedApps = appInstalled;
}
@NonNull
@Override
public MyTrackersListAdapter.TrackerListViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
MyTrackerItemBinding itemBinding = MyTrackerItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
return new TrackerListViewHolder(itemBinding);
}
@Override
public void onBindViewHolder(@NonNull MyTrackersListAdapter.TrackerListViewHolder holder, int position) {
if (myTrackers != null) {
MyTracker myTracker = myTrackers.get(position);
holder.viewDataBinding.trackerName.setText(myTracker.tracker.name);
holder.viewDataBinding.trackerCount.setText(holder.viewDataBinding.trackerCount.getContext().getString(R.string.apps, String.valueOf(myTracker.number)));
holder.viewDataBinding.getRoot().setOnClickListener(v -> trackerClickListener.onTrackerClick(myTracker.tracker.id));
float percent = (float) myTracker.number / (float) max;
int percentApp = myTracker.number * 100 / installedApps;
holder.viewDataBinding.percent.getLayoutParams().width = (int) (viewWidth * percent);
holder.viewDataBinding.percentVal.setText(String.format("%s %%", percentApp));
if (percentApp >= 50)
holder.viewDataBinding.trackerCount.setBackgroundResource(R.drawable.square_red);
else if (percentApp >= 33)
holder.viewDataBinding.trackerCount.setBackgroundResource(R.drawable.square_dark_orange);
else if (percentApp >= 20)
holder.viewDataBinding.trackerCount.setBackgroundResource(R.drawable.square_yellow);
else
holder.viewDataBinding.trackerCount.setBackgroundResource(R.drawable.square_light_blue);
holder.viewDataBinding.percent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
holder.viewDataBinding.percent.getViewTreeObserver().removeOnGlobalLayoutListener(this);
if (viewWidth == 0) {
viewWidth = holder.viewDataBinding.percent.getWidth();
notifyDataSetChanged();
}
}
});
} else
holder.viewDataBinding.trackerName.setText(R.string.no_trackers);
}
@Override
public int getItemCount() {
return myTrackers.size();
}
public interface TrackerClickListener {
void onTrackerClick(long trackerId);
}
static class TrackerListViewHolder extends RecyclerView.ViewHolder {
MyTrackerItemBinding viewDataBinding;
TrackerListViewHolder(MyTrackerItemBinding dataBinding) {
super(dataBinding.getRoot());
viewDataBinding = dataBinding;
}
}
}

View File

@ -10,7 +10,6 @@ import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.eu.exodus_privacy.exodusprivacy.R;
import org.eu.exodus_privacy.exodusprivacy.adapters.ApplicationListAdapter;
@ -22,7 +21,7 @@ import java.util.List;
public class AppListFragment extends Fragment {
private static int firstVisiblePosition = 0;
private ApplistBinding applistBinding;
private List<ApplicationViewModel> applications;
private ApplicationListAdapter adapter;
@ -43,18 +42,6 @@ public class AppListFragment extends Fragment {
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(context);
applistBinding.appList.setLayoutManager(linearLayoutManager);
applistBinding.appList.setVerticalScrollBarEnabled(scrollbarEnabled);
applistBinding.appList.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
firstVisiblePosition = linearLayoutManager.findFirstCompletelyVisibleItemPosition();
}
});
adapter = new ApplicationListAdapter(onAppClickListener);
adapter.displayAppList(applications);
adapter.filter(filterType, filterObject);
@ -105,9 +92,6 @@ public class AppListFragment extends Fragment {
return adapter.getDisplayedApps();
}
public void scrollTo() {
applistBinding.appList.scrollToPosition(firstVisiblePosition);
}
public enum Type {
NAME,

View File

@ -16,7 +16,7 @@ import java.util.Map;
public class ComputeAppList {
private static final String gStore = "com.android.vending";
private static final String fdroid = "ord.fdroid.fdroid";
private static final String fdroid = "org.fdroid.fdroid";
public static List<ApplicationViewModel> compute(PackageManager packageManager,
@ -32,7 +32,7 @@ public class ComputeAppList {
if (userOrderChoice == null) {
userOrderChoice = order.DEFAULT;
}
vms = order(vms, userOrderChoice);
order(vms, userOrderChoice);
return vms;
}
@ -41,9 +41,8 @@ public class ComputeAppList {
*
* @param vms List<ApplicationViewModel>
* @param orderChoice order
* @return List<ApplicationViewModel>
*/
private static List<ApplicationViewModel> order(List<ApplicationViewModel> vms, order orderChoice) {
private static void order(List<ApplicationViewModel> vms, order orderChoice) {
if (orderChoice == order.LESS_TRACKERS) {
Collections.sort(vms, (obj1, obj2) -> Integer.compare(obj1.requestedPermissions != null ? obj1.requestedPermissions.length : 0, obj2.requestedPermissions != null ? obj2.requestedPermissions.length : 0));
Collections.sort(vms, (obj1, obj2) -> Integer.compare(obj1.trackers != null ? obj1.trackers.size() : 0, obj2.trackers != null ? obj2.trackers.size() : 0));
@ -59,7 +58,6 @@ public class ComputeAppList {
} else {
Collections.sort(vms, (obj1, obj2) -> String.valueOf(obj1.label).compareToIgnoreCase(String.valueOf(obj2.label)));
}
return vms;
}
private static void convertPackagesToViewModels(List<ApplicationViewModel> appsToBuild,

View File

@ -1,173 +0,0 @@
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.Collections;
import java.util.List;
import java.util.Map;
public class ComputeAppListTask extends AsyncTask<Void, Void, List<ApplicationViewModel>> {
private static final String gStore = "com.android.vending";
private static final String fdroid = "ord.fdroid.fdroid";
private final WeakReference<PackageManager> packageManagerRef;
private final WeakReference<DatabaseManager> databaseManagerRef;
private final WeakReference<Listener> listenerRef;
order userOrderChoice;
ComputeAppListTask(WeakReference<PackageManager> packageManagerRef,
WeakReference<DatabaseManager> databaseManagerRef,
WeakReference<Listener> listenerRef, order orderChoice) {
this.packageManagerRef = packageManagerRef;
this.databaseManagerRef = databaseManagerRef;
this.listenerRef = listenerRef;
userOrderChoice = orderChoice;
}
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);
}
//Reordering should done here
if (userOrderChoice == null) {
userOrderChoice = order.DEFAULT;
}
vms = order(vms, userOrderChoice);
return vms;
}
private List<ApplicationViewModel> order(List<ApplicationViewModel> vms, order orderChoice) {
if (orderChoice == order.LESS_TRACKERS) {
Collections.sort(vms, (obj1, obj2) -> Integer.compare(obj1.requestedPermissions != null ? obj1.requestedPermissions.length : 0, obj2.requestedPermissions != null ? obj2.requestedPermissions.length : 0));
Collections.sort(vms, (obj1, obj2) -> Integer.compare(obj1.trackers != null ? obj1.trackers.size() : 0, obj2.trackers != null ? obj2.trackers.size() : 0));
} else if (orderChoice == order.MOST_TRACKERS) {
Collections.sort(vms, (obj1, obj2) -> Integer.compare(obj2.requestedPermissions != null ? obj2.requestedPermissions.length : 0, obj1.requestedPermissions != null ? obj1.requestedPermissions.length : 0));
Collections.sort(vms, (obj1, obj2) -> Integer.compare(obj2.trackers != null ? obj2.trackers.size() : 0, obj1.trackers != null ? obj1.trackers.size() : 0));
} else if (orderChoice == order.LESS_PERMISSIONS) {
Collections.sort(vms, (obj1, obj2) -> Integer.compare(obj1.trackers != null ? obj1.trackers.size() : 0, obj2.trackers != null ? obj2.trackers.size() : 0));
Collections.sort(vms, (obj1, obj2) -> Integer.compare(obj1.requestedPermissions != null ? obj1.requestedPermissions.length : 0, obj2.requestedPermissions != null ? obj2.requestedPermissions.length : 0));
} else if (orderChoice == order.MOST_PERMISSIONS) {
Collections.sort(vms, (obj1, obj2) -> Integer.compare(obj2.trackers != null ? obj2.trackers.size() : 0, obj1.trackers != null ? obj1.trackers.size() : 0));
Collections.sort(vms, (obj1, obj2) -> Integer.compare(obj2.requestedPermissions != null ? obj2.requestedPermissions.length : 0, obj1.requestedPermissions != null ? obj1.requestedPermissions.length : 0));
} else {
Collections.sort(vms, (obj1, obj2) -> String.valueOf(obj1.label).compareToIgnoreCase(String.valueOf(obj2.label)));
}
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;
}
public enum order {
DEFAULT,
MOST_TRACKERS,
LESS_TRACKERS,
MOST_PERMISSIONS,
LESS_PERMISSIONS,
}
interface Listener {
void onAppsComputed(List<ApplicationViewModel> apps);
}
}

View File

@ -1,5 +1,6 @@
package org.eu.exodus_privacy.exodusprivacy.fragments;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
@ -71,7 +72,7 @@ public class HomeFragment extends Fragment implements ComputeAppList.Listener, U
packageManager = context.getPackageManager();
homeBinding.swipeRefresh.setOnRefreshListener(this::startRefresh);
SharedPreferences sharedPreferences = getContext().getSharedPreferences(Utils.APP_PREFS, MODE_PRIVATE);
SharedPreferences sharedPreferences = context.getSharedPreferences(Utils.APP_PREFS, MODE_PRIVATE);
last_refresh = sharedPreferences.getString(Utils.LAST_REFRESH, null);
if (packageManager != null) {
@ -94,12 +95,13 @@ public class HomeFragment extends Fragment implements ComputeAppList.Listener, U
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);
@SuppressLint("QueryPermissionsNeeded") List<PackageInfo> packageInstalled = packageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS);
ArrayList<String> packageList = new ArrayList<>();
for (PackageInfo pkgInfo : packageInstalled)
packageList.add(pkgInfo.packageName);
@ -114,7 +116,9 @@ public class HomeFragment extends Fragment implements ComputeAppList.Listener, U
@Override
public void onResume() {
super.onResume();
appListFragment.scrollTo();
if (!refreshInProgress && homeBinding.layoutProgress.getVisibility() == View.VISIBLE) {
onUpdateComplete();
}
}

View File

@ -0,0 +1,172 @@
package org.eu.exodus_privacy.exodusprivacy.fragments;
/*
* Copyright (C) 2020 Thomas Schneider
*
* 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.
*/
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import org.eu.exodus_privacy.exodusprivacy.R;
import org.eu.exodus_privacy.exodusprivacy.adapters.ApplicationViewModel;
import org.eu.exodus_privacy.exodusprivacy.adapters.MyTrackersListAdapter;
import org.eu.exodus_privacy.exodusprivacy.adapters.TrackerListAdapter;
import org.eu.exodus_privacy.exodusprivacy.databinding.MyTrackersBinding;
import org.eu.exodus_privacy.exodusprivacy.manager.DatabaseManager;
import org.eu.exodus_privacy.exodusprivacy.objects.MyTracker;
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.List;
import java.util.Set;
public class MyTrackersFragment extends Fragment implements MyTrackersListAdapter.TrackerClickListener {
private Context context;
private MyTrackersBinding trackerBinding;
private TrackerListAdapter.OnTrackerClickListener onTrackerClickListener;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
context = getContext();
trackerBinding = MyTrackersBinding.inflate(LayoutInflater.from(context));
return trackerBinding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
trackerBinding.loader.setVisibility(View.VISIBLE);
trackerBinding.trackers.setVisibility(View.GONE);
trackerBinding.swipeRefresh.setOnRefreshListener(this::refresh);
trackerBinding.refresh.setOnClickListener(v -> refresh());
refresh();
}
private void refresh() {
new Thread(() -> {
DatabaseManager databaseManager = DatabaseManager.getInstance(getActivity());
PackageManager packageManager = context.getPackageManager();
List<PackageInfo> packageInstalled = packageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS);
List<MyTracker> myTrackers = new ArrayList<>();
List<String> added = new ArrayList<>();
int maxValue = 0;
List<ApplicationViewModel> vms = ComputeAppList.compute(packageManager, DatabaseManager.getInstance(getActivity()), null);
int appInstalled = vms.size();
for (PackageInfo pkgInfo : packageInstalled) {
Report report;
if (pkgInfo.versionName != null)
report = databaseManager.getReportFor(pkgInfo.packageName, pkgInfo.versionName, null);
else {
report = databaseManager.getReportFor(pkgInfo.packageName, pkgInfo.versionCode, null);
}
if (report != null) {
Set<Tracker> trackersApp = databaseManager.getTrackers(report.trackers);
for (Tracker tracker : trackersApp) {
if (added.contains(tracker.codeSignature)) {
for (MyTracker myTracker : myTrackers) {
if (myTracker.signature.compareTo(tracker.codeSignature) == 0) {
myTracker.number += 1;
}
}
} else {
MyTracker myTracker = new MyTracker();
myTracker.signature = tracker.codeSignature;
myTracker.number = 1;
myTracker.tracker = tracker;
myTrackers.add(myTracker);
added.add(myTracker.signature);
}
}
}
}
for (MyTracker myTracker : myTrackers) {
if (myTracker.number > maxValue)
maxValue = myTracker.number;
}
Handler mainHandler = new Handler(Looper.getMainLooper());
int finalMaxValue = maxValue;
Runnable myRunnable = () -> {
if (myTrackers.size() > 0) {
Collections.sort(myTrackers, (obj1, obj2) -> Integer.compare(obj2.number, obj1.number));
MyTrackersListAdapter myTrackersListAdapter = new MyTrackersListAdapter(myTrackers, MyTrackersFragment.this, finalMaxValue, appInstalled);
trackerBinding.trackers.setAdapter(myTrackersListAdapter);
trackerBinding.trackers.setLayoutManager(new LinearLayoutManager(context));
trackerBinding.trackers.setVisibility(View.VISIBLE);
trackerBinding.loader.setVisibility(View.GONE);
trackerBinding.refresh.setVisibility(View.GONE);
} else {
trackerBinding.refresh.setVisibility(View.VISIBLE);
trackerBinding.trackers.setVisibility(View.GONE);
trackerBinding.loader.setVisibility(View.GONE);
}
trackerBinding.swipeRefresh.setRefreshing(false);
};
mainHandler.post(myRunnable);
}).start();
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
this.context = context;
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
menu.findItem(R.id.action_filter).setVisible(false);
menu.findItem(R.id.action_settings).setVisible(false);
menu.findItem(R.id.action_filter_options).setVisible(false);
}
public void setOnTrackerClickListener(TrackerListAdapter.OnTrackerClickListener listener) {
onTrackerClickListener = listener;
}
@Override
public void onTrackerClick(long trackerId) {
onTrackerClickListener.onTrackerClick(trackerId);
}
}

View File

@ -166,6 +166,7 @@ public class ReportFragment extends Fragment implements Updatable {
public void onPrepareOptionsMenu(Menu menu) {
menu.findItem(R.id.action_filter).setVisible(false);
menu.findItem(R.id.action_filter_options).setVisible(false);
menu.findItem(R.id.action_settings).setVisible(true);
}
public ApplicationViewModel getModel() {

View File

@ -221,11 +221,18 @@ public class DatabaseManager extends SQLiteOpenHelper {
if (cursor.moveToFirst()) {
long appId = cursor.getLong(0);
cursor.close();
where = "app_id = ? and version = ? and source = ?";
whereArgs = new String[3];
whereArgs[0] = String.valueOf(appId);
whereArgs[1] = version;
whereArgs[2] = source;
if (source != null) {
where = "app_id = ? and version = ? and source = ?";
whereArgs = new String[3];
whereArgs[0] = String.valueOf(appId);
whereArgs[1] = version;
whereArgs[2] = source;
} else {
where = "app_id = ? and version = ?";
whereArgs = new String[2];
whereArgs[0] = String.valueOf(appId);
whereArgs[1] = version;
}
String order = "id ASC";
cursor = db.query("reports", columns, where, whereArgs, null, null, order);
long reportId;
@ -234,13 +241,20 @@ public class DatabaseManager extends SQLiteOpenHelper {
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;
if (source != null) {
where = "app_id = ? and source = ?";
whereArgs = new String[2];
whereArgs[0] = String.valueOf(appId);
whereArgs[1] = source;
} else {
where = "app_id = ?";
whereArgs = new String[1];
whereArgs[0] = String.valueOf(appId);
}
order = "creation DESC";
//search a recent reports
cursor = db.query("reports", columns, where, whereArgs, null, null, order);

View File

@ -246,7 +246,6 @@ public class NetworkManager {
}
private void getApplications(Message mes) {
mes.listener.onProgress(R.string.get_reports_connection, 0, 0);
URL url;
@ -354,8 +353,8 @@ public class NetworkManager {
JSONObject object = makeDataRequest(mes.context, mes.listener, url);
if (object != null) {
try {
JSONObject application = object.getJSONObject(handle);
if (handle != null) {
JSONObject application = object.getJSONObject(handle);
return parseApplication(application, handle);
}
} catch (JSONException e) {
@ -401,7 +400,7 @@ public class NetworkManager {
report.creationDate = Calendar.getInstance();
report.creationDate.setTimeZone(TimeZone.getTimeZone("UTC"));
report.creationDate.setTime(date);
report.creationDate.setTime(date!=null?date:new Date());
date = dateFormat.parse(object.getString("creation_date"));
if (date != null) {
report.creationDate.setTime(date);

View File

@ -0,0 +1,8 @@
package org.eu.exodus_privacy.exodusprivacy.objects;
public class MyTracker {
public String signature;
public Tracker tracker;
public int number;
}

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M19,3L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM9,17L7,17v-5h2v5zM13,17h-2v-3h2v3zM13,12h-2v-2h2v2zM17,17h-2L15,7h2v10z" />
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M4,8h4L8,4L4,4v4zM10,20h4v-4h-4v4zM4,20h4v-4L4,16v4zM4,14h4v-4L4,10v4zM10,14h4v-4h-4v4zM16,4v4h4L20,4h-4zM10,8h4L14,4h-4v4zM16,14h4v-4h-4v4zM16,20h4v-4h-4v4z" />
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M10,6L8.59,7.41 13.17,12l-4.58,4.59L10,18l6,-6z" />
</vector>

View File

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

View File

@ -1,20 +1,73 @@
<?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"
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/>
<FrameLayout
android:layout_marginEnd="10dp"
android:layout_marginStart="10dp"
android:id="@+id/fragment_container"
tools:context="org.eu.exodus_privacy.exodusprivacy.MainActivity">
<data />
<androidx.drawerlayout.widget.DrawerLayout
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
tools:context=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?colorPrimary"
android:fitsSystemWindows="true"
android:theme="@style/ThemeOverlay.AppCompat.Dark"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/ThemeOverlay.AppCompat.Dark" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager.widget.ViewPager
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="10dp"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginEnd="10dp"
android:layout_marginBottom="?attr/actionBarSize"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="?attr/actionBarSize"
android:visibility="gone"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/nav_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:background="?android:attr/windowBackground" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.drawerlayout.widget.DrawerLayout>
</layout>

View File

@ -0,0 +1,71 @@
<?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 />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_weight="1">
<View
android:id="@+id/percent"
android:layout_width="0dp"
android:layout_height="25dp"
android:layout_marginBottom="10dp"
android:background="@drawable/percent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/percent_val"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
app:layout_constraintBottom_toBottomOf="@+id/percent"
app:layout_constraintStart_toStartOf="@+id/percent"
app:layout_constraintTop_toTopOf="@+id/percent" />
<TextView
android:id="@+id/tracker_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="@color/textColorDark"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/tracker_count"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/percent" />
<TextView
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:id="@+id/tracker_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:textColor="@color/textColorWhite"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/tracker_name"
app:layout_constraintTop_toTopOf="@id/tracker_name" />
</androidx.constraintlayout.widget.ConstraintLayout>
<ImageView
android:id="@+id/details"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center"
android:src="@drawable/ic_baseline_navigate_next_24"
android:contentDescription="@string/list_of_apps" />
</LinearLayout>
</layout>

View File

@ -0,0 +1,51 @@
<?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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/refresh"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/nothing_here"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/trackers"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<RelativeLayout
android:id="@+id/loader"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:indeterminate="true" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@ -11,6 +11,7 @@
type="org.eu.exodus_privacy.exodusprivacy.ReportViewModel" />
</data>
<androidx.core.widget.NestedScrollView
android:background="?android:attr/windowBackground"
android:layout_width="match_parent"
android:layout_height="match_parent">

View File

@ -6,6 +6,7 @@
<data>
</data>
<androidx.core.widget.NestedScrollView
android:background="?android:attr/windowBackground"
android:layout_width="match_parent"
android:layout_height="match_parent">

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/navigation_apps"
android:icon="@drawable/ic_baseline_apps_24"
android:title="@string/title_apps" />
<item
android:id="@+id/navigation_analytics"
android:icon="@drawable/ic_baseline_analytics_24"
android:title="@string/title_trackers" />
</menu>

View File

@ -62,6 +62,11 @@
<string name="submit">Submit</string>
<string name="app_not_analyzed">Would you like to analyze the app?\n\nThe app id will be automatically pasted to the website form. You will only need to submit the form.</string>
<string name="app_not_analyzed_title">This app has not been analyzed!</string>
<string name="title_apps">My apps</string>
<string name="title_trackers">Trackers</string>
<string name="list_of_apps">See the list of apps</string>
<string name="apps">%1$s apps</string>
<string name="nothing_here">Nothing to display currently. You can pull to refresh!</string>
</resources>

View File

@ -1,7 +1,7 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>