Skip to content

Commit

Permalink
Early implementation of custom icon handling
Browse files Browse the repository at this point in the history
As requested by #86, this feature allows the user to be able to select
an icon from icon packs, replacing the default and/or existing icons
provided by the current icon pack.

This implementation caches the icon into a PNG, saving it to the
launcher's files directory. This means that it can't be saved to when
backing up the launcher configuration,
but it will persist even if the source of this icon (the icon pack where
icon came from) is removed.

At the moment, there should be a consideration on the use of simple
index instead of having to cache an entire file to save on space, but
this works for now.
  • Loading branch information
F4uzan committed Dec 16, 2020
1 parent ae096bd commit 3d1835f
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 3 deletions.
45 changes: 44 additions & 1 deletion app/src/main/java/mono/hg/helpers/LauncherIconHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.*

Expand Down Expand Up @@ -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))
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 =
Expand Down
49 changes: 48 additions & 1 deletion app/src/main/java/mono/hg/pages/AppsListPage.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package mono.hg.pages

import android.app.Activity.RESULT_OK
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
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
Expand Down Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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<Bitmap>("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
}
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -494,7 +539,8 @@ class AppsListPage : GenericPage() {
private fun addApp(list: MutableList<App>, 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
}

Expand Down Expand Up @@ -631,5 +677,6 @@ class AppsListPage : GenericPage() {

companion object {
private const val SHORTCUT_MENU_GROUP = 247
private const val SET_ICON_REQUEST = 8000
}
}
5 changes: 5 additions & 0 deletions app/src/main/res/menu/menu_app.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
android:title="@string/action_actions">

<menu android:id="@+id/menu_app_actions">
<item
android:id="@+id/action_icon"
android:orderInCategory="100"
android:title="@string/action_icon"
app:showAsAction="never" />

<item
android:id="@+id/action_unpin"
Expand Down
6 changes: 5 additions & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<string name="action_web_provider_add">Add</string>
<string name="action_web_provider_reset">Reset</string>
<!-- App menu actions -->
<string name="action_icon">Icon</string>
<string name="action_pin">Pin</string>
<string name="action_unpin">Unpin</string>
<string name="action_hide">Hide</string>
Expand All @@ -29,9 +30,12 @@
<string name="search_web_hint">Search for \'%1$s\' online</string>
<string name="search_web_button_prompt">With</string>
<string name="search_web_button">Go</string>
<!-- Dialog titles -->
<string name="dialog_title_add_provider">Add provider</string>
<string name="dialog_title_edit_provider">Edit provider</string>
<string name="dialog_title_shorthand">Set shorthand</string>
<string name="dialog_title_set_icon">Select icon pack</string>
<!-- Dialog actions -->
<string name="dialog_action_close">Close</string>
<string name="dialog_action_add">Add</string>
<string name="dialog_action_edit">Edit</string>
Expand All @@ -40,7 +44,7 @@
<string name="err_invalid_url">Invalid URL.</string>
<string name="err_provider_exists">Search engine name must be unique.</string>
<string name="err_no_method_panel">This is only possible on newer Android.</string>
<!-- Start up dialogue, shown to new user -->
<!-- Start up dialog, shown to new user -->
<string name="dialog_start_hint_title">Welcome!</string>
<string name="dialog_start_hint_message_one">This is your desktop. In here, you can…\n
• Tap to open the app list\n
Expand Down

0 comments on commit 3d1835f

Please sign in to comment.