diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e30947b..b27a486 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -16,6 +16,7 @@
+
@@ -38,6 +39,8 @@
android:excludeFromRecents="true"
android:clearTaskOnLaunch="true"
android:stateNotNeeded="true"
+ android:largeHeap="true"
+ android:hardwareAccelerated="true"
android:screenOrientation="nosensor"
android:windowSoftInputMode="adjustResize"
android:requestLegacyExternalStorage="true">
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 23b0cc4..09ce3b5 100644
--- a/app/src/main/kotlin/rasel/lunar/launcher/apps/AppDrawer.kt
+++ b/app/src/main/kotlin/rasel/lunar/launcher/apps/AppDrawer.kt
@@ -19,15 +19,17 @@
package rasel.lunar.launcher.apps
import android.annotation.SuppressLint
+import android.content.ContentUris
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
-import android.graphics.Rect
+import android.net.Uri
import android.os.Build
import android.os.Bundle
-import android.provider.Settings.Global
+import android.provider.ContactsContract
+import android.util.Log
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
@@ -36,34 +38,24 @@ import android.view.View.VISIBLE
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import androidx.appcompat.app.AlertDialog
-import androidx.core.view.updateLayoutParams
import androidx.core.widget.doOnTextChanged
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
-import androidx.recyclerview.widget.LinearLayoutManager
-import com.google.android.material.textview.MaterialTextView
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import rasel.lunar.launcher.BuildConfig
import rasel.lunar.launcher.LauncherActivity.Companion.lActivity
-import rasel.lunar.launcher.R
import rasel.lunar.launcher.databinding.AppDrawerBinding
-import rasel.lunar.launcher.helpers.Constants.Companion.DEFAULT_GRID_COLUMNS
-import rasel.lunar.launcher.helpers.Constants.Companion.DEFAULT_SCROLLBAR_HEIGHT
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_GRID_COLUMNS
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.KEY_SCROLLBAR_HEIGHT
-import rasel.lunar.launcher.helpers.Constants.Companion.KEY_STATUS_BAR
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
import java.text.Normalizer
-import java.util.*
import java.util.regex.Pattern
@@ -77,6 +69,7 @@ internal class AppDrawer : Fragment() {
companion object {
private var packageManager: PackageManager? = null
private var appsAdapter: AppsAdapter? = null
+ private var contactAdapter : ContactAdapter? = null
private var packageInfoList: MutableList = mutableListOf()
private var packageList = mutableListOf()
@@ -84,11 +77,11 @@ internal class AppDrawer : Fragment() {
// private val alphabetPattern = Pattern.compile("[A-Z]")
@JvmStatic var settingsPrefs: SharedPreferences? = null
@JvmStatic var appNamesPrefs: SharedPreferences? = null
- // @JvmStatic var alphabetList = mutableListOf()
- @JvmStatic var letterPreview: MaterialTextView? = null
+
private fun appName(resolver: ResolveInfo): String {
if(appNamesPrefs?.contains(resolver.activityInfo.packageName) != null && appNamesPrefs?.getString(resolver.activityInfo.packageName,"")?.length ?: 0 > 0) {
+ BLog.LOGE("it.activityInfo.packageName >>>> ${resolver.activityInfo.packageName} == name : ${appNamesPrefs?.getString(resolver.activityInfo.packageName,"") ?: ""}")
return appNamesPrefs?.getString(resolver.activityInfo.packageName,"") ?: ""
} else {
return resolver.loadLabel(packageManager).toString().apply {
@@ -99,6 +92,7 @@ internal class AppDrawer : Fragment() {
}
}
+ fun getInputText() = binding.searchInput.text.toString()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = AppDrawerBinding.inflate(inflater, container, false)
@@ -108,36 +102,106 @@ internal class AppDrawer : Fragment() {
layoutType = settingsPrefs!!.getInt(KEY_APPS_LAYOUT, 0)
packageManager = lActivity?.packageManager
appsAdapter = AppsAdapter(layoutType, packageManager!!, childFragmentManager, binding.appsCount)
- letterPreview = binding.appsCount
+ contactAdapter = ContactAdapter(packageManager!!, childFragmentManager)
binding.appsCount.visibility = if (settingsPrefs!!.getBoolean(KEY_APPS_COUNT, true)) VISIBLE else GONE
+ binding.searchNmap.setOnClickListener {
+ openSearchApps("nmap://search?query=${getInputText()}&appname=${BuildConfig.APPLICATION_ID}","com.nhn.android.nmap")
+ }
+ binding.searchGoogleMap.setOnClickListener {
+ openSearchApps("geo:0,0?q=${getInputText()}","com.google.android.apps.maps")
+ }
+ binding.searchGoogle.setOnClickListener {
+ openSearchApps("https://www.google.com/search?q=${getInputText()}","com.android.chrome")
+ }
+ binding.searchTmap.setOnClickListener {
+ openSearchApps("tmap://search?name=${getInputText()}","com.skt.tmap.ku")
+ }
+ binding.searchNaver.setOnClickListener {
+ openSearchApps("https://search.naver.com/search.naver?where=nexearch&query=${getInputText()}", "com.nhn.android.search")
+ }
+ binding.searchDuckduckgo.setOnClickListener {
+ openSearchApps("https://duckduckgo.com/?t=h_&q=${getInputText()}","com.duckduckgo.mobile.android")
+ }
+ binding.searchNamuwiki.setOnClickListener {
+ openSearchApps("https://namu.wiki/Search?q=${getInputText()}")
+ }
setLayout()
-
return binding.root
}
+
+ val originContactList = arrayListOf()
+ val contactList = arrayListOf()
+ private fun GetContact() {
+ if (originContactList.size > 0) {
+
+ } else {
+ contactList.clear()
+ originContactList.clear()
+ val resolver = lActivity!!.contentResolver
+
+ val phoneUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI
+ val projection = arrayOf(
+ ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
+ ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
+ ContactsContract.CommonDataKinds.Phone.NUMBER,
+ )
+
+ val cursor = resolver.query(phoneUri, projection, null, null, null)
+ if (cursor != null) {
+ while (cursor.moveToNext()) {
+ val idx =cursor.getColumnIndex(projection[0])
+ val nameIndex = cursor.getColumnIndex(projection[1])
+ val numberIndex = cursor.getColumnIndex(projection[2])
+ var contactId = cursor.getString(idx)
+ val name = cursor.getString(nameIndex)
+ var number = cursor.getString(numberIndex)
+ number = number.replace("-", "")
+ if (name?.length ?: 0 > 0 && number?.length ?: 0 > 0) {
+ contactList.add(SimpleContact(contactId,name, number))
+ originContactList.add(SimpleContact(contactId,name, number))
+ }
+ Log.d("GetContact", "이름 : $name 번호 : $number ")
+ }
+ }
+ // 데이터 계열은 반드시 닫아줘야 한다.
+ cursor!!.close()
+ }
+ }
+
+
+ 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)
+ }
+
@SuppressLint("ClickableViewAccessibility")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- binding.reset.setOnClickListener { onResume() }
+ binding.reset.setOnClickListener { filterAppsList("") }
- binding.moveDown.setOnClickListener {
- binding.appsList.smoothScrollToPosition(packageList.size - 1)
- }
+// binding.moveDown.setOnClickListener {
+// binding.appsList.smoothScrollToPosition(packageList.size - 1)
+// }
+//
+// binding.moveUp.setOnClickListener {
+// binding.appsList.smoothScrollToPosition(0)
+// }
- binding.moveUp.setOnClickListener {
- binding.appsList.smoothScrollToPosition(0)
- }
-
- binding.search.setOnClickListener {
- when (isSearchShown) {
- true -> closeSearch()
- false -> openSearch()
- }
- }
+// binding.search.setOnClickListener {
+// when (isSearchShown) {
+// true -> closeSearch()
+// false -> openSearch()
+// }
+// }
binding.searchInput.doOnTextChanged { inputText, _, _, _ ->
binding.searchInput.text?.let { binding.searchInput.setSelection(it.length) }
@@ -148,6 +212,7 @@ internal class AppDrawer : Fragment() {
override fun onResume() {
super.onResume()
fetchApps()
+ GetContact()
setKeyboardPadding()
binding.appsCount.visibility = if (settingsPrefs!!.getBoolean(KEY_APPS_COUNT, true)) VISIBLE else GONE
@@ -156,6 +221,8 @@ internal class AppDrawer : Fragment() {
appsAdapter?.updateGravity(settingsPrefs!!.getInt(KEY_DRAW_ALIGN, Gravity.CENTER))
}
+ contactAdapter?.updateData(contactList)
+
/* pop up the keyboard */
if (settingsPrefs!!.getBoolean(KEY_KEYBOARD_SEARCH, false)) openSearch()
}
@@ -166,16 +233,18 @@ internal class AppDrawer : Fragment() {
}
private fun setLayout() {
- when (layoutType) {
- 0, 1 -> {
- binding.appsList.layoutManager = LinearLayoutManager(requireContext())
- appsAdapter!!.updateGravity(settingsPrefs!!.getInt(KEY_DRAW_ALIGN, Gravity.CENTER))
- }
- 2 -> binding.appsList.layoutManager = GridLayoutManager(requireContext(), Math.min(settingsPrefs!!.getInt(KEY_GRID_COLUMNS, DEFAULT_GRID_COLUMNS), 4))
- }
-
+// when (layoutType) {
+// 0, 1 -> {
+// binding.appsList.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL,false)
+// appsAdapter!!.updateGravity(settingsPrefs!!.getInt(KEY_DRAW_ALIGN, Gravity.CENTER))
+// }
+// 2 -> binding.appsList.layoutManager = GridLayoutManager(requireContext(), Math.min(settingsPrefs!!.getInt(KEY_GRID_COLUMNS, DEFAULT_GRID_COLUMNS), 4), GridLayoutManager.HORIZONTAL,false)
+// }
+ binding.appsList.layoutManager = GridLayoutManager(requireContext(), 2, GridLayoutManager.HORIZONTAL,false)
+ binding.contactList.layoutManager = GridLayoutManager(requireContext(), 2, GridLayoutManager.HORIZONTAL,false)
/* initialize apps list adapter */
binding.appsList.adapter = appsAdapter
+ binding.contactList.adapter = contactAdapter
}
/* update app list with app and package name */
@@ -281,13 +350,25 @@ internal class AppDrawer : Fragment() {
} else {
appsAdapter?.updateData(packageList)
}
+ contactList.clear()
+ for (item in originContactList) {
+ if (item.name.contains(searchString) || item.phoneNumber.contains(searchString)) {
+ contactList.add(item)
+ }
+ }
+ 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 packageInfoList) {
packageList.add(Packages(resolver.activityInfo.packageName, appName(resolver)))
}
appsAdapter?.updateData(packageList)
+ contactAdapter?.updateData(contactList)
}
lastSearchString = searchString
lastSearchStringLength = searchString.length
@@ -302,7 +383,6 @@ internal class AppDrawer : Fragment() {
private fun openSearch() {
isSearchShown = true
- binding.search.setImageResource(R.drawable.ic_close)
binding.searchInput.apply {
visibility = VISIBLE
requestFocus()
@@ -316,7 +396,6 @@ internal class AppDrawer : Fragment() {
/* clear search string, hide keyboard and search box */
private fun closeSearch() {
isSearchShown = false
- binding.search.setImageResource(R.drawable.ic_search)
binding.searchInput.apply {
text?.clear()
visibility = GONE
@@ -328,28 +407,29 @@ internal class AppDrawer : Fragment() {
}
private fun setKeyboardPadding() {
- binding.root.viewTreeObserver.addOnGlobalLayoutListener {
- val rect = Rect()
- binding.root.getWindowVisibleDisplayFrame(rect)
- val screenHeight = binding.root.height
- val keyboardHeight = screenHeight - (rect.bottom - rect.top)
-
- when {
- keyboardHeight > screenHeight * 0.15 -> {
- if (!isKeyboardShowing &&
- !settingsPrefs!!.getBoolean(KEY_STATUS_BAR, false)) {
- isKeyboardShowing = true
- binding.root.setPadding(0, 0, 0, keyboardHeight)
- }
- }
- else -> {
- if (isKeyboardShowing) {
- isKeyboardShowing = false
- binding.root.setPadding(0, 0, 0, 0)
- }
- }
- }
- }
+// binding.root.viewTreeObserver.addOnGlobalLayoutListener {
+// val rect = Rect()
+// binding.root.getWindowVisibleDisplayFrame(rect)
+// val screenHeight = binding.root.height
+// val keyboardHeight = screenHeight - (rect.bottom - rect.top)
+//
+// when {
+// keyboardHeight > screenHeight * 0.15 -> {
+// if (!isKeyboardShowing &&
+// !settingsPrefs!!.getBoolean(KEY_STATUS_BAR, false)) {
+// isKeyboardShowing = true
+// binding.root.setPadding(0, 0, 0, keyboardHeight)
+// }
+// }
+// else -> {
+// if (isKeyboardShowing) {
+// isKeyboardShowing = false
+// binding.root.setPadding(0, 0, 0, 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 7da00b1..4a55523 100644
--- a/app/src/main/kotlin/rasel/lunar/launcher/apps/AppsAdapter.kt
+++ b/app/src/main/kotlin/rasel/lunar/launcher/apps/AppsAdapter.kt
@@ -25,6 +25,7 @@ import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.widget.TextView
import androidx.core.view.updatePadding
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.DiffUtil
@@ -43,7 +44,7 @@ internal class AppsAdapter(
private val layoutType: Int,
private val packageManager: PackageManager,
private val fragmentManager: FragmentManager,
- private val appsCount: MaterialTextView) : RecyclerView.Adapter() {
+ private val appsCount: TextView) : RecyclerView.Adapter() {
private var oldList = mutableListOf()
private var appGravity: Int = Gravity.CENTER
@@ -57,57 +58,19 @@ internal class AppsAdapter(
override fun onBindViewHolder(holder: AppsViewHolder, i: Int) {
val item = oldList[i]
- val fourDp = dpToPx(lActivity!!, R.dimen.four)
- val eightDp = dpToPx(lActivity!!, R.dimen.eight)
- val twelveDp = dpToPx(lActivity!!, R.dimen.twelve)
- val sixteenDp = dpToPx(lActivity!!, R.dimen.sixteen)
+
holder.view.apply {
childTextview.text = item.appName
+ appIconTwo.visibility = View.VISIBLE
- when (layoutType) {
- 0 -> {
- appIcon.visibility = View.GONE
- appIconTwo.visibility = View.GONE
- childTextview.apply {
- gravity = appGravity
- setTextSize(TypedValue.COMPLEX_UNIT_PX, lActivity!!.resources.getDimension(R.dimen.twentyTwo))
- }
- root.setPadding(sixteenDp, fourDp, sixteenDp, fourDp)
- }
- 1 -> {
- appIcon.visibility = View.GONE
- appIconTwo.visibility = View.VISIBLE
-
- MainScope().async {
- getDrawableIconForPackage(item.packageName, packageManager.getApplicationIcon(item.packageName)) {
- appIconTwo.post { appIconTwo.setImageDrawable(it) }
- } }
-
- childTextview.apply {
- gravity = appGravity or Gravity.CENTER_VERTICAL
- setTextSize(TypedValue.COMPLEX_UNIT_PX, lActivity!!.resources.getDimension(R.dimen.twenty))
- updatePadding(left = twelveDp)
- }
- root.setPadding(sixteenDp, eightDp, sixteenDp, eightDp)
- }
- 2 -> {
- appIconTwo.visibility = View.GONE
- appIcon.visibility = View.VISIBLE
- MainScope().async {
- getDrawableIconForPackage(
- item.packageName,
- packageManager.getApplicationIcon(item.packageName)
- ) {
- appIcon.post { appIcon.setImageDrawable(it) }
- }
- }
- childTextview.apply {
- gravity = Gravity.CENTER
- setTextSize(TypedValue.COMPLEX_UNIT_PX, lActivity!!.resources.getDimension(R.dimen.twelve))
- }
- root.setPadding(eightDp, eightDp, eightDp, eightDp)
- }
+ MainScope().async {
+ getDrawableIconForPackage(item.packageName, packageManager.getApplicationIcon(item.packageName)) {
+ appIconTwo.post { appIconTwo.setImageDrawable(it) }
+ } }
+ childTextview.apply {
+ gravity = Gravity.CENTER
+ setTextSize(TypedValue.COMPLEX_UNIT_PX, lActivity!!.resources.getDimension(R.dimen.twelve))
}
}
diff --git a/app/src/main/kotlin/rasel/lunar/launcher/apps/ContactAdapter.kt b/app/src/main/kotlin/rasel/lunar/launcher/apps/ContactAdapter.kt
new file mode 100644
index 0000000..ea906fd
--- /dev/null
+++ b/app/src/main/kotlin/rasel/lunar/launcher/apps/ContactAdapter.kt
@@ -0,0 +1,131 @@
+/*
+ * 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.content.Intent
+import android.content.pm.PackageManager
+import android.net.Uri
+import android.util.TypedValue
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.core.view.updatePadding
+import androidx.fragment.app.FragmentManager
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.RecyclerView
+import com.google.android.material.textview.MaterialTextView
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.async
+import rasel.lunar.launcher.LauncherActivity.Companion.lActivity
+import rasel.lunar.launcher.R
+import rasel.lunar.launcher.apps.IconPackManager.Companion.getDrawableIconForPackage
+import rasel.lunar.launcher.databinding.AppsChildBinding
+import rasel.lunar.launcher.databinding.ContactItemBinding
+import rasel.lunar.launcher.helpers.UniUtils.Companion.dpToPx
+import rasel.lunar.launcher.utils.BLog
+
+
+internal class ContactAdapter (
+ private val packageManager: PackageManager,
+ private val fragmentManager: FragmentManager) : RecyclerView.Adapter() {
+
+ private var oldList = mutableListOf()
+ private var appGravity: Int = Gravity.CENTER
+
+ companion object {
+ @JvmStatic var appsSize: Int? = null
+ }
+
+ override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): ContactViewHolder =
+ ContactViewHolder(ContactItemBinding.inflate(LayoutInflater.from(viewGroup.context), viewGroup, false))
+
+ override fun onBindViewHolder(holder: ContactViewHolder, i: Int) {
+ val item = oldList[i]
+
+
+ holder.view.apply {
+ name.text = item.name
+ number.text= item.phoneNumber
+ }
+
+ holder.view.root.apply {
+ /* on click - open app */
+ setOnClickListener {
+ context.startActivity(Intent(Intent.ACTION_CALL, Uri.parse("tel://${item.phoneNumber}")))
+ }
+
+ /* on long click - open app menu */
+ setOnLongClickListener {
+ BLog.LOGE("item.id.toString() >> ${item.id.toString()}")
+ ContactMenu().show(fragmentManager, item.id.toString())
+ true
+ }
+ }
+ }
+
+ override fun getItemCount(): Int = oldList.size
+
+ inner class ContactViewHolder(var view: ContactItemBinding) : RecyclerView.ViewHolder(view.root)
+
+ /* update app list */
+ fun updateData(newList: List) {
+ val diffUtilResult = DiffUtil.calculateDiff(ContactDiffUtil(oldList, newList))
+
+ oldList.clear()
+ oldList.addAll(newList)
+ diffUtilResult.dispatchUpdatesTo(this)
+
+ newList.size.let {
+ 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 SimpleContact(val id : String,val name : String ,val phoneNumber : String)
+
+internal class ContactDiffUtil(
+ private val oldList: List, private val newList: List
+) : DiffUtil.Callback() {
+
+ override fun getOldListSize(): Int = oldList.size
+ override fun getNewListSize(): Int = newList.size
+
+ override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean =
+ oldList[oldItemPosition].phoneNumber == newList[newItemPosition].phoneNumber
+
+ override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean =
+ oldList[oldItemPosition] == newList[newItemPosition]
+}
diff --git a/app/src/main/kotlin/rasel/lunar/launcher/apps/ContactMenu.kt b/app/src/main/kotlin/rasel/lunar/launcher/apps/ContactMenu.kt
new file mode 100644
index 0000000..233f46f
--- /dev/null
+++ b/app/src/main/kotlin/rasel/lunar/launcher/apps/ContactMenu.kt
@@ -0,0 +1,438 @@
+/*
+ * 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.ContentUris
+import android.content.Context
+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.util.Log
+import android.view.KeyEvent
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.inputmethod.InputMethodManager
+import android.widget.AdapterView
+import android.widget.ArrayAdapter
+import android.widget.Toast
+import androidx.appcompat.widget.LinearLayoutCompat
+import androidx.core.content.FileProvider
+import androidx.core.content.pm.PackageInfoCompat
+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.LauncherActivity.Companion.lActivity
+import rasel.lunar.launcher.R
+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.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.copyToClipboard
+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 ContactMenu : BottomSheetDialogFragment() {
+
+ private lateinit var binding: AppMenuBinding
+ private lateinit var packageName: String
+ private lateinit var packageManager: PackageManager
+ private lateinit var appInfo: ApplicationInfo
+ private lateinit var defAppName: String
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ binding = AppMenuBinding.inflate(inflater, container, false)
+
+ /* get package name from fragment's tag */
+ packageName = tag.toString()
+ val resolver = lActivity!!.contentResolver
+ val phoneUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI
+ val projection = arrayOf(
+ ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
+ ContactsContract.CommonDataKinds.Phone.NUMBER,
+ )
+
+ BLog.LOGE("GetContact", "packageName ${packageName}")
+ 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)
+ var number = cursor.getString(numberIndex)
+ number = number.replace("-", "")
+ BLog.LOGE("GetContact", "이름 : $name 번호 : $number ")
+ }
+ }
+ // 데이터 계열은 반드시 닫아줘야 한다.
+ cursor!!.close()
+
+ /* get application info */
+
+
+ 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() }
+ }
+
+ /* manage initial preview and clicks for favorite apps */
+ @SuppressLint("PrivateResource")
+ private fun favoriteApps() {
+ val sharedPreferences = requireContext().getSharedPreferences(PREFS_FAVORITE_APPS, 0)
+ val enabledStroke =
+ ColorStateList.valueOf(requireContext().getColor(com.google.android.material.R.color.material_on_surface_stroke))
+ val disabledStroke =
+ ColorStateList.valueOf(requireContext().getColor(com.google.android.material.R.color.m3_chip_stroke_color))
+
+ for (position in 1..MAX_FAVORITE_APPS) {
+ val button = outlinedButton
+ val savedPackageName = sharedPreferences.getString(KEY_APP_NO_ + position, "")
+
+ /* set previews */
+ if (packageName == savedPackageName) button.isChecked = true
+ if (savedPackageName?.isNotEmpty() == true) button.strokeColor = enabledStroke
+
+ try {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
+ packageManager.getPackageInfo(savedPackageName!!, PackageManager.PackageInfoFlags.of(0))
+ else
+ packageManager.getPackageInfo(savedPackageName!!, 0)
+ } catch (e: PackageManager.NameNotFoundException) {
+ requireContext().getSharedPreferences(PREFS_FAVORITE_APPS, 0)
+ .edit().remove(KEY_APP_NO_ + position).apply()
+ button.strokeColor = disabledStroke
+ e.printStackTrace()
+ }
+
+ /* listen on clicks */
+ binding.favGroup.addOnButtonCheckedListener { _: MaterialButtonToggleGroup?,
+ checkedId: Int, isChecked: Boolean ->
+ try {
+ if (checkedId == button.id) {
+ if (isChecked) {
+ requireContext().getSharedPreferences(PREFS_FAVORITE_APPS, 0)
+ .edit().putString(KEY_APP_NO_ + position, packageName).apply()
+ button.strokeColor = enabledStroke
+ } else {
+ requireContext().getSharedPreferences(PREFS_FAVORITE_APPS, 0)
+ .edit().remove(KEY_APP_NO_ + position).apply()
+ button.strokeColor = disabledStroke
+ }
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ }
+ }
+ }
+
+ 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
+ }
+ }
+
+ /* 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()
+
+ /* 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 */
+ private fun activityBrowser() {
+ val dialogBinding = ActivityBrowserDialogBinding.inflate(lActivity!!.layoutInflater)
+ val dialogBuilder = MaterialAlertDialogBuilder(lActivity!!)
+ .setView(dialogBinding.root)
+ .setPositiveButton(android.R.string.cancel, null)
+ .show()
+
+ /* show app name */
+ dialogBinding.appName.text = packageManager.getApplicationLabel(appInfo)
+
+ /* get activity info */
+ val activityInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ packageManager.getPackageInfo(
+ packageName, PackageManager.PackageInfoFlags.of(PackageManager.GET_ACTIVITIES.toLong())
+ )
+ } else {
+ packageManager.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES)
+ }
+
+ /* show activity list */
+ val activityAdapter: ArrayAdapter =
+ ArrayAdapter(requireContext(), R.layout.list_item, R.id.itemText, ArrayList())
+ if (activityInfo.activities.isNotEmpty()) {
+ for (activity in activityInfo.activities) {
+ activityAdapter.add(
+ activity.toString().split(" ").toTypedArray()[1].replace("}", "")
+ )
+ }
+ dialogBinding.activityList.adapter = activityAdapter
+ }
+
+ /* listen item clicks */
+ dialogBinding.activityList.onItemClickListener =
+ AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, i: Int, _: Long ->
+ try {
+ /* open activity */
+ val intent = Intent()
+ intent.component = ComponentName(packageName, activityAdapter.getItem(i).toString())
+ intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ requireContext().startActivity(intent)
+ } catch (exception: Exception) {
+ /* couldn't open activity */
+ exception.printStackTrace()
+ val exceptionShort = (exception.toString().split(": ").toTypedArray())[0]
+ Toast.makeText(requireContext(),
+ "${resources.getString(R.string.unable_to_launch)} -\n$exceptionShort", Toast.LENGTH_LONG).show()
+ }
+ dialogBuilder.dismiss()
+ }
+ }
+
+ /* open app's page in app store/market */
+ private fun appStore() {
+ try {
+ val storeIntent = Intent(Intent.ACTION_VIEW)
+ storeIntent.data = Uri.parse("market://details?id=$packageName")
+ storeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ requireContext().startActivity(storeIntent)
+ } catch (activityNotFoundException: ActivityNotFoundException) {
+ /* no app store found exception */
+ Toast.makeText(requireContext(), requireContext().getString(R.string.null_app_store_message),
+ Toast.LENGTH_SHORT).show()
+ activityNotFoundException.printStackTrace()
+ }
+ this.dismiss()
+ }
+
+ /* launch app as a freeform window */
+ private fun freeform() {
+ val freeformIntent = requireContext().packageManager.getLaunchIntentForPackage(packageName)
+ freeformIntent!!.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT or
+ Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
+ val rect = Rect(0, screenHeight / 2, screenWidth, screenHeight)
+ var activityOptions = activityOptions
+ activityOptions = activityOptions.setLaunchBounds(rect)
+ requireContext().startActivity(freeformIntent, activityOptions.toBundle())
+ this.dismiss()
+ }
+
+ /* open android's app info screen */
+ private fun appInfo() {
+ val infoIntent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
+ infoIntent.data = Uri.parse("package:$packageName")
+ infoIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ requireContext().startActivity(infoIntent)
+ this.dismiss()
+ }
+
+ private fun share() {
+ try {
+ // Create a temporary file to copy the APK
+ val apkLabel = packageManager.getApplicationLabel(appInfo).toString().lowercase().replace(" ", "_")
+ val tempApkFile = File(requireContext().externalCacheDir, "$apkLabel.apk")
+
+ // Copy the APK file
+ FileInputStream(File(appInfo.sourceDir)).use { `in` ->
+ FileOutputStream(tempApkFile).use { out ->
+ val buffer = ByteArray(1024)
+ var length: Int
+ while (`in`.read(buffer).also { length = it } > 0) {
+ out.write(buffer, 0, length)
+ }
+ }
+ }
+
+ // Generate a content URI using FileProvider
+ val contentUri =
+ FileProvider.getUriForFile(requireContext(), "${requireContext().packageName}.fileprovider", tempApkFile)
+
+ //requireContext().grantUriPermission(receivers.package.name, contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
+
+ // Create a Share Intent
+ Intent(Intent.ACTION_SEND).apply {
+ type = "application/vnd.android.package-archive"
+ putExtra(Intent.EXTRA_STREAM, contentUri)
+ addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ }.let {
+ // Start the chooser activity
+ startActivity(Intent.createChooser(it, getString(R.string.share_apk_message)))
+ }
+ }
+ catch (e: PackageManager.NameNotFoundException) { e.printStackTrace() }
+ catch (e: IOException) { e.printStackTrace() }
+ this.dismiss()
+ }
+
+ /* uninstall the app */
+ private fun uninstall() {
+ val uninstallIntent = Intent(Intent.ACTION_DELETE)
+ uninstallIntent.data = Uri.parse("package:$packageName")
+ uninstallIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
+ requireContext().startActivity(uninstallIntent)
+ this.dismiss()
+ }
+
+ /* create and add an outlined button to the toggle group */
+ private val outlinedButton: MaterialButton get() {
+ val style = com.google.android.material.R.attr.materialButtonOutlinedStyle
+ val button = MaterialButton(requireContext(), null, style)
+ button.layoutParams = LinearLayoutCompat.LayoutParams(
+ LinearLayoutCompat.LayoutParams.WRAP_CONTENT,
+ LinearLayoutCompat.LayoutParams.WRAP_CONTENT, 1F
+ )
+ binding.favGroup.addView(button)
+ return button
+ }
+
+ /* long value to local date-time format */
+ private fun dateTimeFormat(long: Long) : String = SimpleDateFormat.getDateTimeInstance().format(Date(long))
+
+ /* get and arrange all the permissions for an application */
+ private val permissionsList : String get() {
+ val packageInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ packageManager.getPackageInfo(packageName, PackageManager.PackageInfoFlags.of(PackageManager.GET_PERMISSIONS.toLong()))
+ } else {
+ packageManager.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS)
+ }
+
+ return if (packageInfo.requestedPermissions.isNotEmpty()) {
+ val stringBuilder = StringBuilder()
+ packageInfo.requestedPermissions.indices.forEach { i: Int ->
+ if (i != packageInfo.requestedPermissions.size - 1)
+ stringBuilder.append("${packageInfo.requestedPermissions[i]}\n\n")
+ /* don't add any new line after the last entry */
+ else
+ stringBuilder.append(packageInfo.requestedPermissions[i])
+ }
+ stringBuilder.toString()
+ } else {
+ ""
+ }
+ }
+
+ /* get activity options for launching app in freeform mode */
+ private val activityOptions: ActivityOptions get() {
+ val activityOptions = ActivityOptions.makeBasic()
+ try {
+ val method =
+ ActivityOptions::class.java.getMethod("setLaunchWindowingMode", Int::class.javaPrimitiveType)
+ method.invoke(activityOptions, 5)
+ } catch (exception: Exception) {
+ exception.printStackTrace()
+ }
+ return activityOptions
+ }
+
+}
diff --git a/app/src/main/kotlin/rasel/lunar/launcher/apps/SimpleGesture.kt b/app/src/main/kotlin/rasel/lunar/launcher/apps/SimpleGesture.kt
deleted file mode 100644
index f5b8a66..0000000
--- a/app/src/main/kotlin/rasel/lunar/launcher/apps/SimpleGesture.kt
+++ /dev/null
@@ -1,705 +0,0 @@
-package rasel.lunar.launcher.apps
-
-import android.os.SystemClock
-import android.util.Log
-import android.view.MotionEvent
-import android.view.View
-import android.view.View.OnTouchListener
-import kotlin.math.abs
-import kotlin.math.pow
-import kotlin.math.sqrt
-
-class GestureAnalyser @JvmOverloads constructor(
- swipeSlopeIntolerance: Int = 3,
- doubleTapMaxDelayMillis: Int = 500,
- doubleTapMaxDownMillis: Int = 100
-) {
- private val initialX = DoubleArray(5)
- private val initialY = DoubleArray(5)
- private val finalX = DoubleArray(5)
- private val finalY = DoubleArray(5)
- private val currentX = DoubleArray(5)
- private val currentY = DoubleArray(5)
- private val delX = DoubleArray(5)
- private val delY = DoubleArray(5)
-
- private var numFingers = 0
- private var initialT: Long = 0
- private var finalT: Long = 0
- private var currentT: Long = 0
-
- private var prevInitialT: Long = 0
- private var prevFinalT: Long = 0
-
- private var swipeSlopeIntolerance = 3
-
- private val doubleTapMaxDelayMillis: Long
- private val doubleTapMaxDownMillis: Long
-
- init {
- this.swipeSlopeIntolerance = swipeSlopeIntolerance
- this.doubleTapMaxDownMillis = doubleTapMaxDownMillis.toLong()
- this.doubleTapMaxDelayMillis = doubleTapMaxDelayMillis.toLong()
- }
-
- fun trackGesture(ev: MotionEvent) {
- val n = ev.pointerCount
- for (i in 0 until n) {
- initialX[i] = ev.getX(i).toDouble()
- initialY[i] = ev.getY(i).toDouble()
- }
- numFingers = n
- initialT = SystemClock.uptimeMillis()
- }
-
- fun untrackGesture() {
- numFingers = 0
- prevFinalT = SystemClock.uptimeMillis()
- prevInitialT = initialT
- }
-
- fun getGesture(ev: MotionEvent): GestureType {
- var averageDistance = 0.0
- for (i in 0 until numFingers) {
- finalX[i] = ev.getX(i).toDouble()
- finalY[i] = ev.getY(i).toDouble()
- delX[i] = finalX[i] - initialX[i]
- delY[i] = finalY[i] - initialY[i]
-
- averageDistance += sqrt(
- (finalX[i] - initialX[i]).pow(2.0) + (finalY[i] - initialY[i]).pow(
- 2.0
- )
- )
- }
- averageDistance /= numFingers.toDouble()
-
- finalT = SystemClock.uptimeMillis()
- val gt = GestureType()
- gt.gestureFlag = calcGesture()
- gt.gestureDuration = finalT - initialT
- gt.gestureDistance = averageDistance
- return gt
- }
-
- fun getOngoingGesture(ev: MotionEvent): Int {
- for (i in 0 until numFingers) {
- currentX[i] = ev.getX(i).toDouble()
- currentY[i] = ev.getY(i).toDouble()
- delX[i] = finalX[i] - initialX[i]
- delY[i] = finalY[i] - initialY[i]
- }
- currentT = SystemClock.uptimeMillis()
- return calcGesture()
- }
-
- private fun calcGesture(): Int {
- if (isDoubleTap) {
- return DOUBLE_TAP_1
- }
-
- if (numFingers == 1) {
- if ((-(delY[0])) > (swipeSlopeIntolerance * (abs(
- delX[0]
- )))
- ) {
- return SWIPE_1_UP
- }
-
- if (((delY[0])) > (swipeSlopeIntolerance * (abs(
- delX[0]
- )))
- ) {
- return SWIPE_1_DOWN
- }
-
- if ((-(delX[0])) > (swipeSlopeIntolerance * (abs(
- delY[0]
- )))
- ) {
- return SWIPE_1_LEFT
- }
-
- if (((delX[0])) > (swipeSlopeIntolerance * (abs(
- delY[0]
- )))
- ) {
- return SWIPE_1_RIGHT
- }
- }
- if (numFingers == 2) {
- if (((-delY[0]) > (swipeSlopeIntolerance * abs(
- delX[0]
- ))) && ((-delY[1]) > (swipeSlopeIntolerance * abs(
- delX[1]
- )))
- ) {
- return SWIPE_2_UP
- }
- if (((delY[0]) > (swipeSlopeIntolerance * abs(
- delX[0]
- ))) && ((delY[1]) > (swipeSlopeIntolerance * abs(
- delX[1]
- )))
- ) {
- return SWIPE_2_DOWN
- }
- if (((-delX[0]) > (swipeSlopeIntolerance * abs(
- delY[0]
- ))) && ((-delX[1]) > (swipeSlopeIntolerance * abs(
- delY[1]
- )))
- ) {
- return SWIPE_2_LEFT
- }
- if (((delX[0]) > (swipeSlopeIntolerance * abs(
- delY[0]
- ))) && ((delX[1]) > (swipeSlopeIntolerance * abs(
- delY[1]
- )))
- ) {
- return SWIPE_2_RIGHT
- }
- if (finalFingDist(0, 1) > 2 * (initialFingDist(0, 1))) {
- return UNPINCH_2
- }
- if (finalFingDist(0, 1) < 0.5 * (initialFingDist(0, 1))) {
- return PINCH_2
- }
- }
- if (numFingers == 3) {
- if (((-delY[0]) > (swipeSlopeIntolerance * abs(
- delX[0]
- )))
- && ((-delY[1]) > (swipeSlopeIntolerance * abs(
- delX[1]
- )))
- && ((-delY[2]) > (swipeSlopeIntolerance * abs(
- delX[2]
- )))
- ) {
- return SWIPE_3_UP
- }
- if (((delY[0]) > (swipeSlopeIntolerance * abs(
- delX[0]
- )))
- && ((delY[1]) > (swipeSlopeIntolerance * abs(
- delX[1]
- )))
- && ((delY[2]) > (swipeSlopeIntolerance * abs(
- delX[2]
- )))
- ) {
- return SWIPE_3_DOWN
- }
- if (((-delX[0]) > (swipeSlopeIntolerance * abs(
- delY[0]
- )))
- && ((-delX[1]) > (swipeSlopeIntolerance * abs(
- delY[1]
- )))
- && ((-delX[2]) > (swipeSlopeIntolerance * abs(
- delY[2]
- )))
- ) {
- return SWIPE_3_LEFT
- }
- if (((delX[0]) > (swipeSlopeIntolerance * abs(
- delY[0]
- )))
- && ((delX[1]) > (swipeSlopeIntolerance * abs(
- delY[1]
- )))
- && ((delX[2]) > (swipeSlopeIntolerance * abs(
- delY[2]
- )))
- ) {
- return SWIPE_3_RIGHT
- }
-
- if ((finalFingDist(0, 1) > 1.75 * (initialFingDist(0, 1)))
- && (finalFingDist(1, 2) > 1.75 * (initialFingDist(1, 2)))
- && (finalFingDist(2, 0) > 1.75 * (initialFingDist(2, 0)))
- ) {
- return UNPINCH_3
- }
- if ((finalFingDist(0, 1) < 0.66 * (initialFingDist(0, 1)))
- && (finalFingDist(1, 2) < 0.66 * (initialFingDist(1, 2)))
- && (finalFingDist(2, 0) < 0.66 * (initialFingDist(2, 0)))
- ) {
- return PINCH_3
- }
- }
- if (numFingers == 4) {
- if (((-delY[0]) > (swipeSlopeIntolerance * abs(
- delX[0]
- )))
- && ((-delY[1]) > (swipeSlopeIntolerance * abs(
- delX[1]
- )))
- && ((-delY[2]) > (swipeSlopeIntolerance * abs(
- delX[2]
- )))
- && ((-delY[3]) > (swipeSlopeIntolerance * abs(
- delX[3]
- )))
- ) {
- return SWIPE_4_UP
- }
- if (((delY[0]) > (swipeSlopeIntolerance * abs(
- delX[0]
- )))
- && ((delY[1]) > (swipeSlopeIntolerance * abs(
- delX[1]
- )))
- && ((delY[2]) > (swipeSlopeIntolerance * abs(
- delX[2]
- )))
- && ((delY[3]) > (swipeSlopeIntolerance * abs(
- delX[3]
- )))
- ) {
- return SWIPE_4_DOWN
- }
- if (((-delX[0]) > (swipeSlopeIntolerance * abs(
- delY[0]
- )))
- && ((-delX[1]) > (swipeSlopeIntolerance * abs(
- delY[1]
- )))
- && ((-delX[2]) > (swipeSlopeIntolerance * abs(
- delY[2]
- )))
- && ((-delX[3]) > (swipeSlopeIntolerance * abs(
- delY[3]
- )))
- ) {
- return SWIPE_4_LEFT
- }
- if (((delX[0]) > (swipeSlopeIntolerance * abs(
- delY[0]
- )))
- && ((delX[1]) > (swipeSlopeIntolerance * abs(
- delY[1]
- )))
- && ((delX[2]) > (swipeSlopeIntolerance * abs(
- delY[2]
- )))
- && ((delX[3]) > (swipeSlopeIntolerance * abs(
- delY[3]
- )))
- ) {
- return SWIPE_4_RIGHT
- }
- if ((finalFingDist(0, 1) > 1.5 * (initialFingDist(0, 1)))
- && (finalFingDist(1, 2) > 1.5 * (initialFingDist(1, 2)))
- && (finalFingDist(2, 3) > 1.5 * (initialFingDist(2, 3)))
- && (finalFingDist(3, 0) > 1.5 * (initialFingDist(3, 0)))
- ) {
- return UNPINCH_4
- }
- if ((finalFingDist(0, 1) < 0.8 * (initialFingDist(0, 1)))
- && (finalFingDist(1, 2) < 0.8 * (initialFingDist(1, 2)))
- && (finalFingDist(2, 3) < 0.8 * (initialFingDist(2, 3)))
- && (finalFingDist(3, 0) < 0.8 * (initialFingDist(3, 0)))
- ) {
- return PINCH_4
- }
- }
- return 0
- }
-
- private fun initialFingDist(fingNum1: Int, fingNum2: Int): Double {
- return sqrt(
- (initialX[fingNum1] - initialX[fingNum2]).pow(2.0) + (initialY[fingNum1] - initialY[fingNum2]).pow(
- 2.0
- )
- )
- }
-
- private fun finalFingDist(fingNum1: Int, fingNum2: Int): Double {
- return sqrt(
- (finalX[fingNum1] - finalX[fingNum2]).pow(2.0) + (finalY[fingNum1] - finalY[fingNum2]).pow(
- 2.0
- )
- )
- }
-
- val isDoubleTap: Boolean
- get() = if (initialT - prevFinalT < doubleTapMaxDelayMillis && finalT - initialT < doubleTapMaxDownMillis && prevFinalT - prevInitialT < doubleTapMaxDownMillis) {
- true
- } else {
- false
- }
-
- inner class GestureType {
- var gestureFlag: Int = 0
- var gestureDuration: Long = 0
-
- var gestureDistance: Double = 0.0
- }
-
-
- companion object {
- const val DEBUG: Boolean = true
-
- // Finished gestures flags
- const val SWIPE_1_UP: Int = 11
- const val SWIPE_1_DOWN: Int = 12
- const val SWIPE_1_LEFT: Int = 13
- const val SWIPE_1_RIGHT: Int = 14
- const val SWIPE_2_UP: Int = 21
- const val SWIPE_2_DOWN: Int = 22
- const val SWIPE_2_LEFT: Int = 23
- const val SWIPE_2_RIGHT: Int = 24
- const val SWIPE_3_UP: Int = 31
- const val SWIPE_3_DOWN: Int = 32
- const val SWIPE_3_LEFT: Int = 33
- const val SWIPE_3_RIGHT: Int = 34
- const val SWIPE_4_UP: Int = 41
- const val SWIPE_4_DOWN: Int = 42
- const val SWIPE_4_LEFT: Int = 43
- const val SWIPE_4_RIGHT: Int = 44
- const val PINCH_2: Int = 25
- const val UNPINCH_2: Int = 26
- const val PINCH_3: Int = 35
- const val UNPINCH_3: Int = 36
- const val PINCH_4: Int = 45
- const val UNPINCH_4: Int = 46
-
- const val DOUBLE_TAP_1: Int = 107
-
- //Ongoing gesture flags
- const val SWIPING_1_UP: Int = 101
- const val SWIPING_1_DOWN: Int = 102
- const val SWIPING_1_LEFT: Int = 103
- const val SWIPING_1_RIGHT: Int = 104
- const val SWIPING_2_UP: Int = 201
- const val SWIPING_2_DOWN: Int = 202
- const val SWIPING_2_LEFT: Int = 203
- const val SWIPING_2_RIGHT: Int = 204
- const val PINCHING: Int = 205
- const val UNPINCHING: Int = 206
- private const val TAG = "GestureAnalyser"
- }
-}
-
-class SimpleFingerGestures : OnTouchListener {
- private var debug = true
- var consumeTouchEvents: Boolean = false
-
- protected var tracking: BooleanArray = booleanArrayOf(false, false, false, false, false)
- private var ga: GestureAnalyser
- private var onFingerGestureListener: OnFingerGestureListener? = null
-
-
- /**
- * Constructor that creates an internal [in.championswimmer.sfg.lib.GestureAnalyser] object as well
- */
- constructor() {
- ga = GestureAnalyser()
- }
-
- constructor(
- swipeSlopeIntolerance: Int,
- doubleTapMaxDelayMillis: Int,
- doubleTapMaxDownMillis: Int
- ) {
- ga = GestureAnalyser(swipeSlopeIntolerance, doubleTapMaxDelayMillis, doubleTapMaxDownMillis)
- }
-
- fun setDebug(debug: Boolean) {
- this.debug = debug
- }
-
- constructor(omfgl: OnFingerGestureListener?) {
- ga = GestureAnalyser()
- setOnFingerGestureListener(omfgl)
- }
-
- /**
- * Register a callback to be invoked when multi-finger gestures take place
- *
- *
- *
- *
- *
- * For the callbacks implemented via this, check the interface [in.championswimmer.sfg.lib.SimpleFingerGestures.OnFingerGestureListener]
- *
- *
- * @param omfgl The callback that will run
- */
- fun setOnFingerGestureListener(omfgl: OnFingerGestureListener?) {
- onFingerGestureListener = omfgl
- }
-
-
- override fun onTouch(view: View, ev: MotionEvent): Boolean {
- if (debug) Log.d(TAG, "onTouch")
- when (ev.action and MotionEvent.ACTION_MASK) {
- MotionEvent.ACTION_DOWN -> {
- if (debug) Log.d(TAG, "ACTION_DOWN")
- startTracking(0)
- ga.trackGesture(ev)
- return consumeTouchEvents
- }
-
- MotionEvent.ACTION_UP -> {
- if (debug) Log.d(TAG, "ACTION_UP")
- if (tracking[0]) {
- doCallBack(ga.getGesture(ev))
- }
- stopTracking(0)
- ga.untrackGesture()
- return consumeTouchEvents
- }
-
- MotionEvent.ACTION_POINTER_DOWN -> {
- if (debug) Log.d(TAG, "ACTION_POINTER_DOWN" + " " + "num" + ev.pointerCount)
- startTracking(ev.pointerCount - 1)
- ga.trackGesture(ev)
- return consumeTouchEvents
- }
-
- MotionEvent.ACTION_POINTER_UP -> {
- if (debug) Log.d(TAG, "ACTION_POINTER_UP" + " " + "num" + ev.pointerCount)
- if (tracking[1]) {
- doCallBack(ga.getGesture(ev))
- }
- stopTracking(ev.pointerCount - 1)
- ga.untrackGesture()
- return consumeTouchEvents
- }
-
- MotionEvent.ACTION_CANCEL -> {
- if (debug) Log.d(TAG, "ACTION_CANCEL")
- return true
- }
-
- MotionEvent.ACTION_MOVE -> {
- if (debug) Log.d(TAG, "ACTION_MOVE")
- return consumeTouchEvents
- }
- }
- return consumeTouchEvents
- }
-
- private fun doCallBack(mGt: GestureAnalyser.GestureType) {
- when (mGt.gestureFlag) {
- GestureAnalyser.SWIPE_1_UP -> onFingerGestureListener!!.onSwipeUp(
- 1,
- mGt.gestureDuration,
- mGt.gestureDistance
- )
-
- GestureAnalyser.SWIPE_1_DOWN -> onFingerGestureListener!!.onSwipeDown(
- 1,
- mGt.gestureDuration,
- mGt.gestureDistance
- )
-
- GestureAnalyser.SWIPE_1_LEFT -> onFingerGestureListener!!.onSwipeLeft(
- 1,
- mGt.gestureDuration,
- mGt.gestureDistance
- )
-
- GestureAnalyser.SWIPE_1_RIGHT -> onFingerGestureListener!!.onSwipeRight(
- 1,
- mGt.gestureDuration,
- mGt.gestureDistance
- )
-
- GestureAnalyser.SWIPE_2_UP -> onFingerGestureListener!!.onSwipeUp(
- 2,
- mGt.gestureDuration,
- mGt.gestureDistance
- )
-
- GestureAnalyser.SWIPE_2_DOWN -> onFingerGestureListener!!.onSwipeDown(
- 2,
- mGt.gestureDuration,
- mGt.gestureDistance
- )
-
- GestureAnalyser.SWIPE_2_LEFT -> onFingerGestureListener!!.onSwipeLeft(
- 2,
- mGt.gestureDuration,
- mGt.gestureDistance
- )
-
- GestureAnalyser.SWIPE_2_RIGHT -> onFingerGestureListener!!.onSwipeRight(
- 2,
- mGt.gestureDuration,
- mGt.gestureDistance
- )
-
- GestureAnalyser.PINCH_2 -> onFingerGestureListener!!.onPinch(
- 2,
- mGt.gestureDuration,
- mGt.gestureDistance
- )
-
- GestureAnalyser.UNPINCH_2 -> onFingerGestureListener!!.onUnpinch(
- 2,
- mGt.gestureDuration,
- mGt.gestureDistance
- )
-
- GestureAnalyser.SWIPE_3_UP -> onFingerGestureListener!!.onSwipeUp(
- 3,
- mGt.gestureDuration,
- mGt.gestureDistance
- )
-
- GestureAnalyser.SWIPE_3_DOWN -> onFingerGestureListener!!.onSwipeDown(
- 3,
- mGt.gestureDuration,
- mGt.gestureDistance
- )
-
- GestureAnalyser.SWIPE_3_LEFT -> onFingerGestureListener!!.onSwipeLeft(
- 3,
- mGt.gestureDuration,
- mGt.gestureDistance
- )
-
- GestureAnalyser.SWIPE_3_RIGHT -> onFingerGestureListener!!.onSwipeRight(
- 3,
- mGt.gestureDuration,
- mGt.gestureDistance
- )
-
- GestureAnalyser.PINCH_3 -> onFingerGestureListener!!.onPinch(
- 3,
- mGt.gestureDuration,
- mGt.gestureDistance
- )
-
- GestureAnalyser.UNPINCH_3 -> onFingerGestureListener!!.onUnpinch(
- 3,
- mGt.gestureDuration,
- mGt.gestureDistance
- )
-
- GestureAnalyser.SWIPE_4_UP -> onFingerGestureListener!!.onSwipeUp(
- 4,
- mGt.gestureDuration,
- mGt.gestureDistance
- )
-
- GestureAnalyser.SWIPE_4_DOWN -> onFingerGestureListener!!.onSwipeDown(
- 4,
- mGt.gestureDuration,
- mGt.gestureDistance
- )
-
- GestureAnalyser.SWIPE_4_LEFT -> onFingerGestureListener!!.onSwipeLeft(
- 4,
- mGt.gestureDuration,
- mGt.gestureDistance
- )
-
- GestureAnalyser.SWIPE_4_RIGHT -> onFingerGestureListener!!.onSwipeRight(
- 4,
- mGt.gestureDuration,
- mGt.gestureDistance
- )
-
- GestureAnalyser.PINCH_4 -> onFingerGestureListener!!.onPinch(
- 4,
- mGt.gestureDuration,
- mGt.gestureDistance
- )
-
- GestureAnalyser.UNPINCH_4 -> {
- onFingerGestureListener!!.onUnpinch(4, mGt.gestureDuration, mGt.gestureDistance)
- onFingerGestureListener!!.onDoubleTap(1)
- }
-
- GestureAnalyser.DOUBLE_TAP_1 -> onFingerGestureListener!!.onDoubleTap(1)
- }
- }
-
- private fun startTracking(nthPointer: Int) {
- for (i in 0..nthPointer) {
- tracking[i] = true
- }
- }
-
- private fun stopTracking(nthPointer: Int) {
- for (i in nthPointer until tracking.size) {
- tracking[i] = false
- }
- }
-
-
- /**
- * Interface definition for the callback to be invoked when 2-finger gestures are performed
- */
- interface OnFingerGestureListener {
- /**
- * Called when user swipes **up** with two fingers
- *
- * @param fingers number of fingers involved in this gesture
- * @param gestureDuration duration in milliSeconds
- * @return
- */
- fun onSwipeUp(fingers: Int, gestureDuration: Long, gestureDistance: Double): Boolean
-
- /**
- * Called when user swipes **down** with two fingers
- *
- * @param fingers number of fingers involved in this gesture
- * @param gestureDuration duration in milliSeconds
- * @return
- */
- fun onSwipeDown(fingers: Int, gestureDuration: Long, gestureDistance: Double): Boolean
-
- /**
- * Called when user swipes **left** with two fingers
- *
- * @param fingers number of fingers involved in this gesture
- * @param gestureDuration duration in milliSeconds
- * @return
- */
- fun onSwipeLeft(fingers: Int, gestureDuration: Long, gestureDistance: Double): Boolean
-
- /**
- * Called when user swipes **right** with two fingers
- *
- * @param fingers number of fingers involved in this gesture
- * @param gestureDuration duration in milliSeconds
- * @return
- */
- fun onSwipeRight(fingers: Int, gestureDuration: Long, gestureDistance: Double): Boolean
-
- /**
- * Called when user **pinches** with two fingers (bring together)
- *
- * @param fingers number of fingers involved in this gesture
- * @param gestureDuration duration in milliSeconds
- * @return
- */
- fun onPinch(fingers: Int, gestureDuration: Long, gestureDistance: Double): Boolean
-
- /**
- * Called when user **un-pinches** with two fingers (take apart)
- *
- * @param fingers number of fingers involved in this gesture
- * @param gestureDuration duration in milliSeconds
- * @return
- */
- fun onUnpinch(fingers: Int, gestureDuration: Long, gestureDistance: Double): Boolean
-
- fun onDoubleTap(fingers: Int): Boolean
- }
-
- companion object {
- // Will see if these need to be used. For now just returning duration in milliS
- const val GESTURE_SPEED_SLOW: Long = 1500
- const val GESTURE_SPEED_MEDIUM: Long = 1000
- const val GESTURE_SPEED_FAST: Long = 500
- private const val TAG = "SimpleFingerGestures"
- }
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/rasel/lunar/launcher/home/BatteryReceiver.kt b/app/src/main/kotlin/rasel/lunar/launcher/home/BatteryReceiver.kt
index a24b1fa..99e204e 100644
--- a/app/src/main/kotlin/rasel/lunar/launcher/home/BatteryReceiver.kt
+++ b/app/src/main/kotlin/rasel/lunar/launcher/home/BatteryReceiver.kt
@@ -43,16 +43,9 @@ internal class BatteryReceiver(private val progressBar: CircularProgressIndicato
override fun onReceive(context: Context?, intent: Intent?) {
val animationDuration = try {
- intent?.extras?.keySet()?.let {
- it.iterator().forEach {
- BLog.LOGE("intent key >> ${it}")
- }
- }
- BLog.LOGE("intent >> ${intent}")
-
Settings.Global.getFloat(context?.contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1.0f)
} catch (e: Settings.SettingNotFoundException) {
- e.printStackTrace()
+// e.printStackTrace()
}
/* set battery percentage value to the circular progress bar */
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 d9f838f..fbaee71 100644
--- a/app/src/main/kotlin/rasel/lunar/launcher/home/LauncherHome.kt
+++ b/app/src/main/kotlin/rasel/lunar/launcher/home/LauncherHome.kt
@@ -53,6 +53,7 @@ import rasel.lunar.launcher.qaccess.QuickAccess
import rasel.lunar.launcher.settings.SettingsActivity
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.util.*
@@ -216,7 +217,7 @@ internal class LauncherHome : Fragment() {
return false
}
- override fun onLongPress(targetView: View): Boolean {
+ override fun onLongPress(targetView: View,fingers: Int): Boolean {
if (view?.equals(binding.batteryProgress) ?: false) {
lActivity!!.startActivity(Intent(requireContext(), SettingsActivity::class.java))
} else if (view?.equals(binding.notes) ?: false) {
@@ -238,14 +239,24 @@ internal class LauncherHome : Fragment() {
return false
}
- override fun onClick(targetView: View): Boolean {
- if (view?.equals(binding.batteryProgress) ?: false) {
- requireContext().startActivity(
- Intent(AlarmClock.ACTION_SHOW_ALARMS).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- )
- } else if (view?.equals(binding.batteryProgress) ?: false) {
+ override fun onClick(targetView: View,fingers: Int): Boolean {
+ BLog.LOGE("onClick ${view} , fingers ${fingers}")
+ when(fingers) {
+ 1 -> {
+ if (view?.equals(binding.batteryProgress) ?: false && fingers == 1) {
+ requireContext().startActivity(
+ Intent(AlarmClock.ACTION_SHOW_ALARMS).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ )
+ }
+ }
+ 2-> {
+ lockMethod(settingsPrefs.getInt(KEY_LOCK_METHOD, 0), requireContext(), binding.favAppsGroup)
+ }
+ else -> {
+ }
}
+
return false
}
diff --git a/app/src/main/kotlin/rasel/lunar/launcher/home/weather/WeatherExecutor.kt b/app/src/main/kotlin/rasel/lunar/launcher/home/weather/WeatherExecutor.kt
index 9e597e9..f2097af 100644
--- a/app/src/main/kotlin/rasel/lunar/launcher/home/weather/WeatherExecutor.kt
+++ b/app/src/main/kotlin/rasel/lunar/launcher/home/weather/WeatherExecutor.kt
@@ -37,6 +37,7 @@ internal class WeatherExecutor(sharedPreferences: SharedPreferences) {
companion object {
var lastedCheckTime = 0L
+ var weather: Weather? = null
}
private val cityName: String
@@ -53,7 +54,6 @@ internal class WeatherExecutor(sharedPreferences: SharedPreferences) {
if (System.currentTimeMillis() - lastedCheckTime > (1000 * 60 * 15) && isNetworkAvailable && cityName.isNotEmpty() && owmApi.isNotEmpty()) {
try {
Executors.newSingleThreadExecutor().execute {
- var weather: Weather? = null
WeatherClient().fetchWeather(weatherUrl).let {
if (!it.isNullOrEmpty()) weather = JsonParser().getMyWeather(it)
}
@@ -72,6 +72,17 @@ internal class WeatherExecutor(sharedPreferences: SharedPreferences) {
} catch (exception: Exception) {
exception.printStackTrace()
}
+ } else {
+ Handler(Looper.getMainLooper()).post {
+ if (weather != null) {
+ materialTextView.apply {
+ visibility = View.VISIBLE
+ text = weather!!.temperature.toString().substringBefore(".") +
+ (if (tempUnit == 0) "ºC" else "ºF") +
+ (if (showCity) " at ${weather!!.cityName}" else "")
+ }
+ }
+ }
}
lastedCheckTime = System.currentTimeMillis()
}
diff --git a/app/src/main/kotlin/rasel/lunar/launcher/utils/MainActivity.kt b/app/src/main/kotlin/rasel/lunar/launcher/utils/MainActivity.kt
new file mode 100644
index 0000000..85dfd77
--- /dev/null
+++ b/app/src/main/kotlin/rasel/lunar/launcher/utils/MainActivity.kt
@@ -0,0 +1,78 @@
+//package com.example.ch16_provider
+//
+//import android.content.Intent
+//import android.content.pm.PackageManager
+//import android.os.Bundle
+//import android.provider.ContactsContract
+//import android.util.Log
+//import androidx.activity.result.ActivityResultLauncher
+//import androidx.activity.result.contract.ActivityResultContracts
+//import androidx.appcompat.app.AppCompatActivity
+//import androidx.core.app.ActivityCompat
+//import androidx.core.content.ContextCompat
+//import com.example.ch16_provider.databinding.ActivityMainBinding
+//
+//class MainActivity : AppCompatActivity() {
+// lateinit var binding: ActivityMainBinding
+// lateinit var requestLauncher: ActivityResultLauncher
+//
+// override fun onCreate(savedInstanceState: Bundle?) {
+// super.onCreate(savedInstanceState)
+// binding = ActivityMainBinding.inflate(layoutInflater)
+// setContentView(binding.root)
+//
+// // 퍼미션 허용했는지 확인
+// val status = ContextCompat.checkSelfPermission(this, "android.permission.READ_CONTACTS")
+// if (status == PackageManager.PERMISSION_GRANTED) {
+// Log.d("test", "permission granted")
+// } else {
+// // 퍼미션 요청 다이얼로그 표시
+// ActivityCompat.requestPermissions(this, arrayOf("android.permission.READ_CONTACTS"), 100)
+// Log.d("test", "permission denied")
+// }
+//
+// // ActivityResultLauncher 초기화, 결과 콜백 정의
+// requestLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
+// if (it.resultCode == RESULT_OK) {
+// Log.d("test", "Uri : ${it.data!!.data!!}")
+// val cursor = contentResolver.query(
+// it.data!!.data!!,
+// arrayOf(
+// ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
+// ContactsContract.CommonDataKinds.Phone.NUMBER,
+// ),
+// null,
+// null,
+// null
+// )
+// Log.d("test", "cursor size : ${cursor?.count}")
+//
+// if (cursor!!.moveToFirst()) {
+// val name = cursor.getString(0)
+// val phone = cursor.getString(1)
+// binding.textView.text = "name: $name, phone: $phone"
+// }
+// }
+// }
+//
+// binding.button.setOnClickListener {
+// // 주소록 앱 연동
+// val intent = Intent(Intent.ACTION_PICK, ContactsContract.CommonDataKinds.Phone.CONTENT_URI)
+// requestLauncher.launch(intent)
+// }
+// }
+//
+// // 다이얼로그에서 퍼미션 허용했는지 확인
+// override fun onRequestPermissionsResult(
+// requestCode: Int,
+// permissions: Array,
+// grantResults: IntArray
+// ) {
+// super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+// if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+// Log.d("test", "permission granted")
+// } else {
+// Log.d("test", "permission denied")
+// }
+// }
+//}
\ No newline at end of file
diff --git a/app/src/main/kotlin/rasel/lunar/launcher/utils/SimpleGesture.kt b/app/src/main/kotlin/rasel/lunar/launcher/utils/SimpleGesture.kt
index 8019ea2..c42501f 100644
--- a/app/src/main/kotlin/rasel/lunar/launcher/utils/SimpleGesture.kt
+++ b/app/src/main/kotlin/rasel/lunar/launcher/utils/SimpleGesture.kt
@@ -133,7 +133,7 @@ class GestureAnalyser @JvmOverloads constructor(
BLog.LOGE("initialT = ${initialT} , finalT = ${finalT} , result = ${finalT - initialT}")
if (finalT - initialT < 300) {
return CLICK_1
- } else if(finalT - initialT > 600) {
+ } else if(finalT - initialT > doubleTapMaxDelayMillis) {
return LONG_CLICK_1
}
}
@@ -176,6 +176,11 @@ class GestureAnalyser @JvmOverloads constructor(
if (finalFingDist(0, 1) < 0.5 * (initialFingDist(0, 1))) {
return PINCH_2
}
+ if (finalT - initialT < 300) {
+ return CLICK_2
+ } else if(finalT - initialT > doubleTapMaxDelayMillis) {
+ return LONG_CLICK_2
+ }
}
if (numFingers == 3) {
if (((-delY[0]) > (swipeSlopeIntolerance * abs(
@@ -186,7 +191,7 @@ class GestureAnalyser @JvmOverloads constructor(
)))
&& ((-delY[2]) > (swipeSlopeIntolerance * abs(
delX[2]
- )))
+ ))) && (abs(delY[0]) > minValue || abs(delY[1]) > minValue && abs(delY[2]) > minValue)
) {
return SWIPE_3_UP
}
@@ -198,7 +203,7 @@ class GestureAnalyser @JvmOverloads constructor(
)))
&& ((delY[2]) > (swipeSlopeIntolerance * abs(
delX[2]
- )))
+ ))) && (abs(delY[0]) > minValue || abs(delY[1]) > minValue && abs(delY[2]) > minValue)
) {
return SWIPE_3_DOWN
}
@@ -210,7 +215,7 @@ class GestureAnalyser @JvmOverloads constructor(
)))
&& ((-delX[2]) > (swipeSlopeIntolerance * abs(
delY[2]
- )))
+ ))) && (abs(delX[0]) > minValue || abs(delX[1]) > minValue && abs(delX[2]) > minValue)
) {
return SWIPE_3_LEFT
}
@@ -222,7 +227,7 @@ class GestureAnalyser @JvmOverloads constructor(
)))
&& ((delX[2]) > (swipeSlopeIntolerance * abs(
delY[2]
- )))
+ ))) && (abs(delX[0]) > minValue || abs(delX[1]) > minValue && abs(delX[2]) > minValue)
) {
return SWIPE_3_RIGHT
}
@@ -239,6 +244,12 @@ class GestureAnalyser @JvmOverloads constructor(
) {
return PINCH_3
}
+
+ if (finalT - initialT < 300) {
+ return CLICK_3
+ } else if(finalT - initialT > doubleTapMaxDelayMillis) {
+ return LONG_CLICK_3
+ }
}
if (numFingers == 4) {
if (((-delY[0]) > (swipeSlopeIntolerance * abs(
@@ -424,8 +435,8 @@ class SimpleFingerGestures : OnTouchListener {
ga.minValue = Math.max(screenHeight, 100)
}
this.onFingerGestureListener = onFingerGestureListener
- this.targetView?.setOnClickListener { onFingerGestureListener.onClick(it) }
- this.targetView?.setOnLongClickListener { onFingerGestureListener.onLongPress(it) }
+ this.targetView?.setOnClickListener { onFingerGestureListener.onClick(it,1) }
+ this.targetView?.setOnLongClickListener { onFingerGestureListener.onLongPress(it,1) }
}
/**
* Constructor that creates an internal [in.championswimmer.sfg.lib.GestureAnalyser] object as well
@@ -673,7 +684,17 @@ class SimpleFingerGestures : OnTouchListener {
}
GestureAnalyser.CLICK_1 -> {
BLog.LOGE("GestureAnalyser.CLICK_1")
- onFingerGestureListener!!.onClick(targetView)
+ onFingerGestureListener!!.onClick(targetView, 1)
+// onFingerGestureListener!!.onDoubleTap(1)
+ }
+ GestureAnalyser.CLICK_2 -> {
+ BLog.LOGE("GestureAnalyser.CLICK_2")
+ onFingerGestureListener!!.onClick(targetView, 2)
+// onFingerGestureListener!!.onDoubleTap(1)
+ }
+ GestureAnalyser.CLICK_3 -> {
+ BLog.LOGE("GestureAnalyser.CLICK_3")
+ onFingerGestureListener!!.onClick(targetView, 3)
// onFingerGestureListener!!.onDoubleTap(1)
}
// GestureAnalyser.CLICK_2 -> {
@@ -687,9 +708,16 @@ class SimpleFingerGestures : OnTouchListener {
GestureAnalyser.LONG_CLICK_1 -> {
BLog.LOGE("GestureAnalyser.LONG_CLICK_1")
- onFingerGestureListener!!.onLongPress(targetView)
+ onFingerGestureListener!!.onLongPress(targetView,1)
+ }
+ GestureAnalyser.LONG_CLICK_2 -> {
+ BLog.LOGE("GestureAnalyser.LONG_CLICK_2")
+ onFingerGestureListener!!.onLongPress(targetView,2)
+ }
+ GestureAnalyser.LONG_CLICK_3 -> {
+ BLog.LOGE("GestureAnalyser.LONG_CLICK_3")
+ onFingerGestureListener!!.onLongPress(targetView,3)
}
-
GestureAnalyser.DOUBLE_TAP_1 -> onFingerGestureListener!!.onDoubleTap(targetView,1)
}
@@ -767,8 +795,8 @@ class SimpleFingerGestures : OnTouchListener {
fun onUnpinch(targetView : View,fingers: Int, gestureDuration: Long, gestureDistance: Double): Boolean
fun onDoubleTap(targetView : View,fingers: Int): Boolean
- fun onLongPress(targetView : View): Boolean
- fun onClick(targetView : View): Boolean
+ fun onLongPress(targetView : View, fingers: Int): Boolean
+ fun onClick(targetView : View, fingers: Int): Boolean
}
companion object {
@@ -778,4 +806,5 @@ class SimpleFingerGestures : OnTouchListener {
const val GESTURE_SPEED_FAST: Long = 500
private const val TAG = "SimpleFingerGestures"
}
-}
\ No newline at end of file
+}
+
diff --git a/app/src/main/kotlin/rasel/lunar/launcher/view/CircleImageView.kt b/app/src/main/kotlin/rasel/lunar/launcher/view/CircleImageView.kt
new file mode 100644
index 0000000..f0d1890
--- /dev/null
+++ b/app/src/main/kotlin/rasel/lunar/launcher/view/CircleImageView.kt
@@ -0,0 +1,500 @@
+package rasel.lunar.launcher.view
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.BitmapShader
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.ColorFilter
+import android.graphics.Matrix
+import android.graphics.Outline
+import android.graphics.Paint
+import android.graphics.Rect
+import android.graphics.RectF
+import android.graphics.Shader
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.Drawable
+import android.net.Uri
+import android.os.Build
+import android.util.AttributeSet
+import android.view.MotionEvent
+import android.view.View
+import android.view.ViewOutlineProvider
+import androidx.annotation.ColorRes
+import androidx.annotation.DrawableRes
+import androidx.annotation.NonNull
+import androidx.annotation.RequiresApi
+import rasel.lunar.launcher.R
+import kotlin.math.min
+import kotlin.math.pow
+
+
+class CircleImageView : androidx.appcompat.widget.AppCompatImageView {
+ private val mDrawableRect = RectF()
+ private val mBorderRect = RectF()
+
+ private val mShaderMatrix: Matrix = Matrix()
+ private val mBitmapPaint: Paint = Paint()
+ private val mBorderPaint: Paint = Paint()
+ private val mCircleBackgroundPaint: Paint = Paint()
+
+ private var mBorderColor = DEFAULT_BORDER_COLOR
+ private var mBorderWidth = DEFAULT_BORDER_WIDTH
+ private var mCircleBackgroundColor = DEFAULT_CIRCLE_BACKGROUND_COLOR
+ private var mImageAlpha = DEFAULT_IMAGE_ALPHA
+
+ private var mBitmap: Bitmap? = null
+ private var mBitmapCanvas: Canvas? = null
+
+ private var mDrawableRadius = 0f
+ private var mBorderRadius = 0f
+
+ private var mColorFilter: ColorFilter? = null
+
+ private var mInitialized = false
+ private var mRebuildShader = false
+ private var mDrawableDirty = false
+
+ private var mBorderOverlay = false
+ private var mDisableCircularTransformation = false
+
+ constructor(context: Context) : super(context) {
+ init()
+ }
+
+ @JvmOverloads
+ constructor(context: Context, attrs: AttributeSet?, defStyle: Int = 0) : super(
+ context,
+ attrs,
+ defStyle
+ ) {
+ val a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0)
+
+ mBorderWidth = a.getDimensionPixelSize(
+ R.styleable.CircleImageView_civ_border_width,
+ DEFAULT_BORDER_WIDTH
+ )
+ mBorderColor =
+ a.getColor(R.styleable.CircleImageView_civ_border_color, DEFAULT_BORDER_COLOR)
+ mBorderOverlay =
+ a.getBoolean(R.styleable.CircleImageView_civ_border_overlay, DEFAULT_BORDER_OVERLAY)
+ mCircleBackgroundColor = a.getColor(
+ R.styleable.CircleImageView_civ_circle_background_color,
+ DEFAULT_CIRCLE_BACKGROUND_COLOR
+ )
+
+ a.recycle()
+
+ init()
+ }
+
+ private fun init() {
+ mInitialized = true
+
+ super.setScaleType(SCALE_TYPE)
+
+ mBitmapPaint.setAntiAlias(true)
+ mBitmapPaint.setDither(true)
+ mBitmapPaint.setFilterBitmap(true)
+ mBitmapPaint.setAlpha(mImageAlpha)
+ mBitmapPaint.setColorFilter(mColorFilter)
+
+ mBorderPaint.setStyle(Paint.Style.STROKE)
+ mBorderPaint.setAntiAlias(true)
+ mBorderPaint.setColor(mBorderColor)
+ mBorderPaint.setStrokeWidth(mBorderWidth.toFloat())
+
+ mCircleBackgroundPaint.setStyle(Paint.Style.FILL)
+ mCircleBackgroundPaint.setAntiAlias(true)
+ mCircleBackgroundPaint.setColor(mCircleBackgroundColor)
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ outlineProvider = OutlineProvider()
+ }
+ }
+
+ override fun setScaleType(scaleType: ScaleType) {
+// require(scaleType == SCALE_TYPE) { String.format("ScaleType %s not supported.", scaleType) }
+ super.setScaleType(scaleType)
+ }
+
+ override fun setAdjustViewBounds(adjustViewBounds: Boolean) {
+// require(!adjustViewBounds) { "adjustViewBounds not supported." }
+ super.setAdjustViewBounds(adjustViewBounds)
+ }
+
+ @SuppressLint("CanvasSize")
+ override fun onDraw(canvas: Canvas) {
+ if (mDisableCircularTransformation) {
+ super.onDraw(canvas)
+ return
+ }
+
+ if (mCircleBackgroundColor != Color.TRANSPARENT) {
+ canvas.drawCircle(
+ mDrawableRect.centerX(),
+ mDrawableRect.centerY(),
+ mDrawableRadius,
+ mCircleBackgroundPaint
+ )
+ }
+
+ if (mBitmap != null) {
+ if (mDrawableDirty && mBitmapCanvas != null) {
+ mDrawableDirty = false
+ val drawable = drawable
+ drawable.setBounds(0, 0, mBitmapCanvas!!.getWidth(), mBitmapCanvas!!.getHeight())
+ drawable.draw(mBitmapCanvas!!)
+ }
+
+ if (mRebuildShader) {
+ mRebuildShader = false
+
+ val bitmapShader =
+ BitmapShader(mBitmap!!, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
+ bitmapShader.setLocalMatrix(mShaderMatrix)
+
+ mBitmapPaint.setShader(bitmapShader)
+ }
+
+ canvas.drawCircle(
+ mDrawableRect.centerX(),
+ mDrawableRect.centerY(),
+ mDrawableRadius,
+ mBitmapPaint
+ )
+ }
+
+ if (mBorderWidth > 0) {
+ canvas.drawCircle(
+ mBorderRect.centerX(),
+ mBorderRect.centerY(),
+ mBorderRadius,
+ mBorderPaint
+ )
+ }
+ }
+
+ override fun invalidateDrawable(@NonNull dr: Drawable) {
+ mDrawableDirty = true
+ invalidate()
+ }
+
+ override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+ super.onSizeChanged(w, h, oldw, oldh)
+ updateDimensions()
+ invalidate()
+ }
+
+ override fun setPadding(left: Int, top: Int, right: Int, bottom: Int) {
+ super.setPadding(left, top, right, bottom)
+ updateDimensions()
+ invalidate()
+ }
+
+ override fun setPaddingRelative(start: Int, top: Int, end: Int, bottom: Int) {
+ super.setPaddingRelative(start, top, end, bottom)
+ updateDimensions()
+ invalidate()
+ }
+
+ var borderColor: Int
+ get() = mBorderColor
+ set(borderColor) {
+ if (borderColor == mBorderColor) {
+ return
+ }
+
+ mBorderColor = borderColor
+ mBorderPaint.setColor(borderColor)
+ invalidate()
+ }
+
+ var circleBackgroundColor: Int
+ get() = mCircleBackgroundColor
+ set(circleBackgroundColor) {
+ if (circleBackgroundColor == mCircleBackgroundColor) {
+ return
+ }
+
+ mCircleBackgroundColor = circleBackgroundColor
+ mCircleBackgroundPaint.setColor(circleBackgroundColor)
+ invalidate()
+ }
+
+
+ @Deprecated("Use {@link #setCircleBackgroundColor(int)} instead")
+ fun setCircleBackgroundColorResource(@ColorRes circleBackgroundRes: Int) {
+ circleBackgroundColor = context.resources.getColor(circleBackgroundRes)
+ }
+
+ var borderWidth: Int
+ get() = mBorderWidth
+ set(borderWidth) {
+ if (borderWidth == mBorderWidth) {
+ return
+ }
+
+ mBorderWidth = borderWidth
+ mBorderPaint.setStrokeWidth(borderWidth.toFloat())
+ updateDimensions()
+ invalidate()
+ }
+
+ var isBorderOverlay: Boolean
+ get() = mBorderOverlay
+ set(borderOverlay) {
+ if (borderOverlay == mBorderOverlay) {
+ return
+ }
+
+ mBorderOverlay = borderOverlay
+ updateDimensions()
+ invalidate()
+ }
+
+ var isDisableCircularTransformation: Boolean
+ get() = mDisableCircularTransformation
+ set(disableCircularTransformation) {
+ if (disableCircularTransformation == mDisableCircularTransformation) {
+ return
+ }
+
+ mDisableCircularTransformation = disableCircularTransformation
+
+ if (disableCircularTransformation) {
+ mBitmap = null
+ mBitmapCanvas = null
+ mBitmapPaint.setShader(null)
+ } else {
+ initializeBitmap()
+ }
+
+ invalidate()
+ }
+
+ override fun setImageBitmap(bm: Bitmap) {
+ super.setImageBitmap(bm)
+ initializeBitmap()
+ invalidate()
+ }
+
+ override fun setImageDrawable(drawable: Drawable?) {
+ super.setImageDrawable(drawable)
+ initializeBitmap()
+ invalidate()
+ }
+
+ override fun setImageResource(@DrawableRes resId: Int) {
+ super.setImageResource(resId)
+ initializeBitmap()
+ invalidate()
+ }
+
+ override fun setImageURI(uri: Uri?) {
+ super.setImageURI(uri)
+ initializeBitmap()
+ invalidate()
+ }
+
+ override fun setImageAlpha(alpha: Int) {
+ var alpha = alpha
+ alpha = alpha and 0xFF
+
+ if (alpha == mImageAlpha) {
+ return
+ }
+
+ mImageAlpha = alpha
+
+ // This might be called during ImageView construction before
+ // member initialization has finished on API level >= 16.
+ if (mInitialized) {
+ mBitmapPaint.setAlpha(alpha)
+ invalidate()
+ }
+ }
+
+ override fun getImageAlpha(): Int {
+ return mImageAlpha
+ }
+
+ override fun setColorFilter(cf: ColorFilter) {
+ if (cf === mColorFilter) {
+ return
+ }
+
+ mColorFilter = cf
+
+ // This might be called during ImageView construction before
+ // member initialization has finished on API level <= 19.
+ if (mInitialized) {
+ mBitmapPaint.setColorFilter(cf)
+ invalidate()
+ }
+ }
+
+ override fun getColorFilter(): ColorFilter {
+ return mColorFilter ?: ColorFilter()
+ }
+
+ private fun getBitmapFromDrawable(drawable: Drawable?): Bitmap? {
+ if (drawable == null) {
+ return null
+ }
+
+ if (drawable is BitmapDrawable) {
+ return drawable.bitmap
+ }
+
+ try {
+ val bitmap = if (drawable is ColorDrawable) {
+ Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG)
+ } else {
+ Bitmap.createBitmap(
+ drawable.intrinsicWidth,
+ drawable.intrinsicHeight,
+ BITMAP_CONFIG
+ )
+ }
+
+ val canvas: Canvas = Canvas(bitmap)
+ drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight())
+ drawable.draw(canvas)
+ return bitmap
+ } catch (e: java.lang.Exception) {
+ e.printStackTrace()
+ return null
+ }
+ }
+
+ private fun initializeBitmap() {
+ mBitmap = getBitmapFromDrawable(drawable)
+
+ if (mBitmap != null && mBitmap!!.isMutable) {
+ mBitmapCanvas = Canvas(mBitmap!!)
+ } else {
+ mBitmapCanvas = null
+ }
+
+ if (!mInitialized) {
+ return
+ }
+
+ if (mBitmap != null) {
+ updateShaderMatrix()
+ } else {
+ mBitmapPaint.setShader(null)
+ }
+ }
+
+ private fun updateDimensions() {
+ mBorderRect.set(calculateBounds())
+ mBorderRadius = min(
+ ((mBorderRect.height() - mBorderWidth) / 2.0f).toDouble(),
+ ((mBorderRect.width() - mBorderWidth) / 2.0f).toDouble()
+ )
+ .toFloat()
+
+ mDrawableRect.set(mBorderRect)
+ if (!mBorderOverlay && mBorderWidth > 0) {
+ mDrawableRect.inset(mBorderWidth - 1.0f, mBorderWidth - 1.0f)
+ }
+ mDrawableRadius = min(
+ (mDrawableRect.height() / 2.0f).toDouble(),
+ (mDrawableRect.width() / 2.0f).toDouble()
+ )
+ .toFloat()
+
+ updateShaderMatrix()
+ }
+
+ private fun calculateBounds(): RectF {
+ val availableWidth = width - paddingLeft - paddingRight
+ val availableHeight = height - paddingTop - paddingBottom
+
+ val sideLength =
+ min(availableWidth.toDouble(), availableHeight.toDouble()).toInt()
+
+ val left = paddingLeft + (availableWidth - sideLength) / 2f
+ val top = paddingTop + (availableHeight - sideLength) / 2f
+
+ return RectF(left, top, left + sideLength, top + sideLength)
+ }
+
+ private fun updateShaderMatrix() {
+ if (mBitmap == null) {
+ return
+ }
+
+ val scale: Float
+ var dx = 0f
+ var dy = 0f
+
+ mShaderMatrix.set(null)
+
+ val bitmapHeight = mBitmap!!.height
+ val bitmapWidth = mBitmap!!.width
+
+ if (bitmapWidth * mDrawableRect.height() > mDrawableRect.width() * bitmapHeight) {
+ scale = mDrawableRect.height() / bitmapHeight.toFloat()
+ dx = (mDrawableRect.width() - bitmapWidth * scale) * 0.5f
+ } else {
+ scale = mDrawableRect.width() / bitmapWidth.toFloat()
+ dy = (mDrawableRect.height() - bitmapHeight * scale) * 0.5f
+ }
+
+ mShaderMatrix.setScale(scale, scale)
+ mShaderMatrix.postTranslate(
+ (dx + 0.5f).toInt() + mDrawableRect.left,
+ (dy + 0.5f).toInt() + mDrawableRect.top
+ )
+
+ mRebuildShader = true
+ }
+
+ @SuppressLint("ClickableViewAccessibility")
+ override fun onTouchEvent(event: MotionEvent): Boolean {
+ if (mDisableCircularTransformation) {
+ return super.onTouchEvent(event)
+ }
+
+ return inTouchableArea(event.x, event.y) && super.onTouchEvent(event)
+ }
+
+ private fun inTouchableArea(x: Float, y: Float): Boolean {
+ if (mBorderRect.isEmpty) {
+ return true
+ }
+
+ return (x - mBorderRect.centerX()).pow(2.0F) + (y - mBorderRect.centerY()).pow(2.0F) <= mBorderRadius.pow(2.0F)
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+ private inner class OutlineProvider : ViewOutlineProvider() {
+ override fun getOutline(view: View?, outline: Outline) {
+ if (mDisableCircularTransformation) {
+ BACKGROUND.getOutline(view, outline)
+ } else {
+ val bounds: Rect = Rect()
+ mBorderRect.roundOut(bounds)
+ outline.setRoundRect(bounds, bounds.width() / 2.0f)
+ }
+ }
+ }
+
+ companion object {
+ private val SCALE_TYPE = ScaleType.CENTER_CROP
+
+ private val BITMAP_CONFIG = Bitmap.Config.ARGB_8888
+ private const val COLORDRAWABLE_DIMENSION = 2
+
+ private const val DEFAULT_BORDER_WIDTH = 0
+ private val DEFAULT_BORDER_COLOR: Int = Color.BLACK
+ private val DEFAULT_CIRCLE_BACKGROUND_COLOR: Int = Color.TRANSPARENT
+ private const val DEFAULT_IMAGE_ALPHA = 255
+ private const val DEFAULT_BORDER_OVERLAY = false
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/duckduckgo.xml b/app/src/main/res/drawable/duckduckgo.xml
new file mode 100644
index 0000000..eefd78f
--- /dev/null
+++ b/app/src/main/res/drawable/duckduckgo.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/gmap.png b/app/src/main/res/drawable/gmap.png
new file mode 100644
index 0000000..8543444
Binary files /dev/null and b/app/src/main/res/drawable/gmap.png differ
diff --git a/app/src/main/res/drawable/google.png b/app/src/main/res/drawable/google.png
new file mode 100644
index 0000000..832d03f
Binary files /dev/null and b/app/src/main/res/drawable/google.png differ
diff --git a/app/src/main/res/drawable/namuwiki.png b/app/src/main/res/drawable/namuwiki.png
new file mode 100644
index 0000000..9813ade
Binary files /dev/null and b/app/src/main/res/drawable/namuwiki.png differ
diff --git a/app/src/main/res/drawable/naver.png b/app/src/main/res/drawable/naver.png
new file mode 100644
index 0000000..8a3a31f
Binary files /dev/null and b/app/src/main/res/drawable/naver.png differ
diff --git a/app/src/main/res/drawable/navermap.png b/app/src/main/res/drawable/navermap.png
new file mode 100644
index 0000000..2829b03
Binary files /dev/null and b/app/src/main/res/drawable/navermap.png differ
diff --git a/app/src/main/res/drawable/tmap.png b/app/src/main/res/drawable/tmap.png
new file mode 100644
index 0000000..f20ca6e
Binary files /dev/null and b/app/src/main/res/drawable/tmap.png differ
diff --git a/app/src/main/res/layout/app_drawer.xml b/app/src/main/res/layout/app_drawer.xml
index e85e2c9..99ee65a 100644
--- a/app/src/main/res/layout/app_drawer.xml
+++ b/app/src/main/res/layout/app_drawer.xml
@@ -3,43 +3,114 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
+
android:layout_height="match_parent">
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:layout_width="0dp"
+ android:layout_height="0dp">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+ app:layout_constraintRight_toLeftOf="@id/reset"/>
+
diff --git a/app/src/main/res/layout/apps_child.xml b/app/src/main/res/layout/apps_child.xml
index 25c3e25..173f7f0 100644
--- a/app/src/main/res/layout/apps_child.xml
+++ b/app/src/main/res/layout/apps_child.xml
@@ -1,39 +1,32 @@
+ />
-
-
diff --git a/app/src/main/res/layout/contact_item.xml b/app/src/main/res/layout/contact_item.xml
new file mode 100644
index 0000000..ed64938
--- /dev/null
+++ b/app/src/main/res/layout/contact_item.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
new file mode 100644
index 0000000..ff8702d
--- /dev/null
+++ b/app/src/main/res/values/attrs.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ 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 ed2bb2c..46c24b8 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -1,5 +1,5 @@
-
+
+
+
+
+
\ No newline at end of file