From bdb6a57a55c86e7cce1f442294d3e4154196334d Mon Sep 17 00:00:00 2001 From: lunaticbum <> Date: Thu, 22 Aug 2024 11:54:56 +0900 Subject: [PATCH 1/4] ... --- .../rasel/lunar/launcher/apps/AppDrawer.kt | 210 ++++++++++-------- .../lunar/launcher/apps/ContactAdapter.kt | 2 +- .../rasel/lunar/launcher/apps/ContactMenu.kt | 81 ++----- app/src/main/res/drawable/coupang.png | Bin 0 -> 12642 bytes app/src/main/res/layout/app_drawer.xml | 14 +- app/src/main/res/layout/contact_menu.xml | 144 ++++++++++++ 6 files changed, 294 insertions(+), 157 deletions(-) create mode 100644 app/src/main/res/drawable/coupang.png create mode 100644 app/src/main/res/layout/contact_menu.xml diff --git a/app/src/main/kotlin/rasel/lunar/launcher/apps/AppDrawer.kt b/app/src/main/kotlin/rasel/lunar/launcher/apps/AppDrawer.kt index 95707404..8f149af2 100644 --- a/app/src/main/kotlin/rasel/lunar/launcher/apps/AppDrawer.kt +++ b/app/src/main/kotlin/rasel/lunar/launcher/apps/AppDrawer.kt @@ -28,6 +28,8 @@ import android.content.pm.ResolveInfo import android.net.Uri import android.os.Build import android.os.Bundle +import android.os.Handler +import android.os.Looper import android.provider.ContactsContract import android.util.Log import android.view.Gravity @@ -148,6 +150,12 @@ internal class AppDrawer : Fragment() { binding.searchTranslate.setOnClickListener { openSearchApps("https://translate.google.com/?hl=ko&sl=ko&tl=en&text=${getInputText()}&op=translate","com.android.chrome") } + + binding.searchCoupang.setOnClickListener { + openSearchApps("coupang://search?q=${getInputText()}","com.coupang.mobile") + } + + setLayout() return binding.root @@ -223,13 +231,65 @@ internal class AppDrawer : Fragment() { // false -> openSearch() // } // } - + binding.searchInput.setOnKeyListener { v, keyCode, event -> + BLog.LOGE("v == ${v}, keyCode == ${keyCode}, event == ${event}") + if (keyCode==66) { checkResult() } + return@setOnKeyListener true + } binding.searchInput.doOnTextChanged { inputText, _, _, _ -> binding.searchInput.text?.let { binding.searchInput.setSelection(it.length) } filterAppsList(inputText.toString()) } } + fun checkResult() { + if (lastSearchString.length > 0 && packageList.size == 1) { + var dialog = AlertDialog.Builder(requireContext()) + dialog.setTitle("앱 실행 확인") + dialog.setMessage("${lastSearchString} 검색 결과 '${packageList[0].appName}' 준비됨") + dialog.setCancelable(false) + dialog.setNegativeButton("취소") { s,d-> + s.dismiss() + } + dialog.setPositiveButton("실행") { s, d -> + startActivity(packageManager?.getLaunchIntentForPackage(packageList[0].packageName)) + s.dismiss() + } + dialog.show() + } else if (packageList.size == 0 && contactList.size == 1) { + var dialog = AlertDialog.Builder(requireContext()) + dialog.setTitle("연락처 실행 확인") + dialog.setMessage("${lastSearchString} 검색 결과 '${contactList[0].name}' 준비됨") + dialog.setCancelable(false) + dialog.setNegativeButton("취소") { s,d-> + s.dismiss() + } + dialog.setPositiveButton("실행") { s, d -> + ContactMenu().show(childFragmentManager, contactList[0].id.toString()) + s.dismiss() + } + dialog.show() + } else { + var dialog = AlertDialog.Builder(requireContext()) + dialog.setTitle("검색 실행 확인") + dialog.setMessage("${lastSearchString} 검색 준비됨") + dialog.setCancelable(true) + dialog.setNegativeButton("네이버 지도 검색") { s,d-> + s.dismiss() + binding.searchNmap.performClick() + } + dialog.setNeutralButton("쿠팡 검색") { s,d-> + s.dismiss() + binding.searchCoupang.performClick() + } + dialog.setPositiveButton("구글 검색") { s, d -> + s.dismiss() + binding.searchGoogle.performClick() + + } + dialog.show() + } + } override fun onResume() { super.onResume() @@ -246,7 +306,18 @@ internal class AppDrawer : Fragment() { contactAdapter?.updateData(contactList) /* pop up the keyboard */ - if (settingsPrefs!!.getBoolean(KEY_KEYBOARD_SEARCH, false)) openSearch() + openSearch() + + + chechHandler.postDelayed(cancelSearch, 3000L) + } + + val chechHandler = Handler(Looper.getMainLooper()) + val cancelSearch = Runnable { timerCheck() } + + private fun timerCheck() { + + lActivity?.onBackPressed() } override fun onPause() { @@ -271,62 +342,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() { @@ -357,8 +396,9 @@ internal class AppDrawer : Fragment() { var lastSearchStringLength = 0 var lastSearchString : String = "" private fun filterAppsList(searchString: String) { + chechHandler.removeCallbacks(cancelSearch) /* 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) { @@ -367,51 +407,41 @@ 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) - } + 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) { - contactList.add(item) - } - packageList.clear() - for (resolver in oringinPackageList) { - packageList.add(resolver) - } - appsAdapter?.updateData(packageList) - contactAdapter?.updateData(contactList) + } else { + afterClearSearch() } lastSearchString = searchString lastSearchStringLength = searchString.length + chechHandler.postDelayed(cancelSearch, 3000L) + } + + 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 { 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/res/drawable/coupang.png b/app/src/main/res/drawable/coupang.png new file mode 100644 index 0000000000000000000000000000000000000000..3ca44f429bd7dd777622f0145408f32464bea30f GIT binary patch literal 12642 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 74c64c76177d662711ae88466a543ea1424023a5 Mon Sep 17 00:00:00 2001 From: lunaticbum <> Date: Thu, 22 Aug 2024 16:00:05 +0900 Subject: [PATCH 2/4] ... --- .../rasel/lunar/launcher/LauncherActivity.kt | 22 +- .../rasel/lunar/launcher/apps/AppDrawer.kt | 221 ++++++++++-------- .../rasel/lunar/launcher/apps/AppsAdapter.kt | 17 +- .../rasel/lunar/launcher/apps/SearchMenu.kt | 179 ++++++++++++++ .../rasel/lunar/launcher/helpers/Constants.kt | 2 +- .../rasel/lunar/launcher/home/LauncherHome.kt | 6 +- .../kotlin/rasel/lunar/launcher/utils/BLog.kt | 2 +- .../lunar/launcher/view/CircleImageView.kt | 12 + app/src/main/res/layout/launcher_home.xml | 9 +- app/src/main/res/layout/search_menu.xml | 89 +++++++ app/src/main/res/values/attrs.xml | 1 + app/src/main/res/values/styles.xml | 24 ++ 12 files changed, 464 insertions(+), 120 deletions(-) create mode 100644 app/src/main/kotlin/rasel/lunar/launcher/apps/SearchMenu.kt create mode 100644 app/src/main/res/layout/search_menu.xml diff --git a/app/src/main/kotlin/rasel/lunar/launcher/LauncherActivity.kt b/app/src/main/kotlin/rasel/lunar/launcher/LauncherActivity.kt index db5124c2..26fb6ac2 100644 --- a/app/src/main/kotlin/rasel/lunar/launcher/LauncherActivity.kt +++ b/app/src/main/kotlin/rasel/lunar/launcher/LauncherActivity.kt @@ -44,6 +44,8 @@ import androidx.viewpager2.widget.ViewPager2 import com.google.android.material.color.DynamicColors import com.google.android.material.dialog.MaterialAlertDialogBuilder 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.feeds.Feeds import rasel.lunar.launcher.feeds.WidgetHost @@ -150,13 +152,16 @@ internal class LauncherActivity : AppCompatActivity() { /* set up viewpager2 */ private fun setupView() { - viewPager = binding.viewPager.apply { - adapter = ViewPagerAdapter( - supportFragmentManager, mutableListOf(Feeds(), LauncherHome(), AppDrawer()), lifecycle) - offscreenPageLimit = 1 - setCurrentItem(1, false) - reduceDragSensitivity() - } + viewPager = binding.viewPager.apply { + adapter = ViewPagerAdapter( + supportFragmentManager, + mutableListOf(Feeds(), LauncherHome(), AppDrawer()), + lifecycle + ) + offscreenPageLimit = 1 + setCurrentItem(1, false) + reduceDragSensitivity() + } } private fun setBgColor() { @@ -227,4 +232,7 @@ internal class LauncherActivity : AppCompatActivity() { } } + fun openSearchMenus(keyword : String, dismissCalback: DismissCalback) { + SearchMenu().show(supportFragmentManager,keyword) {dismissCalback?.invoke()} + } } diff --git a/app/src/main/kotlin/rasel/lunar/launcher/apps/AppDrawer.kt b/app/src/main/kotlin/rasel/lunar/launcher/apps/AppDrawer.kt index 8f149af2..face2845 100644 --- a/app/src/main/kotlin/rasel/lunar/launcher/apps/AppDrawer.kt +++ b/app/src/main/kotlin/rasel/lunar/launcher/apps/AppDrawer.kt @@ -32,7 +32,7 @@ import android.os.Handler import android.os.Looper import android.provider.ContactsContract import android.util.Log -import android.view.Gravity +import android.view.KeyEvent.ACTION_UP import android.view.LayoutInflater import android.view.View import android.view.View.GONE @@ -44,16 +44,12 @@ import androidx.core.widget.doOnTextChanged import androidx.fragment.app.Fragment import androidx.recyclerview.widget.GridLayoutManager import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.MainScope import kotlinx.coroutines.launch import rasel.lunar.launcher.BuildConfig import rasel.lunar.launcher.LauncherActivity.Companion.lActivity import rasel.lunar.launcher.databinding.AppDrawerBinding 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_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_SETTINGS import rasel.lunar.launcher.utils.BLog @@ -65,7 +61,7 @@ internal class AppDrawer : Fragment() { private lateinit var binding: AppDrawerBinding private var layoutType: Int = 0 - private var isSearchShown: Boolean = false +// private var isSearchShown: Boolean = false private var isKeyboardShowing: Boolean = false companion object { @@ -155,7 +151,30 @@ internal class AppDrawer : Fragment() { openSearchApps("coupang://search?q=${getInputText()}","com.coupang.mobile") } + binding.reset.setOnClickListener { filterAppsList("") } + binding.searchInput.setOnKeyListener { v, keyCode, event -> + BLog.LOGE("v == ${v}, keyCode == ${keyCode}, event == ${event}") + BLog.LOGE("isAdded == ${isAdded}, isResumed == ${isResumed}, isVisible == ${isVisible}") + clearCancelSearch() + if (v.equals(binding.searchInput) && keyCode == 66 && isAdded && isResumed && isVisible && lActivity?.hasWindowFocus() == true && event.action == ACTION_UP) { + try { + if (lastSearchString.length > 0) { + checkResult(binding.searchInput.text.toString()) + } + return@setOnKeyListener true + } catch (e :Exception) { + e.printStackTrace() + } + } + return@setOnKeyListener false + } + + binding.searchInput.doOnTextChanged { inputText, _, _, _ -> + clearCancelSearch() + binding.searchInput.text?.let { binding.searchInput.setSelection(it.length) } + filterAppsList(inputText.toString()) + } setLayout() return binding.root @@ -202,6 +221,7 @@ internal class AppDrawer : Fragment() { } + fun openSearchApps(schemeString : String, pakage : String? = null) { val gmmIntentUri = Uri.parse(schemeString) val mapIntent = Intent(Intent.ACTION_VIEW, gmmIntentUri) @@ -215,108 +235,133 @@ internal class AppDrawer : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 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 -> - BLog.LOGE("v == ${v}, keyCode == ${keyCode}, event == ${event}") - if (keyCode==66) { checkResult() } - return@setOnKeyListener true - } - binding.searchInput.doOnTextChanged { inputText, _, _, _ -> - binding.searchInput.text?.let { binding.searchInput.setSelection(it.length) } - filterAppsList(inputText.toString()) - } } - fun checkResult() { - if (lastSearchString.length > 0 && packageList.size == 1) { - var dialog = AlertDialog.Builder(requireContext()) - dialog.setTitle("앱 실행 확인") - dialog.setMessage("${lastSearchString} 검색 결과 '${packageList[0].appName}' 준비됨") - dialog.setCancelable(false) - dialog.setNegativeButton("취소") { s,d-> - s.dismiss() + 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.setPositiveButton("실행") { s, d -> - startActivity(packageManager?.getLaunchIntentForPackage(packageList[0].packageName)) - s.dismiss() + dialog?.setCancelable(false) + dialog?.setNegativeButton("취소") { s, d -> + runonUi { s.dismiss() } } - dialog.show() - } else if (packageList.size == 0 && contactList.size == 1) { - var dialog = AlertDialog.Builder(requireContext()) - dialog.setTitle("연락처 실행 확인") - dialog.setMessage("${lastSearchString} 검색 결과 '${contactList[0].name}' 준비됨") - dialog.setCancelable(false) - dialog.setNegativeButton("취소") { s,d-> - 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() } } - dialog.setPositiveButton("실행") { s, d -> - ContactMenu().show(childFragmentManager, contactList[0].id.toString()) - 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.show() + dialog?.setOnDismissListener { registCancelSearch() } + dialog?.show() } else { - var dialog = AlertDialog.Builder(requireContext()) - dialog.setTitle("검색 실행 확인") - dialog.setMessage("${lastSearchString} 검색 준비됨") - dialog.setCancelable(true) - dialog.setNegativeButton("네이버 지도 검색") { s,d-> - s.dismiss() - binding.searchNmap.performClick() + lActivity?.openSearchMenus(keyword) { + registCancelSearch() } - dialog.setNeutralButton("쿠팡 검색") { s,d-> - s.dismiss() - binding.searchCoupang.performClick() - } - dialog.setPositiveButton("구글 검색") { s, d -> - s.dismiss() - binding.searchGoogle.performClick() - - } - dialog.show() } } + 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() setKeyboardPadding() - 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)) - } - contactAdapter?.updateData(contactList) /* pop up the keyboard */ openSearch() - - chechHandler.postDelayed(cancelSearch, 3000L) + registCancelSearch() + BLog.LOGE("onResume after chechHandler.postDelayed(cancelSearch, 3000L)") } val chechHandler = Handler(Looper.getMainLooper()) + val cancelSearch = Runnable { timerCheck() } - private fun timerCheck() { + fun registCancelSearch() { + BLog.LOGE("Called registCancelSearch") + chechHandler.removeCallbacks(cancelSearch) + chechHandler.postDelayed(cancelSearch, 3000L) + } + fun clearCancelSearch() { + chechHandler.removeCallbacks(cancelSearch) + } + + private fun timerCheck() { lActivity?.onBackPressed() } @@ -396,7 +441,6 @@ internal class AppDrawer : Fragment() { var lastSearchStringLength = 0 var lastSearchString : String = "" private fun filterAppsList(searchString: String) { - chechHandler.removeCallbacks(cancelSearch) /* check each app name and add if it matches the search string */ if (searchString.length > 0 && (lastSearchStringLength != searchString.length || lastSearchString.equals(searchString) == false)) { BLog.LOGE("START FILTER") @@ -410,7 +454,7 @@ internal class AppDrawer : Fragment() { packageList.sortBy { it.appName } BLog.LOGE("MIDDLE FILTER") - appsAdapter?.updateData(packageList) + appsAdapter?.updateData(packageList) contactList.clear() for (item in originContactList) { @@ -426,7 +470,7 @@ internal class AppDrawer : Fragment() { } lastSearchString = searchString lastSearchStringLength = searchString.length - chechHandler.postDelayed(cancelSearch, 3000L) + registCancelSearch() } private fun afterClearSearch() { @@ -452,26 +496,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/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..69e626f6 100644 --- a/app/src/main/kotlin/rasel/lunar/launcher/home/LauncherHome.kt +++ b/app/src/main/kotlin/rasel/lunar/launcher/home/LauncherHome.kt @@ -105,6 +105,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 @@ -390,9 +392,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 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/layout/launcher_home.xml b/app/src/main/res/layout/launcher_home.xml index f656e74c..a61e5b2f 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" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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 46c24b88..442537dc 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -34,4 +34,28 @@ #000000 + + + + + + + \ No newline at end of file From 5cc40ee0561e97ef3c80122c0486219b382d4e96 Mon Sep 17 00:00:00 2001 From: lunaticbum <> Date: Thu, 22 Aug 2024 17:51:59 +0900 Subject: [PATCH 3/4] .... --- app/src/main/AndroidManifest.xml | 1 + .../rasel/lunar/launcher/LauncherActivity.kt | 29 ++-- .../rasel/lunar/launcher/home/LauncherHome.kt | 124 +++++++++++++++++- .../launcher/todos/MissedCallsAdapter.kt | 113 ++++++++++++++++ app/src/main/res/layout/launcher_home.xml | 44 ++++++- app/src/main/res/layout/list_item.xml | 2 +- 6 files changed, 295 insertions(+), 18 deletions(-) create mode 100644 app/src/main/kotlin/rasel/lunar/launcher/todos/MissedCallsAdapter.kt 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 @@ + () + + private suspend fun getCallDetails() { + MainScope().async { + 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) +// sb.append("Call Details : ${managedCursor.count}") + var missedCalls = hashMapOf() + 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" + } + } +// if (dircode > 2) { + 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() + return@async missedCalls + }.await().apply { + if (callList.size == this.size) { + + } else { + callList.clear() + this.forEach { t, u -> + callList.add(u) + } + } + } + } + override fun onPause() { super.onPause() /* unregister battery changes */ @@ -383,7 +474,19 @@ internal class LauncherHome : Fragment() { /* to-do list */ private fun showTodoList() { - binding.notes.adapter = TodoAdapter(null, requireContext()) + if (binding.missedCalls.isChecked == true) { + if (callList.size == 0) { + GlobalScope.launch { getCallDetails() } + } else { + BLog.LOGE("callList >>> ${callList.size}") + binding.notes.adapter = MissedCallsAdapter(callList, requireContext()) + binding.notes.post { + binding.notes.invalidate() + } + } + } else { + binding.notes.adapter = TodoAdapter(null, requireContext()) + } } /* get time format string */ @@ -422,3 +525,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/res/layout/launcher_home.xml b/app/src/main/res/layout/launcher_home.xml index a61e5b2f..dda4a512 100644 --- a/app/src/main/res/layout/launcher_home.xml +++ b/app/src/main/res/layout/launcher_home.xml @@ -66,18 +66,54 @@ app:layout_constraintTop_toBottomOf="@+id/date" app:layout_constraintVertical_bias="0.100" /> + + + + + + + app:layout_constraintTop_toBottomOf="@+id/summaryChoose" /> Date: Thu, 22 Aug 2024 18:01:58 +0900 Subject: [PATCH 4/4] ... --- .../rasel/lunar/launcher/home/LauncherHome.kt | 71 +++++++++---------- 1 file changed, 32 insertions(+), 39 deletions(-) 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 f83260c7..32c14ab0 100644 --- a/app/src/main/kotlin/rasel/lunar/launcher/home/LauncherHome.kt +++ b/app/src/main/kotlin/rasel/lunar/launcher/home/LauncherHome.kt @@ -85,7 +85,9 @@ internal class LauncherHome : Fragment() { batteryReceiver = BatteryReceiver(binding.batteryProgress) binding.favAppsGroup.visibility = View.GONE - + Thread("CALLED").run { + getCallDetails() + } return binding.root } @@ -135,24 +137,24 @@ internal class LauncherHome : Fragment() { var callList = arrayListOf() - private suspend fun getCallDetails() { - MainScope().async { - 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) -// sb.append("Call Details : ${managedCursor.count}") - var missedCalls = hashMapOf() + + 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 @@ -186,7 +188,6 @@ internal class LauncherHome : Fragment() { dir = "ANSWERED_EXTERNALLY_TYPE" } } -// if (dircode > 2) { var missed: MissedCall = if (missedCalls.containsKey(phNumber)) { missedCalls.get(phNumber)!!.apply { count = count + 1 @@ -195,20 +196,14 @@ internal class LauncherHome : Fragment() { MissedCall(1,callerName,phNumber, dircode, dir, SimpleDateFormat("yyy/MM/dd-HH:mm:ss").format(callDayTime)) } missedCalls.put(phNumber, missed) -// } } - - - managedCursor.close() - return@async missedCalls - }.await().apply { - if (callList.size == this.size) { - - } else { - callList.clear() - this.forEach { t, u -> - callList.add(u) - } + } + managedCursor.close() + if (callList.size == missedCalls.size) { + } else { + callList.clear() + missedCalls.forEach { t, u -> + callList.add(u) } } } @@ -473,15 +468,13 @@ internal class LauncherHome : Fragment() { } /* to-do list */ + @SuppressLint("NotifyDataSetChanged") private fun showTodoList() { if (binding.missedCalls.isChecked == true) { - if (callList.size == 0) { - GlobalScope.launch { getCallDetails() } - } else { + if (callList.size > 0) { BLog.LOGE("callList >>> ${callList.size}") - binding.notes.adapter = MissedCallsAdapter(callList, requireContext()) - binding.notes.post { - binding.notes.invalidate() + binding.notes.adapter = MissedCallsAdapter(callList, requireContext())?.apply { + this.notifyDataSetChanged() } } } else {