diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c538cc7..de50c2c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -7,6 +7,9 @@
+
+
+
mSearchSheet;
private CombinedAdapter mSearchAdapter;
private Fragment mCurrentFragment;
+ private Drawable mWallpaperDrawable;
private final BroadcastReceiver mPackageReceiver = new BroadcastReceiver() {
@Override
@@ -105,7 +110,7 @@ protected void onCreate(Bundle savedInstanceState) {
mFavouritesHelper = new FavouritesRepository(mExecutor, mMainHandler);
mAppLoader = new AppLoader(this);
- var appSearchProvider = new AppSearchProvider(mAppLoader);
+ var appSearchProvider = new AppSearchProvider(mAppLoader, mFavouritesHelper);
var searchProviders = List.of(
appSearchProvider,
new WebSearchProvider(),
@@ -120,6 +125,7 @@ protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setDimAmount(0f);
setContentView(R.layout.activity_main);
+ loadWallpaperBackground();
initialiseSearchView();
@@ -475,11 +481,44 @@ private void performSearch(String query) {
});
}
- private Fragment findPagerFragment(int position) {
- long itemId = mPagerAdapter.getItemId(position);
+ private void loadWallpaperBackground() {
+ var wallpaperPath = SettingsManager.getWallpaper();
+ if (wallpaperPath == null || wallpaperPath.isEmpty()) {
+ return;
+ }
+
+ var wallpaperUri = Uri.parse(wallpaperPath);
+ try (var inputStream = getContentResolver().openInputStream(wallpaperUri)) {
+ if (inputStream != null) {
+ mWallpaperDrawable = Drawable.createFromStream(inputStream, wallpaperUri.toString());
+ if (mWallpaperDrawable == null) {
+ return;
+ }
+
+ setWallpaperDim();
+ }
+ } catch (Exception ignored) { }
+ }
+
+ private void setWallpaperDim() {
+ var dimColor = getDimColour(SettingsManager.getWallpaperDimAmount());
+ var filter = new BlendModeColorFilter(dimColor, BlendMode.SRC_ATOP);
+ mWallpaperDrawable.setColorFilter(filter);
+ var wallpaperHost = (ImageView) findViewById(R.id.wallpaper_image);
+ wallpaperHost.setImageDrawable(mWallpaperDrawable);
+ }
+
+ private Fragment findPagerFragment(final int position) {
+ var itemId = mPagerAdapter.getItemId(position);
return getSupportFragmentManager().findFragmentByTag("f" + itemId);
}
+ private int getDimColour(float dimAmount) {
+ dimAmount = Math.max(0f, Math.min(dimAmount, 1f));
+ var alpha = (int) (dimAmount * 255);
+ return alpha << 24;
+ }
+
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (SettingsManager.KEY_ICON_PACK.equals(key) ||
@@ -490,5 +529,11 @@ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, Strin
) {
refreshUi();
}
+
+ if (SettingsManager.KEY_WALLPAPER.equals(key) ||
+ SettingsManager.KEY_WALLPAPER_DIM_AMOUNT.equals(key)
+ ) {
+ loadWallpaperBackground();
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/chadderbox/launchbox/search/AppSearchProvider.java b/app/src/main/java/com/chadderbox/launchbox/search/AppSearchProvider.java
index 1eeb60c..2e8649a 100644
--- a/app/src/main/java/com/chadderbox/launchbox/search/AppSearchProvider.java
+++ b/app/src/main/java/com/chadderbox/launchbox/search/AppSearchProvider.java
@@ -6,6 +6,7 @@
import com.chadderbox.launchbox.data.AppItem;
import com.chadderbox.launchbox.data.ListItem;
import com.chadderbox.launchbox.utils.AppLoader;
+import com.chadderbox.launchbox.utils.FavouritesRepository;
import java.util.ArrayList;
import java.util.List;
@@ -20,9 +21,11 @@ public final class AppSearchProvider implements ISearchProvider {
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
private final AppLoader mAppLoader;
+ private final FavouritesRepository mFavouritesRepository;
- public AppSearchProvider(AppLoader appLoader) {
+ public AppSearchProvider(AppLoader appLoader, FavouritesRepository favouritesRepository) {
mAppLoader = appLoader;
+ mFavouritesRepository = favouritesRepository;
}
@Override
@@ -35,18 +38,29 @@ public int getPriority() {
public void searchAsync(String query, Consumer> callback) {
mExecutor.execute(() -> {
var searchQuery = query.toLowerCase(Locale.getDefault());
+
+ var favourites = mFavouritesRepository.loadFavourites();
+
var results = new ArrayList();
+ var favouriteResults = new ArrayList();
+ var normalResults = new ArrayList();
var fuzzyResults = new ArrayList();
var apps = mAppLoader.getInstalledApps();
for (var app : apps) {
if (app.getLabel().toLowerCase(Locale.getDefault()).contains(searchQuery)) {
- results.add(new AppItem(app));
+ if (favourites.contains(app.getPackageName())) {
+ favouriteResults.add(new AppItem(app));
+ } else {
+ normalResults.add(new AppItem(app));
+ }
} else if (searchQuery.length() > 3 && SearchHelpers.calculateLevenshteinDistance(searchQuery, app.getLabel().toLowerCase(Locale.getDefault())) < LEVENSHTEIN_HEURISTIC) {
fuzzyResults.add(new AppItem(app));
}
}
- // We want the fuzzy results to show after actual search results
+ // Show in the order that the user probably wants them
+ results.addAll(favouriteResults);
+ results.addAll(normalResults);
results.addAll(fuzzyResults);
new Handler(Looper.getMainLooper()).post(() -> callback.accept(results));
diff --git a/app/src/main/java/com/chadderbox/launchbox/settings/SettingsActivity.java b/app/src/main/java/com/chadderbox/launchbox/settings/SettingsActivity.java
index cb60cd9..94abf0c 100644
--- a/app/src/main/java/com/chadderbox/launchbox/settings/SettingsActivity.java
+++ b/app/src/main/java/com/chadderbox/launchbox/settings/SettingsActivity.java
@@ -10,6 +10,8 @@
import android.widget.TextView;
import android.widget.Toast;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
@@ -18,13 +20,16 @@
import androidx.recyclerview.widget.RecyclerView;
import com.chadderbox.launchbox.R;
+import com.chadderbox.launchbox.utils.FileHelpers;
import java.util.ArrayList;
import java.util.Objects;
public final class SettingsActivity extends AppCompatActivity {
+ private SettingOptionAdapter mOptionsAdapter;
private ArrayList mOptions;
+ private ActivityResultLauncher mWallpaperPickerLauncher;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -39,6 +44,25 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
+ mWallpaperPickerLauncher = registerForActivityResult(
+ new ActivityResultContracts.StartActivityForResult(),
+ result -> {
+ if (result.getResultCode() == RESULT_OK && result.getData() != null) {
+ var selectedImageUri = result.getData().getData();
+ if (selectedImageUri != null) {
+
+ // By selecting it, we gain permission to access it
+ // That's pretty neat actually, but it's gonna be super annoying if we don't save that permission
+ getContentResolver().takePersistableUriPermission(selectedImageUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ SettingsManager.setWallpaper(selectedImageUri.toString());
+ Toast.makeText(SettingsActivity.this, "Wallpaper selected", Toast.LENGTH_SHORT).show();
+ refreshOptions();
+ }
+ }
+ }
+ );
+
buildOptions();
}
@@ -83,6 +107,18 @@ private void buildOptions() {
ctx -> showThemeDialog()
));
+ mOptions.add(new SettingOption(
+ "Wallpaper",
+ ctx -> getCurrentWallpaper(),
+ ctx -> showWallpaperDialog()
+ ));
+
+ mOptions.add(new SettingOption(
+ "Wallpaper Dim Percentage",
+ ctx -> getWallpaperDimPercentage(),
+ ctx -> showWallpaperDimDialog()
+ ));
+
mOptions.add(new SettingOption(
"Other settingsā¦",
ctx -> "More coming soon",
@@ -95,7 +131,15 @@ private void buildOptions() {
private void setupOptions() {
var recyclerView = (RecyclerView) findViewById(R.id.recyclerViewSettings);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
- recyclerView.setAdapter(new SettingOptionAdapter(mOptions));
+
+ mOptionsAdapter = new SettingOptionAdapter(mOptions);
+ recyclerView.setAdapter(mOptionsAdapter);
+ }
+
+ @SuppressLint("NotifyDataSetChanged")
+ private void refreshOptions() {
+ // Re-evaluate all options
+ mOptionsAdapter.notifyDataSetChanged();
}
private void showCharacterHeadingDialog() {
@@ -114,7 +158,7 @@ private void showCharacterHeadingDialog() {
SettingsManager.setCharacterHeadings(enabled);
Toast.makeText(this, "Character headings: " + (enabled ? "On" : "Off"), Toast.LENGTH_SHORT).show();
- buildOptions();
+ refreshOptions();
dialog.dismiss();
})
.setNegativeButton("Cancel", null)
@@ -161,7 +205,7 @@ private void showIconPackDialog() {
: "Icon pack applied: " + names.get(which),
Toast.LENGTH_SHORT).show();
- buildOptions();
+ refreshOptions();
})
.setNegativeButton("Cancel", null)
.show();
@@ -187,7 +231,7 @@ private void showFontDialog() {
SettingsManager.setFont(chosenFont);
Toast.makeText(this, "Font applied: " + chosenFont, Toast.LENGTH_SHORT).show();
- buildOptions();
+ refreshOptions();
})
.setNegativeButton("Cancel", null)
.show();
@@ -232,7 +276,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
SettingsManager.setFontSize(selectedSize);
Toast.makeText(this, "Size applied: " + selectedSize, Toast.LENGTH_SHORT).show();
- buildOptions();
+ refreshOptions();
})
.setNegativeButton("Cancel", null)
.show();
@@ -255,7 +299,7 @@ private void showThemeDialog() {
getDelegate().applyDayNight();
Toast.makeText(this, "Theme applied: " + themes[which], Toast.LENGTH_SHORT).show();
- buildOptions();
+ refreshOptions();
})
.setNegativeButton("Cancel", null)
.show();
@@ -269,12 +313,101 @@ private String getCurrentThemeName() {
};
}
- @SuppressLint("UnsafeIntentLaunch")
- private void fullRefreshUi() {
- // HACK
- finish();
- overrideActivityTransition(OVERRIDE_TRANSITION_CLOSE, 0, 0);
- startActivity(getIntent());
- overrideActivityTransition(OVERRIDE_TRANSITION_OPEN, 0, 0);
+ private void showWallpaperDialog() {
+
+ var options = new String[]{
+ "Choose Wallpaper",
+ "Clear Wallpaper"
+ };
+
+ new AlertDialog.Builder(this)
+ .setTitle("Select Wallpaper")
+ .setItems(options, (dialog, which) -> {
+ switch (which) {
+ case 0:
+ var intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+ intent.setType("image/*");
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ mWallpaperPickerLauncher.launch(intent);
+ break;
+
+ case 1:
+ new AlertDialog.Builder(this)
+ .setTitle("Clear Wallpaper")
+ .setMessage("Are you sure you want to remove the current wallpaper?")
+ .setPositiveButton("Yes", (d, w) -> {
+ SettingsManager.setWallpaper("");
+ Toast.makeText(this, "Wallpaper cleared", Toast.LENGTH_SHORT).show();
+
+ refreshOptions();
+ })
+ .setNegativeButton("Cancel", null)
+ .show();
+ break;
+ }
+
+ refreshOptions();
+ })
+ .setNegativeButton("Cancel", null)
+ .show();
+ }
+
+ private String getCurrentWallpaper() {
+ var wallpaper = FileHelpers.tryGetFileNameFromString(getApplicationContext(), SettingsManager.getWallpaper());
+ if (wallpaper == null || wallpaper.isEmpty()) {
+ return "None";
+ }
+
+ return wallpaper;
+ }
+
+ private void showWallpaperDimDialog() {
+ var seekBar = new SeekBar(this);
+ seekBar.setMax(100);
+ seekBar.setProgress((int) (SettingsManager.getWallpaperDimAmount() * 100));
+ seekBar.setPadding(40, 40, 40, 40);
+
+ var preview = new TextView(this);
+ preview.setText(getString(R.string.wallpaper_dim_pick, SettingsManager.getWallpaperDimAmount() * 100 + "%"));
+ preview.setTextSize(16);
+ preview.setPadding(40, 20, 40, 20);
+
+ seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ preview.setText(getString(R.string.wallpaper_dim_pick, progress + "%"));
+ }
+
+ @Override public void onStartTrackingTouch(SeekBar seekBar) {}
+ @Override public void onStopTrackingTouch(SeekBar seekBar) {}
+ });
+
+ var layout = new LinearLayout(this);
+ layout.setOrientation(LinearLayout.VERTICAL);
+ layout.addView(preview);
+ layout.addView(seekBar);
+
+ new AlertDialog.Builder(this)
+ .setTitle("Select Dim Amount")
+ .setView(layout)
+ .setPositiveButton("Ok", (dialog, which) -> {
+ var dimAmount = seekBar.getProgress();
+ if (dimAmount == SettingsManager.getWallpaperDimAmount()) {
+ return;
+ }
+
+ // Remember to convert back from percentage
+ SettingsManager.setWallpaperDimAmount((float) dimAmount / 100);
+ Toast.makeText(this, "Wallpaper Dim Amount: " + (dimAmount + "%"), Toast.LENGTH_SHORT).show();
+
+ refreshOptions();
+ })
+ .setNegativeButton("Cancel", null)
+ .show();
+ }
+
+ private String getWallpaperDimPercentage() {
+ return SettingsManager.getWallpaperDimAmount() * 100 + "%";
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/chadderbox/launchbox/settings/SettingsManager.java b/app/src/main/java/com/chadderbox/launchbox/settings/SettingsManager.java
index 9fa905b..7b65ddc 100644
--- a/app/src/main/java/com/chadderbox/launchbox/settings/SettingsManager.java
+++ b/app/src/main/java/com/chadderbox/launchbox/settings/SettingsManager.java
@@ -16,6 +16,8 @@ public final class SettingsManager {
public static final String KEY_FONT_SIZE = "font_size";
public static final String KEY_FAVORITES = "favorites";
public static final String KEY_THEME = "theme";
+ public static final String KEY_WALLPAPER = "wallpaper";
+ public static final String KEY_WALLPAPER_DIM_AMOUNT = "wallpaper_dim_amount";
private static SharedPreferences sPrefs;
@@ -78,4 +80,20 @@ public static void setTheme(int mode) {
public static int getTheme() {
return sPrefs.getInt(KEY_THEME, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
}
+
+ public static void setWallpaper(String wallpaper) {
+ sPrefs.edit().putString(KEY_WALLPAPER, wallpaper).apply();
+ }
+
+ public static String getWallpaper() {
+ return sPrefs.getString(KEY_WALLPAPER, null);
+ }
+
+ public static void setWallpaperDimAmount(float dimAmount) {
+ sPrefs.edit().putFloat(KEY_WALLPAPER_DIM_AMOUNT, dimAmount).apply();
+ }
+
+ public static float getWallpaperDimAmount() {
+ return sPrefs.getFloat(KEY_WALLPAPER_DIM_AMOUNT, 0.375f);
+ }
}
diff --git a/app/src/main/java/com/chadderbox/launchbox/utils/FileHelpers.java b/app/src/main/java/com/chadderbox/launchbox/utils/FileHelpers.java
new file mode 100644
index 0000000..6224207
--- /dev/null
+++ b/app/src/main/java/com/chadderbox/launchbox/utils/FileHelpers.java
@@ -0,0 +1,54 @@
+package com.chadderbox.launchbox.utils;
+
+import android.content.Context;
+import android.net.Uri;
+import android.provider.OpenableColumns;
+
+public final class FileHelpers {
+
+ private FileHelpers() { }
+
+ public static String tryGetFileNameFromString(final Context context, final String uriPath) {
+ Uri uri;
+ try {
+ uri = Uri.parse(uriPath);
+ } catch (Exception ignored) {
+ return null;
+ }
+
+ return tryGetFileNameFromUri(context, uri);
+ }
+
+ public static String tryGetFileNameFromUri(final Context context, final Uri uri) {
+ String result = null;
+ if (uri == null) {
+ return null;
+ }
+
+ if ("content".equals(uri.getScheme())) {
+ try (var cursor = context.getContentResolver().query(uri, null, null, null, null)) {
+ if (cursor != null && cursor.moveToFirst()) {
+ var nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
+ if (nameIndex >= 0) {
+ result = cursor.getString(nameIndex);
+ }
+ }
+ } catch (Exception ignored) { }
+ }
+
+ if (result == null) {
+ var path = uri.getPath();
+ if (path != null) {
+ int cut = path.lastIndexOf('/');
+ if (cut != -1) {
+ result = path.substring(cut + 1);
+ } else {
+ result = path;
+ }
+ }
+ }
+
+ return result;
+ }
+
+}
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 5eae544..096f099 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -6,6 +6,16 @@
android:clickable="true"
android:focusable="true">
+
+
+
+
+ android:scrollbars="none" />
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 0810830..53a64f8 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -9,4 +9,6 @@
Search the web for \"%1$s\"
Play / Pause
"Font size: \"%1$s\"
+ "Font size: \"%1$s\"
+ Wallpaper Background
\ No newline at end of file