This commit is contained in:
lunaticbum 2024-08-13 18:22:27 +09:00
parent 85e3ac1414
commit b8c08da378
16 changed files with 403 additions and 306 deletions

View File

@ -70,4 +70,5 @@ dependencies {
implementation ("com.google.android.material:material:1.10.0")
implementation (kotlin("stdlib", version = kotlinVersion))
implementation ("com.github.cachapa:ExpandableLayout:2.9.2")
implementation ("com.squareup.picasso:picasso:2.8")
}

View File

@ -19,6 +19,9 @@
<!-- api 33+ -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission
android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<queries>
<intent>

View File

@ -1,118 +1,112 @@
/*
* Lunar Launcher
* Copyright (C) 2022 Md Rasel Hossain
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package rasel.lunar.launcher.apps
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.util.AttributeSet
import android.util.TypedValue
import android.view.MotionEvent
import android.view.View
import androidx.core.content.ContextCompat
import rasel.lunar.launcher.apps.AppDrawer.Companion.alphabetList
import rasel.lunar.launcher.apps.AppDrawer.Companion.letterPreview
import rasel.lunar.launcher.apps.AppDrawer.Companion.listenScroll
import rasel.lunar.launcher.apps.AppDrawer.Companion.settingsPrefs
import rasel.lunar.launcher.apps.AppsAdapter.Companion.appsSize
import rasel.lunar.launcher.helpers.Constants
internal class AlphabetScrollbar : View {
private var paint: Paint? = null
private var selectedIndex = -1
private val alphabet get() = alphabetList.distinct()
constructor(context: Context?) : super(context) {
init()
}
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
init()
}
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init()
}
@SuppressLint("ResourceType")
private fun init() {
paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = defaultTextColor
textSize = 16f
}
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val width = width
val height = height
val letterHeight: Int = height / alphabet.count()
alphabet.indices.forEach { i: Int ->
val x = width / 2f - paint!!.measureText(alphabet[i]) / 2f
val y = i * letterHeight + letterHeight / 2f
when (i) {
selectedIndex -> paint!!.textSize = 20f
else -> paint!!.textSize = 16f
}
canvas.drawText(alphabet[i], x, y, paint!!)
}
}
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> {
val y = event.y
val index = (y / height * alphabet.count()).toInt()
if (index != selectedIndex) {
selectedIndex = index
invalidate()
}
if (!settingsPrefs!!.getBoolean(Constants.KEY_APPS_COUNT, true)) letterPreview?.visibility = VISIBLE
try { letterPreview?.text = alphabet[selectedIndex] }
catch (exception: Exception) { exception.printStackTrace() }
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
when {
selectedIndex < 0 -> listenScroll(alphabet[0])
selectedIndex > alphabet.count() - 1 -> listenScroll(alphabet[alphabet.count() - 1])
else -> listenScroll(alphabet[selectedIndex])
}
selectedIndex = -1
invalidate()
if (settingsPrefs!!.getBoolean(Constants.KEY_APPS_COUNT, true)) letterPreview?.text = appsSize.toString()
else letterPreview?.visibility = GONE
}
}
return true
}
private val defaultTextColor: Int get() {
val resolvedAttr = TypedValue()
context.theme.resolveAttribute(android.R.attr.textColorPrimary, resolvedAttr, true)
val colorRes = resolvedAttr.run { if (resourceId != 0) resourceId else data }
return ContextCompat.getColor(context, colorRes)
}
}
///*
// * Lunar Launcher
// * Copyright (C) 2022 Md Rasel Hossain
// *
// * This program is free software: you can redistribute it and/or modify
// * it under the terms of the GNU General Public License as published by
// * the Free Software Foundation, either version 3 of the License, or
// * (at your option) any later version.
// *
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// * GNU General Public License for more details.
// *
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see <http://www.gnu.org/licenses/>.
// */
//
//package rasel.lunar.launcher.apps
//
//import android.annotation.SuppressLint
//import android.content.Context
//import android.graphics.Canvas
//import android.graphics.Paint
//import android.util.AttributeSet
//import android.util.TypedValue
//import android.view.MotionEvent
//import android.view.View
//import androidx.core.content.ContextCompat
//
//
//internal class AlphabetScrollbar : View {
//
// private var paint: Paint? = null
// private var selectedIndex = -1
//// private val alphabet get() = alphabetList.distinct()
//
// constructor(context: Context?) : super(context) {
// init()
// }
//
// constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
// init()
// }
//
// constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
// init()
// }
//
// @SuppressLint("ResourceType")
// private fun init() {
// paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
// color = defaultTextColor
// textSize = 16f
// }
// }
//
// override fun onDraw(canvas: Canvas) {
// super.onDraw(canvas)
// val width = width
// val height = height
// val letterHeight: Int = height / alphabet.count()
// alphabet.indices.forEach { i: Int ->
// val x = width / 2f - paint!!.measureText(alphabet[i]) / 2f
// val y = i * letterHeight + letterHeight / 2f
// when (i) {
// selectedIndex -> paint!!.textSize = 20f
// else -> paint!!.textSize = 16f
// }
// canvas.drawText(alphabet[i], x, y, paint!!)
// }
// }
//
// @SuppressLint("ClickableViewAccessibility")
// override fun onTouchEvent(event: MotionEvent): Boolean {
//// when (event.action) {
//// MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> {
//// val y = event.y
//// val index = (y / height * alphabet.count()).toInt()
//// if (index != selectedIndex) {
//// selectedIndex = index
//// invalidate()
//// }
////
//// if (!settingsPrefs!!.getBoolean(Constants.KEY_APPS_COUNT, true)) letterPreview?.visibility = VISIBLE
//// try { letterPreview?.text = alphabet[selectedIndex] }
//// catch (exception: Exception) { exception.printStackTrace() }
//// }
////
//// MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
//// when {
//// selectedIndex < 0 -> listenScroll(alphabet[0])
//// selectedIndex > alphabet.count() - 1 -> listenScroll(alphabet[alphabet.count() - 1])
//// else -> listenScroll(alphabet[selectedIndex])
//// }
////
//// selectedIndex = -1
//// invalidate()
//// if (settingsPrefs!!.getBoolean(Constants.KEY_APPS_COUNT, true)) letterPreview?.text = appsSize.toString()
//// else letterPreview?.visibility = GONE
//// }
//// }
// return true
// }
//
// private val defaultTextColor: Int get() {
// val resolvedAttr = TypedValue()
// context.theme.resolveAttribute(android.R.attr.textColorPrimary, resolvedAttr, true)
// val colorRes = resolvedAttr.run { if (resourceId != 0) resourceId else data }
// return ContextCompat.getColor(context, colorRes)
// }
//}

View File

@ -27,6 +27,7 @@ import android.content.pm.ResolveInfo
import android.graphics.Rect
import android.os.Build
import android.os.Bundle
import android.provider.Settings.Global
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
@ -41,6 +42,9 @@ import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.textview.MaterialTextView
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import rasel.lunar.launcher.BuildConfig
import rasel.lunar.launcher.LauncherActivity.Companion.lActivity
import rasel.lunar.launcher.R
@ -75,42 +79,25 @@ internal class AppDrawer : Fragment() {
private var appsAdapter: AppsAdapter? = null
private var packageInfoList: MutableList<ResolveInfo> = mutableListOf()
private var packageList = mutableListOf<Packages>()
private var packageListClone = mutableListOf<Packages>()
private val numberPattern = Pattern.compile("[0-9]")
private val alphabetPattern = Pattern.compile("[A-Z]")
// private val numberPattern = Pattern.compile("[0-9]")
// private val alphabetPattern = Pattern.compile("[A-Z]")
@JvmStatic var settingsPrefs: SharedPreferences? = null
@JvmStatic var appNamesPrefs: SharedPreferences? = null
@JvmStatic var alphabetList = mutableListOf<String>()
// @JvmStatic var alphabetList = mutableListOf<String>()
@JvmStatic var letterPreview: MaterialTextView? = null
private fun appName(resolver: ResolveInfo): String {
return appNamesPrefs?.getString(resolver.activityInfo.packageName, resolver.loadLabel(packageManager).toString())!!
if(appNamesPrefs?.contains(resolver.activityInfo.packageName) != null && appNamesPrefs?.getString(resolver.activityInfo.packageName,"")?.length ?: 0 > 0) {
return appNamesPrefs?.getString(resolver.activityInfo.packageName,"") ?: ""
} else {
return resolver.loadLabel(packageManager).toString().apply {
appNamesPrefs?.edit()?.putString(resolver.activityInfo.packageName, this)?.apply()
}
}
}
fun listenScroll(letter: String) {
// packageListClone.clear()
// for (resolver in packageInfoList) {
// when {
// letter == "#" -> {
// if (numberPattern.matcher(appName(resolver).first().uppercase()).matches()) {
// packageListClone.add(Packages(resolver.activityInfo.packageName, appName(resolver)))
// }
// }
// alphabetPattern.matcher(letter).matches() -> {
// if (appName(resolver).first().uppercase() == letter) {
// packageListClone.add(Packages(resolver.activityInfo.packageName, appName(resolver)))
// }
// }
// letter == "⠶" -> {
// if (!numberPattern.matcher(appName(resolver).first().uppercase()).matches() &&
// !alphabetPattern.matcher(appName(resolver).first().uppercase()).matches()) {
// packageListClone.add(Packages(resolver.activityInfo.packageName, appName(resolver)))
// }
// }
// }
// }
// appsAdapter?.updateData(packageListClone.sortedBy { it.appName.lowercase() })
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
@ -126,9 +113,7 @@ internal class AppDrawer : Fragment() {
binding.appsCount.visibility = if (settingsPrefs!!.getBoolean(KEY_APPS_COUNT, true)) VISIBLE else GONE
setLayout()
fetchApps()
getAlphabetItems()
setKeyboardPadding()
return binding.root
}
@ -140,7 +125,7 @@ internal class AppDrawer : Fragment() {
binding.reset.setOnClickListener { onResume() }
binding.moveDown.setOnClickListener {
binding.appsList.smoothScrollToPosition(packageListClone.size - 1)
binding.appsList.smoothScrollToPosition(packageList.size - 1)
}
binding.moveUp.setOnClickListener {
@ -163,7 +148,7 @@ internal class AppDrawer : Fragment() {
override fun onResume() {
super.onResume()
fetchApps()
getAlphabetItems()
setKeyboardPadding()
binding.appsCount.visibility = if (settingsPrefs!!.getBoolean(KEY_APPS_COUNT, true)) VISIBLE else GONE
@ -186,7 +171,7 @@ internal class AppDrawer : Fragment() {
binding.appsList.layoutManager = LinearLayoutManager(requireContext())
appsAdapter!!.updateGravity(settingsPrefs!!.getInt(KEY_DRAW_ALIGN, Gravity.CENTER))
}
2 -> binding.appsList.layoutManager = GridLayoutManager(requireContext(), settingsPrefs!!.getInt(KEY_GRID_COLUMNS, DEFAULT_GRID_COLUMNS))
2 -> binding.appsList.layoutManager = GridLayoutManager(requireContext(), Math.min(settingsPrefs!!.getInt(KEY_GRID_COLUMNS, DEFAULT_GRID_COLUMNS), 4))
}
/* initialize apps list adapter */
@ -195,30 +180,39 @@ internal class AppDrawer : Fragment() {
/* update app list with app and package name */
fun fetchApps() {
packageInfoList = (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
packageManager?.queryIntentActivities(
Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LAUNCHER),
PackageManager.ResolveInfoFlags.of(0)
)
} else {
(packageManager?.queryIntentActivities(
Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LAUNCHER), 0))
})?.apply {
removeIf { it.activityInfo.packageName.equals(BuildConfig.APPLICATION_ID) }
sortWith(ResolveInfo.DisplayNameComparator(packageManager))
}!!
GlobalScope.launch {
packageInfoList = (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
packageManager?.queryIntentActivities(
Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LAUNCHER),
PackageManager.ResolveInfoFlags.of(0)
)
} else {
(packageManager?.queryIntentActivities(
Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LAUNCHER), 0
))
})?.apply {
removeIf { it.activityInfo.packageName.equals(BuildConfig.APPLICATION_ID) }
sortedBy {
appName(it)
}
}!!
/* add package and app names to the list */
packageList.clear()
packageListClone.clear()
for (resolver in packageInfoList) {
packageList.add(Packages(resolver.activityInfo.packageName, appName(resolver)))
packageListClone.add(Packages(resolver.activityInfo.packageName, appName(resolver)))
}
/* add package and app names to the list */
packageList.clear()
var edit = appNamesPrefs?.edit()
for (resolver in packageInfoList) {
packageList.add(Packages(resolver.activityInfo.packageName, appName(resolver)))
}
when {
packageListClone.size < 1 -> return
else -> appsAdapter?.updateData(packageListClone.sortedBy { it.appName.lowercase() })
// when {
// packageList.size < 1 -> return
// else -> {
if (packageList.size > 0) {
MainScope().launch {
appsAdapter?.updateData(packageList)
}
}
// }
}
}
@ -247,43 +241,63 @@ internal class AppDrawer : Fragment() {
// }
}
var lastSearchStringLength = 0
var lastSearchString : String = ""
private fun filterAppsList(searchString: String) {
/* check each app name and add if it matches the search string */
packageListClone.clear()
for (resolver in packageInfoList) {
appName(resolver).let {
BLog.LOGE("searchString >>> ${searchString} , normalize(it) ${normalize(it)} ${normalize(it).contains(searchString)}")
if (normalize(it).contains(searchString)) {
packageListClone.add(Packages(resolver.activityInfo.packageName, it))
if (lastSearchStringLength > 0 && (lastSearchStringLength != searchString.length || lastSearchString.equals(searchString) == false)) {
BLog.LOGE("START FILTER")
packageList.clear()
for (resolver in packageInfoList) {
appName(resolver).let {
// BLog.LOGE("searchString >>> ${searchString} , normalize(it) ${normalize(it)} ${normalize(it).contains(normalize(searchString), false)}")
if (normalize(it).contains(normalize(searchString), false)) {
packageList.add(Packages(resolver.activityInfo.packageName, it))
} else {
}
}
}
}
if (searchString.length > 2 && packageListClone.size == 1 && settingsPrefs!!.getBoolean(KEY_QUICK_LAUNCH, true)) {
var dialog = AlertDialog.Builder(requireContext())
dialog.setTitle("앱 실행 확인")
dialog.setMessage("${searchString} 검색 결과 '${packageListClone[0].appName}' 준비됨")
dialog.setCancelable(false)
dialog.setOnCancelListener {
binding.searchInput.setText("")
it.dismiss()
BLog.LOGE("MIDDLE FILTER")
if (searchString.length > 2 && packageList.size == 1 && settingsPrefs!!.getBoolean(
KEY_QUICK_LAUNCH,
true
)
) {
var dialog = AlertDialog.Builder(requireContext())
dialog.setTitle("앱 실행 확인")
dialog.setMessage("${searchString} 검색 결과 '${packageList[0].appName}' 준비됨")
dialog.setCancelable(false)
dialog.setNegativeButton("취소") { s,d->
binding.searchInput.setText("")
s.dismiss()
}
dialog.setPositiveButton("실행") { s, d ->
startActivity(packageManager?.getLaunchIntentForPackage(packageList[0].packageName))
s.dismiss()
binding.searchInput.setText("")
}
dialog.show()
} else {
appsAdapter?.updateData(packageList)
}
dialog.setPositiveButton("실행") { s,d ->
startActivity(packageManager?.getLaunchIntentForPackage(packageListClone[0].packageName))
s.dismiss()
binding.searchInput.setText("")
BLog.LOGE("END FILTER")
} else if(lastSearchStringLength == 0){
packageList.clear()
for (resolver in packageInfoList) {
packageList.add(Packages(resolver.activityInfo.packageName, appName(resolver)))
}
dialog.show()
appsAdapter?.updateData(packageList)
}
else appsAdapter?.updateData(packageListClone.sortedBy { it.appName.lowercase() })
lastSearchString = searchString
lastSearchStringLength = searchString.length
}
private fun normalize(str: String): String {
val normalizedString =
Normalizer.normalize(str.replace("\\W".toRegex(), ""), Normalizer.Form.NFD)
Normalizer.normalize(str.replace("\\W".toRegex(), ""), Normalizer.Form.NFC)
val pattern = Pattern.compile("\\p{InCombiningDiacriticalMarks}+")
return pattern.matcher(normalizedString).replaceAll("").lowercase()
return pattern.matcher(normalizedString).replaceAll("").toLowerCase()
}
private fun openSearch() {

View File

@ -30,6 +30,8 @@ import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.textview.MaterialTextView
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.async
import rasel.lunar.launcher.LauncherActivity.Companion.lActivity
import rasel.lunar.launcher.R
import rasel.lunar.launcher.apps.IconPackManager.Companion.getDrawableIconForPackage
@ -75,7 +77,13 @@ internal class AppsAdapter(
}
1 -> {
appIcon.visibility = View.GONE
appIconTwo.setImageDrawable(getDrawableIconForPackage(item.packageName, packageManager.getApplicationIcon(item.packageName)))
appIconTwo.visibility = View.VISIBLE
MainScope().async {
getDrawableIconForPackage(item.packageName, packageManager.getApplicationIcon(item.packageName)) {
appIconTwo.post { appIconTwo.setImageDrawable(it) }
} }
childTextview.apply {
gravity = appGravity or Gravity.CENTER_VERTICAL
setTextSize(TypedValue.COMPLEX_UNIT_PX, lActivity!!.resources.getDimension(R.dimen.twenty))
@ -85,7 +93,15 @@ internal class AppsAdapter(
}
2 -> {
appIconTwo.visibility = View.GONE
appIcon.setImageDrawable(getDrawableIconForPackage(item.packageName, packageManager.getApplicationIcon(item.packageName)))
appIcon.visibility = View.VISIBLE
MainScope().async {
getDrawableIconForPackage(
item.packageName,
packageManager.getApplicationIcon(item.packageName)
) {
appIcon.post { appIcon.setImageDrawable(it) }
}
}
childTextview.apply {
gravity = Gravity.CENTER
setTextSize(TypedValue.COMPLEX_UNIT_PX, lActivity!!.resources.getDimension(R.dimen.twelve))
@ -125,7 +141,6 @@ internal class AppsAdapter(
appsCount.text = it.toString()
appsSize = it
}
this.notifyDataSetChanged()
}
/* update text gravity (alignment) */
@ -138,6 +153,10 @@ internal class AppsAdapter(
notifyDataSetChanged()
}
}
fun hideItem(idx: Int) {
}
}
internal data class Packages (

View File

@ -25,7 +25,13 @@ import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.util.Log
import androidx.collection.LruCache
import androidx.core.content.res.ResourcesCompat
import com.squareup.picasso.Picasso
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParserException
import org.xmlpull.v1.XmlPullParserFactory
@ -47,12 +53,14 @@ internal class IconPackManager {
private val packageName = settingsPrefs.getString(KEY_ICON_PACK, DEFAULT_ICON_PACK)
private var loaded = false
private val packagesDrawables = HashMap<String?, String?>()
private val packagesConponentNames = HashMap<String?, String?>()
private val backImages: MutableList<Bitmap> = ArrayList()
private var maskImage: Bitmap? = null
private var frontImage: Bitmap? = null
private var factor = 1.0f
private var totalIcons = 0
private var iconPackRes: Resources? = null
private var appPackageIconDrawables : HashMap<String, Drawable> = hashMapOf()
private fun load() {
/* load appfilter.xml from the icon pack package */
@ -62,11 +70,14 @@ internal class IconPackManager {
val appFilterId = iconPackRes!!.getIdentifier("appfilter", "xml", packageName)
if (appFilterId > 0) {
xpp = iconPackRes!!.getXml(appFilterId)
BLog.LOGE("packageName >>> ${packageName}")
} else {
/* no resource found, try to open it from assets folder */
try {
xpp = XmlPullParserFactory.newInstance().apply { isNamespaceAware = true }
.newPullParser().apply { setInput(iconPackRes!!.assets.open("appfilter.xml"), "utf-8") }
.newPullParser().apply {
BLog.LOGE("packageName >>> ${packageName}")
setInput(iconPackRes!!.assets.open("appfilter.xml"), "utf-8")
}
} catch (e: IOException) {
e.printStackTrace()
BLog.w("", "Couldn't find the appfilter.xml file")
@ -78,36 +89,24 @@ internal class IconPackManager {
if (eventType == XmlPullParser.START_TAG) {
when (xpp.name) {
"iconback" -> {
for (i in 0 until xpp.attributeCount) {
if (xpp.getAttributeName(i).startsWith("img")) {
loadBitmap(xpp.getAttributeValue(i))?.let { backImages.add(it) }
}
}
for (i in 0 until xpp.attributeCount) { if (xpp.getAttributeName(i).startsWith("img")) { loadBitmap(xpp.getAttributeValue(i))?.let { backImages.add(it) }}}
}
"iconmask" -> {
if (xpp.attributeCount > 0 && xpp.getAttributeName(0) == "img1") {
maskImage = loadBitmap(xpp.getAttributeValue(0))
}
if (xpp.attributeCount > 0 && xpp.getAttributeName(0) == "img1") { maskImage = loadBitmap(xpp.getAttributeValue(0)) }
}
"iconupon" -> {
if (xpp.attributeCount > 0 && xpp.getAttributeName(0) == "img1") {
frontImage = loadBitmap(xpp.getAttributeValue(0))
}
if (xpp.attributeCount > 0 && xpp.getAttributeName(0) == "img1") { frontImage = loadBitmap(xpp.getAttributeValue(0)) }
}
"scale" -> {
if (xpp.attributeCount > 0 && xpp.getAttributeName(0) == "factor") {
factor = java.lang.Float.valueOf(xpp.getAttributeValue(0))
}
if (xpp.attributeCount > 0 && xpp.getAttributeName(0) == "factor") { factor = java.lang.Float.valueOf(xpp.getAttributeValue(0)) }
}
"item" -> {
var componentName: String? = null
var drawableName: String? = null
for (i in 0 until xpp.attributeCount) {
when (xpp.getAttributeName(i)) {
"component" -> componentName = xpp.getAttributeValue(i)
"drawable" -> drawableName = xpp.getAttributeValue(i)
}
}
for (i in 0 until xpp.attributeCount) { when (xpp.getAttributeName(i)) {
"component" -> componentName = xpp.getAttributeValue(i)
"drawable" -> drawableName = xpp.getAttributeValue(i)
} }
if (!packagesDrawables.containsKey(componentName)) {
packagesDrawables[componentName] = drawableName
totalIcons += 1
@ -127,12 +126,30 @@ internal class IconPackManager {
e.printStackTrace()
}
}
val maxMemory = (Runtime.getRuntime().maxMemory() / 1024).toInt()
val cacheSize = maxMemory / 8
val bitmapCache = object : LruCache<String, Bitmap>(cacheSize) {
fun sizeOf(key: String?, value: Bitmap?): Int {
return if(value?.byteCount?.toInt() ?: 0 > 1024) {
value?.byteCount!!.div(1024)
} else { 0 }
}
}
private fun loadBitmap(drawableName: String): Bitmap? {
if (packageName != null && packageName.length > 0) {
var bm = bitmapCache.get(packageName)
if (bm != null) return bm
}
iconPackRes!!.getIdentifier(drawableName, "drawable", packageName).let { id ->
if (id > 0) {
ResourcesCompat.getDrawable(iconPackRes!!, id, null).let {
if (it is BitmapDrawable) return it.bitmap
if (it is BitmapDrawable) {
if (packageName != null && packageName.length > 0) {
bitmapCache.put(packageName, it.bitmap)
}
return it.bitmap
}
}
}
}
@ -146,35 +163,69 @@ internal class IconPackManager {
}
}
fun getDrawableIconForPackage(appPackageName: String?, defaultDrawable: Drawable?): Drawable? {
when (packageName) {
DEFAULT_ICON_PACK -> return defaultDrawable
else -> {
if (!loaded) load()
var componentName: String? = null
if (lActivity!!.packageManager.getLaunchIntentForPackage(appPackageName!!) != null) {
componentName = lActivity!!.packageManager.getLaunchIntentForPackage(appPackageName)!!.component.toString()
}
var drawable = packagesDrawables[componentName]
if (!drawable.isNullOrEmpty()) return loadDrawable(drawable)
else {
/* try to get a resource with the component filename */
if (!componentName.isNullOrEmpty()) {
val start = componentName.indexOf("{") + 1
val end = componentName.indexOf("}", start)
if (end > start) {
drawable = componentName.substring(start, end).lowercase(Locale.getDefault()).replace(".", "_").replace("/", "_")
try {
if (iconPackRes!!.getIdentifier(drawable, "drawable", packageName) > 0) return loadDrawable(drawable)
} catch (e: NullPointerException) {
settingsPrefs.edit().putString(KEY_ICON_PACK, DEFAULT_ICON_PACK).apply()
fun putAfterReturn(packages: String, drawable : Drawable?) : Drawable? {
if (drawable != null) {
appPackageIconDrawables.put(packages, drawable)
}
return drawable
}
fun getDrawableIconForPackage(appPackageName: String?, defaultDrawable: Drawable?, onComplete : (Drawable?)->Unit) {
var ddd = if (appPackageIconDrawables.containsKey(appPackageName)) appPackageIconDrawables.get(appPackageName) else null
if (ddd != null) {
onComplete(ddd)
} else {
when (packageName) {
DEFAULT_ICON_PACK -> onComplete.invoke(defaultDrawable)
else -> {
if (!loaded) load()
var componentName: String? = null
componentName = packagesConponentNames.get(appPackageName!!)
if (componentName == null || componentName.length ?: 0 <= 0 ) {
BLog.LOGE("it's compo ${appPackageName}")
var pkgIntent =
lActivity!!.packageManager.getLaunchIntentForPackage(
appPackageName!!
)
if (pkgIntent != null) {
componentName = pkgIntent!!.component.toString()
}
}
var drawable = packagesDrawables[componentName]
if (!drawable.isNullOrEmpty()) onComplete.invoke(
putAfterReturn(
appPackageName,
loadDrawable(drawable)
)
)
else {
if (!componentName.isNullOrEmpty()) {
val start = componentName.indexOf("{") + 1
val end = componentName.indexOf("}", start)
if (end > start) {
drawable = componentName.substring(start, end)
.lowercase(Locale.getDefault()).replace(".", "_")
.replace("/", "_")
try {
if (iconPackRes!!.getIdentifier(
drawable,
"drawable",
packageName
) > 0
) onComplete.invoke(putAfterReturn(appPackageName, loadDrawable(drawable)))
} catch (e: NullPointerException) {
settingsPrefs.edit()
.putString(KEY_ICON_PACK, DEFAULT_ICON_PACK).apply()
}
}
} else {
onComplete.invoke(defaultDrawable)
}
}
}
return defaultDrawable
}
}
}
}

View File

@ -32,4 +32,5 @@ internal class WidgetHost(context: Context, hostId: Int) : AppWidgetHost(context
clearViews()
}
}

View File

@ -36,7 +36,6 @@ internal class WidgetHostView(context: Context) : AppWidgetHostView(context) {
hasPerformedLongPress = false
return true
}
// Watch for long press events at this level to make sure
// users can always pick up this widget
when (ev.action) {

View File

@ -212,11 +212,11 @@ internal class UniUtils {
(iconSize * resources.displayMetrics.density).toInt(), 1F)
}.let { sImageView ->
context.packageManager.getApplicationIcon(packageName).let { defaultIcon ->
sImageView.setImageDrawable(
if (context.getSharedPreferences(PREFS_SETTINGS, 0).getInt(KEY_APPS_LAYOUT, 0) != 0)
getDrawableIconForPackage(packageName, defaultIcon)
else defaultIcon
)
getDrawableIconForPackage(packageName, defaultIcon) {
sImageView.setImageDrawable(it ?: defaultIcon)
}
else sImageView.setImageDrawable(defaultIcon)
}
sImageView.setOnClickListener {
context.startActivity(context.packageManager.getLaunchIntentForPackage(packageName))

View File

@ -26,6 +26,7 @@ import android.provider.Settings
import android.view.animation.AnimationUtils
import com.google.android.material.progressindicator.CircularProgressIndicator
import rasel.lunar.launcher.R
import rasel.lunar.launcher.utils.BLog
internal class BatteryReceiver(private val progressBar: CircularProgressIndicator) : BroadcastReceiver() {
@ -42,7 +43,14 @@ internal class BatteryReceiver(private val progressBar: CircularProgressIndicato
override fun onReceive(context: Context?, intent: Intent?) {
val animationDuration = try {
Settings.Global.getFloat(context?.contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE)
intent?.extras?.keySet()?.let {
it.iterator().forEach {
BLog.LOGE("intent key >> ${it}")
}
}
BLog.LOGE("intent >> ${intent}")
Settings.Global.getFloat(context?.contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1.0f)
} catch (e: Settings.SettingNotFoundException) {
e.printStackTrace()
}

View File

@ -134,12 +134,7 @@ internal class LauncherHome : Fragment() {
gestureDistance: Double
): Boolean {
when(fingers) {
1 ->
if (targetView?.equals(binding.batteryProgress) ?: false) {
QuickAccess().show(fragManager, BOTTOM_SHEET_TAG)
} else {
QuickAccess().show(fragManager, BOTTOM_SHEET_TAG)
}
3 -> QuickAccess().show(fragManager, BOTTOM_SHEET_TAG)
2->{
var startIntene = Intent(Intent.ACTION_MAIN)
startIntene.setComponent(ComponentName("com.mime.dualscreenview","com.mime.dualscreenview.activity.Intro"))
@ -215,11 +210,7 @@ internal class LauncherHome : Fragment() {
override fun onDoubleTap(targetView: View,fingers: Int): Boolean {
when(fingers) {
1 -> if (targetView?.equals(binding.batteryProgress) ?: false) {
lockMethod(settingsPrefs.getInt(KEY_LOCK_METHOD, 0), requireContext(), binding.favAppsGroup)
} else {
lockMethod(settingsPrefs.getInt(KEY_LOCK_METHOD, 0), requireContext(), binding.favAppsGroup)
}
1 -> lockMethod(settingsPrefs.getInt(KEY_LOCK_METHOD, 0), requireContext(), binding.favAppsGroup)
else -> {}
}
return false

View File

@ -35,6 +35,10 @@ import java.util.concurrent.Executors
internal class WeatherExecutor(sharedPreferences: SharedPreferences) {
companion object {
var lastedCheckTime = 0L
}
private val cityName: String
private val owmApi: String
private val weatherUrl: String
@ -44,10 +48,9 @@ internal class WeatherExecutor(sharedPreferences: SharedPreferences) {
@SuppressLint("SetTextI18n")
fun generateWeatherString(materialTextView: MaterialTextView) {
materialTextView.visibility = View.GONE
/* run the executor if network is available,
and city name and owm api values are not empty */
if (isNetworkAvailable && cityName.isNotEmpty() && owmApi.isNotEmpty()) {
if (System.currentTimeMillis() - lastedCheckTime > (1000 * 60 * 15) && isNetworkAvailable && cityName.isNotEmpty() && owmApi.isNotEmpty()) {
try {
Executors.newSingleThreadExecutor().execute {
var weather: Weather? = null
@ -70,6 +73,7 @@ internal class WeatherExecutor(sharedPreferences: SharedPreferences) {
exception.printStackTrace()
}
}
lastedCheckTime = System.currentTimeMillis()
}
init {
@ -77,7 +81,7 @@ internal class WeatherExecutor(sharedPreferences: SharedPreferences) {
owmApi = sharedPreferences.getString(KEY_OWM_API, "").toString()
tempUnit = sharedPreferences.getInt(KEY_TEMP_UNIT, 0)
showCity = sharedPreferences.getBoolean(KEY_SHOW_CITY, false)
weatherUrl = URLEncoder.encode("https://api.openweathermap.org/data/2.5/weather?q=$cityName&APPID=$owmApi&units=" + if (tempUnit == 0) "metric" else "imperial","utf-8")
weatherUrl = "https://api.openweathermap.org/data/2.5/weather?q=$cityName&APPID=$owmApi&units=" + if (tempUnit == 0) "metric" else "imperial"
}
}

View File

@ -127,7 +127,7 @@ internal class Apps : BottomSheetDialogFragment() {
Gravity.RIGHT -> binding.appAlignmentRight.isChecked = true
}
binding.columnsCount.value = settingsPrefs!!.getInt(KEY_GRID_COLUMNS, DEFAULT_GRID_COLUMNS).toFloat()
binding.columnsCount.value = Math.min(settingsPrefs!!.getInt(KEY_GRID_COLUMNS, DEFAULT_GRID_COLUMNS).toFloat(), 4f)
binding.scrollbarHeight.value = settingsPrefs!!.getInt(KEY_SCROLLBAR_HEIGHT, DEFAULT_SCROLLBAR_HEIGHT).toFloat()
return binding.root

View File

@ -7,37 +7,39 @@
<com.google.android.material.textview.MaterialTextView
android:id="@+id/appsCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_height="0dp"
android:textColor="?attr/colorControlHighlight"
android:textSize="@dimen/appsCountText"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@id/appsList"
app:layout_constraintStart_toStartOf="@id/appsList"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/appsList"
android:layout_width="@dimen/zero"
android:layout_height="wrap_content"
android:layout_height="0dp"
android:background="@android:color/transparent"
android:fadingEdgeLength="@dimen/sixteen"
android:overScrollMode="never"
android:requiresFadingEdge="vertical"
android:scrollbars="none"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/searchInput"
app:layout_constraintEnd_toStartOf="@+id/search"
app:layout_constraintStart_toStartOf="parent" />
<rasel.lunar.launcher.apps.AlphabetScrollbar
android:id="@+id/alphabets"
android:layout_width="@dimen/zero"
android:visibility="gone"
android:layout_height="@dimen/zero"
android:layout_marginBottom="@dimen/four"
app:layout_constraintBottom_toTopOf="@+id/reset"
app:layout_constraintStart_toStartOf="@id/reset"
app:layout_constraintEnd_toEndOf="parent" />
<!-- <rasel.lunar.launcher.apps.AlphabetScrollbar-->
<!-- android:id="@+id/alphabets"-->
<!-- android:layout_width="@dimen/zero"-->
<!-- android:visibility="gone"-->
<!-- android:layout_height="@dimen/zero"-->
<!-- android:layout_marginBottom="@dimen/four"-->
<!-- app:layout_constraintBottom_toTopOf="@+id/reset"-->
<!-- app:layout_constraintStart_toStartOf="@id/reset"-->
<!-- app:layout_constraintEnd_toEndOf="parent" />-->
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/reset"
@ -84,13 +86,13 @@
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/searchInput"
android:layout_width="@dimen/oneThirtySix"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:imeOptions="actionSearch"
android:singleLine="true"
android:visibility="gone"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/appsList"
app:layout_constraintStart_toStartOf="@+id/appsList" />
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,30 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@drawable/apps_bg"
android:orientation="vertical">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/appIcon"
android:id="@+id/appIconTwo"
android:visibility="gone"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_width="@dimen/forty"
android:layout_height="@dimen/forty"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="@dimen/four" />
<androidx.appcompat.widget.LinearLayoutCompat
<com.google.android.material.imageview.ShapeableImageView
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginBottom="@dimen/four"
android:visibility="visible"
android:id="@+id/appIcon"
android:layout_width="@dimen/forty"
android:layout_height="@dimen/forty" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/childTextview"
app:layout_constraintTop_toBottomOf="@id/appIconTwo"
app:layout_constraintLeft_toRightOf="@id/appIcon"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:layout_marginBottom="@dimen/four"
android:layout_height="match_parent" />
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/appIconTwo"
android:layout_width="@dimen/forty"
android:layout_height="@dimen/forty" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/childTextview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -231,8 +231,8 @@
android:id="@+id/columnsCount"
android:layout_width="@dimen/zero"
android:layout_height="wrap_content"
android:valueFrom="3"
android:valueTo="7"
android:valueFrom="2"
android:valueTo="5"
android:stepSize="1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"