From 69d93778a46cd27796b6f644bc9a0ef0cb059123 Mon Sep 17 00:00:00 2001 From: lunaticbum Date: Mon, 11 Aug 2025 17:32:22 +0900 Subject: [PATCH] .. --- .../bums/lunatic/launcher/LauncherActivity.kt | 8 +- .../bums/lunatic/launcher/home/RssHome.kt | 72 +++++---- .../launcher/home/SearchBottomSheet.kt | 143 ++++++++++++++++++ .../launcher/model/RssDataInterface.kt | 2 +- .../main/res/layout/bottom_sheet_search.xml | 59 ++++++++ app/src/main/res/layout/launcher_activity.xml | 3 + .../main/res/layout/text_inpu_password.xml | 22 +++ 7 files changed, 280 insertions(+), 29 deletions(-) create mode 100644 app/src/main/kotlin/bums/lunatic/launcher/home/SearchBottomSheet.kt create mode 100644 app/src/main/res/layout/bottom_sheet_search.xml diff --git a/app/src/main/kotlin/bums/lunatic/launcher/LauncherActivity.kt b/app/src/main/kotlin/bums/lunatic/launcher/LauncherActivity.kt index f9137338..5331bc6d 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/LauncherActivity.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/LauncherActivity.kt @@ -26,6 +26,7 @@ import android.content.Intent import android.content.SharedPreferences import android.content.res.Configuration import android.graphics.Color +import android.graphics.Rect import android.net.Uri import android.os.Build import android.os.Bundle @@ -42,8 +43,12 @@ import android.view.KeyEvent.KEYCODE_BUTTON_Y import android.view.KeyEvent.KEYCODE_DPAD_DOWN import android.view.KeyEvent.KEYCODE_DPAD_UP import android.view.MotionEvent +import android.view.View import android.view.WindowInsets import android.view.WindowManager +import android.widget.Button +import android.widget.HorizontalScrollView +import android.widget.LinearLayout import androidx.activity.OnBackPressedCallback import androidx.annotation.RequiresApi import androidx.core.content.ContextCompat @@ -489,7 +494,7 @@ internal class LauncherActivity : CommonActivity() { // } super.onNewIntent(intent) } - + var isKeyboardVisible = false @SuppressLint("NewApi", "MissingPermission") override fun onCreate(savedInstanceState: Bundle?) { @@ -509,6 +514,7 @@ internal class LauncherActivity : CommonActivity() { HeadsetActionButtonReceiver.register(this) + binding.tabs.setOnCheckedChangeListener { g, id -> showContents(id) } diff --git a/app/src/main/kotlin/bums/lunatic/launcher/home/RssHome.kt b/app/src/main/kotlin/bums/lunatic/launcher/home/RssHome.kt index e826f6b3..260be55e 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/home/RssHome.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/home/RssHome.kt @@ -49,6 +49,7 @@ import bums.lunatic.launcher.R import bums.lunatic.launcher.common.letTrue import bums.lunatic.launcher.databinding.LauncherHomeBinding import bums.lunatic.launcher.helpers.Constants.Companion.PREFS_SETTINGS +import bums.lunatic.launcher.home.SearchBottomSheet.OnSearchListener import bums.lunatic.launcher.home.adapters.RssItemAdapter import bums.lunatic.launcher.home.adapters.SwipeToDeleteCallback import bums.lunatic.launcher.model.RssData @@ -272,34 +273,47 @@ internal class RssHome : Fragment() { } } fun searchKeyword() { - val builder: AlertDialog.Builder = AlertDialog.Builder(requireContext()) - builder.setTitle("Keyword") - val viewInflated: View = LayoutInflater.from(requireContext()) - .inflate(R.layout.text_inpu_password, binding.root as ViewGroup?, false) - val input = viewInflated.findViewById(R.id.input) as EditText - val privateMode = viewInflated.findViewById(R.id.private_mode) as CheckBox - val addVote = viewInflated.findViewById(R.id.add_vote) as CheckBox - val addRead = viewInflated.findViewById(R.id.add_read) as CheckBox - privateMode.setOnCheckedChangeListener { v,c-> - binding.geckoWeb.privateMode = c - } - privateMode.visibility = View.GONE - binding.geckoWeb.privateMode = true - builder.setView(viewInflated) - builder.setPositiveButton( - android.R.string.ok, - DialogInterface.OnClickListener { dialog, which -> - dialog.dismiss() - var command = input.editableText?.toString() - if (command?.length ?: 0 > 0) { - queryInfos(keywords = command!!.split(" ")!!, addVote.isChecked, addRead.isChecked) - } - }) - builder.setNegativeButton( - android.R.string.cancel, - DialogInterface.OnClickListener { dialog, which -> dialog.cancel() }) +// val builder: AlertDialog.Builder = AlertDialog.Builder(requireContext()) +// builder.setTitle("Keyword") +// val viewInflated: View = LayoutInflater.from(requireContext()) +// .inflate(R.layout.text_inpu_password, binding.root as ViewGroup?, false) +// val input = viewInflated.findViewById(R.id.input) as EditText +// val privateMode = viewInflated.findViewById(R.id.private_mode) as CheckBox +// val addVote = viewInflated.findViewById(R.id.add_vote) as CheckBox +// val addRead = viewInflated.findViewById(R.id.add_read) as CheckBox +// privateMode.setOnCheckedChangeListener { v,c-> +// binding.geckoWeb.privateMode = c +// } +// privateMode.visibility = View.GONE +// binding.geckoWeb.privateMode = true +// builder.setView(viewInflated) +// builder.setPositiveButton( +// android.R.string.ok, +// DialogInterface.OnClickListener { dialog, which -> +// dialog.dismiss() +// var command = input.editableText?.toString() +// if (command?.length ?: 0 > 0) { +// queryInfos(keywords = command!!.split(" ")!!, addVote.isChecked, addRead.isChecked) +// } +// }) +// builder.setNegativeButton( +// android.R.string.cancel, +// DialogInterface.OnClickListener { dialog, which -> dialog.cancel() }) +// +// builder.show() - builder.show() + val bottomSheet = SearchBottomSheet() + bottomSheet.listener = object : OnSearchListener{ + override fun onSearch( + keyword: String, + category: List, + addReaded: Boolean, + addVoted: Boolean + ) { + queryInfos(keywords = keyword.split(" "), category , addReaded, addVoted) + } + } + bottomSheet.show(childFragmentManager, "SearchBottomSheet") } @@ -730,6 +744,7 @@ internal class RssHome : Fragment() { fun queryInfos( keywords: List, + category: List, includeVote : Boolean = false, includeRead : Boolean = false ) { @@ -737,6 +752,9 @@ internal class RssHome : Fragment() { var rQ = getRealm().query().sort("read", Sort.ASCENDING) if (!includeRead) { rQ = rQ.query("read == $0", 0)} if (!includeVote) { rQ = rQ.query("vote != $0", true)} + keywords.forEach { + rQ = rQ.query("category != $0", it) + } // 사용 예시 val (queryStr, queryArgs) = buildMultiFieldOrQuery( listOf("title", "description"), diff --git a/app/src/main/kotlin/bums/lunatic/launcher/home/SearchBottomSheet.kt b/app/src/main/kotlin/bums/lunatic/launcher/home/SearchBottomSheet.kt new file mode 100644 index 00000000..74d687f4 --- /dev/null +++ b/app/src/main/kotlin/bums/lunatic/launcher/home/SearchBottomSheet.kt @@ -0,0 +1,143 @@ +package bums.lunatic.launcher.home + +import android.content.Context +import android.graphics.Color +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.text.Editable +import android.text.TextWatcher +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.WindowManager +import android.view.inputmethod.EditorInfo +import android.view.inputmethod.InputMethodManager +import android.widget.Button +import android.widget.CheckBox +import android.widget.EditText +import android.widget.LinearLayout +import android.widget.RadioGroup +import android.widget.Toast +import bums.lunatic.launcher.R +import bums.lunatic.launcher.model.RssDataType +import com.google.android.material.bottomsheet.BottomSheetDialogFragment + +class SearchBottomSheet : BottomSheetDialogFragment() { + + private var selectedCategory = HashSet() + private val debounceHandler = Handler(Looper.getMainLooper()) + private var searchRunnable: Runnable? = null + private val debounceDelay = 300L // 0.3초 지연 + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.bottom_sheet_search, container, false) + } + + interface OnSearchListener { + fun onSearch(keyword: String, category: List, addReaded : Boolean, addVoted: Boolean) + } + + var listener: OnSearchListener? = null + private val categoryStates = mutableMapOf() + + lateinit var addVote : CheckBox + lateinit var addRead : CheckBox + lateinit var inputKeyword : EditText + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + inputKeyword = view.findViewById(R.id.inputKeyword) + val categoryContainer = view.findViewById(R.id.categoryContainer) + addVote = view.findViewById(R.id.add_vote) as CheckBox + addRead = view.findViewById(R.id.add_read) as CheckBox + addRead.setOnCheckedChangeListener {v,b->triggerSearchWithDebounce(inputKeyword.text.toString())} + addRead.setOnCheckedChangeListener {v,b->triggerSearchWithDebounce(inputKeyword.text.toString())} + // 카테고리 목록 + val categories = RssDataType.getAll() + categories.forEach { categoryStates[it.name] = true } + // 버튼 동적 생성 + categoryContainer.removeAllViews() + for (category in categories) { + val btn = Button(requireContext()).apply { + text = category.name + isAllCaps = false + setBackgroundResource(android.R.drawable.btn_default) + + // 초기 색상(활성 상태 색 표시) + updateButtonStyle(this, true) + + setOnClickListener { + // 상태 반전 + val current = categoryStates[category.name] ?: true + categoryStates[category.name] = !current + + // 스타일 업데이트 + updateButtonStyle(this, !current) + triggerSearchWithDebounce(inputKeyword.text.toString()) + } + } + categoryContainer.addView(btn) + } + + + + inputKeyword.requestFocus() + // 키보드 강제 오픈 + inputKeyword.postDelayed( { + val imm = requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + imm.showSoftInput(inputKeyword, InputMethodManager.SHOW_IMPLICIT) + },150L) + +// EditText 입력 실시간 감지 + debounce + inputKeyword.addTextChangedListener(object : TextWatcher { + override fun afterTextChanged(s: Editable?) { + triggerSearchWithDebounce(s.toString()) + } + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {} + }) + // 키보드 "검색" 액션 처리 + inputKeyword.setOnEditorActionListener { _, actionId, _ -> + if (actionId == EditorInfo.IME_ACTION_SEARCH) { + val keyword = inputKeyword.text.toString() + if (keyword.isNotEmpty()) { + triggerSearchWithDebounce(keyword) + dismiss() // 필요 시 닫기 + } + true + } else { + false + } + } + } + + private fun updateButtonStyle(button: Button, isActive: Boolean) { + if (isActive) { + button.setBackgroundColor(Color.parseColor("#FF9800")) // 활성화 색상 + button.setTextColor(Color.WHITE) + } else { + button.setBackgroundColor(Color.LTGRAY) // 비활성화 색상 + button.setTextColor(Color.DKGRAY) + } + } + + private fun triggerSearchWithDebounce(keyword: String) { + searchRunnable?.let { debounceHandler.removeCallbacks(it) } + searchRunnable = Runnable { + val disabledCategories = categoryStates.filter { it.value == false }.keys.toList() + listener?.onSearch(keyword, disabledCategories.toList(), addRead.isChecked,addVote.isChecked) + } + debounceHandler.postDelayed(searchRunnable!!, debounceDelay) + } + + override fun onStart() { + super.onStart() + // 전체 창 높이를 키보드에 맞게 조정 + dialog?.window?.setSoftInputMode( + WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE or + WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE + ) + } +} diff --git a/app/src/main/kotlin/bums/lunatic/launcher/model/RssDataInterface.kt b/app/src/main/kotlin/bums/lunatic/launcher/model/RssDataInterface.kt index 9570e16f..84eb92ff 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/model/RssDataInterface.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/model/RssDataInterface.kt @@ -56,7 +56,7 @@ enum class RssDataType { companion object { fun getExcAdt() = arrayListOf(NEWSFEED, REDDIT, DOTAX, FMKORAE, DCINSIDE, RULIWEB, CLIEN, THEQOO, ARCA).map { it.name } fun getAdts() = arrayListOf( REDDIT_NSFW).map { it.name } - //, MOST,GURU + fun getAll() = arrayListOf(TORRENT, YOUTUBE, NEWSFEED, REDDIT, DOTAX, FMKORAE, DCINSIDE, RULIWEB, CLIEN, THEQOO, ARCA,PRIVATE,REDDIT_NSFW) } } diff --git a/app/src/main/res/layout/bottom_sheet_search.xml b/app/src/main/res/layout/bottom_sheet_search.xml new file mode 100644 index 00000000..c4224624 --- /dev/null +++ b/app/src/main/res/layout/bottom_sheet_search.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/launcher_activity.xml b/app/src/main/res/layout/launcher_activity.xml index 3ed31dcd..0528f900 100644 --- a/app/src/main/res/layout/launcher_activity.xml +++ b/app/src/main/res/layout/launcher_activity.xml @@ -79,4 +79,7 @@ android:layout_height="match_parent"/> + + + diff --git a/app/src/main/res/layout/text_inpu_password.xml b/app/src/main/res/layout/text_inpu_password.xml index d2510398..16e0a37d 100644 --- a/app/src/main/res/layout/text_inpu_password.xml +++ b/app/src/main/res/layout/text_inpu_password.xml @@ -59,4 +59,26 @@ android:layout_width="wrap_content" android:layout_height="wrap_content"/> + + + + + + \ No newline at end of file