Skip to content

Commit

Permalink
Merge pull request #2264 from marunjar/fix_package_change
Browse files Browse the repository at this point in the history
fix listening on package change
  • Loading branch information
marunjar authored Apr 2, 2024
2 parents edd1024 + 7338c2c commit 3bf4300
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 59 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
package fr.neamar.kiss.broadcast;

import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.preference.PreferenceManager;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.util.Set;

import fr.neamar.kiss.KissApplication;
import fr.neamar.kiss.pojo.AppPojo;
import fr.neamar.kiss.utils.PackageManagerUtils;
import fr.neamar.kiss.utils.UserHandle;

/**
Expand All @@ -18,55 +26,89 @@
*/
public class PackageAddedRemovedHandler extends BroadcastReceiver {

public static void handleEvent(Context ctx, String action, String packageName, UserHandle user, boolean replacing) {
public static void handleEvent(@NonNull Context ctx, @Nullable String action, @NonNull String[] packageNames, @NonNull UserHandle user, boolean replacing) {
if (packageNames.length == 1 && packageNames[0].equalsIgnoreCase(ctx.getPackageName())) {
// When running KISS locally, sending a new version of the APK immediately triggers a "package removed" for fr.neamar.kiss,
// There is no need to handle this event.
// Discarding it makes startup time much faster locally as apps don't have to be loaded twice.
return;
}

if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
if (!replacing) {
Intent launchIntent = ctx.getPackageManager().getLaunchIntentForPackage(packageName);
// launchIntent can be null for some plugin app
if (launchIntent != null) {
// Add new package to history
if (PreferenceManager.getDefaultSharedPreferences(ctx).getBoolean("enable-app-history", true)) {
String className = launchIntent.getComponent().getClassName();
String pojoID = user.addUserSuffixToString("app://" + packageName + "/" + className, '/');
KissApplication.getApplication(ctx).getDataHandler().addToHistory(pojoID);
for (String packageName : packageNames) {
Intent launchIntent = ctx.getPackageManager().getLaunchIntentForPackage(packageName);
// launchIntent can be null for some plugin app
if (launchIntent != null) {
// Add new package to history
if (PreferenceManager.getDefaultSharedPreferences(ctx).getBoolean("enable-app-history", true)) {
String className = launchIntent.getComponent().getClassName();
String pojoID = user.addUserSuffixToString("app://" + packageName + "/" + className, '/');
KissApplication.getApplication(ctx).getDataHandler().addToHistory(pojoID);
}
}
}
}
}

KissApplication.getApplication(ctx).resetIconsHandler();

if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
if (!replacing) {
KissApplication.getApplication(ctx).resetIconsHandler();
// Reload application list
KissApplication.getApplication(ctx).getDataHandler().reloadApps();
// Remove all installed shortcuts
KissApplication.getApplication(ctx).getDataHandler().removeShortcuts(packageName);
KissApplication.getApplication(ctx).getDataHandler().removeFromExcluded(packageName);
for (String packageName : packageNames) {
KissApplication.getApplication(ctx).getDataHandler().removeShortcuts(packageName);
KissApplication.getApplication(ctx).getDataHandler().removeFromExcluded(packageName);
}
}
} else {
// Reload application list
KissApplication.getApplication(ctx).getDataHandler().reloadApps();
// Reload shortcuts
KissApplication.getApplication(ctx).getDataHandler().reloadShortcuts();
KissApplication.getApplication(ctx).resetIconsHandler();

boolean isAnyPackageVisible = isAnyPackageVisible(ctx, packageNames, user);
if (isAnyPackageVisible) {
// Reload application list
KissApplication.getApplication(ctx).getDataHandler().reloadApps();
// Reload shortcuts
KissApplication.getApplication(ctx).getDataHandler().reloadShortcuts();
}
}
}

/**
* @param ctx
* @param packageNames
* @param userHandle
* @return true, if any of packages has activity for launching and is not excluded from KISS
*/
private static boolean isAnyPackageVisible(Context ctx, String[] packageNames, UserHandle userHandle) {
Set<String> excludedApps = KissApplication.getApplication(ctx).getDataHandler().getExcluded();
for (String packageName : packageNames) {
ComponentName launchingComponent = PackageManagerUtils.getLaunchingComponent(ctx, packageName);
if (launchingComponent != null) {
boolean isExcluded = excludedApps.contains(AppPojo.getComponentName(launchingComponent.getPackageName(), launchingComponent.getClassName(), userHandle));
if (!isExcluded) {
return true;
}
}
}
return false;
}

@Override
public void onReceive(Context ctx, Intent intent) {

String packageName = intent.getData().getSchemeSpecificPart();

if (packageName.equalsIgnoreCase(ctx.getPackageName())) {
// When running KISS locally, sending a new version of the APK immediately triggers a "package removed" for fr.neamar.kiss,
// There is no need to handle this event.
// Discarding it makes startup time much faster locally as apps don't have to be loaded twice.
String[] packageNames = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
if (packageNames == null && intent.getData() != null) {
String packageName = intent.getData().getSchemeSpecificPart();
packageNames = new String[]{packageName};
}
if (packageNames == null) {
return;
}

handleEvent(ctx,
intent.getAction(),
packageName, new UserHandle(),
packageNames, new UserHandle(),
intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)
);

Expand Down
67 changes: 33 additions & 34 deletions app/src/main/java/fr/neamar/kiss/dataprovider/AppProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import android.content.SharedPreferences;
import android.content.pm.LauncherApps;
import android.os.Build;
import android.os.Process;
import android.os.UserManager;
import android.preference.PreferenceManager;

Expand All @@ -28,8 +27,6 @@ public class AppProvider extends Provider<AppPojo> {
@Override
public void onCreate() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// Package installation/uninstallation events for the main
// profile are still handled using PackageAddedRemovedHandler itself
final UserManager manager = (UserManager) this.getSystemService(Context.USER_SERVICE);
assert manager != null;

Expand All @@ -39,71 +36,73 @@ public void onCreate() {
launcher.registerCallback(new LauncherAppsCallback() {
@Override
public void onPackageAdded(String packageName, android.os.UserHandle user) {
handleEvent(Intent.ACTION_PACKAGE_ADDED, packageName, user, false);
handleEvent(Intent.ACTION_PACKAGE_ADDED, new String[]{packageName}, user, false);
}

@Override
public void onPackageChanged(String packageName, android.os.UserHandle user) {
handleEvent(Intent.ACTION_PACKAGE_CHANGED, packageName, user, true);
handleEvent(Intent.ACTION_PACKAGE_CHANGED, new String[]{packageName}, user, true);
}

@Override
public void onPackageRemoved(String packageName, android.os.UserHandle user) {
handleEvent(Intent.ACTION_PACKAGE_REMOVED, packageName, user, false);
handleEvent(Intent.ACTION_PACKAGE_REMOVED, new String[]{packageName}, user, false);
}

@Override
public void onPackagesAvailable(String[] packageNames, android.os.UserHandle user, boolean replacing) {
handleEvent(Intent.ACTION_MEDIA_MOUNTED, null, user, replacing);
handleEvent(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE, packageNames, user, replacing);
}

@Override
public void onPackagesSuspended(String[] packageNames, android.os.UserHandle user) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
handleEvent(Intent.ACTION_PACKAGES_SUSPENDED, null, user, false);
handleEvent(Intent.ACTION_PACKAGES_SUSPENDED, packageNames, user, false);
}
}

@Override
public void onPackagesUnsuspended(String[] packageNames, android.os.UserHandle user) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
handleEvent(Intent.ACTION_PACKAGES_UNSUSPENDED, null, user, false);
handleEvent(Intent.ACTION_PACKAGES_UNSUSPENDED, packageNames, user, false);
}
}

@Override
public void onPackagesUnavailable(String[] packageNames, android.os.UserHandle user, boolean replacing) {
handleEvent(Intent.ACTION_MEDIA_UNMOUNTED, null, user, replacing);
handleEvent(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE, packageNames, user, replacing);
}

private void handleEvent(String action, String packageName, android.os.UserHandle user, boolean replacing) {
if (!Process.myUserHandle().equals(user)) {
PackageAddedRemovedHandler.handleEvent(AppProvider.this,
action,
packageName, new UserHandle(manager.getSerialNumberForUser(user), user), replacing
);
}
private void handleEvent(String action, String[] packageNames, android.os.UserHandle user, boolean replacing) {
PackageAddedRemovedHandler.handleEvent(AppProvider.this,
action,
packageNames, new UserHandle(manager.getSerialNumberForUser(user), user), replacing
);
}
});
} else {
// Get notified when app changes on standard user profile
PackageAddedRemovedHandler packageAddedRemovedHandler = new PackageAddedRemovedHandler();

IntentFilter appChangedFilter = new IntentFilter();
appChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
appChangedFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
appChangedFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
appChangedFilter.addDataScheme("package");
this.registerReceiver(packageAddedRemovedHandler, appChangedFilter);

IntentFilter mediaChangedFilter = new IntentFilter();
mediaChangedFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);
mediaChangedFilter.addAction(Intent.ACTION_MEDIA_REMOVED);
mediaChangedFilter.addDataScheme("file");
this.registerReceiver(packageAddedRemovedHandler, mediaChangedFilter);

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
intentFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
this.registerReceiver(packageAddedRemovedHandler, intentFilter);
}

// Get notified when app changes on standard user profile
IntentFilter appChangedFilter = new IntentFilter();
appChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
appChangedFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
appChangedFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
appChangedFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);
appChangedFilter.addAction(Intent.ACTION_MEDIA_REMOVED);
appChangedFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
appChangedFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
appChangedFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
appChangedFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);
}
appChangedFilter.addDataScheme("package");
appChangedFilter.addDataScheme("file");
this.registerReceiver(new PackageAddedRemovedHandler(), appChangedFilter);

super.onCreate();
}

Expand Down
1 change: 1 addition & 0 deletions fastlane/metadata/android/en-US/changelogs/209.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
* Add operators '×x÷' back to the calculator
* Fix popup menu position for widgets using whole screen
* Fix reset of custom app name
* Fix listening on package changes

0 comments on commit 3bf4300

Please sign in to comment.