diff --git a/app/src/main/java/mono/hg/helpers/LauncherIconHelper.kt b/app/src/main/java/mono/hg/helpers/LauncherIconHelper.kt index 1825ecb8..6eddaaaf 100644 --- a/app/src/main/java/mono/hg/helpers/LauncherIconHelper.kt +++ b/app/src/main/java/mono/hg/helpers/LauncherIconHelper.kt @@ -7,6 +7,7 @@ import android.content.pm.LauncherApps import android.content.pm.PackageManager import android.content.res.Resources import android.graphics.Bitmap +import android.graphics.BitmapFactory import android.graphics.BlurMaskFilter import android.graphics.Canvas import android.graphics.Color @@ -26,6 +27,8 @@ import mono.hg.utils.Utils.LogLevel import org.xmlpull.v1.XmlPullParser import org.xmlpull.v1.XmlPullParserException import org.xmlpull.v1.XmlPullParserFactory +import java.io.File +import java.io.FileOutputStream import java.io.IOException import java.util.* @@ -97,6 +100,29 @@ object LauncherIconHelper { return getDefaultIconDrawable(activity, componentName, user) } + /** + * Caches an icon to the launcher's files directory. + * + * This is only used for custom icon. + * Most icons are not cached to save on space. + * + * @param context Context required to retrieve the path to the files directory. + * @param bitmap The Bitmap to cache. + * @param componentName The component name that will use this cached Bitmap. + * This component name will be reduced to package name. + */ + fun cacheIcon(context: Context, bitmap: Bitmap, componentName: String) { + try { + FileOutputStream(context.filesDir.path + File.separatorChar + componentName).apply { + bitmap.compress(Bitmap.CompressFormat.PNG, 100, this) // Quality is unused here. + flush() + close() + } + } catch (e: Exception) { + Utils.sendLog(3, e.toString()) + } + } + private fun drawAdaptiveShadow(resources: Resources, icon: Drawable): BitmapDrawable { return BitmapDrawable(resources, addShadow(icon, icon.intrinsicHeight, icon.intrinsicWidth)) } @@ -324,8 +350,21 @@ object LauncherIconHelper { val iconPackageName = PreferenceHelper.preference.getString("icon_pack", "default") ?: "default" val defaultIcon: Drawable? = getDefaultIconDrawable(activity, appPackageName, user) + val customIconPath = + activity.filesDir.path + File.separatorChar + AppUtils.getPackageName(appPackageName) try { + // If there is a custom icon set, use that over the default one. + with(File(customIconPath)) { + if (exists() && ! isDirectory) { + BitmapFactory.Options().apply { + inPreferredConfig = Bitmap.Config.ARGB_8888 + }.also { + return BitmapDrawable(activity.resources, BitmapFactory.decodeFile(this.path, it)) + } + } + } + val iconRes = if ("default" != iconPackageName) { packageManager.getResourcesForApplication(iconPackageName) } else { @@ -430,7 +469,11 @@ object LauncherIconHelper { return BitmapDrawable(resources, result) } - private fun getDefaultIconDrawable(activity: Activity, appPackageName: String, user:Long): Drawable? { + private fun getDefaultIconDrawable( + activity: Activity, + appPackageName: String, + user: Long + ): Drawable? { return if (Utils.atLeastLollipop()) { try { val launcher = diff --git a/app/src/main/java/mono/hg/pages/AppsListPage.kt b/app/src/main/java/mono/hg/pages/AppsListPage.kt index 45f0ba83..0f0380bd 100644 --- a/app/src/main/java/mono/hg/pages/AppsListPage.kt +++ b/app/src/main/java/mono/hg/pages/AppsListPage.kt @@ -1,5 +1,6 @@ package mono.hg.pages +import android.app.Activity.RESULT_OK import android.content.BroadcastReceiver import android.content.Context import android.content.Intent @@ -7,6 +8,8 @@ import android.content.IntentFilter import android.content.pm.LauncherApps import android.content.pm.PackageManager import android.content.res.ColorStateList +import android.graphics.Bitmap +import android.graphics.drawable.BitmapDrawable import android.net.Uri import android.os.Bundle import android.os.UserManager @@ -58,6 +61,11 @@ import kotlin.collections.ArrayList * This is the generic implementation of an app list that handles the required features. */ class AppsListPage : GenericPage() { + /* + * Index of an app that is currently being edited. + */ + private var editingAppPosition: Int = -1 + /* * Adapter for installed apps. */ @@ -309,6 +317,29 @@ class AppsListPage : GenericPage() { } } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + + // Update the icon on the selected app. + if (resultCode == RESULT_OK && requestCode == SET_ICON_REQUEST) { + data?.getParcelableExtra("icon")?.let { bitmap -> + appsAdapter.getItem(editingAppPosition).apply { + this?.packageName?.let { + LauncherIconHelper.cacheIcon( + requireContext(), + bitmap, + AppUtils.getPackageName(it) + ) + } + this?.icon = BitmapDrawable(resources, bitmap) + }?.let { appsAdapter.updateItem(it) } + } + + // Reset the index once we're done. + editingAppPosition = -1 + } + } + override fun isAcceptingSearch(): Boolean { return true } @@ -363,6 +394,20 @@ class AppsListPage : GenericPage() { show() setOnMenuItemClickListener { item -> when (item.itemId) { + R.id.action_icon -> { + // Update the index first. + editingAppPosition = appsAdapter.getGlobalPositionOf(app) + + // Load the picker intent. + // TODO: We should use more intent actions here. + Intent("org.adw.launcher.icons.ACTION_PICK_ICON").apply { + startActivityForResult( + Intent.createChooser( + this, getString(R.string.dialog_title_set_icon) + ), SET_ICON_REQUEST + ) + } + } R.id.action_pin -> getLauncherActivity().pinAppHere(app.userPackageName, user) R.id.action_info -> AppUtils.openAppDetails( requireActivity(), @@ -494,7 +539,8 @@ class AppsListPage : GenericPage() { private fun addApp(list: MutableList, componentName: String, user: Long) { // Don't add the app if it has the launcher's package name or if it's hidden. if (componentName.contains(requireContext().packageName) || - excludedAppsList.contains(componentName)) { + excludedAppsList.contains(componentName) + ) { return } @@ -631,5 +677,6 @@ class AppsListPage : GenericPage() { companion object { private const val SHORTCUT_MENU_GROUP = 247 + private const val SET_ICON_REQUEST = 8000 } } \ No newline at end of file diff --git a/app/src/main/res/menu/menu_app.xml b/app/src/main/res/menu/menu_app.xml index 9fd6b5f1..411215a9 100644 --- a/app/src/main/res/menu/menu_app.xml +++ b/app/src/main/res/menu/menu_app.xml @@ -10,6 +10,11 @@ android:title="@string/action_actions"> + Add Reset + Icon Pin Unpin Hide @@ -29,9 +30,12 @@ Search for \'%1$s\' online With Go + Add provider Edit provider Set shorthand + Select icon pack + Close Add Edit @@ -40,7 +44,7 @@ Invalid URL. Search engine name must be unique. This is only possible on newer Android. - + Welcome! This is your desktop. In here, you can…\n • Tap to open the app list\n