This commit is contained in:
lunaticbum 2024-08-22 16:00:05 +09:00
parent bdb6a57a55
commit 74c64c7617
12 changed files with 464 additions and 120 deletions

View File

@ -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()}
}
}

View File

@ -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)
}
}
}

View File

@ -47,7 +47,7 @@ internal class AppsAdapter(
private val appsCount: TextView) : RecyclerView.Adapter<AppsAdapter.AppsViewHolder>() {
private var oldList = mutableListOf<Packages>()
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 (

View File

@ -0,0 +1,179 @@
/*
* Lunar Launcher
* Copyright (C) 2022 Md Rasel Hossain
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package rasel.lunar.launcher.apps
import android.annotation.SuppressLint
import android.app.ActivityOptions
import android.content.ActivityNotFoundException
import android.content.ComponentName
import android.content.DialogInterface
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.res.ColorStateList
import android.graphics.Rect
import android.icu.text.SimpleDateFormat
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.ContactsContract
import android.provider.Settings
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Toast
import androidx.appcompat.widget.LinearLayoutCompat
import androidx.core.content.FileProvider
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTransaction
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.button.MaterialButton
import com.google.android.material.button.MaterialButtonToggleGroup
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import rasel.lunar.launcher.BuildConfig
import rasel.lunar.launcher.LauncherActivity.Companion.lActivity
import rasel.lunar.launcher.R
import rasel.lunar.launcher.databinding.ActivityBrowserDialogBinding
import rasel.lunar.launcher.databinding.ContactMenuBinding
import rasel.lunar.launcher.databinding.SearchMenuBinding
import rasel.lunar.launcher.helpers.Constants.Companion.KEY_APP_NO_
import rasel.lunar.launcher.helpers.Constants.Companion.MAX_FAVORITE_APPS
import rasel.lunar.launcher.helpers.Constants.Companion.PREFS_FAVORITE_APPS
import rasel.lunar.launcher.helpers.UniUtils.Companion.screenHeight
import rasel.lunar.launcher.helpers.UniUtils.Companion.screenWidth
import rasel.lunar.launcher.utils.BLog
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.util.*
internal class SearchMenu : BottomSheetDialogFragment() {
private lateinit var binding: SearchMenuBinding
private lateinit var searchWord: String
private lateinit var packageManager: PackageManager
private lateinit var appInfo: ApplicationInfo
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = SearchMenuBinding.inflate(inflater, container, false)
/* get package name from fragment's tag */
searchWord = tag.toString()
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
(requireDialog() as BottomSheetDialog).dismissWithAnimation = true
/* copy package name */
// binding.appPackage.setOnClickListener {
// copyToClipboard(requireContext(), packageName)
// }
//
appName()
// binding.detailedInfo.setOnClickListener { detailedInfo() }
// binding.activityBrowser.setOnClickListener { activityBrowser() }
// binding.appStore.setOnClickListener { appStore() }
// binding.appFreeform.setOnClickListener { freeform() }
// binding.appInfo.setOnClickListener { appInfo() }
// binding.appShare.setOnClickListener { share() }
// binding.appUninstall.setOnClickListener { uninstall() }
binding.searchNmap.setOnClickListener {
openSearchApps("nmap://search?query=${searchWord}&appname=${BuildConfig.APPLICATION_ID}","com.nhn.android.nmap")
}
binding.searchGoogleMap.setOnClickListener {
openSearchApps("geo:0,0?q=${searchWord}","com.google.android.apps.maps")
}
binding.searchGoogle.setOnClickListener {
openSearchApps("https://www.google.com/search?q=${searchWord}","com.android.chrome")
}
binding.searchTmap.setOnClickListener {
openSearchApps("tmap://search?name=${searchWord}","com.skt.tmap.ku")
}
binding.searchNaver.setOnClickListener {
openSearchApps("https://search.naver.com/search.naver?where=nexearch&query=${searchWord}", "com.nhn.android.search")
}
binding.searchDuckduckgo.setOnClickListener {
openSearchApps("https://duckduckgo.com/?t=h_&q=${searchWord}","com.duckduckgo.mobile.android")
}
binding.searchNamuwiki.setOnClickListener {
openSearchApps("https://namu.wiki/Search?q=${searchWord}")
}
binding.searchTranslate.setOnClickListener {
openSearchApps("https://translate.google.com/?hl=ko&sl=ko&tl=en&text=${searchWord}&op=translate","com.android.chrome")
}
binding.searchCoupang.setOnClickListener {
openSearchApps("coupang://search?q=${searchWord}","com.coupang.mobile")
}
}
fun openSearchApps(schemeString : String, pakage : String? = null) {
val gmmIntentUri = Uri.parse(schemeString)
val mapIntent = Intent(Intent.ACTION_VIEW, gmmIntentUri)
pakage?.let {
mapIntent.setPackage(pakage)
}
startActivity(mapIntent)
try {
dismiss()
} catch (e : Exception) {
e.printStackTrace()
}
}
private fun appName() {
binding.keyworkd.text = searchWord
}
var mDismissCalback : DismissCalback? = null
fun show(manager: FragmentManager, tag: String? , dismissCalback : DismissCalback?) {
this.mDismissCalback = dismissCalback
this.show(manager, tag)
}
override fun show(manager: FragmentManager, tag: String?) {
super.show(manager, tag)
}
override fun dismiss() {
BLog.LOGE("dismiss()")
mDismissCalback?.invoke()
super.dismiss()
}
override fun onDismiss(dialog: DialogInterface) {
BLog.LOGE("onDismiss(dialog: DialogInterface)")
mDismissCalback?.invoke()
super.onDismiss(dialog)
}
}
typealias DismissCalback = ()->Unit

View File

@ -67,7 +67,7 @@ internal class Constants {
const val KEY_LOCK_METHOD = "lock_method"
/* --- */
const val 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

View File

@ -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

View File

@ -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
}

View File

@ -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()
}

View File

@ -2,6 +2,7 @@
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -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" />
<TextClock
android:id="@+id/date"
@ -41,12 +44,14 @@
android:layout_height="wrap_content"
android:gravity="center"
android:maxLines="1"
android:textLocale="en_US"
android:textIsSelectable="false"
app:layout_constraintBottom_toBottomOf="@+id/batteryProgress"
app:layout_constraintEnd_toEndOf="@+id/batteryProgress"
app:layout_constraintStart_toStartOf="@+id/batteryProgress"
app:layout_constraintTop_toBottomOf="@+id/time"
app:layout_constraintVertical_bias="0.075" />
app:layout_constraintVertical_bias="0.075"
tools:ignore="UnusedAttribute" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/weather"

View File

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

View File

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

View File

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