diff --git a/app/src/main/java/org/eu/exodus_privacy/exodusprivacy/MainActivity.java b/app/src/main/java/org/eu/exodus_privacy/exodusprivacy/MainActivity.java index e960d96..591ec68 100644 --- a/app/src/main/java/org/eu/exodus_privacy/exodusprivacy/MainActivity.java +++ b/app/src/main/java/org/eu/exodus_privacy/exodusprivacy/MainActivity.java @@ -18,6 +18,8 @@ package org.eu.exodus_privacy.exodusprivacy; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; import android.support.v4.app.FragmentManager; import android.content.Context; import android.content.Intent; @@ -82,24 +84,30 @@ public class MainActivity extends AppCompatActivity { } }; - ApplicationListAdapter.OnAppClickListener onAppClickListener = packageInfo -> { + ApplicationListAdapter.OnAppClickListener onAppClickListener = vm -> { + try { + PackageManager pm = getPackageManager(); + PackageInfo packageInfo = pm.getPackageInfo(vm.packageName, PackageManager.GET_PERMISSIONS); - report = ReportFragment.newInstance(getPackageManager(),packageInfo); - 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(); + report = ReportFragment.newInstance(pm,packageInfo); + 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; + packageName = packageInfo.packageName; - searchView.clearFocus(); - if (toolbarMenu != null) - (toolbarMenu.findItem(R.id.action_filter)).collapseActionView(); - InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); - assert imm != null; - imm.hideSoftInputFromWindow(mainBinding.fragmentContainer.getWindowToken(), 0); + searchView.clearFocus(); + if (toolbarMenu != null) + (toolbarMenu.findItem(R.id.action_filter)).collapseActionView(); + InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); + assert imm != null; + imm.hideSoftInputFromWindow(mainBinding.fragmentContainer.getWindowToken(), 0); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } }; appList = AppListFragment.newInstance(networkListener,onAppClickListener); diff --git a/app/src/main/java/org/eu/exodus_privacy/exodusprivacy/adapters/ApplicationListAdapter.java b/app/src/main/java/org/eu/exodus_privacy/exodusprivacy/adapters/ApplicationListAdapter.java index 0a13d6a..a848ed0 100644 --- a/app/src/main/java/org/eu/exodus_privacy/exodusprivacy/adapters/ApplicationListAdapter.java +++ b/app/src/main/java/org/eu/exodus_privacy/exodusprivacy/adapters/ApplicationListAdapter.java @@ -19,21 +19,14 @@ package org.eu.exodus_privacy.exodusprivacy.adapters; import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; import android.databinding.DataBindingUtil; -import android.graphics.drawable.Drawable; import android.support.annotation.NonNull; -import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import org.eu.exodus_privacy.exodusprivacy.R; -import org.eu.exodus_privacy.exodusprivacy.Utils; import org.eu.exodus_privacy.exodusprivacy.databinding.AppItemBinding; -import org.eu.exodus_privacy.exodusprivacy.manager.DatabaseManager; import org.eu.exodus_privacy.exodusprivacy.objects.Report; import org.eu.exodus_privacy.exodusprivacy.objects.Tracker; @@ -47,14 +40,10 @@ import java.util.regex.Pattern; public class ApplicationListAdapter extends RecyclerView.Adapter { private List applicationViewModels; - private PackageManager packageManager; private OnAppClickListener onAppClickListener; - private static final String gStore = "com.android.vending"; private String filter = ""; private final int HIDDEN_APP = 0; private final int DISPLAYED_APP = 1; - private Context context; - private Comparator alphaPackageComparator = new Comparator() { @Override @@ -63,83 +52,9 @@ public class ApplicationListAdapter extends RecyclerView.Adapter { } }; - public ApplicationListAdapter(Context context, PackageManager manager, OnAppClickListener listener) { + public ApplicationListAdapter(Context context, OnAppClickListener listener) { applicationViewModels = new ArrayList<>(); onAppClickListener = listener; - this.context = context; - displayApplicationList(manager); - } - - private void setInstalledPackages(List installedPackages) { - List viewModels = convertPackagesToViewModels(installedPackages); - applyStoreFilter(viewModels); - Collections.sort(viewModels, alphaPackageComparator); - applicationViewModels = viewModels; - notifyDataSetChanged(); - } - - private void applyStoreFilter(List apps) { - List toRemove = new ArrayList<>(); - for (ApplicationViewModel app : apps) { - if (!gStore.equals(app.installerPackageName)) { - - String auid = Utils.getCertificateSHA1Fingerprint(packageManager,app.packageName); - String appuid = DatabaseManager.getInstance(context).getAUID(app.packageName); - if(!auid.equalsIgnoreCase(appuid)) { - toRemove.add(app); - } - } - - try { - ApplicationInfo info = packageManager.getApplicationInfo(app.packageName,0); - if(!info.enabled) { - toRemove.add(app); - } - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - } - } - apps.removeAll(toRemove); - } - - private List convertPackagesToViewModels(List infos) { - ArrayList appsToBuild = new ArrayList<>(infos.size()); - for (PackageInfo pi : infos) { - appsToBuild.add(buildViewModelFromPackageInfo(pi)); - } - return appsToBuild; - } - - private ApplicationViewModel buildViewModelFromPackageInfo(PackageInfo pi) { - ApplicationViewModel vm = new ApplicationViewModel(); - - vm.versionName = pi.versionName; - vm.packageName = pi.packageName; - vm.versionCode = pi.versionCode; - vm.requestedPermissions = pi.requestedPermissions; - - DatabaseManager dm = DatabaseManager.getInstance(context); - if(vm.versionName != null) - vm.report = dm.getReportFor(vm.packageName, vm.versionName); - else { - vm.report = dm.getReportFor(vm.packageName, vm.versionCode); - } - - if(vm.report != null) { - vm.trackers = dm.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; - - return vm; } @Override @@ -161,17 +76,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.setViewModel(applicationViewModels.get(position)); + ApplicationViewModel vm = applicationViewModels.get(position); + holder.setViewModel(vm); //noinspection Convert2Lambda holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - try { - PackageInfo packageInfo = packageManager.getPackageInfo(holder.viewModel.packageName, PackageManager.GET_PERMISSIONS); - onAppClickListener.onAppClick(packageInfo); - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - } + onAppClickListener.onAppClick(vm); } }); }else { @@ -186,28 +97,10 @@ public class ApplicationListAdapter extends RecyclerView.Adapter { return applicationViewModels.size(); } - public void displayApplicationList(PackageManager manager) { - packageManager = manager; - if(packageManager != null) { - List installedPackages = packageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS); - setInstalledPackages(installedPackages); - } - } - - /** - * 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 trackers; - public @Nullable Drawable icon; - public CharSequence label; - public String installerPackageName; - public boolean isVisible; + public void displayAppList(List applications) { + applicationViewModels = applications; + Collections.sort(applicationViewModels, alphaPackageComparator); + filter(filter); } class ApplicationEmptyViewHolder extends RecyclerView.ViewHolder{ @@ -284,7 +177,7 @@ public class ApplicationListAdapter extends RecyclerView.Adapter { } public interface OnAppClickListener { - void onAppClick(PackageInfo packageInfo); + void onAppClick(ApplicationViewModel vm); } diff --git a/app/src/main/java/org/eu/exodus_privacy/exodusprivacy/adapters/ApplicationViewModel.java b/app/src/main/java/org/eu/exodus_privacy/exodusprivacy/adapters/ApplicationViewModel.java new file mode 100644 index 0000000..158e4d1 --- /dev/null +++ b/app/src/main/java/org/eu/exodus_privacy/exodusprivacy/adapters/ApplicationViewModel.java @@ -0,0 +1,27 @@ +package org.eu.exodus_privacy.exodusprivacy.adapters; + +import android.graphics.drawable.Drawable; +import android.support.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 trackers; + public @Nullable + Drawable icon; + public CharSequence label; + public String installerPackageName; + public boolean isVisible; +} diff --git a/app/src/main/java/org/eu/exodus_privacy/exodusprivacy/fragments/AppListFragment.java b/app/src/main/java/org/eu/exodus_privacy/exodusprivacy/fragments/AppListFragment.java index cceee85..cca45bf 100644 --- a/app/src/main/java/org/eu/exodus_privacy/exodusprivacy/fragments/AppListFragment.java +++ b/app/src/main/java/org/eu/exodus_privacy/exodusprivacy/fragments/AppListFragment.java @@ -34,22 +34,25 @@ import android.view.ViewGroup; 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.DatabaseManager; import org.eu.exodus_privacy.exodusprivacy.manager.NetworkManager; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; -public class AppListFragment extends Fragment { +public class AppListFragment extends Fragment implements ComputeAppListTask.Listener { - - private PackageManager packageManager; + private @Nullable PackageManager packageManager; private NetworkListener networkListener; private ApplicationListAdapter.OnAppClickListener onAppClickListener; private boolean startupRefresh; private ApplistBinding applistBinding; private @Nullable ApplicationListAdapter adapter; + private List applications; public static AppListFragment newInstance(NetworkListener networkListener, ApplicationListAdapter.OnAppClickListener appClickListener) { AppListFragment fragment = new AppListFragment(); @@ -62,6 +65,8 @@ public class AppListFragment extends Fragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + applications = new ArrayList<>(); } @Override @@ -87,13 +92,10 @@ public class AppListFragment extends Fragment { startupRefresh = false; } applistBinding.noPackageManager.setVisibility(View.GONE); - applistBinding.noAppFound.setVisibility(View.GONE); - adapter = new ApplicationListAdapter(getActivity().getApplicationContext(),packageManager, onAppClickListener); - if(adapter.getItemCount() == 0) { - applistBinding.noAppFound.setVisibility(View.VISIBLE); - } else { - applistBinding.appList.setAdapter(adapter); - } + adapter = new ApplicationListAdapter(context, onAppClickListener); + applistBinding.appList.setAdapter(adapter); + onAppsComputed(applications); + displayAppListAsync(); } else { applistBinding.noPackageManager.setVisibility(View.VISIBLE); } @@ -116,9 +118,7 @@ public class AppListFragment extends Fragment { if(applistBinding != null) { applistBinding.layoutProgress.setVisibility(View.GONE); applistBinding.swipeRefresh.setRefreshing(false); - if(packageManager != null && adapter != null) { - adapter.displayApplicationList(packageManager); - } + displayAppListAsync(); } } @@ -179,4 +179,23 @@ public class AppListFragment extends Fragment { adapter.filter(filter); } } + + private void displayAppListAsync() { + applistBinding.noAppFound.setVisibility(View.GONE); + + new ComputeAppListTask( + new WeakReference<>(packageManager), + new WeakReference<>(DatabaseManager.getInstance(getActivity())), + new WeakReference<>(this) + ).execute(); + } + + @Override + public void onAppsComputed(List apps) { + this.applications = apps; + applistBinding.noAppFound.setVisibility(apps.isEmpty() ? View.VISIBLE : View.GONE); + if(adapter != null) { + adapter.displayAppList(apps); + } + } } diff --git a/app/src/main/java/org/eu/exodus_privacy/exodusprivacy/fragments/ComputeAppListTask.java b/app/src/main/java/org/eu/exodus_privacy/exodusprivacy/fragments/ComputeAppListTask.java new file mode 100644 index 0000000..3ab33c6 --- /dev/null +++ b/app/src/main/java/org/eu/exodus_privacy/exodusprivacy/fragments/ComputeAppListTask.java @@ -0,0 +1,128 @@ +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; + +class ComputeAppListTask extends AsyncTask> { + + interface Listener { + void onAppsComputed(List apps); + } + + private static final String gStore = "com.android.vending"; + + private WeakReference packageManagerRef; + private WeakReference databaseManagerRef; + private WeakReference listenerRef; + + ComputeAppListTask(WeakReference packageManagerRef, + WeakReference databaseManagerRef, + WeakReference listenerRef) { + this.packageManagerRef = packageManagerRef; + this.databaseManagerRef = databaseManagerRef; + this.listenerRef = listenerRef; + } + + protected List doInBackground(Void... params) { + PackageManager packageManager = packageManagerRef.get(); + DatabaseManager databaseManager = databaseManagerRef.get(); + + List vms = new ArrayList<>(); + if(packageManager != null && databaseManager != null) { + List installedPackages = packageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS); + applyStoreFilter(installedPackages, databaseManager, packageManager); + vms = convertPackagesToViewModels(installedPackages, databaseManager, packageManager); + } + return vms; + } + + @Override + protected void onPostExecute(List vms) { + Listener listener = listenerRef.get(); + + if(listener != null) { + listener.onAppsComputed(vms); + } + } + + private List convertPackagesToViewModels(List infos, + DatabaseManager databaseManager, + PackageManager packageManager) { + ArrayList appsToBuild = new ArrayList<>(infos.size()); + for (PackageInfo pi : infos) { + appsToBuild.add(buildViewModelFromPackageInfo(pi, databaseManager, packageManager)); + } + return appsToBuild; + } + + private ApplicationViewModel buildViewModelFromPackageInfo(PackageInfo pi, + DatabaseManager databaseManager, + PackageManager packageManager) { + ApplicationViewModel vm = new ApplicationViewModel(); + + 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); + else { + vm.report = databaseManager.getReportFor(vm.packageName, vm.versionCode); + } + + 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; + + return vm; + } + + private void applyStoreFilter(List packageInfos, + DatabaseManager databaseManager, + PackageManager packageManager) { + List toRemove = new ArrayList<>(); + for (PackageInfo packageInfo : packageInfos) { + String packageName = packageInfo.packageName; + String installerPackageName = packageManager.getInstallerPackageName(packageName); + if (!gStore.equals(installerPackageName)) { + String auid = Utils.getCertificateSHA1Fingerprint(packageManager, packageName); + String appuid = databaseManager.getAUID(packageName); + if(!auid.equalsIgnoreCase(appuid)) { + toRemove.add(packageInfo); + } + } + + try { + ApplicationInfo appInfo = packageManager.getApplicationInfo(packageName,0); + if(!appInfo.enabled) { + toRemove.add(packageInfo); + } + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + } + packageInfos.removeAll(toRemove); + } + +}