Skip to content

Commit 4b7f6a4

Browse files
committed
Redesign the app (un)install receiver
- Packages will be refreshed individually whenever neccesary - Refreshing directly modifies SearchActivity.appInfoList.backingAppInfoList if it's available at WeakReference App.backingAppInfoList - LocaleChangeReceiver triggers a full refresh when changing the system language - Under settings there is a refresh button, just in case - IconCache.tryIconCaching() no longer returns true if icon already exists instead the icon gets refreshed anyway
1 parent b8d7742 commit 4b7f6a4

16 files changed

+433
-294
lines changed

android/src/main/AndroidManifest.xml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,18 @@
7070

7171
<receiver android:name=".background.AppInstallOrRemoveReceiver">
7272
<intent-filter>
73-
<action android:name="android.intent.action.PACKAGE_INSTALL" />
7473
<action android:name="android.intent.action.PACKAGE_ADDED" />
75-
<action android:name="android.intent.action.PACKAGE_REMOVED" />
76-
<action android:name="android.intent.action.PACKAGE_REPLACED" />
7774
<action android:name="android.intent.action.PACKAGE_CHANGED" />
7875
<action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
7976
<data android:scheme="package" />
8077
</intent-filter>
8178
</receiver>
79+
80+
<receiver android:name=".background.LocaleChangeReceiver">
81+
<intent-filter>
82+
<action android:name="android.intent.action.LOCALE_CHANGED" />
83+
</intent-filter>
84+
</receiver>
8285
</application>
8386

8487
</manifest>

android/src/main/java/org/ligi/fast/App.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.ligi.tracedroid.TraceDroid;
1010

1111
import java.io.File;
12+
import java.lang.ref.WeakReference;
1213

1314
public class App extends Application {
1415

@@ -22,6 +23,7 @@ public interface PackageChangedListener {
2223
}
2324

2425
public static PackageChangedListener packageChangedListener;
26+
public static WeakReference<AppInfoList> backingAppInfoList;
2527

2628
@Override
2729
public void onCreate() {

android/src/main/java/org/ligi/fast/background/AppInstallOrRemoveReceiver.java

Lines changed: 125 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,140 @@
33
import android.content.BroadcastReceiver;
44
import android.content.Context;
55
import android.content.Intent;
6+
import android.content.pm.ResolveInfo;
7+
68
import org.ligi.fast.App;
9+
import org.ligi.fast.model.AppInfo;
710
import org.ligi.fast.model.AppInfoList;
811
import org.ligi.fast.util.AppInfoListStore;
912

13+
import java.io.File;
14+
import java.util.Iterator;
15+
import java.util.List;
16+
17+
/**
18+
* Whenever an app is installed, uninstalled or components change
19+
* (e.g. the app disabled one of it's activities to hide it from the launcher)
20+
* this receiver takes care of removing or updating corresponding entries
21+
* (namely all activities and aliases) from AppInfoList and deletes their icons
22+
* from cache to clean up when uninstalling or to cause a refresh when updating.
23+
*/
1024
public class AppInstallOrRemoveReceiver extends BroadcastReceiver {
25+
//public final static String LOG_TAG = "FAST.AppInstallOrRemoveReceiver";
26+
1127
@Override
1228
public void onReceive(Context context, Intent intent) {
13-
final AppInfoListStore appInfoListStore = new AppInfoListStore(context);
29+
String packageName = intent.getData().getSchemeSpecificPart();
30+
String action = intent.getAction();
31+
AppInfoListStore appInfoListStore = new AppInfoListStore(context);
32+
AppInfoList appInfoList = null;
33+
if (App.backingAppInfoList != null) {
34+
appInfoList = App.backingAppInfoList.get();
35+
}
36+
if (appInfoList == null) {
37+
appInfoList = appInfoListStore.load();
38+
}
39+
AppInfoList matchedAppInfoList = new AppInfoList();
40+
Iterator<AppInfo> appInfoIterator = appInfoList.iterator();
41+
while (appInfoIterator.hasNext()) {
42+
AppInfo appInfo = appInfoIterator.next();
43+
if (appInfo.getPackageName().equals(packageName)) {
44+
matchedAppInfoList.add(appInfo);
45+
appInfoIterator.remove();
46+
File icon = new File(App.getBaseDir() + "/" + appInfo.getHash() + ".png");
47+
icon.delete();
48+
/*
49+
if (!icon.delete()) {
50+
Log.d(App.LOG_TAG, "AppInstallOrRemoveReceiver: Icon deletion failed for hash: " + appInfo.getHash());
51+
Log.d(AppInstallOrRemoveReceiver.LOG_TAG, "Icon deletion failed for hash: " + appInfo.getHash());
52+
}
53+
*/
54+
}
55+
}
1456

15-
if (App.packageChangedListener == null) {
16-
App.packageChangedListener = new App.PackageChangedListener() {
17-
@Override
18-
public void onPackageChange(AppInfoList appInfoList) {
19-
appInfoListStore.save(appInfoList);
57+
if (!action.equals(Intent.ACTION_PACKAGE_FULLY_REMOVED)) {
58+
Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
59+
launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER);
60+
launcherIntent.setPackage(packageName);
61+
List<ResolveInfo> resolveInfoList = context.getPackageManager().queryIntentActivities(launcherIntent, 0);
62+
63+
Intent homeIntent = new Intent(Intent.ACTION_MAIN);
64+
homeIntent.addCategory(Intent.CATEGORY_HOME);
65+
homeIntent.setPackage(packageName);
66+
List<ResolveInfo> homeInfoList = context.getPackageManager().queryIntentActivities(homeIntent, 0);
67+
68+
// If there are no activities that should be displayed on the launcher we can quit here
69+
if (resolveInfoList.size() == 0 && homeInfoList.size() == 0) {
70+
//Log.d(App.LOG_TAG, "AppInstallOrRemoveReceiver: No launcher Activities"
71+
// + "\n\tPackage: " + packageName);
72+
return;
73+
}
74+
75+
// Deduplicate Resolve Info of activities with both categories - like SearchActivity (see manifest)
76+
for (ResolveInfo info : resolveInfoList) {
77+
Iterator<ResolveInfo> homeIterator = homeInfoList.iterator();
78+
while (homeIterator.hasNext()) {
79+
ResolveInfo homeInfo = homeIterator.next();
80+
if (homeInfo.activityInfo.name.equals(info.activityInfo.name)) {
81+
homeIterator.remove();
82+
break;
83+
}
84+
}
85+
if (!homeIterator.hasNext()) {
86+
break;
87+
}
88+
}
89+
resolveInfoList.addAll(homeInfoList);
90+
91+
/*
92+
String log =
93+
"AppInstallOrRemoveReceiver: Updating info:"
94+
+ "\n\tAction: " + action
95+
+ "\n\tPackage: " + packageName
96+
+ "\n\tLabel: " + resolveInfoList.get(0).activityInfo.loadLabel(context.getPackageManager())
97+
+ "\n\tActivities: " + String.valueOf(resolveInfoList.size());
98+
Log.d(App.LOG_TAG, log);
99+
Log.d(AppInstallOrRemoveReceiver.LOG_TAG, log);
100+
for (ResolveInfo i : resolveInfoList) {
101+
Log.d(App.LOG_TAG, "\t " + i.activityInfo.name);
102+
Log.d(AppInstallOrRemoveReceiver.LOG_TAG, "\t " + i.activityInfo.name);
103+
}
104+
*/
105+
106+
if (matchedAppInfoList.size() == 0) { // New app: Package name not amongst known apps
107+
for (ResolveInfo info : resolveInfoList) {
108+
appInfoList.add(new AppInfo(context, info));
20109
}
21-
};
110+
} else { // Update: Package name included in known apps
111+
for (ResolveInfo info : resolveInfoList) {
112+
AppInfo actAppInfo = new AppInfo(context, info);
113+
114+
Iterator<AppInfo> oldInfoIterator = matchedAppInfoList.iterator();
115+
while (oldInfoIterator.hasNext()) {
116+
AppInfo oldInfo = oldInfoIterator.next();
117+
if (oldInfo.getActivityName().equals(actAppInfo.getActivityName())) {
118+
if (oldInfo.getLabelMode() == 2) { // AppInfo is alias
119+
oldInfo.setLabel(actAppInfo.getLabel());
120+
oldInfo.setInstallTime(actAppInfo.getInstallTime());
121+
appInfoList.add(oldInfo);
122+
} else {
123+
actAppInfo.setCallCount(oldInfo.getCallCount());
124+
actAppInfo.setPinMode(oldInfo.getPinMode());
125+
actAppInfo.setLabelMode(oldInfo.getLabelMode());
126+
actAppInfo.setOverrideLabel(oldInfo.getOverrideLabel());
127+
}
128+
oldInfoIterator.remove();
129+
}
130+
}
131+
appInfoList.add(actAppInfo);
132+
}
133+
}
22134
}
23135

24-
new BackgroundGatherAsyncTask(context, appInfoListStore.load()).execute();
136+
if (App.packageChangedListener == null) {
137+
appInfoListStore.save(appInfoList);
138+
} else {
139+
App.packageChangedListener.onPackageChange(appInfoList);
140+
}
25141
}
26-
}
142+
}

android/src/main/java/org/ligi/fast/background/BackgroundGatherAsyncTask.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77
import org.ligi.fast.model.AppInfoList;
88
import org.ligi.fast.util.AppInfoListStore;
99

10-
import java.util.List;
11-
1210
public class BackgroundGatherAsyncTask extends BaseAppGatherAsyncTask {
1311

14-
public BackgroundGatherAsyncTask(Context context, AppInfoList oldAppInfoList) {
15-
super(context, oldAppInfoList);
12+
private Context context;
13+
14+
public BackgroundGatherAsyncTask(Context context) {
15+
super(context);
1616
}
1717

1818
@Override
@@ -25,7 +25,9 @@ protected void onPostExecute(Void result) {
2525
super.onPostExecute(result);
2626
if (App.packageChangedListener != null) {
2727
App.packageChangedListener.onPackageChange(appInfoList);
28+
} else {
29+
new AppInfoListStore(context).save(appInfoList);
2830
}
31+
context = null;
2932
}
30-
3133
}

android/src/main/java/org/ligi/fast/background/BaseAppGatherAsyncTask.java

Lines changed: 49 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5,80 +5,81 @@
55
import android.content.pm.ResolveInfo;
66
import android.os.AsyncTask;
77

8+
import org.ligi.fast.App;
89
import org.ligi.fast.model.AppInfo;
910
import org.ligi.fast.model.AppInfoList;
10-
import org.ligi.tracedroid.logging.Log;
11+
import org.ligi.fast.util.AppInfoListStore;
1112

12-
import java.util.ArrayList;
13+
import java.util.Iterator;
1314
import java.util.List;
1415

1516
/**
1617
* Async-Task to Retrieve / Store Application Info needed by this App
1718
*/
1819
public class BaseAppGatherAsyncTask extends AsyncTask<Void, AppInfo, Void> {
19-
private final Context ctx;
20+
private Context ctx;
21+
private AppInfoList oldAppList;
2022
protected int appCount;
2123
protected AppInfoList appInfoList;
22-
private final AppInfoList oldAppList;
2324

2425
public BaseAppGatherAsyncTask(Context ctx) {
25-
this(ctx, null);
26-
}
27-
28-
public BaseAppGatherAsyncTask(Context ctx, AppInfoList oldAppList) {
2926
this.ctx = ctx;
3027
appInfoList = new AppInfoList();
31-
this.oldAppList = oldAppList;
28+
if (App.backingAppInfoList != null) {
29+
this.oldAppList = App.backingAppInfoList.get();
30+
}
31+
if (this.appInfoList == null) {
32+
this.appInfoList = new AppInfoListStore(ctx).load();
33+
}
3234
}
3335

34-
private void processCategory(final String category) {
35-
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
36-
mainIntent.addCategory(category);
37-
try {
38-
final List<ResolveInfo> resolveInfoList = ctx.getPackageManager().queryIntentActivities(mainIntent, 0);
39-
appCount += resolveInfoList.size();
40-
for (ResolveInfo info : resolveInfoList) {
41-
final AppInfo actAppInfo = new AppInfo(ctx, info);
36+
@Override
37+
protected Void doInBackground(Void... params) {
38+
Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
39+
launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER);
40+
List<ResolveInfo> resolveInfoList = ctx.getPackageManager().queryIntentActivities(launcherIntent, 0);
41+
42+
Intent homeIntent = new Intent(Intent.ACTION_MAIN);
43+
homeIntent.addCategory(Intent.CATEGORY_HOME);
44+
resolveInfoList.addAll(ctx.getPackageManager().queryIntentActivities(homeIntent, 0));
4245

43-
if (!ctx.getPackageName().equals(actAppInfo.getPackageName())) { // ignore self
46+
appCount = resolveInfoList.size();
4447

45-
// Update call count from current index that is being used.
46-
// This is because we may have updated the call count since the last time
47-
// we saved the package list. An alternative would be to save the package list
48-
// each time we leave
49-
if (oldAppList != null) {
50-
for(AppInfo oldInfo : oldAppList) {
51-
if (oldInfo.getActivityName().equals(actAppInfo.getActivityName())) {
52-
if (oldInfo.getLabelMode() == 2) {
53-
appInfoList.add(oldInfo);
54-
} else {
55-
actAppInfo.setCallCount(oldInfo.getCallCount());
56-
actAppInfo.setPinMode(oldInfo.getPinMode());
57-
actAppInfo.setLabelMode(oldInfo.getLabelMode());
58-
actAppInfo.setOverrideLabel(oldInfo.getOverrideLabel());
59-
}
48+
for (ResolveInfo info : resolveInfoList) {
49+
if (!ctx.getPackageName().equals(info.activityInfo.packageName)) { // ignore self
50+
AppInfo actAppInfo = new AppInfo(ctx, info);
51+
52+
// Update call count from current index that is being used.
53+
// This is because we may have updated the call count since the last time
54+
// we saved the package list. An alternative would be to save the package list
55+
// each time we leave
56+
if (oldAppList != null) {
57+
Iterator<AppInfo> oldInfoIterator = oldAppList.iterator();
58+
while (oldInfoIterator.hasNext()) {
59+
AppInfo oldInfo = oldInfoIterator.next();
60+
if (oldInfo.getActivityName().equals(actAppInfo.getActivityName())) {
61+
if (oldInfo.getLabelMode() == 2) { // AppInfo is alias
62+
oldInfo.setLabel(actAppInfo.getLabel());
63+
oldInfo.setInstallTime(actAppInfo.getInstallTime());
64+
appInfoList.add(oldInfo);
65+
} else {
66+
actAppInfo.setCallCount(oldInfo.getCallCount());
67+
actAppInfo.setPinMode(oldInfo.getPinMode());
68+
actAppInfo.setLabelMode(oldInfo.getLabelMode());
69+
actAppInfo.setOverrideLabel(oldInfo.getOverrideLabel());
6070
}
71+
oldInfoIterator.remove();
72+
// Can't break here anymore because of aliases
73+
// So instead this removes entries after processing
6174
}
6275
}
63-
appInfoList.add(actAppInfo);
64-
publishProgress(actAppInfo);
6576
}
77+
appInfoList.add(actAppInfo);
78+
publishProgress(actAppInfo);
6679
}
67-
} catch (Exception e) {
68-
Log.d("Exception occurred when getting activities skipping...!");
6980
}
7081

71-
}
72-
73-
74-
@Override
75-
protected Void doInBackground(Void... params) {
76-
// TODO the progressbar could be more exact here by first querying both - calculating the
77-
// total app-count and then process them - but as we do not expect that much launchers we
78-
// should be OK here
79-
appCount=0;
80-
processCategory(Intent.CATEGORY_LAUNCHER);
81-
processCategory(Intent.CATEGORY_HOME);
82+
ctx = null;
8283
return null;
8384
}
8485

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package org.ligi.fast.background;
2+
3+
import android.content.BroadcastReceiver;
4+
import android.content.Context;
5+
import android.content.Intent;
6+
7+
import org.ligi.fast.App;
8+
import org.ligi.fast.model.AppInfoList;
9+
import org.ligi.fast.util.AppInfoListStore;
10+
11+
/**
12+
* Refreshes the whole AppInfoList to update labels when the user changes the system language
13+
*/
14+
public class LocaleChangeReceiver extends BroadcastReceiver {
15+
@Override
16+
public void onReceive(Context context, Intent intent) {
17+
new BackgroundGatherAsyncTask(context).execute();
18+
}
19+
}

android/src/main/java/org/ligi/fast/model/AppIconCache.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,6 @@ private File getIconCacheFile() {
8383
}
8484

8585
private boolean tryIconCaching(IconCacheSpec iconCacheSpec, ResolveInfo ri, PackageManager pm) {
86-
if (getIconCacheFile().exists()) {
87-
return true;
88-
}
89-
9086
try {
9187
final Drawable icon = ri.loadIcon(pm);
9288
if (icon != null) {

0 commit comments

Comments
 (0)