Skip to content

Commit

Permalink
Merge pull request #2025 from LukeNeedham/exclude-shortcut-apps
Browse files Browse the repository at this point in the history
Feature: User can select which apps can show shortcuts
  • Loading branch information
Neamar authored Dec 29, 2022
2 parents 7cb48f6 + 118ac3a commit fc96f7a
Show file tree
Hide file tree
Showing 11 changed files with 306 additions and 67 deletions.
41 changes: 41 additions & 0 deletions app/src/main/java/fr/neamar/kiss/DataHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ public class DataHandler extends BroadcastReceiver
final static private List<String> PROVIDER_NAMES = Arrays.asList(
"app", "contacts", "shortcuts"
);

/**
* Key for a preference that holds a String set of apps which are excluded from showing shortcuts.
* Each string in the set is the packageName of an app which may not show shortcuts.
*/
public final static String PREF_KEY_EXCLUDED_SHORTCUT_APPS = "excluded-shortcut-apps";

private TagsHandler tagsHandler;
final private Context context;
private String currentQuery;
Expand Down Expand Up @@ -550,6 +557,15 @@ public Set<String> getExcludedFavorites() {
return excludedFavorites;
}

@NonNull
public Set<String> getExcludedShortcutApps() {
Set<String> excluded = PreferenceManager.getDefaultSharedPreferences(context).getStringSet(PREF_KEY_EXCLUDED_SHORTCUT_APPS, null);
if (excluded == null) {
excluded = new HashSet<>();
}
return excluded;
}

public void addToExcludedFromHistory(AppPojo app) {
// The set needs to be cloned and then edited,
// modifying in place is not supported by putStringSet()
Expand Down Expand Up @@ -584,6 +600,17 @@ public void addToExcluded(AppPojo app) {
removeShortcuts(app.packageName);
}

/** Add app as an app which is not allowed to show shortcuts */
public void addToExcludedShortcutApps(AppPojo app) {
// The set needs to be cloned and then edited,
// modifying in place is not supported by putStringSet()
Set<String> excluded = new HashSet<>(getExcludedShortcutApps());
excluded.add(app.packageName);
PreferenceManager.getDefaultSharedPreferences(context).edit().putStringSet(PREF_KEY_EXCLUDED_SHORTCUT_APPS, excluded).apply();
app.setExcludedShortcuts(true);
reloadShortcuts();
}

public void removeFromExcluded(AppPojo app) {
// The set needs to be cloned and then edited,
// modifying in place is not supported by putStringSet()
Expand Down Expand Up @@ -625,6 +652,20 @@ public void removeFromExcluded(UserHandle user) {
PreferenceManager.getDefaultSharedPreferences(context).edit().putStringSet("excluded-apps", newExcluded).apply();
}

/**
* Remove app from the apps which are not allowed to show shortcuts -
* that is to say, this app may show shortcuts
*/
public void removeFromExcludedShortcutApps(AppPojo app) {
// The set needs to be cloned and then edited,
// modifying in place is not supported by putStringSet()
Set<String> excluded = new HashSet<>(getExcludedShortcutApps());
excluded.remove(app.packageName);
PreferenceManager.getDefaultSharedPreferences(context).edit().putStringSet(PREF_KEY_EXCLUDED_SHORTCUT_APPS, excluded).apply();
app.setExcludedShortcuts(false);
reloadShortcuts();
}

/**
* Return all applications (including excluded)
*
Expand Down
109 changes: 109 additions & 0 deletions app/src/main/java/fr/neamar/kiss/SettingsActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,12 @@
import fr.neamar.kiss.pojo.TagDummyPojo;
import fr.neamar.kiss.preference.ExcludePreferenceScreen;
import fr.neamar.kiss.preference.PreferenceScreenHelper;
import fr.neamar.kiss.preference.ResetExcludedAppShortcutsPreference;
import fr.neamar.kiss.preference.ResetShortcutsPreference;
import fr.neamar.kiss.preference.SwitchPreference;
import fr.neamar.kiss.searcher.QuerySearcher;
import fr.neamar.kiss.utils.Permission;
import fr.neamar.kiss.utils.ShortcutUtil;

@SuppressWarnings("FragmentInjection")
public class SettingsActivity extends PreferenceActivity implements
Expand Down Expand Up @@ -151,10 +154,25 @@ protected void onCreate(Bundle savedInstanceState) {

// This is reaaally slow, and always need to run asynchronously
Runnable alwaysAsync = () -> {
// TODO: Note that there is a bug here with all of these settings pages:
// These settings pages load the list of AppPojos from DataHandler only once.
// This means that the data shown in these settings pages will be stale if the AppPojo
// data stored in DataHandler is changed by elsewhere in the app.
// You can easily reproduce this bug by:
// 1. Open the 'apps excluded from KISS' page
// 2. Change some values from their defaults
// 3. Go back and use the 'reset apps excluded from KISS' button
// 4. Open the 'apps excluded from KISS' page again. The data shown will be incorrect,
// as it won't have refreshed for the user having reset the list.
// This list will refresh if the user closes and re-opens KISS settings.
SettingsActivity.this.addExcludedAppSettings();
SettingsActivity.this.addExcludedFromHistoryAppSettings();
SettingsActivity.this.addExcludedShortcutAppSettings();
};

addEnableShortcutsSwitch();
addRegenerateShortcutsPreference();
addResetExcludedAppShortcutsPreference();
reorderPreferencesWithDisplayDependency();

if (savedInstanceState == null) {
Expand Down Expand Up @@ -396,6 +414,97 @@ public void onIncluded(final @NonNull AppPojo app) {
category.addPreference(excludedAppsScreen);
}

/**
* Adds the button for resetting the apps excluded from showing shortcuts,
* only if this device supports shortcuts
*/
private void addResetExcludedAppShortcutsPreference() {
if(!ShortcutUtil.canDeviceShowShortcuts()) {
return;
}

ResetExcludedAppShortcutsPreference pref = new ResetExcludedAppShortcutsPreference(this);
pref.setKey("reset-excluded-app-shortcuts");
pref.setOrder(59);
pref.setTitle(R.string.reset_excluded_app_shortcuts_name);
pref.setDialogMessage(R.string.reset_excluded_app_shortcuts_warn);

PreferenceGroup category = (PreferenceGroup) findPreference("exclude_apps_category");
category.addPreference(pref);
}

/**
* Adds the switch for toggling whether shortcuts shown be shown,
* only if this device supports shortcuts
*/
private void addEnableShortcutsSwitch() {
if(!ShortcutUtil.canDeviceShowShortcuts()) {
return;
}

SwitchPreference pref = new SwitchPreference(this);
pref.setDefaultValue(true);
pref.setKey("enable-shortcuts");
pref.setTitle(R.string.shortcuts_name);

PreferenceGroup category = (PreferenceGroup) findPreference("search-providers");
pref.setOrder(3);
category.addPreference(pref);
}

/**
* Adds the button for regenerating the list of shortcuts,
* only if this device supports shortcuts
*/
private void addRegenerateShortcutsPreference() {
if(!ShortcutUtil.canDeviceShowShortcuts()) {
return;
}

ResetShortcutsPreference pref = new ResetShortcutsPreference(this);
pref.setDialogMessage(R.string.regenerate_shortcuts_desc);
pref.setKey("reset");
pref.setTitle(R.string.regenerate_shortcuts);

PreferenceGroup category = (PreferenceGroup) findPreference("search-providers");
pref.setOrder(5);
category.addPreference(pref);
}

private void addExcludedShortcutAppSettings() {
if(!ShortcutUtil.canDeviceShowShortcuts()) {
return;
}

final DataHandler dataHandler = KissApplication.getApplication(this).getDataHandler();

PreferenceScreen excludedAppsScreen = ExcludePreferenceScreen.getInstance(
this,
new ExcludePreferenceScreen.IsExcludedCallback() {
@Override
public boolean isExcluded(@NonNull AppPojo app) {
return app.isExcludedShortcuts();
}
},
new ExcludePreferenceScreen.OnExcludedListener() {
@Override
public void onExcluded(final @NonNull AppPojo app) {
dataHandler.addToExcludedShortcutApps(app);
}

@Override
public void onIncluded(final @NonNull AppPojo app) {
dataHandler.removeFromExcludedShortcutApps(app);
}
},
R.string.ui_excluded_from_shortcuts_apps,
R.string.ui_excluded_apps_dialog_title
);

PreferenceGroup category = (PreferenceGroup) findPreference("exclude_apps_category");
category.addPreference(excludedAppsScreen);
}

private void addCustomSearchProvidersPreferences(SharedPreferences prefs) {
if (prefs.getStringSet("selected-search-provider-names", null) == null) {
// If null, it means this setting has never been accessed before
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,8 @@ public void reload() {

try {
this.initialize(new LoadShortcutsPojos(this));
}
catch(IllegalStateException e) {
if(!notifiedKissNotDefaultLauncher) {
} catch (IllegalStateException e) {
if (!notifiedKissNotDefaultLauncher) {
// Only display this message once per process
Toast.makeText(this, R.string.unable_to_initialize_shortcuts, Toast.LENGTH_LONG).show();
}
Expand Down
10 changes: 6 additions & 4 deletions app/src/main/java/fr/neamar/kiss/loader/LoadAppPojos.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ protected ArrayList<AppPojo> doInBackground(Void... params) {

Set<String> excludedAppList = KissApplication.getApplication(ctx).getDataHandler().getExcluded();
Set<String> excludedFromHistoryAppList = KissApplication.getApplication(ctx).getDataHandler().getExcludedFromHistory();
Set<String> excludedShortcutsAppList = KissApplication.getApplication(ctx).getDataHandler().getExcludedShortcutApps();

if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
UserManager manager = (UserManager) ctx.getSystemService(Context.USER_SERVICE);
Expand All @@ -54,7 +55,7 @@ protected ArrayList<AppPojo> doInBackground(Void... params) {
UserHandle user = new UserHandle(manager.getSerialNumberForUser(profile), profile);
for (LauncherActivityInfo activityInfo : launcher.getActivityList(null, profile)) {
ApplicationInfo appInfo = activityInfo.getApplicationInfo();
final AppPojo app = createPojo(user, appInfo.packageName, activityInfo.getName(), activityInfo.getLabel(), excludedAppList, excludedFromHistoryAppList);
final AppPojo app = createPojo(user, appInfo.packageName, activityInfo.getName(), activityInfo.getLabel(), excludedAppList, excludedFromHistoryAppList, excludedShortcutsAppList);
apps.add(app);
}
}
Expand All @@ -66,7 +67,7 @@ protected ArrayList<AppPojo> doInBackground(Void... params) {

for (ResolveInfo info : manager.queryIntentActivities(mainIntent, 0)) {
ApplicationInfo appInfo = info.activityInfo.applicationInfo;
final AppPojo app = createPojo(new UserHandle(), appInfo.packageName, info.activityInfo.name, info.loadLabel(manager), excludedAppList, excludedFromHistoryAppList);
final AppPojo app = createPojo(new UserHandle(), appInfo.packageName, info.activityInfo.name, info.loadLabel(manager), excludedAppList, excludedFromHistoryAppList, excludedShortcutsAppList);
apps.add(app);
}
}
Expand All @@ -88,13 +89,14 @@ protected ArrayList<AppPojo> doInBackground(Void... params) {
return apps;
}

private AppPojo createPojo(UserHandle userHandle, String packageName, String activityName, CharSequence label, Set<String> excludedAppList, Set<String> excludedFromHistoryAppList) {
private AppPojo createPojo(UserHandle userHandle, String packageName, String activityName, CharSequence label, Set<String> excludedAppList, Set<String> excludedFromHistoryAppList, Set<String> excludedShortcutsAppList) {
String id = userHandle.addUserSuffixToString(pojoScheme + packageName + "/" + activityName, '/');

boolean isExcluded = excludedAppList.contains(AppPojo.getComponentName(packageName, activityName, userHandle));
boolean isExcludedFromHistory = excludedFromHistoryAppList.contains(id);
boolean isExcludedShortcuts = excludedShortcutsAppList.contains(packageName);

AppPojo app = new AppPojo(id, packageName, activityName, userHandle, isExcluded, isExcludedFromHistory);
AppPojo app = new AppPojo(id, packageName, activityName, userHandle, isExcluded, isExcludedFromHistory, isExcludedShortcuts);

app.setName(label.toString());

Expand Down
32 changes: 18 additions & 14 deletions app/src/main/java/fr/neamar/kiss/loader/LoadShortcutsPojos.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ protected ArrayList<ShortcutPojo> doInBackground(Void... arg0) {
DataHandler dataHandler = KissApplication.getApplication(context).getDataHandler();
TagsHandler tagsHandler = dataHandler.getTagsHandler();
Set<String> excludedApps = dataHandler.getExcluded();
Set<String> excludedShortcutApps = dataHandler.getExcludedShortcutApps();

ArrayList<ShortcutPojo> pojos = new ArrayList<>();

Expand All @@ -56,7 +57,7 @@ protected ArrayList<ShortcutPojo> doInBackground(Void... arg0) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
List<ShortcutInfo> shortcutInfos = ShortcutUtil.getAllShortcuts(context);
for (ShortcutInfo shortcutInfo : shortcutInfos) {
if (shortcutVisible(context, shortcutInfo, excludedApps, visibleShortcutIds)) {
if (isShortcutVisible(context, shortcutInfo, excludedApps, excludedShortcutApps, visibleShortcutIds)) {
ShortcutRecord shortcutRecord = ShortcutUtil.createShortcutRecord(context, shortcutInfo, !shortcutInfo.isPinned());
if (shortcutRecord != null) {
ShortcutPojo pojo = createPojo(shortcutRecord, tagsHandler, ShortcutUtil.getComponentName(context, shortcutInfo), shortcutInfo.isPinned(), shortcutInfo.isDynamic());
Expand All @@ -77,19 +78,22 @@ private ShortcutPojo createPojo(ShortcutRecord shortcutRecord, TagsHandler tagsH
}

@RequiresApi(Build.VERSION_CODES.O)
private boolean shortcutVisible(Context context, ShortcutInfo shortcutInfo, Set<String> excludedApps, Set<String> visibleShortcutIds) {
if (shortcutInfo.isEnabled()) {
String componentName = ShortcutUtil.getComponentName(context, shortcutInfo);
// if related package is excluded from KISS then the shortcut must be excluded too
if (!excludedApps.contains(componentName)) {
if (shortcutInfo.isPinned()) {
return visibleShortcutIds.contains(shortcutInfo.getId());
} else {
return true;
}
}
private boolean isShortcutVisible(Context context, ShortcutInfo shortcutInfo, Set<String> excludedApps, Set<String> excludedShortcutApps, Set<String> visibleShortcutIds) {
if (!shortcutInfo.isEnabled()) {
return false;
}
return false;
}
String packageName = shortcutInfo.getPackage();
String componentName = ShortcutUtil.getComponentName(context, shortcutInfo);

// if related package is excluded from KISS then the shortcut must be excluded too
boolean isExcluded = excludedApps.contains(componentName) || excludedShortcutApps.contains(packageName);
if (isExcluded) {
return false;
}

if (shortcutInfo.isPinned()) {
return visibleShortcutIds.contains(shortcutInfo.getId());
}
return true;
}
}
15 changes: 14 additions & 1 deletion app/src/main/java/fr/neamar/kiss/pojo/AppPojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@ public static String getComponentName(String packageName, String activityName,

private boolean excluded;
private boolean excludedFromHistory;
/**
* Whether shortcuts are excluded for this app
*/
private boolean excludedShortcuts;
private long customIconId = 0;

public AppPojo(String id, String packageName, String activityName, UserHandle userHandle,
boolean isExcluded, boolean isExcludedFromHistory) {
boolean isExcluded, boolean isExcludedFromHistory, boolean isExcludedShortcuts) {
super(id);

this.packageName = packageName;
Expand All @@ -29,6 +33,7 @@ public AppPojo(String id, String packageName, String activityName, UserHandle us

this.excluded = isExcluded;
this.excludedFromHistory = isExcludedFromHistory;
this.excludedShortcuts = isExcludedShortcuts;
}

public String getComponentName() {
Expand All @@ -51,6 +56,14 @@ public void setExcludedFromHistory(boolean excludedFromHistory) {
this.excludedFromHistory = excludedFromHistory;
}

public boolean isExcludedShortcuts() {
return excludedShortcuts;
}

public void setExcludedShortcuts(boolean excludedShortcuts) {
this.excludedShortcuts = excludedShortcuts;
}

public void setCustomIconId(long iconId) {
customIconId = iconId;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package fr.neamar.kiss.preference;

import android.content.Context;
import android.content.DialogInterface;
import android.preference.DialogPreference;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
import android.widget.Toast;

import fr.neamar.kiss.DataHandler;
import fr.neamar.kiss.KissApplication;
import fr.neamar.kiss.R;

public class ResetExcludedAppShortcutsPreference extends DialogPreference {

public ResetExcludedAppShortcutsPreference(Context context) {
super(context, null);
}

public ResetExcludedAppShortcutsPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}

@Override
public void onClick(DialogInterface dialog, int which) {
super.onClick(dialog, which);
if (which == DialogInterface.BUTTON_POSITIVE) {
PreferenceManager.getDefaultSharedPreferences(getContext()).edit()
.putStringSet(DataHandler.PREF_KEY_EXCLUDED_SHORTCUT_APPS, null).apply();
DataHandler dataHandler = KissApplication.getApplication(getContext()).getDataHandler();
// Reload shortcuts to refresh the shortcuts shown in KISS
dataHandler.reloadShortcuts();
// Reload apps since the `AppPojo.isExcludedShortcuts` value also needs to be refreshed
dataHandler.reloadApps();
Toast.makeText(getContext(), R.string.excluded_app_list_erased, Toast.LENGTH_LONG).show();
}

}

}
Loading

0 comments on commit fc96f7a

Please sign in to comment.