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 ("com.google.android.material:material:1.10.0")
implementation (kotlin("stdlib", version = kotlinVersion)) implementation (kotlin("stdlib", version = kotlinVersion))
implementation ("com.github.cachapa:ExpandableLayout:2.9.2") implementation ("com.github.cachapa:ExpandableLayout:2.9.2")
implementation ("com.squareup.picasso:picasso:2.8")
} }

View File

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

View File

@ -1,118 +1,112 @@
/* ///*
* Lunar Launcher // * Lunar Launcher
* Copyright (C) 2022 Md Rasel Hossain // * Copyright (C) 2022 Md Rasel Hossain
* // *
* This program is free software: you can redistribute it and/or modify // * 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 // * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or // * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. // * (at your option) any later version.
* // *
* This program is distributed in the hope that it will be useful, // * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of // * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. // * GNU General Public License for more details.
* // *
* You should have received a copy of the GNU General Public License // * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. // * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ // */
//
package rasel.lunar.launcher.apps //package rasel.lunar.launcher.apps
//
import android.annotation.SuppressLint //import android.annotation.SuppressLint
import android.content.Context //import android.content.Context
import android.graphics.Canvas //import android.graphics.Canvas
import android.graphics.Paint //import android.graphics.Paint
import android.util.AttributeSet //import android.util.AttributeSet
import android.util.TypedValue //import android.util.TypedValue
import android.view.MotionEvent //import android.view.MotionEvent
import android.view.View //import android.view.View
import androidx.core.content.ContextCompat //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 //internal class AlphabetScrollbar : View {
import rasel.lunar.launcher.apps.AppDrawer.Companion.settingsPrefs //
import rasel.lunar.launcher.apps.AppsAdapter.Companion.appsSize // private var paint: Paint? = null
import rasel.lunar.launcher.helpers.Constants // private var selectedIndex = -1
//// private val alphabet get() = alphabetList.distinct()
//
internal class AlphabetScrollbar : View { // constructor(context: Context?) : super(context) {
// init()
private var paint: Paint? = null // }
private var selectedIndex = -1 //
private val alphabet get() = alphabetList.distinct() // constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
// init()
constructor(context: Context?) : super(context) { // }
init() //
} // constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
// init()
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) { // }
init() //
} // @SuppressLint("ResourceType")
// private fun init() {
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { // paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
init() // color = defaultTextColor
} // textSize = 16f
// }
@SuppressLint("ResourceType") // }
private fun init() { //
paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { // override fun onDraw(canvas: Canvas) {
color = defaultTextColor // super.onDraw(canvas)
textSize = 16f // val width = width
} // val height = height
} // val letterHeight: Int = height / alphabet.count()
// alphabet.indices.forEach { i: Int ->
override fun onDraw(canvas: Canvas) { // val x = width / 2f - paint!!.measureText(alphabet[i]) / 2f
super.onDraw(canvas) // val y = i * letterHeight + letterHeight / 2f
val width = width // when (i) {
val height = height // selectedIndex -> paint!!.textSize = 20f
val letterHeight: Int = height / alphabet.count() // else -> paint!!.textSize = 16f
alphabet.indices.forEach { i: Int -> // }
val x = width / 2f - paint!!.measureText(alphabet[i]) / 2f // canvas.drawText(alphabet[i], x, y, paint!!)
val y = i * letterHeight + letterHeight / 2f // }
when (i) { // }
selectedIndex -> paint!!.textSize = 20f //
else -> paint!!.textSize = 16f // @SuppressLint("ClickableViewAccessibility")
} // override fun onTouchEvent(event: MotionEvent): Boolean {
canvas.drawText(alphabet[i], x, y, paint!!) //// when (event.action) {
} //// MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> {
} //// val y = event.y
//// val index = (y / height * alphabet.count()).toInt()
@SuppressLint("ClickableViewAccessibility") //// if (index != selectedIndex) {
override fun onTouchEvent(event: MotionEvent): Boolean { //// selectedIndex = index
when (event.action) { //// invalidate()
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> { //// }
val y = event.y ////
val index = (y / height * alphabet.count()).toInt() //// if (!settingsPrefs!!.getBoolean(Constants.KEY_APPS_COUNT, true)) letterPreview?.visibility = VISIBLE
if (index != selectedIndex) { //// try { letterPreview?.text = alphabet[selectedIndex] }
selectedIndex = index //// catch (exception: Exception) { exception.printStackTrace() }
invalidate() //// }
} ////
//// MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
if (!settingsPrefs!!.getBoolean(Constants.KEY_APPS_COUNT, true)) letterPreview?.visibility = VISIBLE //// when {
try { letterPreview?.text = alphabet[selectedIndex] } //// selectedIndex < 0 -> listenScroll(alphabet[0])
catch (exception: Exception) { exception.printStackTrace() } //// selectedIndex > alphabet.count() - 1 -> listenScroll(alphabet[alphabet.count() - 1])
} //// else -> listenScroll(alphabet[selectedIndex])
//// }
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { ////
when { //// selectedIndex = -1
selectedIndex < 0 -> listenScroll(alphabet[0]) //// invalidate()
selectedIndex > alphabet.count() - 1 -> listenScroll(alphabet[alphabet.count() - 1]) //// if (settingsPrefs!!.getBoolean(Constants.KEY_APPS_COUNT, true)) letterPreview?.text = appsSize.toString()
else -> listenScroll(alphabet[selectedIndex]) //// else letterPreview?.visibility = GONE
} //// }
//// }
selectedIndex = -1 // return true
invalidate() // }
if (settingsPrefs!!.getBoolean(Constants.KEY_APPS_COUNT, true)) letterPreview?.text = appsSize.toString() //
else letterPreview?.visibility = GONE // private val defaultTextColor: Int get() {
} // val resolvedAttr = TypedValue()
} // context.theme.resolveAttribute(android.R.attr.textColorPrimary, resolvedAttr, true)
return true // val colorRes = resolvedAttr.run { if (resourceId != 0) resourceId else data }
} // return ContextCompat.getColor(context, colorRes)
// }
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.graphics.Rect
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.provider.Settings.Global
import android.view.Gravity import android.view.Gravity
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@ -41,6 +42,9 @@ import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.textview.MaterialTextView 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.BuildConfig
import rasel.lunar.launcher.LauncherActivity.Companion.lActivity import rasel.lunar.launcher.LauncherActivity.Companion.lActivity
import rasel.lunar.launcher.R import rasel.lunar.launcher.R
@ -75,42 +79,25 @@ internal class AppDrawer : Fragment() {
private var appsAdapter: AppsAdapter? = null private var appsAdapter: AppsAdapter? = null
private var packageInfoList: MutableList<ResolveInfo> = mutableListOf() private var packageInfoList: MutableList<ResolveInfo> = mutableListOf()
private var packageList = mutableListOf<Packages>() private var packageList = mutableListOf<Packages>()
private var packageListClone = mutableListOf<Packages>()
private val numberPattern = Pattern.compile("[0-9]") // private val numberPattern = Pattern.compile("[0-9]")
private val alphabetPattern = Pattern.compile("[A-Z]") // private val alphabetPattern = Pattern.compile("[A-Z]")
@JvmStatic var settingsPrefs: SharedPreferences? = null @JvmStatic var settingsPrefs: SharedPreferences? = null
@JvmStatic var appNamesPrefs: SharedPreferences? = null @JvmStatic var appNamesPrefs: SharedPreferences? = null
@JvmStatic var alphabetList = mutableListOf<String>() // @JvmStatic var alphabetList = mutableListOf<String>()
@JvmStatic var letterPreview: MaterialTextView? = null @JvmStatic var letterPreview: MaterialTextView? = null
private fun appName(resolver: ResolveInfo): String { 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 { 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 binding.appsCount.visibility = if (settingsPrefs!!.getBoolean(KEY_APPS_COUNT, true)) VISIBLE else GONE
setLayout() setLayout()
fetchApps()
getAlphabetItems()
setKeyboardPadding()
return binding.root return binding.root
} }
@ -140,7 +125,7 @@ internal class AppDrawer : Fragment() {
binding.reset.setOnClickListener { onResume() } binding.reset.setOnClickListener { onResume() }
binding.moveDown.setOnClickListener { binding.moveDown.setOnClickListener {
binding.appsList.smoothScrollToPosition(packageListClone.size - 1) binding.appsList.smoothScrollToPosition(packageList.size - 1)
} }
binding.moveUp.setOnClickListener { binding.moveUp.setOnClickListener {
@ -163,7 +148,7 @@ internal class AppDrawer : Fragment() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
fetchApps() fetchApps()
getAlphabetItems() setKeyboardPadding()
binding.appsCount.visibility = if (settingsPrefs!!.getBoolean(KEY_APPS_COUNT, true)) VISIBLE else GONE 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()) binding.appsList.layoutManager = LinearLayoutManager(requireContext())
appsAdapter!!.updateGravity(settingsPrefs!!.getInt(KEY_DRAW_ALIGN, Gravity.CENTER)) 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 */ /* initialize apps list adapter */
@ -195,30 +180,39 @@ internal class AppDrawer : Fragment() {
/* update app list with app and package name */ /* update app list with app and package name */
fun fetchApps() { fun fetchApps() {
packageInfoList = (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { GlobalScope.launch {
packageManager?.queryIntentActivities( packageInfoList = (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LAUNCHER), packageManager?.queryIntentActivities(
PackageManager.ResolveInfoFlags.of(0) Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LAUNCHER),
) PackageManager.ResolveInfoFlags.of(0)
} else { )
(packageManager?.queryIntentActivities( } else {
Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LAUNCHER), 0)) (packageManager?.queryIntentActivities(
})?.apply { Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LAUNCHER), 0
removeIf { it.activityInfo.packageName.equals(BuildConfig.APPLICATION_ID) } ))
sortWith(ResolveInfo.DisplayNameComparator(packageManager)) })?.apply {
}!! removeIf { it.activityInfo.packageName.equals(BuildConfig.APPLICATION_ID) }
sortedBy {
appName(it)
}
}!!
/* add package and app names to the list */ /* add package and app names to the list */
packageList.clear() packageList.clear()
packageListClone.clear() var edit = appNamesPrefs?.edit()
for (resolver in packageInfoList) { for (resolver in packageInfoList) {
packageList.add(Packages(resolver.activityInfo.packageName, appName(resolver))) packageList.add(Packages(resolver.activityInfo.packageName, appName(resolver)))
packageListClone.add(Packages(resolver.activityInfo.packageName, appName(resolver))) }
}
when { // when {
packageListClone.size < 1 -> return // packageList.size < 1 -> return
else -> appsAdapter?.updateData(packageListClone.sortedBy { it.appName.lowercase() }) // 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) { private fun filterAppsList(searchString: String) {
/* check each app name and add if it matches the search string */ /* check each app name and add if it matches the search string */
packageListClone.clear() if (lastSearchStringLength > 0 && (lastSearchStringLength != searchString.length || lastSearchString.equals(searchString) == false)) {
for (resolver in packageInfoList) { BLog.LOGE("START FILTER")
appName(resolver).let { packageList.clear()
BLog.LOGE("searchString >>> ${searchString} , normalize(it) ${normalize(it)} ${normalize(it).contains(searchString)}") for (resolver in packageInfoList) {
if (normalize(it).contains(searchString)) { appName(resolver).let {
packageListClone.add(Packages(resolver.activityInfo.packageName, it)) // 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 {
}
} }
} }
} BLog.LOGE("MIDDLE FILTER")
if (searchString.length > 2 && packageList.size == 1 && settingsPrefs!!.getBoolean(
if (searchString.length > 2 && packageListClone.size == 1 && settingsPrefs!!.getBoolean(KEY_QUICK_LAUNCH, true)) { KEY_QUICK_LAUNCH,
var dialog = AlertDialog.Builder(requireContext()) true
dialog.setTitle("앱 실행 확인") )
dialog.setMessage("${searchString} 검색 결과 '${packageListClone[0].appName}' 준비됨") ) {
dialog.setCancelable(false) var dialog = AlertDialog.Builder(requireContext())
dialog.setOnCancelListener { dialog.setTitle("앱 실행 확인")
binding.searchInput.setText("") dialog.setMessage("${searchString} 검색 결과 '${packageList[0].appName}' 준비됨")
it.dismiss() 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 -> BLog.LOGE("END FILTER")
startActivity(packageManager?.getLaunchIntentForPackage(packageListClone[0].packageName)) } else if(lastSearchStringLength == 0){
s.dismiss() packageList.clear()
binding.searchInput.setText("") 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 { private fun normalize(str: String): String {
val normalizedString = 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}+") val pattern = Pattern.compile("\\p{InCombiningDiacriticalMarks}+")
return pattern.matcher(normalizedString).replaceAll("").lowercase() return pattern.matcher(normalizedString).replaceAll("").toLowerCase()
} }
private fun openSearch() { private fun openSearch() {

View File

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

View File

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

View File

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

View File

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

View File

@ -26,6 +26,7 @@ import android.provider.Settings
import android.view.animation.AnimationUtils import android.view.animation.AnimationUtils
import com.google.android.material.progressindicator.CircularProgressIndicator import com.google.android.material.progressindicator.CircularProgressIndicator
import rasel.lunar.launcher.R import rasel.lunar.launcher.R
import rasel.lunar.launcher.utils.BLog
internal class BatteryReceiver(private val progressBar: CircularProgressIndicator) : BroadcastReceiver() { 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?) { override fun onReceive(context: Context?, intent: Intent?) {
val animationDuration = try { 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) { } catch (e: Settings.SettingNotFoundException) {
e.printStackTrace() e.printStackTrace()
} }

View File

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

View File

@ -35,6 +35,10 @@ import java.util.concurrent.Executors
internal class WeatherExecutor(sharedPreferences: SharedPreferences) { internal class WeatherExecutor(sharedPreferences: SharedPreferences) {
companion object {
var lastedCheckTime = 0L
}
private val cityName: String private val cityName: String
private val owmApi: String private val owmApi: String
private val weatherUrl: String private val weatherUrl: String
@ -44,10 +48,9 @@ internal class WeatherExecutor(sharedPreferences: SharedPreferences) {
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
fun generateWeatherString(materialTextView: MaterialTextView) { fun generateWeatherString(materialTextView: MaterialTextView) {
materialTextView.visibility = View.GONE materialTextView.visibility = View.GONE
/* run the executor if network is available, /* run the executor if network is available,
and city name and owm api values are not empty */ 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 { try {
Executors.newSingleThreadExecutor().execute { Executors.newSingleThreadExecutor().execute {
var weather: Weather? = null var weather: Weather? = null
@ -70,6 +73,7 @@ internal class WeatherExecutor(sharedPreferences: SharedPreferences) {
exception.printStackTrace() exception.printStackTrace()
} }
} }
lastedCheckTime = System.currentTimeMillis()
} }
init { init {
@ -77,7 +81,7 @@ internal class WeatherExecutor(sharedPreferences: SharedPreferences) {
owmApi = sharedPreferences.getString(KEY_OWM_API, "").toString() owmApi = sharedPreferences.getString(KEY_OWM_API, "").toString()
tempUnit = sharedPreferences.getInt(KEY_TEMP_UNIT, 0) tempUnit = sharedPreferences.getInt(KEY_TEMP_UNIT, 0)
showCity = sharedPreferences.getBoolean(KEY_SHOW_CITY, false) 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 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() binding.scrollbarHeight.value = settingsPrefs!!.getInt(KEY_SCROLLBAR_HEIGHT, DEFAULT_SCROLLBAR_HEIGHT).toFloat()
return binding.root return binding.root

View File

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

View File

@ -1,30 +1,40 @@
<?xml version="1.0" encoding="utf-8"?> <?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_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@drawable/apps_bg" android:background="@drawable/apps_bg"
android:orientation="vertical"> android:orientation="vertical">
<com.google.android.material.imageview.ShapeableImageView <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_width="@dimen/forty"
android:layout_height="@dimen/forty" android:layout_height="@dimen/forty"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:layout_marginBottom="@dimen/four" /> 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_width="match_parent"
android:layout_height="wrap_content" android:layout_marginBottom="@dimen/four"
android:orientation="horizontal"> android:layout_height="match_parent" />
<com.google.android.material.imageview.ShapeableImageView </androidx.constraintlayout.widget.ConstraintLayout>
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>

View File

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