Skip to content

Commit 72e7e1d

Browse files
committed
refactor Adapter class out of inner class
1 parent 10e1f27 commit 72e7e1d

3 files changed

Lines changed: 316 additions & 293 deletions

File tree

Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
package tw.idv.palatis.xappdebug.adapters;
2+
3+
import static android.content.pm.ApplicationInfo.FLAG_DEBUGGABLE;
4+
import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
5+
import static com.google.android.material.snackbar.BaseTransientBottomBar.LENGTH_SHORT;
6+
import static tw.idv.palatis.xappdebug.Constants.PREF_KEY_SHOW_DEBUG;
7+
import static tw.idv.palatis.xappdebug.Constants.PREF_KEY_SHOW_DEBUGGABLE_FIRST;
8+
import static tw.idv.palatis.xappdebug.Constants.PREF_KEY_SHOW_SYSTEM;
9+
import static tw.idv.palatis.xappdebug.Constants.PREF_KEY_SORT_ORDER;
10+
import static tw.idv.palatis.xappdebug.Constants.SORT_ORDER_INSTALL_TIME;
11+
import static tw.idv.palatis.xappdebug.Constants.SORT_ORDER_LABEL;
12+
import static tw.idv.palatis.xappdebug.Constants.SORT_ORDER_PACKAGE_NAME;
13+
import static tw.idv.palatis.xappdebug.Constants.SORT_ORDER_UPDATE_TIME;
14+
15+
import android.content.Context;
16+
import android.content.SharedPreferences;
17+
import android.content.pm.PackageInfo;
18+
import android.content.pm.PackageManager;
19+
import android.graphics.Typeface;
20+
import android.graphics.drawable.Drawable;
21+
import android.text.TextUtils;
22+
import android.view.LayoutInflater;
23+
import android.view.View;
24+
import android.view.ViewGroup;
25+
import android.widget.CompoundButton;
26+
27+
import androidx.annotation.NonNull;
28+
import androidx.appcompat.widget.AppCompatImageView;
29+
import androidx.appcompat.widget.AppCompatTextView;
30+
import androidx.appcompat.widget.SwitchCompat;
31+
import androidx.collection.SparseArrayCompat;
32+
import androidx.recyclerview.widget.RecyclerView;
33+
34+
import com.google.android.material.snackbar.Snackbar;
35+
36+
import java.util.ArrayList;
37+
import java.util.Comparator;
38+
import java.util.List;
39+
40+
import tw.idv.palatis.xappdebug.Configuration;
41+
import tw.idv.palatis.xappdebug.R;
42+
43+
public class InstalledPackageAdapter extends RecyclerView.Adapter<InstalledPackageAdapter.ViewHolder>
44+
implements SharedPreferences.OnSharedPreferenceChangeListener {
45+
@Override
46+
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
47+
switch (key) {
48+
case PREF_KEY_SORT_ORDER:
49+
case PREF_KEY_SHOW_DEBUGGABLE_FIRST:
50+
case PREF_KEY_SHOW_SYSTEM:
51+
case PREF_KEY_SHOW_DEBUG:
52+
filterAndSort();
53+
notifyDataSetChanged();
54+
break;
55+
}
56+
}
57+
58+
private static class PackageInfoCache {
59+
public PackageInfo pkg;
60+
61+
private CharSequence mLabel = null;
62+
private Drawable mIcon = null;
63+
64+
public PackageInfoCache(PackageInfo pkg) {
65+
this.pkg = pkg;
66+
}
67+
68+
public Drawable getIcon(final PackageManager pm) {
69+
if (mIcon == null)
70+
mIcon = pm.getApplicationIcon(pkg.applicationInfo);
71+
return mIcon;
72+
}
73+
74+
public CharSequence getLabel(final PackageManager pm) {
75+
if (mLabel == null)
76+
mLabel = pm.getApplicationLabel(pkg.applicationInfo);
77+
return mLabel;
78+
}
79+
80+
public String getPackageName() {
81+
return pkg.packageName;
82+
}
83+
84+
public boolean hasFlag(int flag) {
85+
return (pkg.applicationInfo.flags & flag) == flag;
86+
}
87+
88+
public boolean isDebugApp() {
89+
return hasFlag(FLAG_DEBUGGABLE);
90+
}
91+
92+
public boolean isSystemApp() {
93+
return hasFlag(FLAG_SYSTEM);
94+
}
95+
96+
public boolean isDebuggable() {
97+
return Configuration.isEnabled(getPackageName());
98+
}
99+
100+
public long getInstallTime() {
101+
return pkg.firstInstallTime;
102+
}
103+
104+
public long getUpdateTime() {
105+
return pkg.lastUpdateTime;
106+
}
107+
}
108+
109+
private final SharedPreferences mSharedPreferences;
110+
private final PackageManager mPackageManager;
111+
private List<PackageInfoCache> mInstalledPackages = new ArrayList<>();
112+
private final List<PackageInfoCache> mFilteredPackages = new ArrayList<>();
113+
private String mFilterQuery = "";
114+
115+
public InstalledPackageAdapter(PackageManager pkgMgr, SharedPreferences prefs) {
116+
mPackageManager = pkgMgr;
117+
mSharedPreferences = prefs;
118+
setHasStableIds(true);
119+
}
120+
121+
@NonNull
122+
@Override
123+
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
124+
final Context context = parent.getContext();
125+
final View view = LayoutInflater.from(context).inflate(R.layout.item_app, parent, false);
126+
return new ViewHolder(view);
127+
}
128+
129+
@Override
130+
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
131+
holder.bind(mFilteredPackages.get(position));
132+
}
133+
134+
@Override
135+
public int getItemCount() {
136+
return mFilteredPackages.size();
137+
}
138+
139+
@Override
140+
public long getItemId(int position) {
141+
return mFilteredPackages.get(position).getPackageName().hashCode();
142+
}
143+
144+
@Override
145+
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
146+
mSharedPreferences.registerOnSharedPreferenceChangeListener(this);
147+
}
148+
149+
@Override
150+
public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
151+
mSharedPreferences.unregisterOnSharedPreferenceChangeListener(this);
152+
}
153+
154+
public void updateInstalledPackages(List<PackageInfo> packages) {
155+
final SparseArrayCompat<PackageInfoCache> map = new SparseArrayCompat<>(mInstalledPackages.size());
156+
for (PackageInfoCache pkg : mInstalledPackages)
157+
map.put(pkg.getPackageName().hashCode(), pkg);
158+
159+
mInstalledPackages = new ArrayList<>(packages.size());
160+
for (PackageInfo pkg1 : packages) {
161+
final PackageInfoCache pkg2 = map.containsKey(pkg1.packageName.hashCode()) ?
162+
map.get(pkg1.packageName.hashCode()) :
163+
new PackageInfoCache(pkg1);
164+
pkg2.pkg = pkg1;
165+
mInstalledPackages.add(pkg2);
166+
}
167+
168+
filterAndSort();
169+
notifyDataSetChanged();
170+
}
171+
172+
public void setFilterQuery(String query) {
173+
mFilterQuery = query;
174+
filterAndSort();
175+
notifyDataSetChanged();
176+
}
177+
178+
private List<PackageInfoCache> filterSystem(List<PackageInfoCache> target) {
179+
if (mSharedPreferences.getBoolean(PREF_KEY_SHOW_SYSTEM, false))
180+
return target;
181+
182+
final List<PackageInfoCache> filtered = new ArrayList<>(target.size());
183+
for (final PackageInfoCache pkg : target)
184+
if (!pkg.isSystemApp())
185+
filtered.add(pkg);
186+
return filtered;
187+
}
188+
189+
private List<PackageInfoCache> filterDebuggable(List<PackageInfoCache> target) {
190+
if (mSharedPreferences.getBoolean(PREF_KEY_SHOW_DEBUG, false))
191+
return target;
192+
193+
final List<PackageInfoCache> filtered = new ArrayList<>(target.size());
194+
for (final PackageInfoCache pkg : target)
195+
if (!pkg.isDebugApp())
196+
filtered.add(pkg);
197+
return filtered;
198+
}
199+
200+
private List<PackageInfoCache> filterQuery(List<PackageInfoCache> target) {
201+
if (TextUtils.isEmpty(mFilterQuery))
202+
return target;
203+
204+
final List<PackageInfoCache> filtered = new ArrayList<>(target.size());
205+
for (final PackageInfoCache pkg : target)
206+
if (pkg.getLabel(mPackageManager).toString().toLowerCase().contains(mFilterQuery) ||
207+
pkg.getPackageName().toLowerCase().contains(mFilterQuery))
208+
filtered.add(pkg);
209+
return filtered;
210+
}
211+
212+
private void filterAndSort() {
213+
List<PackageInfoCache> filtered;
214+
filtered = filterSystem(mInstalledPackages);
215+
filtered = filterDebuggable(filtered);
216+
filtered = filterQuery(filtered);
217+
218+
Comparator<PackageInfoCache> orderComparator = null;
219+
switch (mSharedPreferences.getInt(PREF_KEY_SORT_ORDER, SORT_ORDER_LABEL)) {
220+
case SORT_ORDER_LABEL:
221+
orderComparator = Comparator.comparing(pkg -> pkg.getLabel(mPackageManager).toString());
222+
break;
223+
case SORT_ORDER_PACKAGE_NAME:
224+
orderComparator = Comparator.comparing(PackageInfoCache::getPackageName);
225+
break;
226+
case SORT_ORDER_INSTALL_TIME:
227+
orderComparator = Comparator.comparingLong(PackageInfoCache::getInstallTime).reversed();
228+
break;
229+
case SORT_ORDER_UPDATE_TIME:
230+
orderComparator = Comparator.comparingLong(PackageInfoCache::getUpdateTime).reversed();
231+
break;
232+
}
233+
234+
if (mSharedPreferences.getBoolean(PREF_KEY_SHOW_DEBUGGABLE_FIRST, false)) {
235+
final Comparator<PackageInfoCache> debuggableComparator = (pkg1, pkg2) -> {
236+
if (pkg1.isDebuggable() && !pkg2.isDebuggable())
237+
return 1;
238+
if (!pkg1.isDebuggable() && pkg2.isDebuggable())
239+
return -1;
240+
return 0;
241+
};
242+
filtered.sort(debuggableComparator.reversed().thenComparing(orderComparator));
243+
} else {
244+
filtered.sort(orderComparator);
245+
}
246+
247+
mFilteredPackages.clear();
248+
mFilteredPackages.addAll(filtered);
249+
}
250+
251+
public static class ViewHolder extends RecyclerView.ViewHolder implements CompoundButton.OnCheckedChangeListener, View.OnClickListener {
252+
private final AppCompatImageView icon;
253+
private final AppCompatTextView applicationLabel;
254+
private final AppCompatTextView packageName;
255+
private final SwitchCompat toggle;
256+
257+
private PackageInfoCache pkg = null;
258+
259+
public ViewHolder(@NonNull View itemView) {
260+
super(itemView);
261+
icon = itemView.findViewById(R.id.icon);
262+
applicationLabel = itemView.findViewById(R.id.application_label);
263+
packageName = itemView.findViewById(R.id.package_name);
264+
toggle = itemView.findViewById(R.id.toggle);
265+
266+
itemView.setOnClickListener(this);
267+
}
268+
269+
public void bind(final PackageInfoCache pkg) {
270+
this.pkg = pkg;
271+
final Context context = itemView.getContext();
272+
final PackageManager pm = context.getPackageManager();
273+
274+
icon.setImageDrawable(pkg.getIcon(pm));
275+
applicationLabel.setText(pkg.getLabel(pm));
276+
applicationLabel.setTypeface(null, getTypeFace(pkg.isSystemApp(), pkg.isDebugApp()));
277+
packageName.setText(pkg.getPackageName());
278+
toggle.setOnCheckedChangeListener(null);
279+
toggle.setChecked(Configuration.isEnabled(pkg.getPackageName()));
280+
toggle.setOnCheckedChangeListener(this);
281+
}
282+
283+
@Override
284+
public void onCheckedChanged(CompoundButton button, boolean isChecked) {
285+
if (isChecked)
286+
Configuration.add(pkg.getPackageName());
287+
else
288+
Configuration.remove(pkg.getPackageName());
289+
Snackbar snackbar = Snackbar.make(itemView, R.string.restart_required, LENGTH_SHORT);
290+
snackbar.setAction(R.string.dismiss, v -> snackbar.dismiss());
291+
snackbar.show();
292+
}
293+
294+
@Override
295+
public void onClick(View view) {
296+
toggle.toggle();
297+
}
298+
299+
private static int getTypeFace(boolean system, boolean debug) {
300+
if (system && debug)
301+
return Typeface.BOLD_ITALIC;
302+
if (system)
303+
return Typeface.BOLD;
304+
if (debug)
305+
return Typeface.ITALIC;
306+
return Typeface.NORMAL;
307+
}
308+
}
309+
}

0 commit comments

Comments
 (0)