diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 0aebb899..026ec983 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -18,6 +18,7 @@
+
closeSearch()
-// false -> openSearch()
-// }
-// }
-
binding.searchInput.setOnKeyListener { v, keyCode, event ->
if (keyCode == 66 && contactList.size < 1 && packageList.size < 1) {
binding.searchGoogle.performClick()
- true
+ true
}else {
false
}
}
- binding.searchInput.doOnTextChanged { inputText, _, _, _ ->
+ binding.searchInput.doOnTextChanged{ inputText, _, _, _ ->
binding.searchInput.text?.let { binding.searchInput.setSelection(it.length) }
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() {
super.onResume()
+ BLog.LOGE("onResume")
fetchApps()
GetContact()
+
binding.appsCount.visibility = if (settingsPrefs!!.getBoolean(KEY_APPS_COUNT, true)) VISIBLE else GONE
- if (settingsPrefs!!.getInt(KEY_APPS_LAYOUT, 0) in 0..1) {
- appsAdapter?.updateGravity(settingsPrefs!!.getInt(KEY_DRAW_ALIGN, Gravity.CENTER))
- }
+// if (settingsPrefs!!.getInt(KEY_APPS_LAYOUT, 0) in 0..1) {
+// appsAdapter?.updateGravity(settingsPrefs!!.getInt(KEY_DRAW_ALIGN, Gravity.CENTER))
+// }
contactAdapter?.updateData(contactList)
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() {
@@ -308,62 +413,30 @@ internal class AppDrawer : Fragment() {
/* update app list with app and package name */
fun fetchApps() {
- if (oringinPackageList.size > 0) {
- packageList.clear()
- for(pkg in oringinPackageList) {
- packageList.add(pkg)
- }
- } else {
- packageList.clear()
- oringinPackageList.clear()
- GlobalScope.launch {
- packageInfoList = (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- packageManager?.queryIntentActivities(
- Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LAUNCHER),
- PackageManager.ResolveInfoFlags.of(0)
- )
- } else {
- (packageManager?.queryIntentActivities(
- Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LAUNCHER), 0
- ))
- })?.apply {
- removeIf { it.activityInfo.packageName.equals(BuildConfig.APPLICATION_ID) }
-
- 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)))
-// }
+ packageList.clear()
+ oringinPackageList.clear()
+ GlobalScope.launch {
+ packageInfoList = (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ packageManager?.queryIntentActivities(
+ Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LAUNCHER),
+ PackageManager.ResolveInfoFlags.of(0)
+ )
+ } else {
+ (packageManager?.queryIntentActivities(
+ Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LAUNCHER), 0
+ ))
+ })?.apply {
+ removeIf { it.activityInfo.packageName.equals(BuildConfig.APPLICATION_ID) }
+ forEach { oringinPackageList.add(Packages(it.activityInfo.packageName, normalize(appName(it)), getCategory(it.activityInfo.applicationInfo.category))) }
+ oringinPackageList.sortBy { it.appName }
+ packageList.addAll(
+ oringinPackageList
+ )
+ binding.appsList.post { if (packageList.size > 0) {
+ appsAdapter?.updateData(packageList)
+ } }
+ }!!
}
-// when {
-// packageList.size < 1 -> return
-// else -> {
- if (packageList.size > 0) {
- MainScope().launch {
- appsAdapter?.updateData(packageList)
- }
- }
-// }
-
}
private fun getAlphabetItems() {
@@ -395,7 +468,7 @@ internal class AppDrawer : Fragment() {
var lastSearchString : String = ""
private fun filterAppsList(searchString: 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")
packageList.clear()
for (pkg in oringinPackageList) {
@@ -404,38 +477,22 @@ internal class AppDrawer : Fragment() {
packageList.add(pkg)
}
}
+ packageList.sortBy { it.appName }
BLog.LOGE("MIDDLE FILTER")
- if (searchString.length > 2 && packageList.size == 1 && settingsPrefs!!.getBoolean(
- KEY_QUICK_LAUNCH,
- true
- )
- ) {
- var dialog = AlertDialog.Builder(requireContext())
- dialog.setTitle("앱 실행 확인")
- dialog.setMessage("${searchString} 검색 결과 '${packageList[0].appName}' 준비됨")
- dialog.setCancelable(false)
- dialog.setNegativeButton("취소") { s,d->
- binding.searchInput.setText("")
- s.dismiss()
- }
- dialog.setPositiveButton("실행") { s, d ->
- startActivity(packageManager?.getLaunchIntentForPackage(packageList[0].packageName))
- s.dismiss()
- binding.searchInput.setText("")
- }
- dialog.show()
- } else {
- appsAdapter?.updateData(packageList)
- }
+
+ appsAdapter?.updateData(packageList)
+
contactList.clear()
for (item in originContactList) {
if (item.name.contains(searchString) || item.phoneNumber.contains(searchString)) {
contactList.add(item)
}
}
+ contactList.sortBy { it.name }
contactAdapter?.updateData(contactList)
BLog.LOGE("END FILTER")
+
} else if(lastSearchStringLength == 0){
contactList.clear()
for (item in originContactList) {
@@ -447,9 +504,28 @@ internal class AppDrawer : Fragment() {
}
appsAdapter?.updateData(packageList)
contactAdapter?.updateData(contactList)
+ } else {
+ afterClearSearch()
+
}
lastSearchString = searchString
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 {
@@ -460,26 +536,21 @@ internal class AppDrawer : Fragment() {
}
private fun openSearch() {
- isSearchShown = true
binding.searchInput.apply {
visibility = VISIBLE
requestFocus()
let {
- (lActivity!!.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager)
- .showSoftInput(it, InputMethodManager.SHOW_IMPLICIT)
+ (lActivity?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager)?.showSoftInput(it, InputMethodManager.SHOW_IMPLICIT)
}
}
}
/* clear search string, hide keyboard and search box */
private fun closeSearch() {
- isSearchShown = false
binding.searchInput.apply {
- text?.clear()
- visibility = GONE
let {
- (lActivity!!.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager)
- .hideSoftInputFromWindow(it.windowToken, 0)
+ text?.clear()
+ (lActivity?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager)?.hideSoftInputFromWindow(it.windowToken, 0)
}
}
}
diff --git a/app/src/main/kotlin/rasel/lunar/launcher/apps/AppsAdapter.kt b/app/src/main/kotlin/rasel/lunar/launcher/apps/AppsAdapter.kt
index 17f48a20..e3795231 100644
--- a/app/src/main/kotlin/rasel/lunar/launcher/apps/AppsAdapter.kt
+++ b/app/src/main/kotlin/rasel/lunar/launcher/apps/AppsAdapter.kt
@@ -47,7 +47,7 @@ internal class AppsAdapter(
private val appsCount: TextView) : RecyclerView.Adapter() {
private var oldList = mutableListOf()
- private var appGravity: Int = Gravity.CENTER
+// private var appGravity: Int = Gravity.CENTER
companion object {
@JvmStatic var appsSize: Int? = null
@@ -106,21 +106,6 @@ internal class AppsAdapter(
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 (
diff --git a/app/src/main/kotlin/rasel/lunar/launcher/apps/ContactAdapter.kt b/app/src/main/kotlin/rasel/lunar/launcher/apps/ContactAdapter.kt
index ba93ddcd..37ac76c5 100644
--- a/app/src/main/kotlin/rasel/lunar/launcher/apps/ContactAdapter.kt
+++ b/app/src/main/kotlin/rasel/lunar/launcher/apps/ContactAdapter.kt
@@ -70,7 +70,7 @@ internal class ContactAdapter (
holder.view.root.apply {
/* on click - open app */
setOnClickListener {
- context.startActivity(Intent(Intent.ACTION_DIAL, Uri.parse("tel://${item.phoneNumber}")))
+ ContactMenu().show(fragmentManager, item.id.toString())
}
/* on long click - open app menu */
diff --git a/app/src/main/kotlin/rasel/lunar/launcher/apps/ContactMenu.kt b/app/src/main/kotlin/rasel/lunar/launcher/apps/ContactMenu.kt
index 233f46fd..8fd934a4 100644
--- a/app/src/main/kotlin/rasel/lunar/launcher/apps/ContactMenu.kt
+++ b/app/src/main/kotlin/rasel/lunar/launcher/apps/ContactMenu.kt
@@ -58,6 +58,7 @@ import rasel.lunar.launcher.apps.AppDrawer.Companion.appNamesPrefs
import rasel.lunar.launcher.databinding.ActivityBrowserDialogBinding
import rasel.lunar.launcher.databinding.AppInfoDialogBinding
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.MAX_FAVORITE_APPS
import rasel.lunar.launcher.helpers.Constants.Companion.PREFS_FAVORITE_APPS
@@ -74,14 +75,15 @@ import java.util.*
internal class ContactMenu : BottomSheetDialogFragment() {
- private lateinit var binding: AppMenuBinding
+ private lateinit var binding: ContactMenuBinding
private lateinit var packageName: String
private lateinit var packageManager: PackageManager
private lateinit var appInfo: ApplicationInfo
private lateinit var defAppName: String
-
+ var contactName : String = ""
+ var contactPhoneNumber : String = ""
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 */
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)
if (cursor != null) {
while (cursor.moveToNext()) {
-// val idx =cursor.getColumnIndex(projection[0])
val nameIndex = cursor.getColumnIndex(projection[0])
val numberIndex = cursor.getColumnIndex(projection[1])
-// var contactId = cursor.getInt(idx)
- val name = cursor.getString(nameIndex)
+ contactName = cursor.getString(nameIndex)
var number = cursor.getString(numberIndex)
- number = number.replace("-", "")
- BLog.LOGE("GetContact", "이름 : $name 번호 : $number ")
+ contactPhoneNumber = number.replace("-", "")
+ BLog.LOGE("GetContact", "이름 : $contactName 번호 : $contactPhoneNumber ")
}
}
// 데이터 계열은 반드시 닫아줘야 한다.
@@ -124,8 +124,8 @@ internal class ContactMenu : BottomSheetDialogFragment() {
// copyToClipboard(requireContext(), packageName)
// }
//
-// appName()
-// binding.detailedInfo.setOnClickListener { detailedInfo() }
+ appName()
+ binding.detailedInfo.setOnClickListener { detailedInfo() }
// binding.activityBrowser.setOnClickListener { activityBrowser() }
// binding.appStore.setOnClickListener { appStore() }
// binding.appFreeform.setOnClickListener { freeform() }
@@ -165,7 +165,7 @@ internal class ContactMenu : BottomSheetDialogFragment() {
/* listen on clicks */
binding.favGroup.addOnButtonCheckedListener { _: MaterialButtonToggleGroup?,
- checkedId: Int, isChecked: Boolean ->
+ checkedId: Int, isChecked: Boolean ->
try {
if (checkedId == button.id) {
if (isChecked) {
@@ -186,66 +186,17 @@ internal class ContactMenu : BottomSheetDialogFragment() {
}
private fun appName() {
- binding.appName.setOnFocusChangeListener { _, hasFocus ->
- if (hasFocus) binding.appName.minWidth = resources.getDimensionPixelOffset(R.dimen.twoSeventySix)
- 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
- }
+ binding.appName.text = contactName
+ binding.phoneNumber.text = contactPhoneNumber
}
/* detailed info dialog */
@SuppressLint("SetTextI18n")
private fun detailedInfo() {
- val dialogBinding = AppInfoDialogBinding.inflate(lActivity!!.layoutInflater)
- MaterialAlertDialogBuilder(lActivity!!)
- .setView(dialogBinding.root)
- .setPositiveButton(android.R.string.cancel, null)
- .show()
+ var intent = Intent(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse(ContactsContract.Contacts.CONTENT_URI.toString() + "/" + packageName));
+ startActivity(intent);
- /* 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 */
diff --git a/app/src/main/kotlin/rasel/lunar/launcher/apps/SearchMenu.kt b/app/src/main/kotlin/rasel/lunar/launcher/apps/SearchMenu.kt
new file mode 100644
index 00000000..4f85a98f
--- /dev/null
+++ b/app/src/main/kotlin/rasel/lunar/launcher/apps/SearchMenu.kt
@@ -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 .
+ */
+
+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
diff --git a/app/src/main/kotlin/rasel/lunar/launcher/helpers/Constants.kt b/app/src/main/kotlin/rasel/lunar/launcher/helpers/Constants.kt
index 87d366bd..b117d2b2 100644
--- a/app/src/main/kotlin/rasel/lunar/launcher/helpers/Constants.kt
+++ b/app/src/main/kotlin/rasel/lunar/launcher/helpers/Constants.kt
@@ -67,7 +67,7 @@ internal class Constants {
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_PACK = "default_icon_pack"
const val DEFAULT_GRID_COLUMNS = 4
diff --git a/app/src/main/kotlin/rasel/lunar/launcher/home/LauncherHome.kt b/app/src/main/kotlin/rasel/lunar/launcher/home/LauncherHome.kt
index fbaee711..32c14ab0 100644
--- a/app/src/main/kotlin/rasel/lunar/launcher/home/LauncherHome.kt
+++ b/app/src/main/kotlin/rasel/lunar/launcher/home/LauncherHome.kt
@@ -25,14 +25,23 @@ import android.content.IntentFilter
import android.content.SharedPreferences
import android.os.Bundle
import android.provider.AlarmClock
+import android.provider.CallLog
import android.text.format.DateFormat
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.widget.RadioButton
import android.widget.Toast
import androidx.biometric.BiometricPrompt
+import androidx.core.view.get
import androidx.fragment.app.Fragment
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.R
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.qaccess.QuickAccess
import rasel.lunar.launcher.settings.SettingsActivity
+import rasel.lunar.launcher.todos.MissedCallsAdapter
import rasel.lunar.launcher.todos.TodoAdapter
import rasel.lunar.launcher.todos.TodoManager
import rasel.lunar.launcher.utils.BLog
import rasel.lunar.launcher.utils.SimpleFingerGestures
+import java.text.SimpleDateFormat
import java.util.*
@@ -74,7 +85,9 @@ internal class LauncherHome : Fragment() {
batteryReceiver = BatteryReceiver(binding.batteryProgress)
binding.favAppsGroup.visibility = View.GONE
-
+ Thread("CALLED").run {
+ getCallDetails()
+ }
return binding.root
}
@@ -105,6 +118,8 @@ internal class LauncherHome : Fragment() {
requireContext().registerReceiver(batteryReceiver, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
/* time and date */
+ binding.time.textLocale = Locale.US
+ binding.date.textLocale = Locale.US
if (DateFormat.is24HourFormat(requireContext())) {
binding.time.format24Hour = timeFormat
binding.date.format24Hour = dateFormat
@@ -120,6 +135,79 @@ internal class LauncherHome : Fragment() {
}
}
+ var callList = arrayListOf()
+
+
+ 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(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()
+ 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() {
super.onPause()
/* unregister battery changes */
@@ -380,8 +468,18 @@ internal class LauncherHome : Fragment() {
}
/* to-do list */
+ @SuppressLint("NotifyDataSetChanged")
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 */
@@ -390,9 +488,9 @@ internal class LauncherHome : Fragment() {
0 -> return if (DateFormat.is24HourFormat(requireContext())) {
"kk:mm"
} else {
- "h:mm a"
+ "HH:mm a"
}
- 1 -> return "h:mm a"
+ 1 -> return "HH:mm a"
2 -> return "kk:mm"
}
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 = ""
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/rasel/lunar/launcher/todos/MissedCallsAdapter.kt b/app/src/main/kotlin/rasel/lunar/launcher/todos/MissedCallsAdapter.kt
new file mode 100644
index 00000000..236441ba
--- /dev/null
+++ b/app/src/main/kotlin/rasel/lunar/launcher/todos/MissedCallsAdapter.kt
@@ -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 .
+ */
+
+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,
+ private val context: Context) : RecyclerView.Adapter() {
+
+ 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)
+ }
+ }
+ }
+
+}
diff --git a/app/src/main/kotlin/rasel/lunar/launcher/utils/BLog.kt b/app/src/main/kotlin/rasel/lunar/launcher/utils/BLog.kt
index d46efbae..0e247e26 100644
--- a/app/src/main/kotlin/rasel/lunar/launcher/utils/BLog.kt
+++ b/app/src/main/kotlin/rasel/lunar/launcher/utils/BLog.kt
@@ -5,7 +5,7 @@ import rasel.lunar.launcher.BuildConfig
import java.lang.Exception
object BLog {
- val DEFAULT_TAG = "MyEBook_TAG"
+ val DEFAULT_TAG = "Lunatic"
enum class BLogType {
D,I,E
}
diff --git a/app/src/main/kotlin/rasel/lunar/launcher/view/CircleImageView.kt b/app/src/main/kotlin/rasel/lunar/launcher/view/CircleImageView.kt
index f0d18906..27fb7ec0 100644
--- a/app/src/main/kotlin/rasel/lunar/launcher/view/CircleImageView.kt
+++ b/app/src/main/kotlin/rasel/lunar/launcher/view/CircleImageView.kt
@@ -38,6 +38,7 @@ class CircleImageView : androidx.appcompat.widget.AppCompatImageView {
private val mShaderMatrix: Matrix = Matrix()
private val mBitmapPaint: Paint = Paint()
private val mBorderPaint: Paint = Paint()
+ private val mLabelPaint: Paint = Paint()
private val mCircleBackgroundPaint: Paint = Paint()
private var mBorderColor = DEFAULT_BORDER_COLOR
@@ -60,6 +61,7 @@ class CircleImageView : androidx.appcompat.widget.AppCompatImageView {
private var mBorderOverlay = false
private var mDisableCircularTransformation = false
+ private var label : String = ""
constructor(context: Context) : super(context) {
init()
}
@@ -85,6 +87,8 @@ class CircleImageView : androidx.appcompat.widget.AppCompatImageView {
DEFAULT_CIRCLE_BACKGROUND_COLOR
)
+ label = a.getString(R.styleable.CircleImageView_civ_label) ?: ""
+
a.recycle()
init()
@@ -110,6 +114,9 @@ class CircleImageView : androidx.appcompat.widget.AppCompatImageView {
mCircleBackgroundPaint.setAntiAlias(true)
mCircleBackgroundPaint.setColor(mCircleBackgroundColor)
+ mLabelPaint.color = Color.WHITE
+ mLabelPaint.isFakeBoldText = true
+ mLabelPaint.textAlign = Paint.Align.CENTER
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
outlineProvider = OutlineProvider()
}
@@ -175,6 +182,10 @@ class CircleImageView : androidx.appcompat.widget.AppCompatImageView {
mBorderPaint
)
}
+
+ if (label.length > 0) {
+ canvas.drawText(label.toUpperCase(),mBorderRect.centerX(),mBorderRect.height() * 0.98f, mLabelPaint)
+ }
}
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) {
super.onSizeChanged(w, h, oldw, oldh)
updateDimensions()
+ mLabelPaint.textSize = h * 0.2f
invalidate()
}
diff --git a/app/src/main/res/drawable/coupang.png b/app/src/main/res/drawable/coupang.png
new file mode 100644
index 00000000..3ca44f42
Binary files /dev/null and b/app/src/main/res/drawable/coupang.png differ
diff --git a/app/src/main/res/layout/app_drawer.xml b/app/src/main/res/layout/app_drawer.xml
index fb71cb0c..927e89d5 100644
--- a/app/src/main/res/layout/app_drawer.xml
+++ b/app/src/main/res/layout/app_drawer.xml
@@ -110,6 +110,7 @@
style="@style/SearchIcons"
android:id="@+id/search_store"/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/launcher_home.xml b/app/src/main/res/layout/launcher_home.xml
index f656e74c..dda4a512 100644
--- a/app/src/main/res/layout/launcher_home.xml
+++ b/app/src/main/res/layout/launcher_home.xml
@@ -2,6 +2,7 @@
@@ -29,11 +30,13 @@
android:textIsSelectable="false"
android:textSize="@dimen/clockText"
android:textStyle="bold"
+ android:textLocale="en_US"
app:layout_constraintBottom_toBottomOf="@+id/batteryProgress"
app:layout_constraintEnd_toEndOf="@+id/batteryProgress"
app:layout_constraintStart_toStartOf="@+id/batteryProgress"
app:layout_constraintTop_toTopOf="@+id/batteryProgress"
- app:layout_constraintVertical_bias="0.450" />
+ app:layout_constraintVertical_bias="0.450"
+ tools:ignore="UnusedAttribute" />
+ app:layout_constraintVertical_bias="0.075"
+ tools:ignore="UnusedAttribute" />
+
+
+
+
+
+
+ app:layout_constraintTop_toBottomOf="@+id/summaryChoose" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
index ff8702de..84d292d5 100644
--- a/app/src/main/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
@@ -5,5 +5,6 @@
+
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 85b2a019..033f6316 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -37,4 +37,28 @@
- #000000
+
+
+
+
+
+
+
\ No newline at end of file