-
Notifications
You must be signed in to change notification settings - Fork 189
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
280 additions
and
261 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
134 changes: 97 additions & 37 deletions
134
app/src/main/java/de/szalkowski/activitylauncher/services/IconLoaderService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,46 +1,106 @@ | ||
package de.szalkowski.activitylauncher.todo; | ||
|
||
import android.annotation.TargetApi; | ||
import android.content.Context; | ||
import android.content.pm.PackageManager; | ||
import android.content.res.Resources; | ||
import android.graphics.drawable.Drawable; | ||
import android.os.Build; | ||
|
||
class IconLoader { | ||
private final PackageManager pm; | ||
private final Context context; | ||
|
||
IconLoader(Context context) { | ||
this.context = context; | ||
this.pm = context.getPackageManager(); | ||
} | ||
package de.szalkowski.activitylauncher.services | ||
|
||
@TargetApi(19) | ||
static Drawable getDrawable(Resources res, int id) { | ||
return res.getDrawable(id); | ||
} | ||
import android.annotation.TargetApi | ||
import android.content.Context | ||
import android.content.pm.PackageManager | ||
import android.content.pm.PackageManager.NameNotFoundException | ||
import android.content.res.Resources | ||
import android.content.res.Resources.Theme | ||
import android.graphics.drawable.Drawable | ||
import android.os.Build | ||
import android.util.DisplayMetrics | ||
import android.widget.Toast | ||
import dagger.hilt.android.qualifiers.ActivityContext | ||
import de.szalkowski.activitylauncher.R | ||
import de.szalkowski.activitylauncher.ui.AsyncProvider | ||
import de.szalkowski.activitylauncher.ui.IconListAdapter | ||
import java.util.TreeMap | ||
import javax.inject.Inject | ||
|
||
@TargetApi(21) | ||
static Drawable getDrawable(Resources res, int id, Resources.Theme theme) { | ||
return res.getDrawable(id, theme); | ||
} | ||
interface IconLoaderService { | ||
fun getIcon(iconResourceString: String): Drawable | ||
fun tryGetIcon(iconResourceString: String): Result<Drawable> | ||
fun loadIcons(updater: AsyncProvider<IconListAdapter>.Updater?): List<IconInfo> | ||
|
||
data class IconInfo( | ||
val iconResourceName: String, val icon: Drawable | ||
) | ||
|
||
class NullResourceException : Exception("Resource ID is zero") | ||
} | ||
|
||
class IconLoaderServiceImpl @Inject constructor( | ||
@ActivityContext private val context: Context, | ||
private val packageListService: PackageListService, | ||
private val activityListService: ActivityListService, | ||
settingsService: SettingsService, | ||
) : IconLoaderService { | ||
private val pm: PackageManager = context.packageManager | ||
private val configuration = settingsService.getLocaleConfiguration() | ||
|
||
override fun getIcon(iconResourceString: String): Drawable = | ||
tryGetIcon(iconResourceString).getOrElse { | ||
val errorText = when (it) { | ||
is IconLoaderService.NullResourceException -> R.string.error_invalid_icon_resource | ||
is NameNotFoundException -> R.string.error_invalid_icon_resource | ||
else -> R.string.error_invalid_icon_format | ||
} | ||
|
||
Toast.makeText(context, errorText, Toast.LENGTH_LONG).show() | ||
pm.defaultActivityIcon | ||
} | ||
|
||
|
||
override fun tryGetIcon(iconResourceString: String): Result<Drawable> { | ||
return runCatching { | ||
val pack = iconResourceString.substringBefore(":") | ||
val typeAndName = iconResourceString.substringAfter(":") | ||
val type = typeAndName.substringBefore("/") | ||
val name = typeAndName.substringAfter("/") | ||
|
||
val res = pm.getResourcesForApplication(pack) | ||
res.updateConfiguration(configuration, DisplayMetrics()) | ||
val id = res.getIdentifier(name, type, pack) | ||
|
||
Drawable getIcon(String icon_resource_string) { | ||
try { | ||
String pack = icon_resource_string.substring(0, icon_resource_string.indexOf(':')); | ||
String type = icon_resource_string.substring(icon_resource_string.indexOf(':') + 1, icon_resource_string.indexOf('/')); | ||
String name = icon_resource_string.substring(icon_resource_string.indexOf('/') + 1); | ||
Resources res = this.pm.getResourcesForApplication(pack); | ||
int id = res.getIdentifier(name, type, pack); | ||
if (id == 0) throw IconLoaderService.NullResourceException() | ||
|
||
if (Build.VERSION.SDK_INT >= 21) { | ||
return IconLoader.getDrawable(res, id, this.context.getTheme()); | ||
getDrawable(res, id, context.theme) | ||
} else { | ||
return IconLoader.getDrawable(res, id); | ||
getDrawable(res, id) | ||
} | ||
} catch (Exception e) { | ||
return this.pm.getDefaultActivityIcon(); | ||
} | ||
} | ||
} | ||
|
||
override fun loadIcons(updater: AsyncProvider<IconListAdapter>.Updater?): List<IconLoaderService.IconInfo> { | ||
val icons: TreeMap<String, Drawable> = TreeMap() | ||
|
||
val packages = packageListService.packages | ||
updater?.updateMax(packages.size) | ||
updater?.update(0) | ||
|
||
for (pack in packages.withIndex()) { | ||
updater?.update(pack.index + 1) | ||
|
||
runCatching { | ||
val activities = activityListService.getActivities(pack.value.packageName) | ||
for (activity in listOfNotNull(activities.defaultActivity) + activities.activities) { | ||
activity.iconResourceName?.let { icons[it] = activity.icon } | ||
} | ||
} | ||
} | ||
|
||
return icons.map { entry -> IconLoaderService.IconInfo(entry.key, entry.value) }.toList() | ||
} | ||
|
||
companion object { | ||
private fun getDrawable(res: Resources, id: Int): Drawable { | ||
return res.getDrawable(id) | ||
} | ||
|
||
@TargetApi(21) | ||
private fun getDrawable(res: Resources, id: Int, theme: Theme?): Drawable { | ||
return res.getDrawable(id, theme) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
137 changes: 59 additions & 78 deletions
137
app/src/main/java/de/szalkowski/activitylauncher/ui/AsyncProvider.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,106 +1,87 @@ | ||
package de.szalkowski.activitylauncher.todo; | ||
|
||
import android.content.Context; | ||
import android.os.AsyncTask; | ||
import android.view.LayoutInflater; | ||
|
||
import androidx.appcompat.app.AlertDialog; | ||
|
||
import java.text.NumberFormat; | ||
import java.util.Locale; | ||
|
||
import de.szalkowski.activitylauncher.R; | ||
import de.szalkowski.activitylauncher.databinding.ProgressDialogBinding; | ||
|
||
public abstract class AsyncProvider<ReturnType> extends AsyncTask<Void, Integer, ReturnType> { | ||
private final CharSequence message; | ||
private final Listener<ReturnType> listener; | ||
private final AlertDialog dialog; | ||
private final ProgressDialogBinding binding; | ||
private final NumberFormat progressPercentFormat = NumberFormat.getPercentInstance(); | ||
private int max; | ||
|
||
AsyncProvider(Context context, Listener<ReturnType> listener, boolean showProgressDialog) { | ||
this.message = context.getText(R.string.dialog_progress_loading); | ||
this.listener = listener; | ||
|
||
package de.szalkowski.activitylauncher.ui | ||
|
||
import android.content.Context | ||
import android.os.AsyncTask | ||
import android.view.LayoutInflater | ||
import androidx.appcompat.app.AlertDialog | ||
import de.szalkowski.activitylauncher.R | ||
import de.szalkowski.activitylauncher.databinding.ProgressDialogBinding | ||
import java.text.NumberFormat | ||
import java.util.Locale | ||
|
||
abstract class AsyncProvider<ReturnType> internal constructor( | ||
context: Context, private val listener: Listener<ReturnType>?, showProgressDialog: Boolean | ||
) : AsyncTask<Void?, Int?, ReturnType>() { | ||
private val message = context.getText(R.string.dialog_progress_loading) | ||
private var dialog: AlertDialog? = null | ||
private var binding: ProgressDialogBinding? = null | ||
private val progressPercentFormat: NumberFormat = NumberFormat.getPercentInstance() | ||
private var max = 0 | ||
|
||
init { | ||
if (showProgressDialog) { | ||
this.binding = ProgressDialogBinding.inflate(LayoutInflater.from(context)); | ||
this.dialog = new AlertDialog.Builder(context).setView(binding.getRoot()).create(); | ||
this.progressPercentFormat.setMaximumFractionDigits(0); | ||
this.binding = ProgressDialogBinding.inflate(LayoutInflater.from(context)) | ||
this.dialog = AlertDialog.Builder(context).setView(binding!!.getRoot()).create() | ||
progressPercentFormat.maximumFractionDigits = 0 | ||
} else { | ||
this.binding = null; | ||
this.dialog = null; | ||
this.binding = null | ||
this.dialog = null | ||
} | ||
} | ||
|
||
@Override | ||
protected void onProgressUpdate(Integer... values) { | ||
if (this.binding != null && values.length > 0) { | ||
int value = values[0]; | ||
protected fun onProgressUpdate(vararg values: Int) { | ||
if (values.isNotEmpty()) { | ||
val value = values[0] | ||
|
||
if (value == 0) { | ||
this.binding.progress.setIndeterminate(false); | ||
this.binding.progress.setMax(this.max); | ||
binding?.progress?.isIndeterminate = false | ||
binding?.progress?.max = this.max | ||
} | ||
|
||
this.binding.progress.setProgress(value); | ||
this.binding.progressNumber.setText(String.format(Locale.getDefault(), "%1d/%2d", value, this.max)); | ||
double percent = (double) value / (double) this.max; | ||
this.binding.progressPercent.setText(this.progressPercentFormat.format(percent)); | ||
binding?.progress?.progress = value | ||
binding?.progressNumber?.text = String.format( | ||
Locale.getDefault(), "%1d/%2d", value, this.max | ||
) | ||
val percent = value.toDouble() / max.toDouble() | ||
binding?.progressPercent?.text = progressPercentFormat.format(percent) | ||
} | ||
} | ||
|
||
@Override | ||
protected void onPreExecute() { | ||
super.onPreExecute(); | ||
override fun onPreExecute() { | ||
super.onPreExecute() | ||
|
||
if (this.dialog != null && this.binding != null) { | ||
this.dialog.setCancelable(false); | ||
this.dialog.setTitle(this.message); | ||
this.binding.progress.setIndeterminate(true); | ||
this.dialog.show(); | ||
} | ||
dialog?.setCancelable(false) | ||
dialog?.setTitle(this.message) | ||
binding?.progress?.isIndeterminate = true | ||
dialog?.show() | ||
} | ||
|
||
@Override | ||
protected void onPostExecute(ReturnType result) { | ||
super.onPostExecute(result); | ||
if (this.listener != null) { | ||
this.listener.onProviderFinished(this, result); | ||
} | ||
override fun onPostExecute(result: ReturnType) { | ||
super.onPostExecute(result) | ||
listener?.onProviderFinished(this, result) | ||
|
||
if (this.dialog != null) { | ||
try { | ||
this.dialog.dismiss(); | ||
} catch (IllegalArgumentException e) { /* ignore */ } | ||
runCatching { | ||
dialog?.dismiss() | ||
} | ||
} | ||
|
||
abstract protected ReturnType run(Updater updater); | ||
protected abstract fun run(updater: Updater?): ReturnType | ||
|
||
@Override | ||
protected ReturnType doInBackground(Void... params) { | ||
return run(new Updater(this)); | ||
override fun doInBackground(vararg params: Void?): ReturnType { | ||
return run(Updater(this)) | ||
} | ||
|
||
public interface Listener<ReturnType> { | ||
void onProviderFinished(AsyncProvider<ReturnType> task, ReturnType value); | ||
interface Listener<ReturnType> { | ||
fun onProviderFinished(task: AsyncProvider<ReturnType>?, value: ReturnType) | ||
} | ||
|
||
class Updater { | ||
private final AsyncProvider<ReturnType> provider; | ||
|
||
Updater(AsyncProvider<ReturnType> provider) { | ||
this.provider = provider; | ||
} | ||
|
||
void update(int value) { | ||
this.provider.publishProgress(value); | ||
inner class Updater(private val provider: AsyncProvider<ReturnType>) { | ||
fun update(value: Int) { | ||
provider.publishProgress(value) | ||
} | ||
|
||
void updateMax(int value) { | ||
this.provider.max = value; | ||
fun updateMax(value: Int) { | ||
provider.max = value | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.