Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: User can select which apps can show shortcuts #2025

Merged
merged 8 commits into from
Dec 29, 2022
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 @@ -45,10 +45,13 @@
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.PackageManagerUtils;
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:
marunjar marked this conversation as resolved.
Show resolved Hide resolved
// 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