feat: speed up application start and back navigation

This commit is contained in:
Benjamin Orsini 2019-06-06 23:50:53 +02:00
parent c321bc790f
commit 71866e8ed4
5 changed files with 219 additions and 144 deletions

View File

@ -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);

View File

@ -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<ApplicationViewModel> 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<ApplicationViewModel> alphaPackageComparator = new Comparator<ApplicationViewModel>() {
@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<PackageInfo> installedPackages) {
List<ApplicationViewModel> viewModels = convertPackagesToViewModels(installedPackages);
applyStoreFilter(viewModels);
Collections.sort(viewModels, alphaPackageComparator);
applicationViewModels = viewModels;
notifyDataSetChanged();
}
private void applyStoreFilter(List<ApplicationViewModel> apps) {
List<ApplicationViewModel> 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<ApplicationViewModel> convertPackagesToViewModels(List<PackageInfo> infos) {
ArrayList<ApplicationViewModel> 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<PackageInfo> 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<Tracker> trackers;
public @Nullable Drawable icon;
public CharSequence label;
public String installerPackageName;
public boolean isVisible;
public void displayAppList(List<ApplicationViewModel> 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);
}

View File

@ -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<Tracker> trackers;
public @Nullable
Drawable icon;
public CharSequence label;
public String installerPackageName;
public boolean isVisible;
}

View File

@ -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<ApplicationViewModel> 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<ApplicationViewModel> apps) {
this.applications = apps;
applistBinding.noAppFound.setVisibility(apps.isEmpty() ? View.VISIBLE : View.GONE);
if(adapter != null) {
adapter.displayAppList(apps);
}
}
}

View File

@ -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<Void, Void, List<ApplicationViewModel>> {
interface Listener {
void onAppsComputed(List<ApplicationViewModel> apps);
}
private static final String gStore = "com.android.vending";
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);
applyStoreFilter(installedPackages, databaseManager, packageManager);
vms = convertPackagesToViewModels(installedPackages, databaseManager, packageManager);
}
return vms;
}
@Override
protected void onPostExecute(List<ApplicationViewModel> vms) {
Listener listener = listenerRef.get();
if(listener != null) {
listener.onAppsComputed(vms);
}
}
private List<ApplicationViewModel> convertPackagesToViewModels(List<PackageInfo> infos,
DatabaseManager databaseManager,
PackageManager packageManager) {
ArrayList<ApplicationViewModel> 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<PackageInfo> packageInfos,
DatabaseManager databaseManager,
PackageManager packageManager) {
List<PackageInfo> 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);
}
}