# Conflicts:
#	app/src/main/kotlin/rasel/lunar/launcher/apps/AppDrawer.kt
This commit is contained in:
lunaticbum 2024-08-22 20:44:27 +09:00
commit ed24af2d41
20 changed files with 955 additions and 213 deletions

View File

@ -18,6 +18,7 @@
<uses-permission android:name="com.android.alarm.permission.SET_ALARM"/> <uses-permission android:name="com.android.alarm.permission.SET_ALARM"/>
<uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_CALL_LOG"/>
<!-- api 33+ --> <!-- api 33+ -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission <uses-permission

View File

@ -27,7 +27,10 @@ import android.graphics.Color
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.provider.CallLog
import android.provider.ContactsContract
import android.provider.Settings import android.provider.Settings
import android.util.Log
import android.view.WindowInsets import android.view.WindowInsets
import android.view.WindowManager import android.view.WindowManager
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
@ -44,11 +47,12 @@ import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.color.DynamicColors import com.google.android.material.color.DynamicColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import rasel.lunar.launcher.apps.AppDrawer import rasel.lunar.launcher.apps.AppDrawer
import rasel.lunar.launcher.apps.DismissCalback
import rasel.lunar.launcher.apps.SearchMenu
import rasel.lunar.launcher.databinding.LauncherActivityBinding import rasel.lunar.launcher.databinding.LauncherActivityBinding
import rasel.lunar.launcher.feeds.Feeds import rasel.lunar.launcher.feeds.Feeds
import rasel.lunar.launcher.feeds.WidgetHost import rasel.lunar.launcher.feeds.WidgetHost
import rasel.lunar.launcher.helpers.Constants.Companion.KEY_APPLICATION_THEME import rasel.lunar.launcher.helpers.Constants.Companion.KEY_APPLICATION_THEME
import rasel.lunar.launcher.helpers.Constants.Companion.KEY_BACK_HOME
import rasel.lunar.launcher.helpers.Constants.Companion.KEY_FIRST_LAUNCH import rasel.lunar.launcher.helpers.Constants.Companion.KEY_FIRST_LAUNCH
import rasel.lunar.launcher.helpers.Constants.Companion.KEY_STATUS_BAR import rasel.lunar.launcher.helpers.Constants.Companion.KEY_STATUS_BAR
import rasel.lunar.launcher.helpers.Constants.Companion.KEY_WINDOW_BACKGROUND import rasel.lunar.launcher.helpers.Constants.Companion.KEY_WINDOW_BACKGROUND
@ -59,6 +63,7 @@ import rasel.lunar.launcher.helpers.UniUtils.Companion.getColorResId
import rasel.lunar.launcher.helpers.ViewPagerAdapter import rasel.lunar.launcher.helpers.ViewPagerAdapter
import rasel.lunar.launcher.home.LauncherHome import rasel.lunar.launcher.home.LauncherHome
import rasel.lunar.launcher.utils.BLog import rasel.lunar.launcher.utils.BLog
import java.util.Date
internal class LauncherActivity : AppCompatActivity() { internal class LauncherActivity : AppCompatActivity() {
@ -152,7 +157,10 @@ internal class LauncherActivity : AppCompatActivity() {
private fun setupView() { private fun setupView() {
viewPager = binding.viewPager.apply { viewPager = binding.viewPager.apply {
adapter = ViewPagerAdapter( adapter = ViewPagerAdapter(
supportFragmentManager, mutableListOf(Feeds(), LauncherHome(), AppDrawer()), lifecycle) supportFragmentManager,
mutableListOf(Feeds(), LauncherHome(), AppDrawer()),
lifecycle
)
offscreenPageLimit = 1 offscreenPageLimit = 1
setCurrentItem(1, false) setCurrentItem(1, false)
reduceDragSensitivity() reduceDragSensitivity()
@ -227,4 +235,9 @@ internal class LauncherActivity : AppCompatActivity() {
} }
} }
fun openSearchMenus(keyword : String, dismissCalback: DismissCalback) {
SearchMenu().show(supportFragmentManager,keyword) {dismissCalback?.invoke()}
}
} }

View File

@ -28,9 +28,11 @@ import android.content.pm.ResolveInfo
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.provider.ContactsContract import android.provider.ContactsContract
import android.util.Log import android.util.Log
import android.view.Gravity import android.view.KeyEvent.ACTION_UP
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.View.GONE import android.view.View.GONE
@ -42,16 +44,12 @@ import androidx.core.widget.doOnTextChanged
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch 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.databinding.AppDrawerBinding import rasel.lunar.launcher.databinding.AppDrawerBinding
import rasel.lunar.launcher.helpers.Constants.Companion.KEY_APPS_COUNT import rasel.lunar.launcher.helpers.Constants.Companion.KEY_APPS_COUNT
import rasel.lunar.launcher.helpers.Constants.Companion.KEY_APPS_LAYOUT import rasel.lunar.launcher.helpers.Constants.Companion.KEY_APPS_LAYOUT
import rasel.lunar.launcher.helpers.Constants.Companion.KEY_DRAW_ALIGN
import rasel.lunar.launcher.helpers.Constants.Companion.KEY_KEYBOARD_SEARCH
import rasel.lunar.launcher.helpers.Constants.Companion.KEY_QUICK_LAUNCH
import rasel.lunar.launcher.helpers.Constants.Companion.PREFS_APP_NAMES import rasel.lunar.launcher.helpers.Constants.Companion.PREFS_APP_NAMES
import rasel.lunar.launcher.helpers.Constants.Companion.PREFS_SETTINGS import rasel.lunar.launcher.helpers.Constants.Companion.PREFS_SETTINGS
import rasel.lunar.launcher.utils.BLog import rasel.lunar.launcher.utils.BLog
@ -63,7 +61,7 @@ internal class AppDrawer : Fragment() {
private lateinit var binding: AppDrawerBinding private lateinit var binding: AppDrawerBinding
private var layoutType: Int = 0 private var layoutType: Int = 0
private var isSearchShown: Boolean = false // private var isSearchShown: Boolean = false
private var isKeyboardShowing: Boolean = false private var isKeyboardShowing: Boolean = false
companion object { companion object {
@ -226,6 +224,7 @@ internal class AppDrawer : Fragment() {
} }
fun openSearchApps(schemeString : String, pakage : String? = null) { fun openSearchApps(schemeString : String, pakage : String? = null) {
val gmmIntentUri = Uri.parse(schemeString) val gmmIntentUri = Uri.parse(schemeString)
val mapIntent = Intent(Intent.ACTION_VIEW, gmmIntentUri) val mapIntent = Intent(Intent.ACTION_VIEW, gmmIntentUri)
@ -238,52 +237,158 @@ internal class AppDrawer : Fragment() {
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
binding.reset.setOnClickListener { filterAppsList("") }
// binding.moveDown.setOnClickListener {
// binding.appsList.smoothScrollToPosition(packageList.size - 1)
// }
//
// binding.moveUp.setOnClickListener {
// binding.appsList.smoothScrollToPosition(0)
// }
// binding.search.setOnClickListener {
// when (isSearchShown) {
// true -> closeSearch()
// false -> openSearch()
// }
// }
binding.searchInput.setOnKeyListener { v, keyCode, event -> binding.searchInput.setOnKeyListener { v, keyCode, event ->
if (keyCode == 66 && contactList.size < 1 && packageList.size < 1) { if (keyCode == 66 && contactList.size < 1 && packageList.size < 1) {
binding.searchGoogle.performClick() binding.searchGoogle.performClick()
true true
}else { }else {
false false
} }
} }
binding.searchInput.doOnTextChanged { inputText, _, _, _ -> binding.searchInput.doOnTextChanged{ inputText, _, _, _ ->
binding.searchInput.text?.let { binding.searchInput.setSelection(it.length) } binding.searchInput.text?.let { binding.searchInput.setSelection(it.length) }
filterAppsList(inputText.toString()) filterAppsList(inputText.toString())
} }
} }
fun checkResult(keyword: String) {
if(!isAdded || !isResumed || keyword.length < 1) return
var dialog : AlertDialog.Builder? = null
var filted = packageList.filter { it.appName.equals(keyword) }
BLog.LOGE("filted >> ${filted.size}")
var filtedContact = contactList.filter { it.name.equals(keyword) }
BLog.LOGE("filtedContact >> ${filtedContact.size}")
if (keyword.length > 0 && (packageList.size == 1 || filted.size > 0)) {
dialog = AlertDialog.Builder(requireContext())
dialog?.setTitle("앱 실행 확인")
if (packageList.size == 1) {
dialog?.setMessage("${keyword} 검색 결과 '${packageList[0].appName}' 준비됨")
dialog?.setPositiveButton("실행") { s, d ->
runonUi {
startActivity(packageManager?.getLaunchIntentForPackage(packageList[0].packageName))
s.dismiss()
}
}
} else if (filted.size > 0) {
dialog?.setMessage("${keyword} 검색 결과 '${filted[0].appName}' 준비됨")
dialog?.setPositiveButton("${filted[0].appName} 실행") { s, d ->
runonUi {
startActivity(packageManager?.getLaunchIntentForPackage(filted[0].packageName))
s.dismiss()
}
}
if(filted.size > 1) {
dialog?.setNeutralButton("${filted[1].appName} 실행") { s, d ->
runonUi {
startActivity(packageManager?.getLaunchIntentForPackage(filted[1].packageName))
s.dismiss()
}
}
}
}
dialog?.setCancelable(false)
dialog?.setNegativeButton("취소") { s, d ->
runonUi { s.dismiss() }
}
dialog?.setOnDismissListener { registCancelSearch() }
dialog?.show()
} else if (contactList.size == 1 || filtedContact.size > 0) {
dialog = AlertDialog.Builder(requireContext())
dialog?.setTitle("연락처 실행 확인")
dialog?.setCancelable(false)
dialog?.setNegativeButton("취소") { s, d ->
runonUi { s.dismiss() }
}
if (contactList.size == 1) {
dialog?.setMessage("${keyword} 검색 결과 '${contactList[0].name}' 준비됨")
dialog?.setPositiveButton("자세히 보기") { s, d ->
runonUi {
ContactMenu().show(childFragmentManager, contactList[0].id.toString())
s.dismiss()
}
}
} else if(filtedContact.size > 0) {
dialog?.setMessage("${keyword} 검색 결과 '${filtedContact[0].name}' 준비됨")
dialog?.setPositiveButton("'${filtedContact[0].name},\n${filtedContact[0].phoneNumber}'\n자세히 보기") { s, d ->
runonUi {
ContactMenu().show(childFragmentManager, filtedContact[0].id.toString())
s.dismiss()
}
}
if (filtedContact.size > 1) {
dialog?.setNeutralButton("'${filtedContact[1].name},\n${filtedContact[1].phoneNumber}'\n자세히 보기") { s, d ->
runonUi {
ContactMenu().show(childFragmentManager, filtedContact[1].id.toString())
s.dismiss()
}
}
}
}
dialog?.setOnDismissListener { registCancelSearch() }
dialog?.show()
} else {
lActivity?.openSearchMenus(keyword) {
registCancelSearch()
}
}
}
fun runonUi(invoke : () -> Unit) {
Handler(Looper.getMainLooper()).run {
try {
invoke.invoke()
}catch (e : Exception) {
e.printStackTrace()
}
}
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
BLog.LOGE("onResume")
fetchApps() fetchApps()
GetContact() GetContact()
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
if (settingsPrefs!!.getInt(KEY_APPS_LAYOUT, 0) in 0..1) { // if (settingsPrefs!!.getInt(KEY_APPS_LAYOUT, 0) in 0..1) {
appsAdapter?.updateGravity(settingsPrefs!!.getInt(KEY_DRAW_ALIGN, Gravity.CENTER)) // appsAdapter?.updateGravity(settingsPrefs!!.getInt(KEY_DRAW_ALIGN, Gravity.CENTER))
} // }
contactAdapter?.updateData(contactList) contactAdapter?.updateData(contactList)
openSearch() openSearch()
setKeyboardPadding()
contactAdapter?.updateData(contactList)
/* pop up the keyboard */
openSearch()
registCancelSearch()
BLog.LOGE("onResume after chechHandler.postDelayed(cancelSearch, 3000L)")
}
val chechHandler = Handler(Looper.getMainLooper())
val cancelSearch = Runnable { timerCheck() }
fun registCancelSearch() {
BLog.LOGE("Called registCancelSearch")
chechHandler.removeCallbacks(cancelSearch)
chechHandler.postDelayed(cancelSearch, 3000L)
}
fun clearCancelSearch() {
chechHandler.removeCallbacks(cancelSearch)
}
private fun timerCheck() {
lActivity?.onBackPressed()
} }
override fun onPause() { override fun onPause() {
@ -308,62 +413,30 @@ internal class AppDrawer : Fragment() {
/* update app list with app and package name */ /* update app list with app and package name */
fun fetchApps() { fun fetchApps() {
if (oringinPackageList.size > 0) { packageList.clear()
packageList.clear() oringinPackageList.clear()
for(pkg in oringinPackageList) { GlobalScope.launch {
packageList.add(pkg) packageInfoList = (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
} packageManager?.queryIntentActivities(
} else { Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LAUNCHER),
packageList.clear() PackageManager.ResolveInfoFlags.of(0)
oringinPackageList.clear() )
GlobalScope.launch { } else {
packageInfoList = (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { (packageManager?.queryIntentActivities(
packageManager?.queryIntentActivities( Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LAUNCHER), 0
Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LAUNCHER), ))
PackageManager.ResolveInfoFlags.of(0) })?.apply {
) removeIf { it.activityInfo.packageName.equals(BuildConfig.APPLICATION_ID) }
} else { forEach { oringinPackageList.add(Packages(it.activityInfo.packageName, normalize(appName(it)), getCategory(it.activityInfo.applicationInfo.category))) }
(packageManager?.queryIntentActivities( oringinPackageList.sortBy { it.appName }
Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LAUNCHER), 0 packageList.addAll(
)) oringinPackageList
})?.apply { )
removeIf { it.activityInfo.packageName.equals(BuildConfig.APPLICATION_ID) } binding.appsList.post { if (packageList.size > 0) {
appsAdapter?.updateData(packageList)
forEach { } }
oringinPackageList.add( }!!
Packages(
it.activityInfo.packageName,
appName(it),
getCategory(it.activityInfo.applicationInfo.category)
)
)
packageList.add(
Packages(
it.activityInfo.packageName,
appName(it),
getCategory(it.activityInfo.applicationInfo.category)
)
)
}
}!!
}
/* add package and app names to the list */
// var edit = appNamesPrefs?.edit()
// for (resolver in packageInfoList) {
// packageList.add(Packages(resolver.activityInfo.packageName, appName(resolver)))
// }
} }
// when {
// packageList.size < 1 -> return
// else -> {
if (packageList.size > 0) {
MainScope().launch {
appsAdapter?.updateData(packageList)
}
}
// }
} }
private fun getAlphabetItems() { private fun getAlphabetItems() {
@ -395,7 +468,7 @@ internal class AppDrawer : Fragment() {
var lastSearchString : String = "" 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 */
if (lastSearchStringLength > 0 && (lastSearchStringLength != searchString.length || lastSearchString.equals(searchString) == false)) { if (searchString.length > 0 && (lastSearchStringLength != searchString.length || lastSearchString.equals(searchString) == false)) {
BLog.LOGE("START FILTER") BLog.LOGE("START FILTER")
packageList.clear() packageList.clear()
for (pkg in oringinPackageList) { for (pkg in oringinPackageList) {
@ -404,38 +477,22 @@ internal class AppDrawer : Fragment() {
packageList.add(pkg) packageList.add(pkg)
} }
} }
packageList.sortBy { it.appName }
BLog.LOGE("MIDDLE FILTER") BLog.LOGE("MIDDLE FILTER")
if (searchString.length > 2 && packageList.size == 1 && settingsPrefs!!.getBoolean(
KEY_QUICK_LAUNCH, appsAdapter?.updateData(packageList)
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)
}
contactList.clear() contactList.clear()
for (item in originContactList) { for (item in originContactList) {
if (item.name.contains(searchString) || item.phoneNumber.contains(searchString)) { if (item.name.contains(searchString) || item.phoneNumber.contains(searchString)) {
contactList.add(item) contactList.add(item)
} }
} }
contactList.sortBy { it.name }
contactAdapter?.updateData(contactList) contactAdapter?.updateData(contactList)
BLog.LOGE("END FILTER") BLog.LOGE("END FILTER")
} else if(lastSearchStringLength == 0){ } else if(lastSearchStringLength == 0){
contactList.clear() contactList.clear()
for (item in originContactList) { for (item in originContactList) {
@ -447,9 +504,28 @@ internal class AppDrawer : Fragment() {
} }
appsAdapter?.updateData(packageList) appsAdapter?.updateData(packageList)
contactAdapter?.updateData(contactList) contactAdapter?.updateData(contactList)
} else {
afterClearSearch()
} }
lastSearchString = searchString lastSearchString = searchString
lastSearchStringLength = searchString.length lastSearchStringLength = searchString.length
registCancelSearch()
}
private fun afterClearSearch() {
contactList.clear()
packageList.clear()
for (item in originContactList) {
contactList.add(item)
}
for (resolver in oringinPackageList) {
packageList.add(resolver)
}
packageList.sortBy { it.appName }
contactList.sortBy { it.name }
appsAdapter?.updateData(packageList)
contactAdapter?.updateData(contactList)
} }
private fun normalize(str: String): String { private fun normalize(str: String): String {
@ -460,26 +536,21 @@ internal class AppDrawer : Fragment() {
} }
private fun openSearch() { private fun openSearch() {
isSearchShown = true
binding.searchInput.apply { binding.searchInput.apply {
visibility = VISIBLE visibility = VISIBLE
requestFocus() requestFocus()
let { let {
(lActivity!!.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager) (lActivity?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager)?.showSoftInput(it, InputMethodManager.SHOW_IMPLICIT)
.showSoftInput(it, InputMethodManager.SHOW_IMPLICIT)
} }
} }
} }
/* clear search string, hide keyboard and search box */ /* clear search string, hide keyboard and search box */
private fun closeSearch() { private fun closeSearch() {
isSearchShown = false
binding.searchInput.apply { binding.searchInput.apply {
text?.clear()
visibility = GONE
let { let {
(lActivity!!.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager) text?.clear()
.hideSoftInputFromWindow(it.windowToken, 0) (lActivity?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager)?.hideSoftInputFromWindow(it.windowToken, 0)
} }
} }
} }

View File

@ -47,7 +47,7 @@ internal class AppsAdapter(
private val appsCount: TextView) : RecyclerView.Adapter<AppsAdapter.AppsViewHolder>() { private val appsCount: TextView) : RecyclerView.Adapter<AppsAdapter.AppsViewHolder>() {
private var oldList = mutableListOf<Packages>() private var oldList = mutableListOf<Packages>()
private var appGravity: Int = Gravity.CENTER // private var appGravity: Int = Gravity.CENTER
companion object { companion object {
@JvmStatic var appsSize: Int? = null @JvmStatic var appsSize: Int? = null
@ -106,21 +106,6 @@ internal class AppsAdapter(
appsSize = it appsSize = it
} }
} }
/* update text gravity (alignment) */
@SuppressLint("RtlHardcoded", "NotifyDataSetChanged")
fun updateGravity(gravity: Int){
/* the first check is to avoid calling notifyDataSetChanged() everytime */
if (gravity != appGravity &&
(gravity == Gravity.LEFT || gravity == Gravity.CENTER || gravity == Gravity.RIGHT)) {
appGravity = gravity
notifyDataSetChanged()
}
}
fun hideItem(idx: Int) {
}
} }
data class Packages ( data class Packages (

View File

@ -70,7 +70,7 @@ internal class ContactAdapter (
holder.view.root.apply { holder.view.root.apply {
/* on click - open app */ /* on click - open app */
setOnClickListener { setOnClickListener {
context.startActivity(Intent(Intent.ACTION_DIAL, Uri.parse("tel://${item.phoneNumber}"))) ContactMenu().show(fragmentManager, item.id.toString())
} }
/* on long click - open app menu */ /* on long click - open app menu */

View File

@ -58,6 +58,7 @@ import rasel.lunar.launcher.apps.AppDrawer.Companion.appNamesPrefs
import rasel.lunar.launcher.databinding.ActivityBrowserDialogBinding import rasel.lunar.launcher.databinding.ActivityBrowserDialogBinding
import rasel.lunar.launcher.databinding.AppInfoDialogBinding import rasel.lunar.launcher.databinding.AppInfoDialogBinding
import rasel.lunar.launcher.databinding.AppMenuBinding import rasel.lunar.launcher.databinding.AppMenuBinding
import rasel.lunar.launcher.databinding.ContactMenuBinding
import rasel.lunar.launcher.helpers.Constants.Companion.KEY_APP_NO_ import rasel.lunar.launcher.helpers.Constants.Companion.KEY_APP_NO_
import rasel.lunar.launcher.helpers.Constants.Companion.MAX_FAVORITE_APPS import rasel.lunar.launcher.helpers.Constants.Companion.MAX_FAVORITE_APPS
import rasel.lunar.launcher.helpers.Constants.Companion.PREFS_FAVORITE_APPS import rasel.lunar.launcher.helpers.Constants.Companion.PREFS_FAVORITE_APPS
@ -74,14 +75,15 @@ import java.util.*
internal class ContactMenu : BottomSheetDialogFragment() { internal class ContactMenu : BottomSheetDialogFragment() {
private lateinit var binding: AppMenuBinding private lateinit var binding: ContactMenuBinding
private lateinit var packageName: String private lateinit var packageName: String
private lateinit var packageManager: PackageManager private lateinit var packageManager: PackageManager
private lateinit var appInfo: ApplicationInfo private lateinit var appInfo: ApplicationInfo
private lateinit var defAppName: String private lateinit var defAppName: String
var contactName : String = ""
var contactPhoneNumber : String = ""
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = AppMenuBinding.inflate(inflater, container, false) binding = ContactMenuBinding.inflate(inflater, container, false)
/* get package name from fragment's tag */ /* get package name from fragment's tag */
packageName = tag.toString() packageName = tag.toString()
@ -96,14 +98,12 @@ internal class ContactMenu : BottomSheetDialogFragment() {
val cursor = resolver.query(phoneUri, projection, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + packageName, null , null) val cursor = resolver.query(phoneUri, projection, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + packageName, null , null)
if (cursor != null) { if (cursor != null) {
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
// val idx =cursor.getColumnIndex(projection[0])
val nameIndex = cursor.getColumnIndex(projection[0]) val nameIndex = cursor.getColumnIndex(projection[0])
val numberIndex = cursor.getColumnIndex(projection[1]) val numberIndex = cursor.getColumnIndex(projection[1])
// var contactId = cursor.getInt(idx) contactName = cursor.getString(nameIndex)
val name = cursor.getString(nameIndex)
var number = cursor.getString(numberIndex) var number = cursor.getString(numberIndex)
number = number.replace("-", "") contactPhoneNumber = number.replace("-", "")
BLog.LOGE("GetContact", "이름 : $name 번호 : $number ") BLog.LOGE("GetContact", "이름 : $contactName 번호 : $contactPhoneNumber ")
} }
} }
// 데이터 계열은 반드시 닫아줘야 한다. // 데이터 계열은 반드시 닫아줘야 한다.
@ -124,8 +124,8 @@ internal class ContactMenu : BottomSheetDialogFragment() {
// copyToClipboard(requireContext(), packageName) // copyToClipboard(requireContext(), packageName)
// } // }
// //
// appName() appName()
// binding.detailedInfo.setOnClickListener { detailedInfo() } binding.detailedInfo.setOnClickListener { detailedInfo() }
// binding.activityBrowser.setOnClickListener { activityBrowser() } // binding.activityBrowser.setOnClickListener { activityBrowser() }
// binding.appStore.setOnClickListener { appStore() } // binding.appStore.setOnClickListener { appStore() }
// binding.appFreeform.setOnClickListener { freeform() } // binding.appFreeform.setOnClickListener { freeform() }
@ -165,7 +165,7 @@ internal class ContactMenu : BottomSheetDialogFragment() {
/* listen on clicks */ /* listen on clicks */
binding.favGroup.addOnButtonCheckedListener { _: MaterialButtonToggleGroup?, binding.favGroup.addOnButtonCheckedListener { _: MaterialButtonToggleGroup?,
checkedId: Int, isChecked: Boolean -> checkedId: Int, isChecked: Boolean ->
try { try {
if (checkedId == button.id) { if (checkedId == button.id) {
if (isChecked) { if (isChecked) {
@ -186,66 +186,17 @@ internal class ContactMenu : BottomSheetDialogFragment() {
} }
private fun appName() { private fun appName() {
binding.appName.setOnFocusChangeListener { _, hasFocus -> binding.appName.text = contactName
if (hasFocus) binding.appName.minWidth = resources.getDimensionPixelOffset(R.dimen.twoSeventySix) binding.phoneNumber.text = contactPhoneNumber
else {
(requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager)
.hideSoftInputFromWindow(binding.appName.windowToken, 0)
binding.appName.apply {
minWidth = resources.getDimensionPixelOffset(R.dimen.zero)
if (text!!.isBlank()) setText(defAppName)
else setText(text!!.trim())
if (text.toString() == defAppName) appNamesPrefs?.edit()!!.remove(packageName).apply()
else appNamesPrefs?.edit()!!.putString(packageName, text.toString()).apply()
(requireParentFragment() as AppDrawer).fetchApps()
}
}
}
binding.appName.setOnKeyListener { _, keyCode, event ->
if (event.action == KeyEvent.ACTION_DOWN) {
if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_BACK) {
binding.appName.clearFocus()
return@setOnKeyListener true
}
}
false
}
} }
/* detailed info dialog */ /* detailed info dialog */
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
private fun detailedInfo() { private fun detailedInfo() {
val dialogBinding = AppInfoDialogBinding.inflate(lActivity!!.layoutInflater) var intent = Intent(Intent.ACTION_VIEW);
MaterialAlertDialogBuilder(lActivity!!) intent.setData(Uri.parse(ContactsContract.Contacts.CONTENT_URI.toString() + "/" + packageName));
.setView(dialogBinding.root) startActivity(intent);
.setPositiveButton(android.R.string.cancel, null)
.show()
/* show app name */
dialogBinding.appName.text = packageManager.getApplicationLabel(appInfo)
/* get package info */
val packageInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
packageManager.getPackageInfo(packageName, PackageManager.PackageInfoFlags.of(0))
} else {
packageManager.getPackageInfo(packageName, 0)
}
/* show infos */
dialogBinding.mixed.text =
"${resources.getString(R.string.version)}: ${packageInfo.versionName} (${PackageInfoCompat.getLongVersionCode(packageInfo).toInt()})\n" +
"${resources.getString(R.string.sdk)}: ${appInfo.minSdkVersion} ~ ${appInfo.targetSdkVersion}\n" +
"${resources.getString(R.string.uid)}: ${appInfo.uid}\n" +
"${resources.getString(R.string.first_install)}: ${dateTimeFormat(packageInfo.firstInstallTime)}\n" +
"${resources.getString(R.string.last_update)}: ${dateTimeFormat(packageInfo.lastUpdateTime)}"
/* show permissions */
dialogBinding.permissions.text = permissionsList
} }
/* activity browser dialog */ /* activity browser dialog */

View File

@ -0,0 +1,179 @@
/*
* 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.app.ActivityOptions
import android.content.ActivityNotFoundException
import android.content.ComponentName
import android.content.DialogInterface
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.res.ColorStateList
import android.graphics.Rect
import android.icu.text.SimpleDateFormat
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.ContactsContract
import android.provider.Settings
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Toast
import androidx.appcompat.widget.LinearLayoutCompat
import androidx.core.content.FileProvider
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTransaction
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.button.MaterialButton
import com.google.android.material.button.MaterialButtonToggleGroup
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import rasel.lunar.launcher.BuildConfig
import rasel.lunar.launcher.LauncherActivity.Companion.lActivity
import rasel.lunar.launcher.R
import rasel.lunar.launcher.databinding.ActivityBrowserDialogBinding
import rasel.lunar.launcher.databinding.ContactMenuBinding
import rasel.lunar.launcher.databinding.SearchMenuBinding
import rasel.lunar.launcher.helpers.Constants.Companion.KEY_APP_NO_
import rasel.lunar.launcher.helpers.Constants.Companion.MAX_FAVORITE_APPS
import rasel.lunar.launcher.helpers.Constants.Companion.PREFS_FAVORITE_APPS
import rasel.lunar.launcher.helpers.UniUtils.Companion.screenHeight
import rasel.lunar.launcher.helpers.UniUtils.Companion.screenWidth
import rasel.lunar.launcher.utils.BLog
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.util.*
internal class SearchMenu : BottomSheetDialogFragment() {
private lateinit var binding: SearchMenuBinding
private lateinit var searchWord: String
private lateinit var packageManager: PackageManager
private lateinit var appInfo: ApplicationInfo
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = SearchMenuBinding.inflate(inflater, container, false)
/* get package name from fragment's tag */
searchWord = tag.toString()
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
(requireDialog() as BottomSheetDialog).dismissWithAnimation = true
/* copy package name */
// binding.appPackage.setOnClickListener {
// copyToClipboard(requireContext(), packageName)
// }
//
appName()
// binding.detailedInfo.setOnClickListener { detailedInfo() }
// binding.activityBrowser.setOnClickListener { activityBrowser() }
// binding.appStore.setOnClickListener { appStore() }
// binding.appFreeform.setOnClickListener { freeform() }
// binding.appInfo.setOnClickListener { appInfo() }
// binding.appShare.setOnClickListener { share() }
// binding.appUninstall.setOnClickListener { uninstall() }
binding.searchNmap.setOnClickListener {
openSearchApps("nmap://search?query=${searchWord}&appname=${BuildConfig.APPLICATION_ID}","com.nhn.android.nmap")
}
binding.searchGoogleMap.setOnClickListener {
openSearchApps("geo:0,0?q=${searchWord}","com.google.android.apps.maps")
}
binding.searchGoogle.setOnClickListener {
openSearchApps("https://www.google.com/search?q=${searchWord}","com.android.chrome")
}
binding.searchTmap.setOnClickListener {
openSearchApps("tmap://search?name=${searchWord}","com.skt.tmap.ku")
}
binding.searchNaver.setOnClickListener {
openSearchApps("https://search.naver.com/search.naver?where=nexearch&query=${searchWord}", "com.nhn.android.search")
}
binding.searchDuckduckgo.setOnClickListener {
openSearchApps("https://duckduckgo.com/?t=h_&q=${searchWord}","com.duckduckgo.mobile.android")
}
binding.searchNamuwiki.setOnClickListener {
openSearchApps("https://namu.wiki/Search?q=${searchWord}")
}
binding.searchTranslate.setOnClickListener {
openSearchApps("https://translate.google.com/?hl=ko&sl=ko&tl=en&text=${searchWord}&op=translate","com.android.chrome")
}
binding.searchCoupang.setOnClickListener {
openSearchApps("coupang://search?q=${searchWord}","com.coupang.mobile")
}
}
fun openSearchApps(schemeString : String, pakage : String? = null) {
val gmmIntentUri = Uri.parse(schemeString)
val mapIntent = Intent(Intent.ACTION_VIEW, gmmIntentUri)
pakage?.let {
mapIntent.setPackage(pakage)
}
startActivity(mapIntent)
try {
dismiss()
} catch (e : Exception) {
e.printStackTrace()
}
}
private fun appName() {
binding.keyworkd.text = searchWord
}
var mDismissCalback : DismissCalback? = null
fun show(manager: FragmentManager, tag: String? , dismissCalback : DismissCalback?) {
this.mDismissCalback = dismissCalback
this.show(manager, tag)
}
override fun show(manager: FragmentManager, tag: String?) {
super.show(manager, tag)
}
override fun dismiss() {
BLog.LOGE("dismiss()")
mDismissCalback?.invoke()
super.dismiss()
}
override fun onDismiss(dialog: DialogInterface) {
BLog.LOGE("onDismiss(dialog: DialogInterface)")
mDismissCalback?.invoke()
super.onDismiss(dialog)
}
}
typealias DismissCalback = ()->Unit

View File

@ -67,7 +67,7 @@ internal class Constants {
const val KEY_LOCK_METHOD = "lock_method" const val KEY_LOCK_METHOD = "lock_method"
/* --- */ /* --- */
const val DEFAULT_DATE_FORMAT = "EEE dx MMM, yyyy" const val DEFAULT_DATE_FORMAT = "EEE, dd, MM, yyyy"
const val DEFAULT_ICON_SIZE = 44 const val DEFAULT_ICON_SIZE = 44
const val DEFAULT_ICON_PACK = "default_icon_pack" const val DEFAULT_ICON_PACK = "default_icon_pack"
const val DEFAULT_GRID_COLUMNS = 4 const val DEFAULT_GRID_COLUMNS = 4

View File

@ -25,14 +25,23 @@ import android.content.IntentFilter
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.Bundle import android.os.Bundle
import android.provider.AlarmClock import android.provider.AlarmClock
import android.provider.CallLog
import android.text.format.DateFormat import android.text.format.DateFormat
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.RadioButton
import android.widget.Toast import android.widget.Toast
import androidx.biometric.BiometricPrompt import androidx.biometric.BiometricPrompt
import androidx.core.view.get
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
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.databinding.LauncherHomeBinding import rasel.lunar.launcher.databinding.LauncherHomeBinding
@ -51,10 +60,12 @@ import rasel.lunar.launcher.helpers.UniUtils.Companion.lockMethod
import rasel.lunar.launcher.home.weather.WeatherExecutor import rasel.lunar.launcher.home.weather.WeatherExecutor
import rasel.lunar.launcher.qaccess.QuickAccess import rasel.lunar.launcher.qaccess.QuickAccess
import rasel.lunar.launcher.settings.SettingsActivity import rasel.lunar.launcher.settings.SettingsActivity
import rasel.lunar.launcher.todos.MissedCallsAdapter
import rasel.lunar.launcher.todos.TodoAdapter import rasel.lunar.launcher.todos.TodoAdapter
import rasel.lunar.launcher.todos.TodoManager import rasel.lunar.launcher.todos.TodoManager
import rasel.lunar.launcher.utils.BLog import rasel.lunar.launcher.utils.BLog
import rasel.lunar.launcher.utils.SimpleFingerGestures import rasel.lunar.launcher.utils.SimpleFingerGestures
import java.text.SimpleDateFormat
import java.util.* import java.util.*
@ -74,7 +85,9 @@ internal class LauncherHome : Fragment() {
batteryReceiver = BatteryReceiver(binding.batteryProgress) batteryReceiver = BatteryReceiver(binding.batteryProgress)
binding.favAppsGroup.visibility = View.GONE binding.favAppsGroup.visibility = View.GONE
Thread("CALLED").run {
getCallDetails()
}
return binding.root return binding.root
} }
@ -105,6 +118,8 @@ internal class LauncherHome : Fragment() {
requireContext().registerReceiver(batteryReceiver, IntentFilter(Intent.ACTION_BATTERY_CHANGED)) requireContext().registerReceiver(batteryReceiver, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
/* time and date */ /* time and date */
binding.time.textLocale = Locale.US
binding.date.textLocale = Locale.US
if (DateFormat.is24HourFormat(requireContext())) { if (DateFormat.is24HourFormat(requireContext())) {
binding.time.format24Hour = timeFormat binding.time.format24Hour = timeFormat
binding.date.format24Hour = dateFormat binding.date.format24Hour = dateFormat
@ -120,6 +135,79 @@ internal class LauncherHome : Fragment() {
} }
} }
var callList = arrayListOf<MissedCall>()
private fun getCallDetails() {
var dateParam = Date(System.currentTimeMillis() - (1000 * 60 * 60 * 24 * 3)).time.toString()
val managedCursor = lActivity!!.managedQuery(
CallLog.Calls.CONTENT_URI, arrayOf(
CallLog.Calls.NUMBER,
CallLog.Calls.TYPE,
CallLog.Calls.DATE,
CallLog.Calls.DURATION,
CallLog.Calls.CACHED_NAME,
), CallLog.Calls.DATE + "> ? AND " + CallLog.Calls.TYPE + " > ?", arrayOf<String>(dateParam, "2"), CallLog.Calls.DATE + " desc")
val number = managedCursor.getColumnIndex(CallLog.Calls.NUMBER)
val type = managedCursor.getColumnIndex(CallLog.Calls.TYPE)
val date = managedCursor.getColumnIndex(CallLog.Calls.DATE)
val duration = managedCursor.getColumnIndex(CallLog.Calls.DURATION)
val name = managedCursor.getColumnIndex(CallLog.Calls.CACHED_NAME)
var missedCalls = hashMapOf<String, MissedCall>()
if (missedCalls.size != callList.size) {
while (managedCursor.moveToNext()) {
val phNumber = managedCursor.getString(number) // mobile number
val callType = managedCursor.getString(type) // call type
val callDate = managedCursor.getString(date) // call date
val callDayTime: Date = Date(callDate.toLong())
val callDuration = managedCursor.getString(duration)
val callerName = managedCursor.getString(name)
var dir: String = ""
val dircode = callType.toInt()
when (dircode) {
CallLog.Calls.INCOMING_TYPE -> {
dir = "INCOMING_TYPE"
}
CallLog.Calls.OUTGOING_TYPE -> {
dir = "OUTGOING_TYPE"
}
CallLog.Calls.MISSED_TYPE -> {
dir = "MISSED_TYPE"
}
CallLog.Calls.VOICEMAIL_TYPE -> {
dir = "VOICEMAIL_TYPE"
}
CallLog.Calls.REJECTED_TYPE -> {
dir = "REJECTED_TYPE"
}
CallLog.Calls.BLOCKED_TYPE -> {
dir = "BLOCKED_TYPE"
}
CallLog.Calls.ANSWERED_EXTERNALLY_TYPE -> {
dir = "ANSWERED_EXTERNALLY_TYPE"
}
}
var missed: MissedCall = if (missedCalls.containsKey(phNumber)) {
missedCalls.get(phNumber)!!.apply {
count = count + 1
}
} else {
MissedCall(1,callerName,phNumber, dircode, dir, SimpleDateFormat("yyy/MM/dd-HH:mm:ss").format(callDayTime))
}
missedCalls.put(phNumber, missed)
}
}
managedCursor.close()
if (callList.size == missedCalls.size) {
} else {
callList.clear()
missedCalls.forEach { t, u ->
callList.add(u)
}
}
}
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
/* unregister battery changes */ /* unregister battery changes */
@ -380,8 +468,18 @@ internal class LauncherHome : Fragment() {
} }
/* to-do list */ /* to-do list */
@SuppressLint("NotifyDataSetChanged")
private fun showTodoList() { private fun showTodoList() {
binding.notes.adapter = TodoAdapter(null, requireContext()) if (binding.missedCalls.isChecked == true) {
if (callList.size > 0) {
BLog.LOGE("callList >>> ${callList.size}")
binding.notes.adapter = MissedCallsAdapter(callList, requireContext())?.apply {
this.notifyDataSetChanged()
}
}
} else {
binding.notes.adapter = TodoAdapter(null, requireContext())
}
} }
/* get time format string */ /* get time format string */
@ -390,9 +488,9 @@ internal class LauncherHome : Fragment() {
0 -> return if (DateFormat.is24HourFormat(requireContext())) { 0 -> return if (DateFormat.is24HourFormat(requireContext())) {
"kk:mm" "kk:mm"
} else { } else {
"h:mm a" "HH:mm a"
} }
1 -> return "h:mm a" 1 -> return "HH:mm a"
2 -> return "kk:mm" 2 -> return "kk:mm"
} }
return null return null
@ -420,3 +518,22 @@ internal class LauncherHome : Fragment() {
} }
} }
class MissedCall {
constructor(count: Int, name: String?, number: String, type: Int, typeString: String, date : String) {
this.count = count
this.name = name ?: "unknown"
this.number = number
this.type = type
this.typeString = typeString
this.date = date
}
var count : Int = 1
var name : String = "how"
var number : String = ""
var type : Int = 0
var typeString : String = ""
var date : String = ""
}

View File

@ -0,0 +1,113 @@
/*
* 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.todos
import android.annotation.SuppressLint
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetDialog
import rasel.lunar.launcher.LauncherActivity.Companion.lActivity
import rasel.lunar.launcher.R
import rasel.lunar.launcher.databinding.ListItemBinding
import rasel.lunar.launcher.databinding.TodoDialogBinding
import rasel.lunar.launcher.helpers.UniUtils.Companion.copyToClipboard
import rasel.lunar.launcher.home.MissedCall
import rasel.lunar.launcher.utils.BLog
import java.util.*
import kotlin.collections.ArrayList
internal class MissedCallsAdapter(
private val callList: ArrayList<MissedCall>,
private val context: Context) : RecyclerView.Adapter<MissedCallsAdapter.MissedCallsHolder>() {
private val currentFragment = lActivity!!.supportFragmentManager.findFragmentById(R.id.mainFragmentsContainer)
override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): MissedCallsHolder {
val binding = ListItemBinding.inflate(LayoutInflater.from(viewGroup.context), viewGroup, false)
return MissedCallsHolder(binding)
}
override fun getItemCount(): Int {
// BLog.LOGE("callList.size >>> ${callList.size}")
return callList.size
}
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: MissedCallsHolder, position: Int) {
val todo = callList[position]
BLog.LOGE("callList >>> ${callList[position]}")
holder.view.itemText.text = "\u25CF ${if(todo.name.equals("unknown")) todo.number else { todo.name}} , ${todo.typeString} : ${todo.count} : ${todo.date}"
/* multiline texts are enabled for TodoManager */
holder.view.itemText.isSingleLine = false
/* launch edit or update dialog on item click */
holder.view.itemText.setOnClickListener { updateDialog(position) }
/* copy texts on long click */
holder.view.itemText.setOnLongClickListener {
copyToClipboard(context, todo.name)
true
}
}
inner class MissedCallsHolder(var view: ListItemBinding) : RecyclerView.ViewHolder(view.root)
/* update dialog */
private fun updateDialog(position: Int) {
val bottomSheetDialog = BottomSheetDialog(lActivity!!, R.style.BottomSheetDialog)
val dialogBinding = TodoDialogBinding.inflate(LayoutInflater.from(context))
bottomSheetDialog.setContentView(dialogBinding.root)
bottomSheetDialog.show()
bottomSheetDialog.dismissWithAnimation = true
val databaseHandler = DatabaseHandler(context)
val todo = databaseHandler.todos[position]
dialogBinding.apply {
deleteAllConfirmation.visibility = View.GONE
todoInput.setText(todo.name)
todoCancel.text = context.getString(R.string.delete)
todoCancel.setTextColor(ContextCompat.getColor(context, android.R.color.holo_red_light))
todoOk.text = context.getString(R.string.update)
}
/* delete the item */
dialogBinding.todoCancel.setOnClickListener {
}
/* update the item */
dialogBinding.todoOk.setOnClickListener {
val updatedTodoString = Objects.requireNonNull(dialogBinding.todoInput.text).toString().trim { it <= ' ' }
if (updatedTodoString.isNotEmpty()) {
todo.name = updatedTodoString
databaseHandler.updateTodo(todo)
bottomSheetDialog.dismiss()
} else {
dialogBinding.todoInput.error = context.getString(R.string.empty_text_field)
}
}
}
}

View File

@ -5,7 +5,7 @@ import rasel.lunar.launcher.BuildConfig
import java.lang.Exception import java.lang.Exception
object BLog { object BLog {
val DEFAULT_TAG = "MyEBook_TAG" val DEFAULT_TAG = "Lunatic"
enum class BLogType { enum class BLogType {
D,I,E D,I,E
} }

View File

@ -38,6 +38,7 @@ class CircleImageView : androidx.appcompat.widget.AppCompatImageView {
private val mShaderMatrix: Matrix = Matrix() private val mShaderMatrix: Matrix = Matrix()
private val mBitmapPaint: Paint = Paint() private val mBitmapPaint: Paint = Paint()
private val mBorderPaint: Paint = Paint() private val mBorderPaint: Paint = Paint()
private val mLabelPaint: Paint = Paint()
private val mCircleBackgroundPaint: Paint = Paint() private val mCircleBackgroundPaint: Paint = Paint()
private var mBorderColor = DEFAULT_BORDER_COLOR private var mBorderColor = DEFAULT_BORDER_COLOR
@ -60,6 +61,7 @@ class CircleImageView : androidx.appcompat.widget.AppCompatImageView {
private var mBorderOverlay = false private var mBorderOverlay = false
private var mDisableCircularTransformation = false private var mDisableCircularTransformation = false
private var label : String = ""
constructor(context: Context) : super(context) { constructor(context: Context) : super(context) {
init() init()
} }
@ -85,6 +87,8 @@ class CircleImageView : androidx.appcompat.widget.AppCompatImageView {
DEFAULT_CIRCLE_BACKGROUND_COLOR DEFAULT_CIRCLE_BACKGROUND_COLOR
) )
label = a.getString(R.styleable.CircleImageView_civ_label) ?: ""
a.recycle() a.recycle()
init() init()
@ -110,6 +114,9 @@ class CircleImageView : androidx.appcompat.widget.AppCompatImageView {
mCircleBackgroundPaint.setAntiAlias(true) mCircleBackgroundPaint.setAntiAlias(true)
mCircleBackgroundPaint.setColor(mCircleBackgroundColor) mCircleBackgroundPaint.setColor(mCircleBackgroundColor)
mLabelPaint.color = Color.WHITE
mLabelPaint.isFakeBoldText = true
mLabelPaint.textAlign = Paint.Align.CENTER
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
outlineProvider = OutlineProvider() outlineProvider = OutlineProvider()
} }
@ -175,6 +182,10 @@ class CircleImageView : androidx.appcompat.widget.AppCompatImageView {
mBorderPaint mBorderPaint
) )
} }
if (label.length > 0) {
canvas.drawText(label.toUpperCase(),mBorderRect.centerX(),mBorderRect.height() * 0.98f, mLabelPaint)
}
} }
override fun invalidateDrawable(@NonNull dr: Drawable) { override fun invalidateDrawable(@NonNull dr: Drawable) {
@ -185,6 +196,7 @@ class CircleImageView : androidx.appcompat.widget.AppCompatImageView {
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh) super.onSizeChanged(w, h, oldw, oldh)
updateDimensions() updateDimensions()
mLabelPaint.textSize = h * 0.2f
invalidate() invalidate()
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -110,6 +110,7 @@
style="@style/SearchIcons" style="@style/SearchIcons"
android:id="@+id/search_store"/> android:id="@+id/search_store"/>
</LinearLayout> </LinearLayout>
<TextView <TextView
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"

View File

@ -0,0 +1,144 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:padding="@dimen/twelve"
android:clickable="true"
android:focusableInTouchMode="true">
<TextView
android:id="@+id/appName"
app:layout_constraintTop_toTopOf="parent"
android:layout_width="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_height="wrap_content"
android:minWidth="@dimen/zero"
android:gravity="center"
android:padding="@dimen/eight"
android:inputType="textNoSuggestions"
/>
<TextView
android:id="@+id/phoneNumber"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/eight"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appName" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/activityBrowser"
style="@style/Widget.Material3.ExtendedFloatingActionButton.Surface"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/eight"
android:contentDescription="@null"
android:src="@drawable/ic_activity"
android:tooltipText="@string/activity_browser"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/detailedInfo"
app:layout_constraintTop_toBottomOf="@+id/phoneNumber" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/detailedInfo"
style="@style/Widget.Material3.ExtendedFloatingActionButton.Surface"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/eight"
android:layout_marginEnd="@dimen/eight"
android:contentDescription="@null"
android:src="@drawable/ic_info"
android:tooltipText="@string/detailed_info"
app:layout_constraintEnd_toStartOf="@id/activityBrowser"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/phoneNumber" />
<com.google.android.material.button.MaterialButtonToggleGroup
android:id="@+id/favGroup"
android:layout_width="@dimen/zero"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/eight"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/detailedInfo"
app:singleSelection="true" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/appInfo"
style="@style/Widget.Material3.ExtendedFloatingActionButton.Surface"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/eight"
android:layout_marginEnd="@dimen/eight"
android:contentDescription="@null"
android:src="@drawable/ic_info2"
android:tooltipText="@string/app_info"
app:layout_constraintEnd_toStartOf="@id/appFreeform"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/favGroup" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/appFreeform"
style="@style/Widget.Material3.ExtendedFloatingActionButton.Surface"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/eight"
android:layout_marginEnd="@dimen/eight"
android:contentDescription="@null"
android:src="@drawable/ic_pip"
android:tooltipText="@string/freeform"
app:layout_constraintEnd_toStartOf="@id/appStore"
app:layout_constraintStart_toEndOf="@id/appInfo"
app:layout_constraintTop_toBottomOf="@+id/favGroup" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/appStore"
style="@style/Widget.Material3.ExtendedFloatingActionButton.Surface"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/eight"
android:layout_marginEnd="@dimen/eight"
android:contentDescription="@null"
android:src="@drawable/ic_store"
android:tooltipText="@string/app_store"
app:layout_constraintEnd_toStartOf="@id/appShare"
app:layout_constraintStart_toEndOf="@id/appFreeform"
app:layout_constraintTop_toBottomOf="@+id/favGroup" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/appShare"
style="@style/Widget.Material3.ExtendedFloatingActionButton.Surface"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/eight"
android:layout_marginEnd="@dimen/eight"
android:contentDescription="@null"
android:src="@drawable/ic_share"
android:tooltipText="@string/share"
app:layout_constraintEnd_toStartOf="@id/appUninstall"
app:layout_constraintStart_toEndOf="@id/appStore"
app:layout_constraintTop_toBottomOf="@+id/favGroup" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/appUninstall"
style="@style/Widget.Material3.ExtendedFloatingActionButton.Surface"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/eight"
android:contentDescription="@null"
android:src="@drawable/ic_delete"
android:tooltipText="@string/uninstall"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/appShare"
app:layout_constraintTop_toBottomOf="@+id/favGroup"
app:tint="@android:color/holo_red_light" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -2,6 +2,7 @@
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@ -29,11 +30,13 @@
android:textIsSelectable="false" android:textIsSelectable="false"
android:textSize="@dimen/clockText" android:textSize="@dimen/clockText"
android:textStyle="bold" android:textStyle="bold"
android:textLocale="en_US"
app:layout_constraintBottom_toBottomOf="@+id/batteryProgress" app:layout_constraintBottom_toBottomOf="@+id/batteryProgress"
app:layout_constraintEnd_toEndOf="@+id/batteryProgress" app:layout_constraintEnd_toEndOf="@+id/batteryProgress"
app:layout_constraintStart_toStartOf="@+id/batteryProgress" app:layout_constraintStart_toStartOf="@+id/batteryProgress"
app:layout_constraintTop_toTopOf="@+id/batteryProgress" app:layout_constraintTop_toTopOf="@+id/batteryProgress"
app:layout_constraintVertical_bias="0.450" /> app:layout_constraintVertical_bias="0.450"
tools:ignore="UnusedAttribute" />
<TextClock <TextClock
android:id="@+id/date" android:id="@+id/date"
@ -41,12 +44,14 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:gravity="center"
android:maxLines="1" android:maxLines="1"
android:textLocale="en_US"
android:textIsSelectable="false" android:textIsSelectable="false"
app:layout_constraintBottom_toBottomOf="@+id/batteryProgress" app:layout_constraintBottom_toBottomOf="@+id/batteryProgress"
app:layout_constraintEnd_toEndOf="@+id/batteryProgress" app:layout_constraintEnd_toEndOf="@+id/batteryProgress"
app:layout_constraintStart_toStartOf="@+id/batteryProgress" app:layout_constraintStart_toStartOf="@+id/batteryProgress"
app:layout_constraintTop_toBottomOf="@+id/time" app:layout_constraintTop_toBottomOf="@+id/time"
app:layout_constraintVertical_bias="0.075" /> app:layout_constraintVertical_bias="0.075"
tools:ignore="UnusedAttribute" />
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/weather" android:id="@+id/weather"
@ -61,18 +66,54 @@
app:layout_constraintTop_toBottomOf="@+id/date" app:layout_constraintTop_toBottomOf="@+id/date"
app:layout_constraintVertical_bias="0.100" /> app:layout_constraintVertical_bias="0.100" />
<RadioGroup
android:id="@+id/summaryChoose"
android:layout_width="0dp"
android:gravity="center"
app:layout_constraintLeft_toLeftOf="parent"
android:orientation="horizontal"
app:layout_constraintRight_toRightOf="parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/batteryProgress"
>
<RadioButton
android:id="@+id/missedCalls"
android:button="@null"
android:gravity="center"
android:text="전화"
android:checked="true"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<RadioButton
android:gravity="center"
android:button="@null"
android:text="투두"
android:checked="false"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<RadioButton
android:gravity="center"
android:button="@null"
android:text="문자"
android:checked="false"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RadioGroup>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/notes" android:id="@+id/notes"
android:layout_width="@dimen/zero" android:layout_width="@dimen/zero"
android:layout_height="@dimen/zero" android:layout_height="200dp"
android:overScrollMode="never" android:overScrollMode="never"
android:padding="@dimen/fortyEight" android:padding="20dp"
android:scrollbars="none" android:scrollbars="none"
app:layoutManager="LinearLayoutManager" app:layoutManager="LinearLayoutManager"
app:layout_constraintBottom_toTopOf="@id/favAppsGroup"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/batteryProgress" />
app:layout_constraintTop_toBottomOf="@+id/summaryChoose" />
<androidx.appcompat.widget.LinearLayoutCompat <androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/favAppsGroup" android:id="@+id/favAppsGroup"

View File

@ -7,7 +7,7 @@
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/itemText" android:id="@+id/itemText"
android:layout_width="@dimen/zero" android:layout_width="@dimen/zero"
android:layout_height="wrap_content" android:layout_height="40dp"
android:padding="@dimen/twelve" android:padding="@dimen/twelve"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:padding="@dimen/twelve"
android:clickable="true"
android:focusableInTouchMode="true">
<TextView
android:id="@+id/keyworkd"
app:layout_constraintTop_toTopOf="parent"
android:layout_width="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_height="wrap_content"
android:minWidth="@dimen/zero"
android:textSize="24dp"
android:gravity="center"
android:padding="@dimen/eight"
android:inputType="textNoSuggestions"
/>
<LinearLayout
style="@style/SearchMenus"
android:id="@+id/quickSearch"
android:orientation="horizontal"
app:layout_constraintTop_toBottomOf="@id/keyworkd">
<rasel.lunar.launcher.view.CircleImageView
style="@style/SearchMenuItem"
android:src="@drawable/google"
android:id="@+id/search_google"/>
<rasel.lunar.launcher.view.CircleImageView
style="@style/SearchMenuItem"
android:src="@drawable/naver"
android:id="@+id/search_naver"/>
<rasel.lunar.launcher.view.CircleImageView
style="@style/SearchMenuItem"
app:civ_label="DDuckGo"
android:src="@drawable/duckduckgo"
android:id="@+id/search_duckduckgo"/>
</LinearLayout>
<LinearLayout
android:id="@+id/quickSearch2"
android:orientation="horizontal"
app:layout_constraintTop_toBottomOf="@id/quickSearch"
style="@style/SearchMenus">
<rasel.lunar.launcher.view.CircleImageView
android:src="@drawable/namuwiki"
android:id="@+id/search_namuwiki"
style="@style/SearchMenuItem"/>
<rasel.lunar.launcher.view.CircleImageView
style="@style/SearchMenuItem"
android:src="@drawable/gmap"
android:id="@+id/search_googleMap"/>
<rasel.lunar.launcher.view.CircleImageView
style="@style/SearchMenuItem"
android:src="@drawable/navermap"
android:id="@+id/search_nmap"/>
</LinearLayout>
<LinearLayout
android:id="@+id/quickSearch3"
android:orientation="horizontal"
app:layout_constraintTop_toBottomOf="@id/quickSearch2"
style="@style/SearchMenus">
<rasel.lunar.launcher.view.CircleImageView
style="@style/SearchMenuItem"
android:src="@drawable/coupang"
android:id="@+id/search_coupang"/>
<rasel.lunar.launcher.view.CircleImageView
android:src="@drawable/tmap"
style="@style/SearchMenuItem"
android:id="@+id/search_tmap"/>
<rasel.lunar.launcher.view.CircleImageView
android:src="@drawable/translate"
style="@style/SearchMenuItem"
android:id="@+id/search_translate"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -5,5 +5,6 @@
<attr name="civ_border_color" format="color" /> <attr name="civ_border_color" format="color" />
<attr name="civ_border_overlay" format="boolean" /> <attr name="civ_border_overlay" format="boolean" />
<attr name="civ_circle_background_color" format="color" /> <attr name="civ_circle_background_color" format="color" />
<attr name="civ_label" format="string" />
</declare-styleable> </declare-styleable>
</resources> </resources>

View File

@ -37,4 +37,28 @@
<item name="civ_border_color">#000000</item> <item name="civ_border_color">#000000</item>
</style> </style>
<style name="SearchMenuItem">
<item name="android:layout_margin">5dp</item>
<item name="android:adjustViewBounds">true</item>
<item name="android:scaleType">fitCenter</item>
<item name="android:background">@null</item>
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_weight">1</item>
<item name="civ_border_width">1dp</item>
<item name="civ_border_color">#000000</item>
</style>
<style name="SearchMenus">
<item name="layout_constraintLeft_toLeftOf">parent</item>
<item name="layout_constraintRight_toRightOf">parent</item>
<item name="android:orientation">horizontal</item>
<item name="android:background">@null</item>
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">80dp</item>
</style>
</resources> </resources>