diff --git a/app/src/main/java/com/emanuelef/remote_capture/AppAdapter.java b/app/src/main/java/com/emanuelef/remote_capture/AppAdapter.java index 3cad9ffb..4f46eaa1 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/AppAdapter.java +++ b/app/src/main/java/com/emanuelef/remote_capture/AppAdapter.java @@ -32,22 +32,24 @@ import androidx.recyclerview.widget.RecyclerView; import java.util.List; public class AppAdapter extends RecyclerView.Adapter { - private LayoutInflater layoutInflater; - private List listStorage; + private final LayoutInflater mLayoutInflater; private View.OnClickListener mListener; + private List listStorage; - AppAdapter(Context context, List customizedListView, final View.OnClickListener listener) { - layoutInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + AppAdapter(Context context, List customizedListView) { + mLayoutInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); listStorage = customizedListView; - mListener = listener; + mListener = null; } @NonNull @Override public AppAdapter.AppViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.installed_app_list, parent, false); + View view = mLayoutInflater.inflate(R.layout.installed_app_list, parent, false); AppViewHolder recyclerViewHolder = new AppViewHolder(view); - view.setOnClickListener(mListener); + + if(mListener != null) + view.setOnClickListener(mListener); return(recyclerViewHolder); } @@ -77,4 +79,13 @@ public class AppAdapter extends RecyclerView.Adapter { packageInListView= view.findViewById(R.id.app_package); } } + + public void setApps(List apps) { + listStorage = apps; + notifyDataSetChanged(); + } + + public void setOnClickListener(final View.OnClickListener listener) { + mListener = listener; + } } diff --git a/app/src/main/java/com/emanuelef/remote_capture/AppDescriptor.java b/app/src/main/java/com/emanuelef/remote_capture/AppDescriptor.java index 3153e859..91221299 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/AppDescriptor.java +++ b/app/src/main/java/com/emanuelef/remote_capture/AppDescriptor.java @@ -21,12 +21,12 @@ package com.emanuelef.remote_capture; import android.graphics.drawable.Drawable; -class AppDescriptor { - private String name; - private Drawable icon; - private String package_name; - private int uid; - private boolean is_system; +class AppDescriptor implements Comparable { + final private String name; + final private Drawable icon; + final private String package_name; + final private int uid; + final private boolean is_system; AppDescriptor(String name, Drawable icon, String package_name, int uid, boolean is_system) { this.name = name; @@ -53,4 +53,14 @@ class AppDescriptor { } boolean isSystem() { return is_system; } + + @Override + public int compareTo(AppDescriptor o) { + int rv = getName().toLowerCase().compareTo(o.getName().toLowerCase()); + + if(rv == 0) + rv = getPackageName().compareTo(o.getPackageName()); + + return rv; + } } diff --git a/app/src/main/java/com/emanuelef/remote_capture/AppsView.java b/app/src/main/java/com/emanuelef/remote_capture/AppsView.java index 5075aaae..8e55d32b 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/AppsView.java +++ b/app/src/main/java/com/emanuelef/remote_capture/AppsView.java @@ -20,41 +20,105 @@ package com.emanuelef.remote_capture; import android.content.Context; -import android.view.View; +import android.util.AttributeSet; +import android.util.Log; +import android.widget.Filter; +import android.widget.Filterable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.widget.SearchView; import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; +import java.util.ArrayList; import java.util.List; -// TODO add searchbar -// https://stackoverflow.com/questions/31085086/how-to-implement-floating-searchwidget-android +class AppsView extends EmptyRecyclerView implements SearchView.OnQueryTextListener, Filterable { + private List mInstalledApps; + private AppAdapter mAdapter; -class AppsView extends RecyclerView { - List mInstalledApps; + public AppsView(@NonNull Context context) { + super(context); + initialize(context); + } + + public AppsView(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + initialize(context); + } + + public AppsView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + initialize(context); + } + + private void initialize(Context context) { + mInstalledApps = null; + setLayoutManager(new LinearLayoutManager(context)); + setHasFixedSize(true); + } + + @Override + public Filter getFilter() { + return new Filter() { + + @Override + protected FilterResults performFiltering(CharSequence constraint) { + String charString = constraint.toString().toLowerCase(); + List appsFiltered; + + if(charString.isEmpty()) + appsFiltered = mInstalledApps; + else { + appsFiltered = new ArrayList<>(); + + for(AppDescriptor app : mInstalledApps) { + if(app.getPackageName().toLowerCase().contains(charString) + || app.getName().toLowerCase().contains(charString)) { + appsFiltered.add(app); + } + } + } + + FilterResults filterResults = new FilterResults(); + filterResults.values = appsFiltered; + return filterResults; + } + + @Override + protected void publishResults(CharSequence constraint, FilterResults results) { + List appsFiltered = (List) results.values; + mAdapter.setApps(appsFiltered); + } + }; + } + + @Override + public boolean onQueryTextSubmit(String query) { + return false; + } + + @Override + public boolean onQueryTextChange(String newText) { + getFilter().filter(newText); + return true; + } public interface OnSelectedAppListener { void onSelectedApp(AppDescriptor app); } - public AppsView(Context context, List installedApps) { - super(context); - + public void setApps(List installedApps) { mInstalledApps = installedApps; - setLayoutManager(new LinearLayoutManager(context)); - setHasFixedSize(true); + mAdapter = new AppAdapter(getContext(), mInstalledApps); + setAdapter(mAdapter); } public void setSelectedAppListener(final OnSelectedAppListener listener) { - AppAdapter installedAppAdapter = new AppAdapter(getContext(), mInstalledApps, new OnClickListener() { - @Override - public void onClick(View view) { - int itemPosition = getChildLayoutPosition(view); - AppDescriptor app = mInstalledApps.get(itemPosition); - listener.onSelectedApp(app); - } + mAdapter.setOnClickListener(view -> { + int itemPosition = getChildLayoutPosition(view); + AppDescriptor app = mInstalledApps.get(itemPosition); + listener.onSelectedApp(app); }); - - setAdapter(installedAppAdapter); } } diff --git a/app/src/main/java/com/emanuelef/remote_capture/ConnectionsAdapter.java b/app/src/main/java/com/emanuelef/remote_capture/ConnectionsAdapter.java index 4f5626d7..bd778e91 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/ConnectionsAdapter.java +++ b/app/src/main/java/com/emanuelef/remote_capture/ConnectionsAdapter.java @@ -76,11 +76,13 @@ class ViewHolder extends RecyclerView.ViewHolder { public class ConnectionsAdapter extends RecyclerView.Adapter { private static final String TAG = "ConnectionsAdapter"; private final MainActivity mActivity; + private final LayoutInflater mLayoutInflater; private final Drawable mUnknownIcon; private View.OnClickListener mListener; ConnectionsAdapter(MainActivity context) { mActivity = context; + mLayoutInflater = (LayoutInflater) mActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mUnknownIcon = ContextCompat.getDrawable(mActivity, android.R.drawable.ic_menu_help); mListener = null; } @@ -95,10 +97,7 @@ public class ConnectionsAdapter extends RecyclerView.Adapter { @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - LayoutInflater inflater = (LayoutInflater) mActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - assert inflater != null; - - View view = inflater.inflate(R.layout.connection_item, parent, false); + View view = mLayoutInflater.inflate(R.layout.connection_item, parent, false); if(mListener != null) view.setOnClickListener(mListener); diff --git a/app/src/main/java/com/emanuelef/remote_capture/MainActivity.java b/app/src/main/java/com/emanuelef/remote_capture/MainActivity.java index 99d3b998..7344c252 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/MainActivity.java +++ b/app/src/main/java/com/emanuelef/remote_capture/MainActivity.java @@ -32,6 +32,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.SearchView; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; @@ -48,6 +49,8 @@ import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import android.view.View; +import android.widget.TextView; import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayoutMediator; @@ -465,22 +468,21 @@ public class MainActivity extends AppCompatActivity implements LoaderManager.Loa return; } - // Filter non-system apps - List user_apps = new ArrayList<>(); - - for(int i=0; i { diff --git a/app/src/main/java/com/emanuelef/remote_capture/Utils.java b/app/src/main/java/com/emanuelef/remote_capture/Utils.java index 38fe439b..91894b46 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/Utils.java +++ b/app/src/main/java/com/emanuelef/remote_capture/Utils.java @@ -95,10 +95,6 @@ public class Utils { List apps = new ArrayList<>(); List packs = pm.getInstalledPackages(0); - // Add the "No Filter" app - Drawable icon = ContextCompat.getDrawable(context, android.R.color.transparent); - apps.add(new AppDescriptor("", icon, context.getResources().getString(R.string.no_filter), -1, false)); - Log.d("APPS", "num apps (system+user): " + packs.size()); long tstart = now(); @@ -112,7 +108,7 @@ public class Utils { String appName = p.applicationInfo.loadLabel(pm).toString(); // NOTE: this call is expensive - icon = p.applicationInfo.loadIcon(pm); + Drawable icon = p.applicationInfo.loadIcon(pm); int uid = p.applicationInfo.uid; apps.add(new AppDescriptor(appName, icon, package_name, uid, is_system)); @@ -121,6 +117,12 @@ public class Utils { } } + Collections.sort(apps); + + // Add the "No Filter" app + Drawable icon = ContextCompat.getDrawable(context, android.R.color.transparent); + apps.add(0, new AppDescriptor("", icon, context.getResources().getString(R.string.no_filter), -1, false)); + Log.d("APPS", packs.size() + " apps loaded in " + (now() - tstart) +" seconds"); return apps; } diff --git a/app/src/main/res/layout/apps_selector.xml b/app/src/main/res/layout/apps_selector.xml new file mode 100644 index 00000000..4ef2a06f --- /dev/null +++ b/app/src/main/res/layout/apps_selector.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b7c9d1e2..97ab4f65 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -79,5 +79,8 @@ Packets Sent Packets Received DNS Queries + Search Apps + No apps available + Show system apps