diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index bfe70823..b44adb58 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -101,6 +101,11 @@
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|screenLayout|layoutDirection|navigation"
android:windowSoftInputMode="adjustResize"
android:exported="true">
+
+
+
+
+
@@ -117,6 +122,7 @@
+
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/LauncherActivity.kt b/app/src/main/kotlin/bums/lunatic/launcher/LauncherActivity.kt
index 83efebd5..30c1a8b7 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/LauncherActivity.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/LauncherActivity.kt
@@ -14,6 +14,7 @@ import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
+import android.view.GestureDetector
import android.view.KeyEvent
import android.view.KeyEvent.ACTION_UP
import android.view.KeyEvent.KEYCODE_BUTTON_A
@@ -38,6 +39,7 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
+import bums.lunatic.launcher.apps.AppDrawerBottomSheet
import bums.lunatic.launcher.common.CommonActivity
import bums.lunatic.launcher.databinding.LauncherActivityBinding
import bums.lunatic.launcher.feeds.WidgetHost
@@ -79,6 +81,97 @@ import java.util.Date
open class LauncherActivity : CommonActivity() {
+// LauncherActivity 내부 (inner class)
+
+ inner class HomeGestureListener : GestureDetector.SimpleOnGestureListener() {
+
+ // 1. 스와이프 감지 (상하좌우)
+ override fun onFling(
+ e1: MotionEvent?,
+ e2: MotionEvent,
+ velocityX: Float,
+ velocityY: Float
+ ): Boolean {
+ if (e1 == null) return false
+
+ val diffY = e2.y - e1.y
+ val diffX = e2.x - e1.x
+ val SWIPE_THRESHOLD = 100
+ val SWIPE_VELOCITY_THRESHOLD = 100
+
+ if (Math.abs(diffX) > Math.abs(diffY)) {
+ // 좌우 스와이프
+ if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
+ if (diffX > 0) {
+ onSwipeRight()
+ } else {
+ onSwipeLeft()
+ }
+ return true
+ }
+ } else {
+ // 상하 스와이프
+ if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
+ if (diffY > 0) {
+ onSwipeDown()
+ } else {
+ onSwipeUp()
+ }
+ return true
+ }
+ }
+ return false
+ }
+
+ // 2. 더블 클릭 감지 (빈 공간)
+ override fun onDoubleTap(e: MotionEvent): Boolean {
+ // 더블 클릭 액션
+ showToast("더블 클릭: 설정 열기")
+ // 예: startActivity(Intent(this@LauncherActivity, SettingsActivity::class.java))
+ return true
+ }
+
+ // 3. 싱글 클릭 감지 (빈 공간)
+ override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
+ // 싱글 클릭 액션 (필요하다면)
+ // showToast("빈 공간 클릭됨")
+ return true
+ }
+
+ // 4. 롱프레스 감지 (빈 공간)
+ override fun onLongPress(e: MotionEvent) {
+ // 위젯 추가 메뉴 등을 띄우려면 여기서 처리
+ // 주의: 위젯 위에서 롱프레스하면 위젯 드래그가 먼저 작동하도록 설계해야 함
+ showToast("바탕화면 롱프레스: 위젯 추가")
+ selectWidget() // 기존에 만든 위젯 추가 함수 호출
+ }
+
+ // onDown은 true를 반환해야 다른 제스처들이 시작됨
+ override fun onDown(e: MotionEvent): Boolean {
+ return true
+ }
+ }
+
+ // (편의용) 토스트 함수
+ fun showToast(msg: String) {
+ android.widget.Toast.makeText(this, msg, android.widget.Toast.LENGTH_SHORT).show()
+ }
+
+ // 각 스와이프 동작 정의
+ fun onSwipeUp() {
+ showAppDrawer() // 지난번에 만든 바텀시트 앱서랍 열기
+ }
+ fun onSwipeDown() {
+ // 알림창 내리기 등
+ try {
+ val service = getSystemService("statusbar")
+ val statusbarManager = Class.forName("android.app.StatusBarManager")
+ val expand = statusbarManager.getMethod("expandNotificationsPanel")
+ expand.invoke(service)
+ } catch (e: Exception) { e.printStackTrace() }
+ }
+ fun onSwipeLeft() { /* 페이지 이동 등 */ }
+ fun onSwipeRight() { /* 페이지 이동 등 */ }
private lateinit var binding: LauncherActivityBinding
@@ -405,14 +498,20 @@ open class LauncherActivity : CommonActivity() {
}
// 두 뷰가 겹치는지 확인하는 헬퍼 함수
- private fun isViewOverlapping(v1: View, v2: View): Boolean {
- val rect1 = android.graphics.Rect()
- v1.getGlobalVisibleRect(rect1)
+ private fun isViewOverlapping(dragView: View, targetView: View): Boolean {
+ val targetRect = android.graphics.Rect()
+ targetView.getGlobalVisibleRect(targetRect)
- val rect2 = android.graphics.Rect()
- v2.getGlobalVisibleRect(rect2)
+ // 2. 드래그 중인 뷰의 화면상 절대 좌표 영역 구하기
+ val dragRect = android.graphics.Rect()
+ dragView.getGlobalVisibleRect(dragRect)
- return android.graphics.Rect.intersects(rect1, rect2)
+ // 3. 드래그 뷰의 중심점 계산
+ val centerX = dragRect.centerX()
+ val centerY = dragRect.centerY()
+
+ // 4. 중심점이 타겟 영역 안에 있는지 확인
+ return targetRect.contains(centerX, centerY)
}
// 위젯 관련 변수
private var appWidgetManager: AppWidgetManager? = null
@@ -421,6 +520,13 @@ open class LauncherActivity : CommonActivity() {
private val REQUEST_PICK_APPWIDGET = 100
private val REQUEST_CREATE_APPWIDGET = 101
+
+ fun showAppDrawer() {
+ val bottomSheet = AppDrawerBottomSheet.newInstance()
+ bottomSheet.show(supportFragmentManager, AppDrawerBottomSheet.TAG)
+ }
+ private lateinit var homeGestureDetector: androidx.core.view.GestureDetectorCompat
+
@SuppressLint("NewApi", "MissingPermission", "ClickableViewAccessibility")
override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
@@ -446,6 +552,18 @@ open class LauncherActivity : CommonActivity() {
DynamicColors.applyToActivityIfAvailable(this)
binding = LauncherActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
+ homeGestureDetector = androidx.core.view.GestureDetectorCompat(this, HomeGestureListener())
+ binding.widgetContainer.setOnTouchListener { view, event ->
+ // 위젯 드래그 중이 아닐 때만 제스처 처리
+ if (!isDraggingWidget) {
+ homeGestureDetector.onTouchEvent(event)
+ // true를 반환해야 이벤트가 소비되어 onSingleTap 등이 정상 동작함
+ // 하지만 자식 뷰(위젯)의 클릭을 막지 않으려면 주의 필요.
+ // onTouchEvent가 true를 반환하면 이벤트 체인이 여기서 끝납니다.
+ return@setOnTouchListener true
+ }
+ false
+ }
HeadsetActionButtonReceiver.register(this)
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view, windowInsets ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
@@ -789,6 +907,15 @@ open class LauncherActivity : CommonActivity() {
R.id.setting ->{
startActivity(Intent(this, SettingsActivity::class.java))
}
+ R.id.close ->{
+ supportFragmentManager.findFragmentById(R.id.fragment_container)?.let {
+ supportFragmentManager.beginTransaction()
+ .remove(it)
+ .commit()
+ binding.fragmentContainer.visibility = View.GONE
+ }
+
+ }
else -> {}
}
binding.floatingActionMenu.close(false)
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/apps/AppDrawer.kt b/app/src/main/kotlin/bums/lunatic/launcher/apps/AppDrawer.kt
index 388afcd5..4c52e22c 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/apps/AppDrawer.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/apps/AppDrawer.kt
@@ -45,6 +45,7 @@ import bums.lunatic.launcher.helpers.PrefBoolean
import bums.lunatic.launcher.helpers.PrefLong
import bums.lunatic.launcher.helpers.PrefString
import bums.lunatic.launcher.model.AppInfo
+import bums.lunatic.launcher.model.SimpleContact
import bums.lunatic.launcher.utils.Blog
import bums.lunatic.launcher.utils.JamoUtils
import bums.lunatic.launcher.workers.WorkersDb
@@ -87,7 +88,7 @@ class AppDrawer : CommonActivity() {
// super.onAttach(context)
//// }
//
-//
+//
//
// override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/apps/AppDrawerBottomSheet.kt b/app/src/main/kotlin/bums/lunatic/launcher/apps/AppDrawerBottomSheet.kt
new file mode 100644
index 00000000..3b1410b1
--- /dev/null
+++ b/app/src/main/kotlin/bums/lunatic/launcher/apps/AppDrawerBottomSheet.kt
@@ -0,0 +1,338 @@
+package bums.lunatic.launcher.apps
+
+import android.app.Dialog
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import android.view.KeyEvent
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowManager
+import androidx.core.widget.doOnTextChanged
+import androidx.lifecycle.lifecycleScope
+import androidx.recyclerview.widget.GridLayoutManager
+import bums.lunatic.launcher.BuildConfig
+import bums.lunatic.launcher.R
+import bums.lunatic.launcher.databinding.BottomSheetAppDrawerBinding // XML 이름에 맞춰 바인딩 클래스 생성됨
+import bums.lunatic.launcher.helpers.PrefBoolean
+import bums.lunatic.launcher.helpers.PrefLong
+import bums.lunatic.launcher.model.AppInfo
+import bums.lunatic.launcher.model.SimpleContact
+import bums.lunatic.launcher.utils.Blog
+import bums.lunatic.launcher.utils.JamoUtils
+import bums.lunatic.launcher.workers.WorkersDb
+import com.google.android.gms.common.wrappers.PackageManagerWrapper
+import com.google.android.gms.common.wrappers.Wrappers.packageManager
+import com.google.android.material.bottomsheet.BottomSheetBehavior
+import com.google.android.material.bottomsheet.BottomSheetDialog
+import com.google.android.material.bottomsheet.BottomSheetDialogFragment
+import io.realm.kotlin.ext.query
+import io.realm.kotlin.query.Sort
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+class AppDrawerBottomSheet : BottomSheetDialogFragment() {
+
+ private var _binding: BottomSheetAppDrawerBinding? = null
+ private val binding get() = _binding!!
+
+ private var appsAdapter: AppsAdapter? = null
+ private var contactAdapter: ContactAdapter? = null
+ private val packageList = mutableListOf()
+ private val contactList = arrayListOf() // SimpleContact 클래스가 import 되어야 함
+
+ companion object {
+ const val TAG = "AppDrawerBottomSheet"
+
+ fun newInstance(): AppDrawerBottomSheet {
+ return AppDrawerBottomSheet()
+ }
+ }
+
+ // 배경을 투명하게 하거나 스타일을 적용하기 위해
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // res/values/themes.xml 등에 BottomSheet 스타일 정의 필요 (모서리 둥글게 등)
+ setStyle(STYLE_NORMAL, R.style.CustomBottomSheetDialogTheme)
+ }
+
+ // 키보드가 올라올 때 바텀시트가 가려지지 않도록 설정
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ val dialog = super.onCreateDialog(savedInstanceState)
+ dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
+
+ // 바텀시트가 완전히 펼쳐진 상태로 시작하게 설정
+ dialog.setOnShowListener { dialogInterface ->
+ val bottomSheetDialog = dialogInterface as BottomSheetDialog
+ val bottomSheet = bottomSheetDialog.findViewById(com.google.android.material.R.id.design_bottom_sheet)
+ bottomSheet?.let {
+ val behavior = BottomSheetBehavior.from(it)
+ behavior.state = BottomSheetBehavior.STATE_EXPANDED
+ behavior.skipCollapsed = true
+ }
+ }
+ return dialog
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
+ ): View {
+ _binding = BottomSheetAppDrawerBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ setupAdapters()
+ setupSearchButtons()
+ setupListeners()
+
+ // 초기 데이터 로드
+ fetchApps()
+ }
+
+ override fun onStart() {
+ super.onStart()
+ val dialog = dialog as? com.google.android.material.bottomsheet.BottomSheetDialog
+ dialog?.findViewById(com.google.android.material.R.id.design_bottom_sheet)?.let { bottomSheet ->
+
+ // 1. 동작 설정 (완전히 펼치기)
+ val behavior = com.google.android.material.bottomsheet.BottomSheetBehavior.from(bottomSheet)
+ behavior.state = com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
+ behavior.skipCollapsed = true
+
+ // 2. [핵심] 높이 강제 설정 (이게 없으면 리스트가 0dp일 때 안 보임)
+ val layoutParams = bottomSheet.layoutParams
+ layoutParams.height = android.view.ViewGroup.LayoutParams.MATCH_PARENT
+ bottomSheet.layoutParams = layoutParams
+ }
+ }
+ private var recAdapter: AppsAdapter? = null
+ private fun setupAdapters() {
+ // 기존 Activity의 packageManager 대신 requireContext().packageManager 사용
+ val pm = requireContext().packageManager
+ appsAdapter = AppsAdapter(pm, childFragmentManager, binding.appsCount)
+ contactAdapter = ContactAdapter(pm, childFragmentManager)
+
+ // 가로 그리드 개수 4~5개 정도로 조정 (기존 2개였으면 그대로 유지)
+ binding.appsList.layoutManager = GridLayoutManager(context, 3,GridLayoutManager.HORIZONTAL,false)
+ binding.appsList.adapter = appsAdapter
+ binding.appsList.setItemViewCacheSize(20)
+ binding.appsList.setHasFixedSize(true) //
+
+ binding.contactList.layoutManager = GridLayoutManager(context, 3,GridLayoutManager.HORIZONTAL,false)
+ binding.contactList.adapter = contactAdapter
+
+ recAdapter = AppsAdapter(pm, childFragmentManager,null)
+ binding.recAppsList.layoutManager = GridLayoutManager(context, 1,GridLayoutManager.HORIZONTAL,false)
+ binding.recAppsList.adapter = recAdapter
+ }
+
+ private fun setupListeners() {
+ // 검색어 입력 리스너
+ binding.searchInput.doOnTextChanged { inputText, _, _, _ ->
+ filterAppsList(inputText.toString())
+ }
+
+ // 엔터키 입력 시 검색 실행
+ binding.searchInput.setOnKeyListener { _, keyCode, event ->
+ if (PrefBoolean.useQuickLaunch.get(false) && keyCode == KeyEvent.KEYCODE_ENTER && event.action == KeyEvent.ACTION_UP) {
+ checkResult(binding.searchInput.text.toString())
+ true
+ } else {
+ false
+ }
+ }
+
+ // 새로고침 버튼
+ binding.reset.setOnClickListener {
+ binding.searchInput.text?.clear()
+ fetchApps()
+ }
+ }
+
+ private fun setupSearchButtons() {
+ // 기존 AppDrawer의 버튼 연결
+ binding.searchNmap.setOnClickListener {
+ openSearchApps("nmap://search?query=${getInputText()}&appname=${BuildConfig.APPLICATION_ID}", "com.nhn.android.nmap")
+ }
+ binding.searchYoutube.setOnClickListener {
+ openSearchApps("https://www.youtube.com/results?search_query=${getInputText()}", "com.google.android.youtube")
+ }
+ binding.searchGoogle.setOnClickListener {
+ openSearchApps("https://www.google.com/search?q=${getInputText()}", "com.android.chrome")
+ }
+ binding.searchNaver.setOnClickListener {
+ openSearchApps("https://search.naver.com/search.naver?where=nexearch&query=${getInputText()}", "com.nhn.android.search")
+ }
+ // ... 나머지 버튼들도 동일하게 추가 ...
+ }
+
+ private fun getInputText() = binding.searchInput.text.toString()
+
+ private fun openSearchApps(schemeString: String, packageName: String? = null) {
+ try {
+ val intent = Intent(Intent.ACTION_VIEW, Uri.parse(schemeString))
+ packageName?.let {
+ intent.setPackage(it)
+ WorkersDb.updateAppUse(it)
+ }
+ startActivity(intent)
+ dismiss() // 실행 후 닫기
+ } catch (e: Exception) {
+ e.printStackTrace()
+ // 앱이 없을 경우 웹으로 열기 등의 예외처리 권장
+ }
+ }
+
+ private fun checkResult(keyword: String) {
+ // 기존 Activity의 checkResult 로직 구현 (필요시 부모 액티비티 함수 호출)
+ // (lActivity as? LauncherActivity)?.openSearchMenus(keyword) ...
+ dismiss()
+ }
+
+ private fun filterAppsList(searchString: String) {
+ fetchApps(searchString)
+ }
+
+
+ /**
+ * 앱 목록을 불러오는 함수
+ * - 검색어(keyword)가 있으면 필터링을 수행합니다.
+ * - 검색어가 없으면 '문맥 기반 추천 앱'을 최상단에 배치합니다.
+ * - IO 스레드에서 실행하여 UI 끊김을 방지합니다.
+ */
+ private fun fetchApps(keyword: String? = null) {
+ lifecycleScope.launch(Dispatchers.IO) {
+ val realm = WorkersDb.getRealm()
+ val pm = requireContext().packageManager
+
+ // 1. [추천 로직] 에러가 나도 앱 목록 로딩은 진행되도록 try-catch 분리
+ val recommendedPkgNames = try {
+ if (keyword.isNullOrEmpty()) {
+ WorkersDb.getContextualRecommendations(limit = 5)
+ } else {
+ emptyList() // 검색 중에는 추천 안 함
+ }
+ } catch (e: Exception) { emptyList() }
+
+ try {
+ // 2. [쿼리 구성]
+ var appQuery = realm.query()
+
+ if (!keyword.isNullOrEmpty()) {
+ val firstChar = keyword.first().toString()
+ if (JamoUtils.CHOSUNG.contains(firstChar)) {
+ appQuery = appQuery.query("appNameChosung CONTAINS[c] $0 OR alphaCho CONTAINS[c] $0", keyword)
+ } else if (java.util.regex.Pattern.matches("^[가-힣]*\$", keyword)) {
+ appQuery = appQuery.query("appName CONTAINS[c] $0 OR koreanName CONTAINS[c] $0", keyword)
+ } else {
+ appQuery = appQuery.query("appName CONTAINS[c] $0 OR pkgName CONTAINS[c] $0 OR category CONTAINS[c] $0", keyword)
+ }
+ }
+
+ // 3. [DB 조회]
+ val results = appQuery
+ .sort("clickCount", Sort.DESCENDING)
+ .sort("lastUseDate", Sort.DESCENDING)
+ .find()
+
+ // [중요] DB가 비어있다면(앱 설치 직후 등), 여기서 앱 스캔을 요청하거나 빈 상태 처리
+ if (results.isEmpty() && keyword.isNullOrEmpty()) {
+ // 필요 시 AppInfoGetter 워커를 즉시 실행하는 로직 추가 가능
+ }
+
+ // 4. [데이터 가공]
+ val allApps = results.map { realm.copyFromRealm(it) }
+ .filter { appInfo ->
+ try {
+ pm.getLaunchIntentForPackage(appInfo.pkgName ?: "") != null
+ } catch (e: Exception) {
+ false
+ }
+ }
+// C. [추천 앱 객체 추출]
+ val recAppList = recommendedPkgNames.mapNotNull { pkg ->
+ allApps.find { it.pkgName == pkg }
+ }
+
+ val mainAppList = allApps
+
+ var contactQuery = realm.query()
+
+ if (!keyword.isNullOrEmpty()) {
+ // 이름, 초성, 전화번호로 검색
+ val firstChar = keyword.first().toString()
+ if (JamoUtils.CHOSUNG.contains(firstChar)) {
+ contactQuery = contactQuery.query("chosung CONTAINS[c] $0", keyword)
+ } else if (java.util.regex.Pattern.matches("^[가-힣]*\$", keyword)) {
+ contactQuery = contactQuery.query("name CONTAINS[c] $0", keyword)
+ } else {
+ contactQuery = contactQuery.query("name CONTAINS[c] $0 OR phoneNumber CONTAINS $0", keyword)
+ }
+ } else {
+ // 검색어 없을 때: 자주 쓰는 연락처(터치 횟수 순) 또는 최근 연락처 상위 10개만 노출 (너무 많으면 스크롤 힘듦)
+
+ }
+ contactQuery = contactQuery.sort("touchCount", Sort.DESCENDING).limit(10)
+
+ val contactsResult = contactQuery.find()
+ val contactsList = contactsResult.map { realm.copyFromRealm(it) }
+
+ // 6. [UI 업데이트]
+ withContext(Dispatchers.Main) {
+ if (recAppList.isNotEmpty() && keyword.isNullOrEmpty()) {
+ binding.titleRecommend.visibility = View.VISIBLE
+ binding.recAppsList.visibility = View.VISIBLE
+ recAdapter?.updateData(recAppList)
+ } else {
+ // 검색 중이거나 데이터 없으면 숨김
+ binding.titleRecommend.visibility = View.GONE
+ binding.recAppsList.visibility = View.GONE
+ }
+
+ // 2. 전체 앱 리스트 업데이트
+ packageList.clear()
+ packageList.addAll(mainAppList)
+ appsAdapter?.updateData(packageList)
+ binding.appsCount.text = "${packageList.size} Apps"
+
+ if (contactsList.isNotEmpty()) {
+ binding.titleContact.visibility = View.VISIBLE
+ binding.contactList.visibility = View.VISIBLE
+ contactAdapter?.updateData(contactsList)
+ } else {
+ // 검색 결과가 없으면 숨김
+ binding.titleContact.visibility = View.GONE
+ binding.contactList.visibility = View.GONE
+ contactAdapter?.updateData(emptyList()) // 빈 리스트로 갱신하여 잔상 제거
+ }
+
+ }
+
+ } catch (e: Exception) {
+ e.printStackTrace() // 여기서 에러가 나면 앱 목록이 안 뜹니다. 로그캣(Logcat)을 확인해보세요.
+ }
+
+
+
+ }
+ }
+
+ private fun isPackageInstalled(packageName: String, packageManager: PackageManagerWrapper): Boolean {
+ return try {
+ packageManager.getPackageInfo(packageName, 0)
+ true
+ } catch (e: Exception) {
+ false
+ }
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/apps/AppsAdapter.kt b/app/src/main/kotlin/bums/lunatic/launcher/apps/AppsAdapter.kt
index 2a4a8dff..bbc8c0f3 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/apps/AppsAdapter.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/apps/AppsAdapter.kt
@@ -38,6 +38,7 @@ import bums.lunatic.launcher.workers.WorkersDb
import io.realm.kotlin.ext.query
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.async
+import kotlinx.coroutines.launch
internal class AppsAdapter(
@@ -64,11 +65,7 @@ internal class AppsAdapter(
holder.view.apply {
childTextview.text = item.appName
appIconTwo.visibility = View.VISIBLE
-
- MainScope().async {
- getDrawableIconForPackage(item.pkgName, packageManager.getApplicationIcon(item.pkgName!!)) {
- appIconTwo.post { appIconTwo.setImageDrawable(it) }
- } }
+ loadIconAsync(appIconTwo, item.pkgName)
childTextview.apply {
gravity = Gravity.CENTER
setTextSize(TypedValue.COMPLEX_UNIT_PX, lActivity!!.resources.getDimension(R.dimen.twelve))
@@ -90,6 +87,7 @@ internal class AppsAdapter(
}
}
}
+ item.pkgName?.let { WorkersDb.logAppUsage(it,"APP") }
context.startActivity(packageManager.getLaunchIntentForPackage(item.pkgName!!))
}
@@ -112,6 +110,28 @@ internal class AppsAdapter(
}
}
+ private fun loadIconAsync(imageView: android.widget.ImageView, pkgName: String?) {
+ if (pkgName == null) return
+
+ // 1. 이미지가 로딩되기 전 초기화 (재사용 뷰 깜빡임 방지)
+ imageView.setImageDrawable(null)
+
+ // Tag를 사용하여 뷰홀더가 재사용되었을 때 이전 작업 취소 식별
+ imageView.tag = pkgName
+
+ // CoroutineScope (lifecycleScope나 adapter 내부 scope 사용)
+ kotlinx.coroutines.CoroutineScope(kotlinx.coroutines.Dispatchers.Main).launch {
+ val icon = kotlinx.coroutines.withContext(kotlinx.coroutines.Dispatchers.IO) {
+ IconPackManager.getDrawableIconForPackage(imageView.context, pkgName)
+ }
+
+ // 로딩이 끝났는데 뷰가 여전히 같은 앱을 가리키고 있는지 확인
+ if (imageView.tag == pkgName) {
+ imageView.setImageDrawable(icon)
+ }
+ }
+ }
+
override fun getItemCount(): Int = oldList.size
inner class AppsViewHolder(var view: AppsChildBinding) : RecyclerView.ViewHolder(view.root)
@@ -119,10 +139,17 @@ internal class AppsAdapter(
/* update app list */
fun updateData(newList: List) {
val diffUtilResult = DiffUtil.calculateDiff(AppsDiffUtil(oldList, newList))
-//
- diffUtilResult.dispatchUpdatesTo(this)
+
+ // [수정 전] dispatchUpdatesTo가 먼저 있어서 에러 발생함
+ // diffUtilResult.dispatchUpdatesTo(this)
+ // oldList.clear()
+ // oldList.addAll(newList)
+
+ // [수정 후] 반드시 리스트 데이터를 먼저 갱신하고 나서 알림을 보내야 합니다!
oldList.clear()
oldList.addAll(newList)
+ diffUtilResult.dispatchUpdatesTo(this) // <-- 순서 변경 (맨 뒤로)
+
newList.size.let {
appsCount?.text = it.toString()
appsSize = it
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/apps/ContactAdapter.kt b/app/src/main/kotlin/bums/lunatic/launcher/apps/ContactAdapter.kt
index fe549031..8b34ca28 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/apps/ContactAdapter.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/apps/ContactAdapter.kt
@@ -27,6 +27,7 @@ import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import bums.lunatic.launcher.databinding.ContactItemBinding
+import bums.lunatic.launcher.model.SimpleContact
import bums.lunatic.launcher.utils.JamoUtils
import io.realm.kotlin.types.RealmObject
import io.realm.kotlin.types.annotations.PrimaryKey
@@ -80,9 +81,9 @@ internal class ContactAdapter (
synchronized(oldList) {
try {
val diffUtilResult = DiffUtil.calculateDiff(ContactDiffUtil(oldList, newList))
- diffUtilResult.dispatchUpdatesTo(this)
oldList.clear()
oldList.addAll(newList)
+ diffUtilResult.dispatchUpdatesTo(this)
newList.size.let {
appsSize = it
}
@@ -107,25 +108,7 @@ internal class ContactAdapter (
}
}
-class SimpleContact : RealmObject {
- @PrimaryKey
- var id : String? = ""
- var name : String? = ""
- var chosung : String? = ""
- var phoneNumber : String? = ""
- var touchCount = 0
- var lastedTouchDateTime = 0L
- constructor(id: String, name: String, phoneNumber: String) {
- this.id = id
- this.name = name
- this.phoneNumber = phoneNumber
- chosung = JamoUtils.split(name).joinToString("")
- }
-
- constructor()
-
-}
internal class ContactDiffUtil(
private val oldList: List, private val newList: List
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/apps/ContactMenu.kt b/app/src/main/kotlin/bums/lunatic/launcher/apps/ContactMenu.kt
index 39fe6187..63c8b049 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/apps/ContactMenu.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/apps/ContactMenu.kt
@@ -27,6 +27,7 @@ import android.view.View
import android.view.ViewGroup
import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
import bums.lunatic.launcher.databinding.ContactMenuBinding
+import bums.lunatic.launcher.model.SimpleContact
import bums.lunatic.launcher.utils.Blog
import bums.lunatic.launcher.workers.WorkersDb
import com.google.android.material.bottomsheet.BottomSheetDialog
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/apps/IconPackManager.kt b/app/src/main/kotlin/bums/lunatic/launcher/apps/IconPackManager.kt
index ee4566d9..8c5d578e 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/apps/IconPackManager.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/apps/IconPackManager.kt
@@ -1,243 +1,43 @@
-/*
- * 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 bums.lunatic.launcher.apps
-import android.annotation.SuppressLint
-import android.content.pm.PackageManager
-import android.content.res.Resources
-import android.graphics.Bitmap
-import android.graphics.drawable.BitmapDrawable
+import android.content.Context
import android.graphics.drawable.Drawable
-import androidx.collection.LruCache
-import androidx.core.content.res.ResourcesCompat
-import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
-import bums.lunatic.launcher.helpers.Constants.Companion.DEFAULT_ICON_PACK
-import bums.lunatic.launcher.helpers.Constants.Companion.KEY_ICON_PACK
-import bums.lunatic.launcher.helpers.Constants.Companion.PREFS_PKGICS
-import bums.lunatic.launcher.helpers.Constants.Companion.PREFS_SETTINGS
-import bums.lunatic.launcher.utils.Blog
-import bums.lunatic.launcher.utils.ImageUtils
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.async
-import org.xmlpull.v1.XmlPullParser
-import org.xmlpull.v1.XmlPullParserException
-import org.xmlpull.v1.XmlPullParserFactory
-import java.io.IOException
-import java.util.Locale
+import android.util.LruCache // 이거 임포트
-
-internal class IconPackManager {
-
- @SuppressLint("DiscouragedApi")
+class IconPackManager {
companion object {
-
- private val settingsPrefs = lActivity!!.getSharedPreferences(PREFS_SETTINGS, 0)
- private val icsPrefs = lActivity!!.getSharedPreferences(PREFS_PKGICS,0)
- private val packageName = settingsPrefs.getString(KEY_ICON_PACK, DEFAULT_ICON_PACK)
- private var loaded = false
- private val packagesDrawables = HashMap()
- private val packagesConponentNames = HashMap()
- private val backImages: MutableList = ArrayList()
- private var maskImage: Bitmap? = null
- private var frontImage: Bitmap? = null
- private var factor = 1.0f
- private var totalIcons = 0
- private var iconPackRes: Resources? = null
- private var appPackageIconDrawables : HashMap = hashMapOf()
-
- private fun load() {
- /* load appfilter.xml from the icon pack package */
- try {
- var xpp: XmlPullParser? = null
- iconPackRes = lActivity!!.packageManager.getResourcesForApplication(packageName!!)
- val appFilterId = iconPackRes!!.getIdentifier("appfilter", "xml", packageName)
- if (appFilterId > 0) {
- xpp = iconPackRes!!.getXml(appFilterId)
-// BLog.LOGE("packageName >>> ${packageName}")
- } else {
- try {
- xpp = XmlPullParserFactory.newInstance().apply { isNamespaceAware = true }
- .newPullParser().apply {
-// BLog.LOGE("packageName >>> ${packageName}")
- setInput(iconPackRes!!.assets.open("appfilter.xml"), "utf-8")
- }
- } catch (e: IOException) {
- e.printStackTrace()
- Blog.w("", "Couldn't find the appfilter.xml file")
- }
- }
- if (xpp != null) {
- var eventType = xpp.eventType
- while (eventType != XmlPullParser.END_DOCUMENT) {
- if (eventType == XmlPullParser.START_TAG) {
- when (xpp.name) {
- "iconback" -> {
- for (i in 0 until xpp.attributeCount) { if (xpp.getAttributeName(i).startsWith("img")) { loadBitmap(xpp.getAttributeValue(i))?.let { backImages.add(it) }}}
- }
- "iconmask" -> {
- if (xpp.attributeCount > 0 && xpp.getAttributeName(0) == "img1") { maskImage = loadBitmap(xpp.getAttributeValue(0)) }
- }
- "iconupon" -> {
- if (xpp.attributeCount > 0 && xpp.getAttributeName(0) == "img1") { frontImage = loadBitmap(xpp.getAttributeValue(0)) }
- }
- "scale" -> {
- if (xpp.attributeCount > 0 && xpp.getAttributeName(0) == "factor") { factor = java.lang.Float.valueOf(xpp.getAttributeValue(0)) }
- }
- "item" -> {
- var componentName: String? = null
- var drawableName: String? = null
- for (i in 0 until xpp.attributeCount) { when (xpp.getAttributeName(i)) {
- "component" -> componentName = xpp.getAttributeValue(i)
- "drawable" -> drawableName = xpp.getAttributeValue(i)
- } }
- if (!packagesDrawables.containsKey(componentName)) {
- packagesDrawables[componentName] = drawableName
- totalIcons += 1
- }
- }
- }
- }
- eventType = xpp.next()
- }
- }
- loaded = true
- } catch (e: PackageManager.NameNotFoundException) {
- Blog.w("", "Failed to load the icon pack")
- } catch (e: XmlPullParserException) {
- Blog.w("", "Failed to parse the appfilter.xml file")
- } catch (e: IOException) {
- e.printStackTrace()
- }
- }
- val maxMemory = (Runtime.getRuntime().maxMemory() / 1024).toInt()
- val cacheSize = maxMemory / 8
- val bitmapCache = object : LruCache(cacheSize) {
- fun sizeOf(key: String?, value: Bitmap?): Int {
- return if(value?.byteCount?.toInt() ?: 0 > 1024) {
- value?.byteCount!!.div(1024)
- } else { 0 }
- }
+ // [추가] 메모리 캐시 (최대 4MB 또는 아이콘 100개 분량 등 설정)
+ private val iconCache = object : LruCache(100) {
+ // 사이즈 측정 로직이 필요하면 sizeOf 오버라이드 (여기선 갯수 기준 100개)
}
- private fun loadBitmap(drawableName: String): Bitmap? {
- if (packageName != null && packageName.length > 0) {
- var bm = bitmapCache.get(packageName)
- GlobalScope.async {
- bm?.let { ImageUtils.bitmapToBase64String(it)?.let {
- icsPrefs.contains(packageName)
- } }
- }
- if (bm != null) return bm
+ fun getDrawableIconForPackage(context: Context, packageName: String): Drawable? {
+ // 1. 캐시에 있는지 확인
+ val cachedIcon = iconCache.get(packageName)
+ if (cachedIcon != null) {
+ return cachedIcon
}
- iconPackRes!!.getIdentifier(drawableName, "drawable", packageName).let { id ->
- if (id > 0) {
- ResourcesCompat.getDrawable(iconPackRes!!, id, null).let {
- if (it is BitmapDrawable) {
- if (packageName != null && packageName.length > 0) {
- bitmapCache.put(packageName, it.bitmap)
- }
- return it.bitmap
- }
- }
- }
+
+ // 2. 없으면 로딩 (기존 로직 수행)
+ // ... (기존의 아이콘팩 로딩 또는 pm.getApplicationIcon 코드) ...
+ val loadedIcon = try {
+ context.packageManager.getApplicationIcon(packageName)
+ // 만약 아이콘팩 적용 로직이 있다면 여기서 변환 수행
+ } catch (e: Exception) {
+ context.resources.getDrawable(android.R.drawable.sym_def_app_icon, null)
}
- return null
+
+ // 3. 로딩된 아이콘 캐시에 저장
+ if (loadedIcon != null) {
+ iconCache.put(packageName, loadedIcon)
+ }
+
+ return loadedIcon
}
- private fun loadDrawable(drawableName: String): Drawable? {
- iconPackRes!!.getIdentifier(drawableName, "drawable", packageName).let {
- return if (it > 0) ResourcesCompat.getDrawable(iconPackRes!!, it, null)
- else null
- }
+ // 아이콘팩 변경 시 호출해줄 함수
+ fun clearCache() {
+ iconCache.evictAll()
}
-
- fun putAfterReturn(packages: String, drawable : Drawable?) : Drawable? {
- if (drawable != null) {
- appPackageIconDrawables.put(packages, drawable)
- (drawable as? BitmapDrawable)?.let {
- if(icsPrefs.contains(packageName)) {
-
- } else {
- icsPrefs.edit().putString(packageName, ImageUtils.bitmapToBase64String(it.bitmap)).apply()
- }
- }
- }
- return drawable
- }
-
- fun getDrawableIconForPackage(appPackageName: String?, defaultDrawable: Drawable?, onComplete : (Drawable?)->Unit) {
- var ddd = if (appPackageIconDrawables.containsKey(appPackageName)) appPackageIconDrawables.get(appPackageName) else null
- if (ddd != null) {
- onComplete(ddd)
- } else {
- when (packageName) {
- DEFAULT_ICON_PACK -> onComplete.invoke(defaultDrawable)
- else -> {
- if (!loaded) load()
- var componentName: String? = null
- componentName = packagesConponentNames.get(appPackageName!!)
- if (componentName == null || componentName.length ?: 0 <= 0 ) {
- Blog.LOGE("it's compo ${appPackageName}")
- var pkgIntent =
- lActivity!!.packageManager.getLaunchIntentForPackage(
- appPackageName!!
- )
- if (pkgIntent != null) {
- componentName = pkgIntent!!.component.toString()
- }
- }
- var drawable = packagesDrawables[componentName]
- if (!drawable.isNullOrEmpty()) onComplete.invoke(
- putAfterReturn(
- appPackageName,
- loadDrawable(drawable)
- )
- )
- else {
- if (!componentName.isNullOrEmpty()) {
- val start = componentName.indexOf("{") + 1
- val end = componentName.indexOf("}", start)
- if (end > start) {
- drawable = componentName.substring(start, end)
- .lowercase(Locale.getDefault()).replace(".", "_")
- .replace("/", "_")
- try {
- if (iconPackRes!!.getIdentifier(
- drawable,
- "drawable",
- packageName
- ) > 0
- ) onComplete.invoke(putAfterReturn(appPackageName, loadDrawable(drawable)))
- } catch (e: NullPointerException) {
- settingsPrefs.edit()
- .putString(KEY_ICON_PACK, DEFAULT_ICON_PACK).apply()
- }
- }
- } else {
- onComplete.invoke(defaultDrawable)
- }
- }
- }
- }
- }
-
- }
-
}
-}
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/apps/RecommendedAppsAdapter.kt b/app/src/main/kotlin/bums/lunatic/launcher/apps/RecommendedAppsAdapter.kt
new file mode 100644
index 00000000..9e3fe1e3
--- /dev/null
+++ b/app/src/main/kotlin/bums/lunatic/launcher/apps/RecommendedAppsAdapter.kt
@@ -0,0 +1,68 @@
+package bums.lunatic.launcher.apps
+
+import android.content.pm.PackageManager
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import bums.lunatic.launcher.databinding.AppsChildBinding // 기존 레이아웃 재사용 (또는 별도 레이아웃 생성)
+import bums.lunatic.launcher.databinding.AppsChildRecBinding
+import bums.lunatic.launcher.model.AppInfo
+import bums.lunatic.launcher.workers.WorkersDb
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+class RecommendedAppsAdapter(private val pm: PackageManager) : RecyclerView.Adapter() {
+
+ private val items = ArrayList()
+
+ fun submitList(newItems: List) {
+ items.clear()
+ items.addAll(newItems)
+ notifyDataSetChanged()
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+ // 기존 apps_child.xml을 재사용하거나, 아이콘만 보여주는 새로운 xml을 만들어도 됨
+ val binding = AppsChildRecBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+ return ViewHolder(binding)
+ }
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ val item = items[position]
+
+ // 앱 이름 (추천 영역에서는 숨기거나 작게 표시 가능)
+ holder.binding.childTextview.text = item.appName
+ // holder.binding.appName.visibility = View.GONE // 이름 숨기고 싶으면 주석 해제
+
+ // 아이콘 비동기 로딩 (이전 성능 최적화 코드 적용)
+ holder.binding.appIconTwo.setImageDrawable(null)
+ holder.binding.appIconTwo.tag = item.pkgName
+
+ CoroutineScope(Dispatchers.Main).launch {
+ val icon = withContext(Dispatchers.IO) {
+ IconPackManager.getDrawableIconForPackage(holder.binding.root.context, item.pkgName ?: "")
+ }
+ if (holder.binding.appIconTwo.tag == item.pkgName) {
+ holder.binding.appIconTwo.setImageDrawable(icon)
+ }
+ }
+
+ // 클릭 이벤트
+ holder.itemView.setOnClickListener {
+ try {
+ val intent = pm.getLaunchIntentForPackage(item.pkgName ?: "")
+ if (intent != null) {
+ holder.itemView.context.startActivity(intent)
+ // [중요] 사용 로그 저장 -> 추천 정확도 상승
+ WorkersDb.logAppUsage(item.pkgName ?: "", "APP")
+ }
+ } catch (e: Exception) { e.printStackTrace() }
+ }
+ }
+
+ override fun getItemCount(): Int = items.size
+
+ class ViewHolder(val binding: AppsChildRecBinding) : RecyclerView.ViewHolder(binding.root)
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/helpers/ForeGroundService.kt b/app/src/main/kotlin/bums/lunatic/launcher/helpers/ForeGroundService.kt
index 43f38b9f..2d1b529d 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/helpers/ForeGroundService.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/helpers/ForeGroundService.kt
@@ -20,29 +20,19 @@ import android.os.IBinder
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
+import androidx.work.CoroutineWorker
import androidx.work.ExistingPeriodicWorkPolicy
+import androidx.work.ExistingWorkPolicy
+import androidx.work.OneTimeWorkRequestBuilder
+import androidx.work.OutOfQuotaPolicy
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
+import androidx.work.WorkerParameters
import bums.lunatic.launcher.LauncherActivity
import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
import bums.lunatic.launcher.R
import bums.lunatic.launcher.utils.Blog
-import bums.lunatic.launcher.workers.ArcaGetter
-import bums.lunatic.launcher.workers.ClienGetter
-import bums.lunatic.launcher.workers.DCGetter
-import bums.lunatic.launcher.workers.DotaxGetter
-import bums.lunatic.launcher.workers.DotaxGetter.Companion.COMIC2_WORK_TAG
-import bums.lunatic.launcher.workers.FmKoreaGetter
-import bums.lunatic.launcher.workers.FmKoreaGetter.Companion.FM_WORK_TAG
-import bums.lunatic.launcher.workers.LocationGetter
-import bums.lunatic.launcher.workers.NewsFeedsGetter
-import bums.lunatic.launcher.workers.NewsFeedsGetter.Companion.FEDDS_WORK_TAG
-import bums.lunatic.launcher.workers.RedditGetter
-import bums.lunatic.launcher.workers.RedditGetter.Companion.REDDIT_WORK_TAG
-import bums.lunatic.launcher.workers.RuliWebGetter
-import bums.lunatic.launcher.workers.TheQooGetter
-import bums.lunatic.launcher.workers.YoutubeGetter
-import bums.lunatic.launcher.workers.YoutubeGetter.Companion.YT_WORK_TAG
+import bums.lunatic.launcher.workers.TaskAggregator
import com.yausername.youtubedl_android.YoutubeDL
import com.yausername.youtubedl_android.YoutubeDLRequest
import kotlinx.coroutines.CoroutineScope
@@ -58,6 +48,22 @@ import java.util.UUID
import java.util.concurrent.TimeUnit
+class AggregatedSystemWorker(private val context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
+ override suspend fun doWork(): Result {
+ // 통합 시스템 작업 실행
+ TaskAggregator.refreshSystemInfo(context)
+ return Result.success()
+ }
+}
+
+class AggregatedNewsWorker(private val context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
+ override suspend fun doWork(): Result {
+ // 통합 뉴스 작업 실행
+ TaskAggregator.refreshNewsFeeds(context = context)
+ return Result.success()
+ }
+}
+
class ForeGroundService : Service() {
companion object {
val ACTION_SENDMSG = "ACTION_SEND_TO_LOVE"
@@ -225,85 +231,26 @@ class ForeGroundService : Service() {
}
fun refreshFeeds() {
- mWorkManager?.cancelAllWork()
- mWorkManager?.cancelAllWorkByTag(RuliWebGetter.TAG)
- mWorkManager?.enqueueUniquePeriodicWork(
- RuliWebGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
- PeriodicWorkRequestBuilder(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
- .addTag(RuliWebGetter.TAG)
- .build())
+ val workManager = WorkManager.getInstance(applicationContext)
- mWorkManager?.cancelAllWorkByTag(FEDDS_WORK_TAG)
- mWorkManager?.enqueueUniquePeriodicWork(
- FEDDS_WORK_TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
- PeriodicWorkRequestBuilder(PrefLong.shortTimePeriod.get(), TimeUnit.MINUTES)
- .addTag(FEDDS_WORK_TAG)
- .build())
- mWorkManager?.cancelAllWorkByTag(YT_WORK_TAG)
- mWorkManager?.enqueueUniquePeriodicWork(
- YT_WORK_TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
- PeriodicWorkRequestBuilder(PrefLong.longTimePeriod.get(), TimeUnit.MINUTES)
- .addTag(YT_WORK_TAG)
- .build())
- mWorkManager?.cancelAllWorkByTag(REDDIT_WORK_TAG)
- mWorkManager?.enqueueUniquePeriodicWork(
- REDDIT_WORK_TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
- PeriodicWorkRequestBuilder(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
- .addTag(REDDIT_WORK_TAG)
- .build())
- mWorkManager?.cancelAllWorkByTag(FM_WORK_TAG)
- mWorkManager?.enqueueUniquePeriodicWork(
- FM_WORK_TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
- PeriodicWorkRequestBuilder(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
- .addTag(FM_WORK_TAG)
- .build())
- mWorkManager?.cancelAllWorkByTag(COMIC2_WORK_TAG)
- mWorkManager?.enqueueUniquePeriodicWork(
- COMIC2_WORK_TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
- PeriodicWorkRequestBuilder(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
- .addTag(COMIC2_WORK_TAG)
- .build())
- mWorkManager?.cancelAllWorkByTag(ClienGetter.TAG)
- mWorkManager?.enqueueUniquePeriodicWork(
- ClienGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
- PeriodicWorkRequestBuilder(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
- .addTag(ClienGetter.TAG)
- .build())
- mWorkManager?.cancelAllWorkByTag(DCGetter.TAG)
- mWorkManager?.enqueueUniquePeriodicWork(
- DCGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
- PeriodicWorkRequestBuilder(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
- .addTag(DCGetter.TAG)
- .build())
- mWorkManager?.cancelAllWorkByTag(TheQooGetter.TAG)
- mWorkManager?.enqueueUniquePeriodicWork(
- TheQooGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
- PeriodicWorkRequestBuilder(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
- .addTag(TheQooGetter.TAG)
- .build())
- mWorkManager?.cancelAllWorkByTag(ArcaGetter.TAG)
- mWorkManager?.enqueueUniquePeriodicWork(
- ArcaGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
- PeriodicWorkRequestBuilder(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
- .addTag(ArcaGetter.TAG)
- .build())
- mWorkManager?.cancelAllWorkByTag(LocationGetter.TAG)
- mWorkManager?.enqueueUniquePeriodicWork(
- LocationGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
- PeriodicWorkRequestBuilder(PrefLong.locationTimePeriod.get(), TimeUnit.MINUTES)
- .addTag(LocationGetter.TAG)
- .build())
- mWorkManager?.cancelAllWorkByTag(ServiceWatchdogWorker.TAG)
- mWorkManager?.enqueueUniquePeriodicWork(
- ServiceWatchdogWorker.TAG,
- ExistingPeriodicWorkPolicy.REPLACE,
- PeriodicWorkRequestBuilder(15, TimeUnit.MINUTES)
- .addTag(ServiceWatchdogWorker.TAG)
- .build())
+ TaskAggregator.refreshSystemInfo(applicationContext)
+
+ // 1. 시스템 정보: 자주 변경되지 않으므로 1~3시간 간격
+ val systemRequest = PeriodicWorkRequestBuilder(PrefLong.longTimePeriod.get(120L), TimeUnit.MINUTES)
+ .build()
+
+ // 2. 뉴스 피드: 사용자가 설정한 간격 (예: 1시간)
+ val newsRequest = PeriodicWorkRequestBuilder(PrefLong.shortTimePeriod.get(20L), TimeUnit.MINUTES)
+ .build()
+
+ // 기존의 수많은 enqueue 코드를 이 두 개로 대체
+ workManager.enqueueUniquePeriodicWork("AggregatedSystemWork", ExistingPeriodicWorkPolicy.KEEP, systemRequest)
+ workManager.enqueueUniquePeriodicWork("AggregatedNewsWork", ExistingPeriodicWorkPolicy.KEEP, newsRequest)
}
+
fun workmanager() : WorkManager? {
if (mWorkManager == null && lActivity != null) {
mWorkManager = WorkManager.getInstance(lActivity!!)
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/model/AppInfo.kt b/app/src/main/kotlin/bums/lunatic/launcher/model/AppInfo.kt
index 8edb077c..50f9abc7 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/model/AppInfo.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/model/AppInfo.kt
@@ -18,4 +18,9 @@ class AppInfo : RealmObject {
var category : String? = null
var currentInstalled : Boolean = false
var isInstalled : Boolean = false
+ // [신규] 0: 항상 보이기(기본), 1: 검색 시만 보이기 (숨김)
+ var visibilityMode: Int = 0
+
+ // [신규] true면 추천 리스트에 절대 안 뜸
+ var blockRecommend: Boolean = false
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/model/AppUsageLog.kt b/app/src/main/kotlin/bums/lunatic/launcher/model/AppUsageLog.kt
new file mode 100644
index 00000000..385ad52d
--- /dev/null
+++ b/app/src/main/kotlin/bums/lunatic/launcher/model/AppUsageLog.kt
@@ -0,0 +1,19 @@
+package bums.lunatic.launcher.model
+
+import io.realm.kotlin.types.RealmObject
+import io.realm.kotlin.types.annotations.PrimaryKey
+import org.mongodb.kbson.ObjectId
+
+class AppUsageLog : RealmObject {
+ @PrimaryKey
+ var _id: ObjectId = ObjectId()
+
+ var itemKey: String = "" // 패키지명(앱) 또는 연락처 URI(연락처)
+ var itemType: String = "APP" // "APP" 또는 "CONTACT"
+
+ var timestamp: Long = 0L // 사용 시간 (밀리초)
+ var month: Int = 0 // 0~11 (Calendar.MONTH)
+ var dayOfMonth: Int = 0 // 1~31
+ var dayOfWeek: Int = 0 // 1(일)~7(토)
+ var hour: Int = 0 // 0~23 (24시간제)
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/model/SimpleContact.kt b/app/src/main/kotlin/bums/lunatic/launcher/model/SimpleContact.kt
new file mode 100644
index 00000000..710d74ca
--- /dev/null
+++ b/app/src/main/kotlin/bums/lunatic/launcher/model/SimpleContact.kt
@@ -0,0 +1,29 @@
+package bums.lunatic.launcher.model
+
+import bums.lunatic.launcher.utils.JamoUtils
+import io.realm.kotlin.types.RealmObject
+import io.realm.kotlin.types.annotations.PrimaryKey
+
+class SimpleContact : RealmObject {
+ @PrimaryKey
+ var id : String? = ""
+ var name : String? = ""
+ var chosung : String? = ""
+ var phoneNumber : String? = ""
+ var touchCount = 0
+ var lastedTouchDateTime = 0L
+ // [신규] 0: 항상 보이기(기본), 1: 검색 시만 보이기 (숨김)
+ var visibilityMode: Int = 0
+
+ // [신규] true면 추천 리스트에 절대 안 뜸
+ var blockRecommend: Boolean = false
+ constructor(id: String, name: String, phoneNumber: String) {
+ this.id = id
+ this.name = name
+ this.phoneNumber = phoneNumber
+ chosung = JamoUtils.split(name).joinToString("")
+ }
+
+ constructor()
+
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/qaccess/QuickAccess.kt b/app/src/main/kotlin/bums/lunatic/launcher/qaccess/QuickAccess.kt
index 319e14ae..fe6346f9 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/qaccess/QuickAccess.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/qaccess/QuickAccess.kt
@@ -45,7 +45,6 @@ import androidx.appcompat.widget.LinearLayoutCompat
import androidx.core.content.ContextCompat
import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
import bums.lunatic.launcher.R
-import bums.lunatic.launcher.apps.SimpleContact
import bums.lunatic.launcher.databinding.QuickAccessBinding
import bums.lunatic.launcher.databinding.ShortcutMakerBinding
import bums.lunatic.launcher.helpers.ColorPicker
@@ -60,6 +59,7 @@ import bums.lunatic.launcher.helpers.Constants.Companion.SEPARATOR
import bums.lunatic.launcher.helpers.Constants.Companion.SHORTCUT_TYPE_PHONE
import bums.lunatic.launcher.helpers.Constants.Companion.SHORTCUT_TYPE_URL
import bums.lunatic.launcher.model.AppInfo
+import bums.lunatic.launcher.model.SimpleContact
import bums.lunatic.launcher.utils.Blog
import bums.lunatic.launcher.workers.WorkersDb
import com.google.android.material.bottomsheet.BottomSheetDialog
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/receiver/PackageEventReceiver.kt b/app/src/main/kotlin/bums/lunatic/launcher/receiver/PackageEventReceiver.kt
index 96c72f94..ca5e9278 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/receiver/PackageEventReceiver.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/receiver/PackageEventReceiver.kt
@@ -43,9 +43,9 @@ class PackageEventReceiver : BroadcastReceiver() {
}
fun startAppInfoGetter(context: Context) {
- var mWorkManager = WorkManager.getInstance(context)
- Executors.newSingleThreadScheduledExecutor().schedule({
- mWorkManager.enqueue(OneTimeWorkRequest.from(AppInfoGetter::class.java))
- }, 5, TimeUnit.SECONDS)
+// var mWorkManager = WorkManager.getInstance(context)
+// Executors.newSingleThreadScheduledExecutor().schedule({
+// mWorkManager.enqueue(OneTimeWorkRequest.from(AppInfoGetter::class.java))
+// }, 5, TimeUnit.SECONDS)
}
}
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/settings/SettingsActivity.kt b/app/src/main/kotlin/bums/lunatic/launcher/settings/SettingsActivity.kt
index 33ef4d3c..414c11a9 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/settings/SettingsActivity.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/settings/SettingsActivity.kt
@@ -25,7 +25,6 @@ import android.os.Bundle
import android.os.Environment
import bums.lunatic.launcher.BuildConfig
import bums.lunatic.launcher.R
-import bums.lunatic.launcher.apps.SimpleContact
import bums.lunatic.launcher.common.CommonActivity
import bums.lunatic.launcher.databinding.AboutBinding
import bums.lunatic.launcher.databinding.SettingsActivityBinding
@@ -34,6 +33,7 @@ import bums.lunatic.launcher.helpers.Constants.Companion.PREFS_SETTINGS
import bums.lunatic.launcher.helpers.PrefBoolean
import bums.lunatic.launcher.helpers.PrefHelper
import bums.lunatic.launcher.model.AppInfo
+import bums.lunatic.launcher.model.SimpleContact
import bums.lunatic.launcher.settings.childs.Apps
import bums.lunatic.launcher.settings.childs.HomeSettings
import bums.lunatic.launcher.settings.childs.Misc
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/AppInfoGetter.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/AppInfoGetter.kt
index b607ef92..b56345dd 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/workers/AppInfoGetter.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/AppInfoGetter.kt
@@ -12,8 +12,10 @@ import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
import bums.lunatic.launcher.apps.AppDrawer.Companion.appNamesPrefs
import bums.lunatic.launcher.model.AppInfo
import bums.lunatic.launcher.utils.AlphabetToChosungMap
+import bums.lunatic.launcher.utils.Blog
import bums.lunatic.launcher.utils.JamoUtils
import io.realm.kotlin.ext.query
+import io.realm.kotlin.types.RealmObject
import java.text.Normalizer
import java.util.regex.Pattern
@@ -21,48 +23,49 @@ class AppInfoGetter : BaseGetter {
companion object {
val TAG = "AppInfoGetter"
}
- constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
+ constructor(context: Context) : super(context) {
}
- override fun realWork(): Result {
+
+
+ override fun realWork(): List {
+ var result = mutableListOf()
try {
+ val packageManager = lActivity?.packageManager ?: return result
- var packageManager = lActivity?.packageManager
- var packageInfoList: MutableList = mutableListOf()
- packageInfoList = (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- packageManager?.queryIntentActivities(
- Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LAUNCHER),
- PackageManager.ResolveInfoFlags.of(0)
- )
+ // 1. 설치된 앱 목록 가져오기 (시스템 호출 1회)
+ val intent = Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LAUNCHER)
+ val resolveInfos = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ packageManager.queryIntentActivities(intent, PackageManager.ResolveInfoFlags.of(0))
} else {
- (packageManager?.queryIntentActivities(
- Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LAUNCHER), 0
- ))
- })?.apply {
- removeIf { it.activityInfo.packageName.equals(BuildConfig.APPLICATION_ID) }
- forEach {
- val result = WorkersDb.getRealm().query().query("pkgName == $0",it.activityInfo.packageName).find()
- if (result.size < 1) {
- val info = AppInfo()
- info.appName = normalize(appName(it))
- info.pkgName = it.activityInfo.packageName
- info.category = getCategory(it.activityInfo.applicationInfo.category)
- info.alphaCho = AlphabetToChosungMap.getCho(info.appName!!)
- info.appNameChosung = JamoUtils.split(info.appName).joinToString("")
- WorkersDb.update(info)
- } else{
- val info = WorkersDb.getRealm().copyFromRealm(result.first())
- info.alphaCho = AlphabetToChosungMap.getCho(info.appName!!)
- info.appNameChosung = JamoUtils.split(info.appName).joinToString("")
- WorkersDb.update(info)
- }
- }
- }!!
+ packageManager.queryIntentActivities(intent, 0)
+ }
- } catch (e : Exception) {e.printStackTrace()}
- return Result.success()
+ resolveInfos.forEach { ri ->
+ val pkgName = ri.activityInfo.packageName
+ if (pkgName == BuildConfig.APPLICATION_ID) return@forEach
+
+ // 이미 DB에 있는지 Map에서 확인 (고속 검색)
+ // 신규 앱 발견 -> 추가
+ val appName = normalize(appName(ri))
+
+ result.add(AppInfo().apply {
+ this.appName = appName
+ this.pkgName = pkgName
+ this.category = getCategory(ri.activityInfo.applicationInfo.category)
+ this.alphaCho = AlphabetToChosungMap.getCho(appName)
+ this.appNameChosung = JamoUtils.split(appName).joinToString("")
+ })
+ }
+
+ } catch (e: Exception) {
+ e.printStackTrace()
+ return result
+ }
+ return result
}
+
fun appName(resolver: ResolveInfo): String {
return resolver.loadLabel(lActivity?.packageManager!!).toString().apply {
appNamesPrefs?.edit()?.putString(resolver.activityInfo.packageName, this)?.apply()
@@ -90,4 +93,4 @@ class AppInfoGetter : BaseGetter {
val pattern = Pattern.compile("\\p{InCombiningDiacriticalMarks}+")
return pattern.matcher(normalizedString).replaceAll("").toLowerCase()
}
-}
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/ArcaGetter.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/ArcaGetter.kt
index 115c5958..de318dfd 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/workers/ArcaGetter.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/ArcaGetter.kt
@@ -1,108 +1,108 @@
-package bums.lunatic.launcher.workers
-
-//import bums.lunatic.launcher.workers.WorkersDb.blockKeyword
-import android.content.Context
-import androidx.work.WorkerParameters
-import bums.lunatic.launcher.model.Arca
-import bums.lunatic.launcher.model.RssDataInterface
-import bums.lunatic.launcher.model.getT
-import bums.lunatic.launcher.utils.beforeOneDay
-import org.jsoup.nodes.Element
-
-class ArcaGetter : BaseGetter {
- companion object {
- val TAG = "ArcaGetter"
- }
- constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
-
- }
-
- override fun realWork(): Result {
-// RssDataType.ARCA.isOn {
-// try {
-// Blog.LOGE("realWork() ${this::class.simpleName}")
-// temp.clear()
-// val urls = arrayListOf(
-// "https://arca.live/b/singbung?mode=best",
-//// "https://arca.live/b/headline",
-//// "https://arca.live/b/live",
-// "https://arca.live/b/namuhotnow",
-// "https://arca.live/b/society",
-//// "https://arca.live/b/replay",
-//// "https://arca.live/b/breaking"
-// )
-// urls.forEach {
-//// try {
-//// Jsoup.connect(it)
-//// .userAgent(USAGT)
-//// .header("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8")
-//// .header("accept-language", "ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7")
-//// .header("cache-control", "no-cache")
-//// .header("pragma", "no-cache")
-//// .ignoreContentType(true)
-//// .timeout(5000)
-//// .get().let { arca ->
-////// BLog.LOGE("url >> ${it} >> ${arca}")
-//// arca.getElementsByClass("vrow hybrid").forEach { araca_li ->
-//// if (araca_li.html().contains("title ") == true) {
-//// parseArcaLi(araca_li).apply {
-//// this.forEach {
-//// if (it.pubDate() > commicsDateTime) {
-//// temp.add(it.getRssData())
-//// }
-//// }
-//// }
-//// }
-//// }
-//// }
-//// } catch (e : Exception) {
-//// e.printStackTrace()
-//// }
+//package bums.lunatic.launcher.workers
//
-// }
-//// Jsoup.connect("https://projrctjav.com").userAgent(USAGT).get().let { projectj ->
-//// BLog.LOGE("projectj >>>>> ${projectj}")
+////import bums.lunatic.launcher.workers.WorkersDb.blockKeyword
+//import android.content.Context
+//import androidx.work.WorkerParameters
+//import bums.lunatic.launcher.model.Arca
+//import bums.lunatic.launcher.model.RssDataInterface
+//import bums.lunatic.launcher.model.getT
+//import bums.lunatic.launcher.utils.beforeOneDay
+//import org.jsoup.nodes.Element
+//
+//class ArcaGetter : BaseGetter {
+// companion object {
+// val TAG = "ArcaGetter"
+// }
+// constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
+//
+// }
+//
+// override fun realWork(): Result {
+//// RssDataType.ARCA.isOn {
+//// try {
+//// Blog.LOGE("realWork() ${this::class.simpleName}")
+//// temp.clear()
+//// val urls = arrayListOf(
+//// "https://arca.live/b/singbung?mode=best",
+////// "https://arca.live/b/headline",
+////// "https://arca.live/b/live",
+//// "https://arca.live/b/namuhotnow",
+//// "https://arca.live/b/society",
+////// "https://arca.live/b/replay",
+////// "https://arca.live/b/breaking"
+//// )
+//// urls.forEach {
+////// try {
+////// Jsoup.connect(it)
+////// .userAgent(USAGT)
+////// .header("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8")
+////// .header("accept-language", "ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7")
+////// .header("cache-control", "no-cache")
+////// .header("pragma", "no-cache")
+////// .ignoreContentType(true)
+////// .timeout(5000)
+////// .get().let { arca ->
+//////// BLog.LOGE("url >> ${it} >> ${arca}")
+////// arca.getElementsByClass("vrow hybrid").forEach { araca_li ->
+////// if (araca_li.html().contains("title ") == true) {
+////// parseArcaLi(araca_li).apply {
+////// this.forEach {
+////// if (it.pubDate() > commicsDateTime) {
+////// temp.add(it.getRssData())
+////// }
+////// }
+////// }
+////// }
+////// }
+////// }
+////// } catch (e : Exception) {
+////// e.printStackTrace()
+////// }
+////
+//// }
+////// Jsoup.connect("https://projrctjav.com").userAgent(USAGT).get().let { projectj ->
+////// BLog.LOGE("projectj >>>>> ${projectj}")
+////// }
+//// } catch (e: Exception) {
+//// e.printStackTrace()
//// }
-// } catch (e: Exception) {
-// e.printStackTrace()
-// }
+//// }
+// return Result.success().apply {
+//// WorkersDb.insertBulkData(temp)
// }
- return Result.success().apply {
-// WorkersDb.insertBulkData(temp)
- }
- }
-
- private fun parseArcaLi(aracaLi: Element) : ArrayList {
- var tempArray = arrayListOf()
-// BLog.LOGE("aracaLi >>> ${aracaLi}")
- var title = aracaLi.getElementsByClass("title hybrid-title").getT()
- var desc = aracaLi.getElementsByClass("badge").getT()
- desc.plus(aracaLi.getElementsByClass("user-info ").getT())
- var dateTime = aracaLi.getElementsByTag("time").attr("datetime")
- var tumbnail = aracaLi.getElementsByTag("img").attr("src")
- var link = "https://arca.live".plus(if(aracaLi.getElementsByClass("title hybrid-title").size > 0) aracaLi.getElementsByClass("title hybrid-title").get(0).attr("href") else if(aracaLi.getElementsByTag("a").size > 0) aracaLi.getElementsByTag("a").get(0).attr("href") else "")
- if (title.length > 0 && link.length > 20) {
-// if(blockKeyword.filter { desc.contains(it) }.size == 0) {
- Arca().apply {
- this.link = link
- this.title = title
- if (tumbnail.length > 0) {
- this.thumbnail = "https:".plus(tumbnail)
+// }
//
-// BLog.LOGE("Arca thumbnail >>> ${thumbnail}")
- }
- this.desc = desc
- this.dateTiem = dateTime
- }.apply {
-// BLog.LOGE("parseArcaLi >>>> ${this}")
- if (this.pubDate() > beforeOneDay()) {
- tempArray.add(this)
- }
- }
-// }
- }
- return tempArray
- }
-
-
-}
\ No newline at end of file
+// private fun parseArcaLi(aracaLi: Element) : ArrayList {
+// var tempArray = arrayListOf()
+//// BLog.LOGE("aracaLi >>> ${aracaLi}")
+// var title = aracaLi.getElementsByClass("title hybrid-title").getT()
+// var desc = aracaLi.getElementsByClass("badge").getT()
+// desc.plus(aracaLi.getElementsByClass("user-info ").getT())
+// var dateTime = aracaLi.getElementsByTag("time").attr("datetime")
+// var tumbnail = aracaLi.getElementsByTag("img").attr("src")
+// var link = "https://arca.live".plus(if(aracaLi.getElementsByClass("title hybrid-title").size > 0) aracaLi.getElementsByClass("title hybrid-title").get(0).attr("href") else if(aracaLi.getElementsByTag("a").size > 0) aracaLi.getElementsByTag("a").get(0).attr("href") else "")
+// if (title.length > 0 && link.length > 20) {
+//// if(blockKeyword.filter { desc.contains(it) }.size == 0) {
+// Arca().apply {
+// this.link = link
+// this.title = title
+// if (tumbnail.length > 0) {
+// this.thumbnail = "https:".plus(tumbnail)
+////
+//// BLog.LOGE("Arca thumbnail >>> ${thumbnail}")
+// }
+// this.desc = desc
+// this.dateTiem = dateTime
+// }.apply {
+//// BLog.LOGE("parseArcaLi >>>> ${this}")
+// if (this.pubDate() > beforeOneDay()) {
+// tempArray.add(this)
+// }
+// }
+//// }
+// }
+// return tempArray
+// }
+//
+//
+//}
\ No newline at end of file
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/BaseGetter.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/BaseGetter.kt
index 4a0bbe13..58c1d893 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/workers/BaseGetter.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/BaseGetter.kt
@@ -8,10 +8,11 @@ import bums.lunatic.launcher.LunaticLauncher
import bums.lunatic.launcher.model.RssData
import bums.lunatic.launcher.utils.beforeDay
import bums.lunatic.launcher.utils.beforeOneDay
+import io.realm.kotlin.types.RealmObject
import java.util.Calendar
import java.util.Date
-open abstract class BaseGetter : Worker {
+abstract class BaseGetter(internal val context: Context) {
protected companion object {
var lastedUpdateTime = 0L
val defaultDay = 3
@@ -29,21 +30,8 @@ open abstract class BaseGetter : Worker {
val commicsDateTime = beforeDay(1)
val temp = arrayListOf()
- constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
+ abstract fun realWork() : List
+ open suspend fun fetchData(): List {
+ return realWork()
}
-
- @CallSuper
- override fun doWork(): Result {
- LunaticLauncher.mHourlyLogWriter?.writeLog("${this::class.java.simpleName} doWork()")
- val currentTime = before10Min()
- if (lastedUpdateTime > 0L && currentTime > lastedUpdateTime) {
- return Result.success().apply {
-
- }
- }
- return realWork().apply {
- LunaticLauncher.mHourlyLogWriter?.writeLog("${this@BaseGetter::class.java.simpleName} return realWork() ")
- }
- }
- abstract fun realWork() : Result
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/CalendarGetter.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/CalendarGetter.kt
index ac2f29d7..52c6c643 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/workers/CalendarGetter.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/CalendarGetter.kt
@@ -1,149 +1,149 @@
-package bums.lunatic.launcher.workers
-
-import android.content.Context
-import android.net.Uri
-import androidx.work.WorkerParameters
-import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
-import bums.lunatic.launcher.utils.Blog
-
-
-class CalendarGetter : BaseGetter {
- companion object {
- val TAG = "DCGetter"
- }
- constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
-
- }
- override fun realWork(): Result {
- setCalendar()
- return Result.success().apply {
-
- }
- }
-
- fun setCalendar() {
-
-
- val calendars = Uri.parse("content://com.android.calendar/events")
-
- val projection = arrayOf(
- "calendar_id",
-// "htmlUri",
- "title",
-// "eventLocation",
- "description",
-// "eventStatus",
-// "selfAttendeeStatus",
-// "commentsUri",
- "dtstart",
- "dtend",
-// "eventTimezone",
-// "duration",
-// "allDay",
-// "visibility",
-// "transparency",
-// "hasAlarm",
-// "hasExtendedProperties",
-// "rrule",
- "rdate",
-// "exrule",
-// "exdate",
-// "originalEvent",
-// "originalInstanceTime",
-// "originalAllDay",
-// "lastDate",
-// "hasAttendeeData",
-// "guestsCanModify",
-// "guestsCanInviteOthers",
-// "guestsCanSeeGuests",
-// "organizer",
-// "deleted"
- )
-// val managedCursor: Cursor =
- lActivity?.contentResolver?.query(calendars, projection, null, null, null)?.let { managedCursor ->
- if (managedCursor.moveToFirst()) {
- val calendar_id = IntArray(managedCursor.count)
-
-// val htmlUri = arrayOfNulls(managedCursor.count)
- val title = arrayOfNulls(managedCursor.count)
-// val eventLocation = arrayOfNulls(managedCursor.count)
- val description = arrayOfNulls(managedCursor.count)
-// val eventStatus = IntArray(managedCursor.count)
-// val selfAttendeeStatus = IntArray(managedCursor.count)
-// val commentsUri = arrayOfNulls(managedCursor.count)
- val dtstart = arrayOfNulls(managedCursor.count)
- val dtend = arrayOfNulls(managedCursor.count)
-// val eventTimezone = arrayOfNulls(managedCursor.count)
-// val duration = arrayOfNulls(managedCursor.count)
-// val allDay = IntArray(managedCursor.count)
-// val visibility = IntArray(managedCursor.count)
-// val transparency = IntArray(managedCursor.count)
-// val hasAlarm = IntArray(managedCursor.count)
-// val hasExtendedProperties = IntArray(managedCursor.count)
-// val rrule = arrayOfNulls(managedCursor.count)
- val rdate = arrayOfNulls(managedCursor.count)
-// val exrule = arrayOfNulls(managedCursor.count)
-// val exdate = arrayOfNulls(managedCursor.count)
-// val originalEvent = arrayOfNulls(managedCursor.count)
-// val originalInstanceTime = IntArray(managedCursor.count)
-// val originalAllDay = IntArray(managedCursor.count)
-// val lastDate = IntArray(managedCursor.count)
-// val hasAttendeeData = IntArray(managedCursor.count)
-// val guestsCanModify = IntArray(managedCursor.count)
-// val guestsCanInviteOthers = IntArray(managedCursor.count)
-// val guestsCanSeeGuests = IntArray(managedCursor.count)
-// val organizer = arrayOfNulls(managedCursor.count)
-// val deleted = IntArray(managedCursor.count)
-
- for (i in title.indices) {
- calendar_id[i] = managedCursor.getInt(0)
- Blog.LOGE("Calendar ID : " + calendar_id[i])
-// htmlUri[i] = managedCursor.getString(1)
-// Log.i("Calendar", "htmlUri : " + htmlUri[i])
- title[i] = managedCursor.getString(1)
- Blog.LOGE("Calendar title : " + title[i])
-// eventLocation[i] = managedCursor.getString(3)
-// Log.i("Calendar", "eventLocation : " + eventLocation[i])
- description[i] = managedCursor.getString(2)
-// eventStatus[i] = managedCursor.getInt(5)
-// selfAttendeeStatus[i] = managedCursor.getInt(6)
-// commentsUri[i] = managedCursor.getString(7)
- dtstart[i] = managedCursor.getString(3)
- Blog.LOGE("Calendar dtstart : " + rdate[i])
- dtend[i] = managedCursor.getString(4)
- Blog.LOGE("Calendar dtend : " + rdate[i])
-// eventTimezone[i] = managedCursor.getString(10)
-// duration[i] = managedCursor.getString(11)
-// allDay[i] = managedCursor.getInt(12)
-// visibility[i] = managedCursor.getInt(13)
-// transparency[i] = managedCursor.getInt(14)
-// hasAlarm[i] = managedCursor.getInt(15)
-// hasExtendedProperties[i] = managedCursor.getInt(16)
-// rrule[i] = managedCursor.getString(17)
- rdate[i] = managedCursor.getString(5)
- Blog.LOGE("Calendar rdate : " + rdate[i])
-// exrule[i] = managedCursor.getString(19)
-// exdate[i] = managedCursor.getString(20)
-// originalEvent[i] = managedCursor.getString(21)
-// originalInstanceTime[i] = managedCursor.getInt(22)
-// originalAllDay[i] = managedCursor.getInt(23)
-// lastDate[i] = managedCursor.getInt(24)
-// hasAttendeeData[i] = managedCursor.getInt(25)
-// guestsCanModify[i] = managedCursor.getInt(26)
-// guestsCanInviteOthers[i] = managedCursor.getInt(27)
-// guestsCanSeeGuests[i] = managedCursor.getInt(28)
-// organizer[i] = managedCursor.getString(29)
-// deleted[i] = managedCursor.getInt(30)
-
- if (title[i] != null) {
- Blog.LOGE("title[i] ${title[i]}")
- }
-
- managedCursor.moveToNext()
- }
- }
- managedCursor.close()
- }
- }
-
-}
\ No newline at end of file
+//package bums.lunatic.launcher.workers
+//
+//import android.content.Context
+//import android.net.Uri
+//import androidx.work.WorkerParameters
+//import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
+//import bums.lunatic.launcher.utils.Blog
+//import io.realm.kotlin.types.RealmObject
+//
+//
+//class CalendarGetter : BaseGetter {
+// companion object {
+// val TAG = "DCGetter"
+// }
+// constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
+//
+// }
+//
+//
+// override fun realWork(): List {
+// return setCalendar()
+// }
+//
+// fun setCalendar() {
+//
+//
+// val calendars = Uri.parse("content://com.android.calendar/events")
+//
+// val projection = arrayOf(
+// "calendar_id",
+//// "htmlUri",
+// "title",
+//// "eventLocation",
+// "description",
+//// "eventStatus",
+//// "selfAttendeeStatus",
+//// "commentsUri",
+// "dtstart",
+// "dtend",
+//// "eventTimezone",
+//// "duration",
+//// "allDay",
+//// "visibility",
+//// "transparency",
+//// "hasAlarm",
+//// "hasExtendedProperties",
+//// "rrule",
+// "rdate",
+//// "exrule",
+//// "exdate",
+//// "originalEvent",
+//// "originalInstanceTime",
+//// "originalAllDay",
+//// "lastDate",
+//// "hasAttendeeData",
+//// "guestsCanModify",
+//// "guestsCanInviteOthers",
+//// "guestsCanSeeGuests",
+//// "organizer",
+//// "deleted"
+// )
+//// val managedCursor: Cursor =
+// lActivity?.contentResolver?.query(calendars, projection, null, null, null)?.let { managedCursor ->
+// if (managedCursor.moveToFirst()) {
+// val calendar_id = IntArray(managedCursor.count)
+//
+//// val htmlUri = arrayOfNulls(managedCursor.count)
+// val title = arrayOfNulls(managedCursor.count)
+//// val eventLocation = arrayOfNulls(managedCursor.count)
+// val description = arrayOfNulls(managedCursor.count)
+//// val eventStatus = IntArray(managedCursor.count)
+//// val selfAttendeeStatus = IntArray(managedCursor.count)
+//// val commentsUri = arrayOfNulls(managedCursor.count)
+// val dtstart = arrayOfNulls(managedCursor.count)
+// val dtend = arrayOfNulls(managedCursor.count)
+//// val eventTimezone = arrayOfNulls(managedCursor.count)
+//// val duration = arrayOfNulls(managedCursor.count)
+//// val allDay = IntArray(managedCursor.count)
+//// val visibility = IntArray(managedCursor.count)
+//// val transparency = IntArray(managedCursor.count)
+//// val hasAlarm = IntArray(managedCursor.count)
+//// val hasExtendedProperties = IntArray(managedCursor.count)
+//// val rrule = arrayOfNulls(managedCursor.count)
+// val rdate = arrayOfNulls(managedCursor.count)
+//// val exrule = arrayOfNulls(managedCursor.count)
+//// val exdate = arrayOfNulls(managedCursor.count)
+//// val originalEvent = arrayOfNulls(managedCursor.count)
+//// val originalInstanceTime = IntArray(managedCursor.count)
+//// val originalAllDay = IntArray(managedCursor.count)
+//// val lastDate = IntArray(managedCursor.count)
+//// val hasAttendeeData = IntArray(managedCursor.count)
+//// val guestsCanModify = IntArray(managedCursor.count)
+//// val guestsCanInviteOthers = IntArray(managedCursor.count)
+//// val guestsCanSeeGuests = IntArray(managedCursor.count)
+//// val organizer = arrayOfNulls(managedCursor.count)
+//// val deleted = IntArray(managedCursor.count)
+//
+// for (i in title.indices) {
+// calendar_id[i] = managedCursor.getInt(0)
+// Blog.LOGE("Calendar ID : " + calendar_id[i])
+//// htmlUri[i] = managedCursor.getString(1)
+//// Log.i("Calendar", "htmlUri : " + htmlUri[i])
+// title[i] = managedCursor.getString(1)
+// Blog.LOGE("Calendar title : " + title[i])
+//// eventLocation[i] = managedCursor.getString(3)
+//// Log.i("Calendar", "eventLocation : " + eventLocation[i])
+// description[i] = managedCursor.getString(2)
+//// eventStatus[i] = managedCursor.getInt(5)
+//// selfAttendeeStatus[i] = managedCursor.getInt(6)
+//// commentsUri[i] = managedCursor.getString(7)
+// dtstart[i] = managedCursor.getString(3)
+// Blog.LOGE("Calendar dtstart : " + rdate[i])
+// dtend[i] = managedCursor.getString(4)
+// Blog.LOGE("Calendar dtend : " + rdate[i])
+//// eventTimezone[i] = managedCursor.getString(10)
+//// duration[i] = managedCursor.getString(11)
+//// allDay[i] = managedCursor.getInt(12)
+//// visibility[i] = managedCursor.getInt(13)
+//// transparency[i] = managedCursor.getInt(14)
+//// hasAlarm[i] = managedCursor.getInt(15)
+//// hasExtendedProperties[i] = managedCursor.getInt(16)
+//// rrule[i] = managedCursor.getString(17)
+// rdate[i] = managedCursor.getString(5)
+// Blog.LOGE("Calendar rdate : " + rdate[i])
+//// exrule[i] = managedCursor.getString(19)
+//// exdate[i] = managedCursor.getString(20)
+//// originalEvent[i] = managedCursor.getString(21)
+//// originalInstanceTime[i] = managedCursor.getInt(22)
+//// originalAllDay[i] = managedCursor.getInt(23)
+//// lastDate[i] = managedCursor.getInt(24)
+//// hasAttendeeData[i] = managedCursor.getInt(25)
+//// guestsCanModify[i] = managedCursor.getInt(26)
+//// guestsCanInviteOthers[i] = managedCursor.getInt(27)
+//// guestsCanSeeGuests[i] = managedCursor.getInt(28)
+//// organizer[i] = managedCursor.getString(29)
+//// deleted[i] = managedCursor.getInt(30)
+//
+// if (title[i] != null) {
+// Blog.LOGE("title[i] ${title[i]}")
+// }
+//
+// managedCursor.moveToNext()
+// }
+// }
+// managedCursor.close()
+// }
+// }
+//
+//}
\ No newline at end of file
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/ClienGetter.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/ClienGetter.kt
index fee02a0d..9640bb83 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/workers/ClienGetter.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/ClienGetter.kt
@@ -9,14 +9,14 @@ import bums.lunatic.launcher.model.getHref
import bums.lunatic.launcher.model.getRssData
import bums.lunatic.launcher.model.getT
import bums.lunatic.launcher.utils.Blog
+import io.realm.kotlin.types.RealmObject
import org.jsoup.Jsoup
-class ClienGetter : BaseGetter {
+class ClienGetter(context: Context) : BaseGetter(context = context) {
companion object {
val TAG = "ClienGetter"
}
- constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
- }
+
fun parseClien(div_clien : org.jsoup.nodes.Element) {
// BLog.LOGE("div_clien >>>> ${div_clien}")
@@ -44,33 +44,12 @@ class ClienGetter : BaseGetter {
}
}
}
- // var desc = tq_tr.getElementsByClass("cate").getT()
-// var title = tq_tr.getElementsByClass("title").getT()
-// var pageLink = tq_tr.getElementsByTag("a").getHref()
-// var dateTime = tq_tr.getElementsByClass("time").getT()
-
-// BLog.LOGE("${TAG} :::: desc >>> $desc")
-// BLog.LOGE("${TAG} :::: title >>> $title")
-// BLog.LOGE("${TAG} :::: pageLink >>> $pageLink")
-// BLog.LOGE("${TAG} :::: dateTime >>> $dateTime")
-
-// if (title.length > 0 && pageLink.length > 0) {
-// TheQoo().let { tq ->
-// tq.title = title
-// tq.link = "https://theqoo.net".plus(pageLink)
-// tq.dateTiem = dateTime
-// tq.desc = desc
-// if (tq.pubDate() > limitDateTime) {
-// temp.add(tq.getRssData())
-// }
-// }
-// }
-
-
}
+
+
@SuppressLint("RestrictedApi")
- override fun realWork(): Result {
+ override fun realWork(): List {
RssDataType.CLIEN.isOn {
Blog.LOGE("realWork() ${this::class.simpleName}")
try {
@@ -90,8 +69,6 @@ class ClienGetter : BaseGetter {
e.printStackTrace()
}
}
- return Result.success().apply {
- WorkersDb.insertBulkData(temp)
- }
+ return temp
}
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/ContactInfoGetter.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/ContactInfoGetter.kt
index d92785bc..40e7b6be 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/workers/ContactInfoGetter.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/ContactInfoGetter.kt
@@ -4,17 +4,18 @@ import android.content.Context
import android.provider.ContactsContract
import androidx.work.WorkerParameters
import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
-import bums.lunatic.launcher.apps.SimpleContact
+import bums.lunatic.launcher.model.SimpleContact
+import bums.lunatic.launcher.utils.Blog
import io.realm.kotlin.ext.query
+import io.realm.kotlin.types.RealmObject
-class ContactInfoGetter : BaseGetter {
+class ContactInfoGetter(context: Context) : BaseGetter(context) {
companion object {
val TAG = "ContactInfoGetter"
}
- constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
- }
- override fun realWork(): Result {
+ override fun realWork(): List {
+ var temp = mutableListOf()
val phoneUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI
val projection = arrayOf(
ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
@@ -23,28 +24,31 @@ class ContactInfoGetter : BaseGetter {
)
try {
- val cursor = lActivity?.contentResolver?.query(phoneUri, projection, null, null, null)
- if (cursor != null) {
+
+ lActivity?.contentResolver?.query(phoneUri, projection, null, null, null)?.use { cursor ->
+ val idIdx = cursor.getColumnIndex(projection[0])
+ val nameIdx = cursor.getColumnIndex(projection[1])
+ val numberIdx = cursor.getColumnIndex(projection[2])
+
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) {
- if (WorkersDb.getRealm().query("id == $0", contactId).find().size == 0) {
- WorkersDb.update(SimpleContact(contactId,name,number))
- }
+ val contactId = cursor.getString(idIdx)
+
+ val name = cursor.getString(nameIdx)
+ // 루프 내 불필요한 객체 생성 방지 및 null 처리
+ val rawNumber = cursor.getString(numberIdx)
+ val number = rawNumber?.replace("-", "") ?: ""
+
+ if (!name.isNullOrEmpty() && number.isNotEmpty()) {
+ temp.add(SimpleContact(contactId, name, number))
}
}
}
- // 데이터 계열은 반드시 닫아줘야 한다.
- cursor?.close()
- } catch ( e : Exception) {
+
+
+ } catch (e: Exception) {
e.printStackTrace()
}
- return Result.success()
+ Blog.LOGE("ContactInfoGetter >>> ${temp.size}")
+ return temp
}
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/DCGetter.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/DCGetter.kt
index b9ca070d..4d9eab9b 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/workers/DCGetter.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/DCGetter.kt
@@ -9,15 +9,15 @@ import bums.lunatic.launcher.model.RssDataInterface
import bums.lunatic.launcher.model.RssDataType
import bums.lunatic.launcher.model.getRssData
import bums.lunatic.launcher.utils.Blog
+import io.realm.kotlin.types.RealmObject
import org.jsoup.Jsoup
import java.text.SimpleDateFormat
-class DCGetter : BaseGetter {
+class DCGetter(context: Context) : BaseGetter(context) {
companion object {
val TAG = "DCGetter"
}
- constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
- }
+
fun parseDcLi(dc_li : org.jsoup.nodes.Element) : ArrayList{
var temp = arrayListOf()
@@ -71,7 +71,7 @@ class DCGetter : BaseGetter {
}
val df = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
@SuppressLint("RestrictedApi")
- override fun realWork(): Result {
+ override fun realWork(): List {
// Blog.LOGE("${TAG} RssDataType.DCINSIDE.isOn >>>> ${PrefHelper.getBoolean(RssDataType.DCINSIDE.name,false)}")
RssDataType.DCINSIDE.isOn {
Blog.LOGE("realWork() ${this::class.simpleName}")
@@ -161,12 +161,6 @@ class DCGetter : BaseGetter {
e.printStackTrace()
}
}
- return Result.success().apply {
- try {
- WorkersDb.insertBulkData(temp)
- } catch (e : Exception) {
-
- }
- }
+ return temp
}
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/DotaxGetter.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/DotaxGetter.kt
index 52267624..2a5ca005 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/workers/DotaxGetter.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/DotaxGetter.kt
@@ -6,30 +6,24 @@ import androidx.work.WorkerParameters
import bums.lunatic.launcher.LauncherActivity
import bums.lunatic.launcher.model.RssDataType
import bums.lunatic.launcher.utils.Blog
+import io.realm.kotlin.types.RealmObject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
-class DotaxGetter : BaseGetter {
+class DotaxGetter(context: Context) : BaseGetter(context) {
companion object {
val COMIC2_WORK_TAG = "ComicGetter2"
}
- constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
- }
@SuppressLint("RestrictedApi")
- override fun realWork(): Result {
+ override fun realWork(): List {
RssDataType.DOTAX.isOn {
- try {
- Blog.LOGE("realWork() ${this::class.simpleName}")
- temp.clear()
-// val dotaxUrls = arrayListOf("https://cafe.daum.net/dotax",
-// "https://m.cafe.daum.net/dotax/Elgq"
-//// "https://m.cafe.daum.net/dotax/_rec?page=3"
-// )
-// dotaxUrls?.forEach { url ->
+ try {
+ Blog.LOGE("realWork() ${this::class.simpleName}")
+ temp.clear()
CoroutineScope(Dispatchers.Main).launch {
withContext(Dispatchers.Main) {
LauncherActivity.lActivity?.let {
@@ -37,32 +31,8 @@ class DotaxGetter : BaseGetter {
}
}
}
-// }
-// dotaxUrls?.forEach {
-// Jsoup.connect(it).timeout(3000).userAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36").get()?.let { dotax ->
-// Blog.LOGE("dotax.html() >>> ${dotax.html()}")
-// dotax.getElementsByTag("tr").forEach { dotax_li ->
-// Blog.LOGE("dotax_li >>> ${dotax_li.text()}")
-// if (dotax_li.getElementsByTag("a").size > 0) {
-// val pageLink = dotax_li.getElementsByTag("a").get(0).attr("href")
-// val desc = dotax_li.getElementsByClass("board_name").text()
-// val dateTime = dotax_li.getElementsByClass("created_at").text()
-// val title = dotax_li.getElementsByClass("txt_detail").text()
-// val thumbnail = dotax_li.getElementsByClass("article_thumb").text()
-// if (pageLink.length > 0 && desc.length > 0 && dateTime.length > 0 && title.length > 0) {
-// Dotax(pageLink, desc, dateTime, title, thumbnail).let { dotax ->
-// if(dotax.pubDate() > commicsDateTime) {
-// temp.add(dotax.getRssData())
-// }
-// }
-// }
-// }
-// }
-// }
-// }
- } catch (e : Exception) {e.printStackTrace()}}
- return Result.success().apply {
- WorkersDb.insertBulkData(temp)
- }
+
+ } catch (e : Exception) {e.printStackTrace()}}
+ return temp
}
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/FmKoreaGetter.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/FmKoreaGetter.kt
index cf50fa41..c19a45b5 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/workers/FmKoreaGetter.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/FmKoreaGetter.kt
@@ -7,16 +7,14 @@ import bums.lunatic.launcher.model.FmKorea
import bums.lunatic.launcher.model.RssDataType
import bums.lunatic.launcher.model.getRssData
import bums.lunatic.launcher.utils.Blog
+import io.realm.kotlin.types.RealmObject
import org.jsoup.Jsoup
import java.util.Date
-class FmKoreaGetter : BaseGetter {
+class FmKoreaGetter (context: Context): BaseGetter(context) {
companion object {
val FM_WORK_TAG = "FmKoreaGetter"
}
- constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
-
- }
fun extractDocumentSrl(url: String): String? {
val uri = java.net.URI(url)
@@ -33,7 +31,8 @@ class FmKoreaGetter : BaseGetter {
}
@SuppressLint("RestrictedApi")
- override fun realWork(): Result {
+ override fun realWork(): List {
+ temp.clear()
RssDataType.FMKORAE.isOn {
val now = Date()
try {
@@ -97,8 +96,6 @@ class FmKoreaGetter : BaseGetter {
e.printStackTrace()
}
}
- return Result.success().apply {
- WorkersDb.insertBulkData(temp)
- }
+ return temp
}
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/LocationGetter.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/LocationGetter.kt
index 7302d0d1..60a3c3e1 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/workers/LocationGetter.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/LocationGetter.kt
@@ -1,54 +1,55 @@
package bums.lunatic.launcher.workers
-
-import android.annotation.SuppressLint
-import android.content.Context
-import android.location.Location
-import androidx.work.WorkerParameters
-import bums.lunatic.launcher.common.letTrue
-import bums.lunatic.launcher.helpers.PrefBoolean
-import bums.lunatic.launcher.utils.Blog
-import bums.lunatic.launcher.workers.LocationUpdateService.Companion.pushLocation
-import com.google.android.gms.location.LocationServices
-import com.google.android.gms.location.Priority
-import com.google.android.gms.tasks.CancellationTokenSource
+//
+//import android.annotation.SuppressLint
+//import android.content.Context
+//import android.location.Location
+//import androidx.work.WorkerParameters
+//import bums.lunatic.launcher.common.letTrue
+//import bums.lunatic.launcher.helpers.PrefBoolean
+//import bums.lunatic.launcher.utils.Blog
+//import bums.lunatic.launcher.workers.LocationUpdateService.Companion.pushLocation
+//import com.google.android.gms.location.LocationServices
+//import com.google.android.gms.location.Priority
+//import com.google.android.gms.tasks.CancellationTokenSource
+//import io.realm.kotlin.types.RealmObject
import kotlin.math.cos
-
-class LocationGetter(context: Context, workerParams: WorkerParameters) : BaseGetter(context, workerParams) {
- companion object {
- val TAG = "LocationGetter"
- var longitude: Double = 0.0
- var latitude: Double = 0.0
- }
-
- @SuppressLint("MissingPermission")
- override fun realWork(): Result {
-// Blog.LOGE("${OpenWeatherGetter.TAG} realWork()")
-
- LocationServices.getFusedLocationProviderClient(this.applicationContext)
- .getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, CancellationTokenSource().token)
- .addOnSuccessListener{ success: Location? ->
- success?.let {
- Blog.LOGE("Location >>> $it")
- Blog.LOGE("Location >>> (latitude)${it.longitude}/(longitude)${it.latitude}")
- longitude = it.longitude
- latitude = it.latitude
-// runWeatherGetter()
- PrefBoolean.location.get().letTrue {
- pushLocation(this.applicationContext,it.latitude, it.longitude)
- }
- }
- }.addOnFailureListener{
- Blog.LOGE("Location error >>> $it")
- }
-
- return Result.success()
- }
-
-
-
-}
-
-
+//
+//class LocationGetter(context: Context) : BaseGetter(context) {
+// companion object {
+// val TAG = "LocationGetter"
+// var longitude: Double = 0.0
+// var latitude: Double = 0.0
+// }
+//
+// @SuppressLint("MissingPermission")
+// override fun realWork(): List {
+//// Blog.LOGE("${OpenWeatherGetter.TAG} realWork()")
+//
+// LocationServices.getFusedLocationProviderClient(context)
+// .getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, CancellationTokenSource().token)
+// .addOnSuccessListener{ success: Location? ->
+// success?.let {
+// Blog.LOGE("Location >>> $it")
+// Blog.LOGE("Location >>> (latitude)${it.longitude}/(longitude)${it.latitude}")
+// longitude = it.longitude
+// latitude = it.latitude
+//// runWeatherGetter()
+// PrefBoolean.location.get().letTrue {
+// pushLocation(context,it.latitude, it.longitude)
+// }
+// }
+// }.addOnFailureListener{
+// Blog.LOGE("Location error >>> $it")
+// }
+//
+// return temp
+// }
+//
+//
+//
+//}
+//
+//
val EARTH_RADIUS_METERS = 6371000
val LATITUDE_DEGREE_PER_METER: Double = 1.0 / (2 * Math.PI * EARTH_RADIUS_METERS / 360)
@@ -70,8 +71,8 @@ fun longitudeRange(latitude: Double, longitude: Double, radiusInMeters: Int): Do
return doubleArrayOf(minLongitude, maxLongitude)
}
-
-
-//https://jinkpark.tistory.com/296
-//https://develoyummer.tistory.com/103
-//https://ghj1001020.tistory.com/300
\ No newline at end of file
+//
+//
+////https://jinkpark.tistory.com/296
+////https://develoyummer.tistory.com/103
+////https://ghj1001020.tistory.com/300
\ No newline at end of file
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/LocationUpdateService.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/LocationUpdateService.kt
index ed4862f5..9f21ff2c 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/workers/LocationUpdateService.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/LocationUpdateService.kt
@@ -18,8 +18,6 @@ import bums.lunatic.launcher.helpers.PrefString
import bums.lunatic.launcher.model.LocationLog
import bums.lunatic.launcher.utils.Blog
import bums.lunatic.launcher.utils.inRange
-import bums.lunatic.launcher.workers.LocationGetter.Companion.latitude
-import bums.lunatic.launcher.workers.LocationGetter.Companion.longitude
import com.google.android.gms.location.LocationServices
import com.google.gson.Gson
import io.realm.kotlin.ext.query
@@ -42,6 +40,8 @@ import java.util.concurrent.TimeUnit
class LocationUpdateService : Service(), LocationListener {
companion object {
+ var longitude: Double = 0.0
+ var latitude: Double = 0.0
fun pushLocation(context: Context, lat :Double, long : Double) {
try {
Blog.LOGE("Location >>> ${lat},${long}")
@@ -179,7 +179,6 @@ class LocationUpdateService : Service(), LocationListener {
).show()
longitude = location.longitude
latitude = location.latitude
-// runWeatherGetter()
pushLocation(this.applicationContext, location.latitude, location.longitude)
}
}
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/NewsFeedsGetter.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/NewsFeedsGetter.kt
index 1458dc97..fddee994 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/workers/NewsFeedsGetter.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/NewsFeedsGetter.kt
@@ -7,17 +7,17 @@ import bums.lunatic.launcher.home.adapters.RssFeedsParser
import bums.lunatic.launcher.model.RssDataType
import bums.lunatic.launcher.model.getRssData
import bums.lunatic.launcher.utils.RssList
+import io.realm.kotlin.types.RealmObject
-class NewsFeedsGetter : BaseGetter {
+class NewsFeedsGetter(context: Context) : BaseGetter(context) {
companion object {
val FEDDS_WORK_TAG = "NewsFeedsGetter"
}
var feddsUrls = arrayListOf()
- constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
- }
+
@SuppressLint("RestrictedApi")
- override fun realWork(): Result {
+ override fun realWork(): List {
RssDataType.NEWSFEED.isOn {
feddsUrls.clear()
feddsUrls.addAll(RssList.newsFeeds)
@@ -32,15 +32,7 @@ class NewsFeedsGetter : BaseGetter {
}
}
- return Result.success().apply {
- WorkersDb.insertBulkData(temp)
- }
-
-// temp.forEach { synchronized(rssSet){
-// rssSet.put(it.originPage(), it)
-// } }.run {
-// rssSetTouchCount -= 1
-// Result.success() }
+ return temp
}
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/OpenWeatherGetter.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/OpenWeatherGetter.kt
index 6dc3ab64..f766e79b 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/workers/OpenWeatherGetter.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/OpenWeatherGetter.kt
@@ -1,89 +1,90 @@
-package bums.lunatic.launcher.workers
-
-import android.content.Context
-import androidx.work.WorkerParameters
-import bums.lunatic.launcher.helpers.PrefString
-import bums.lunatic.launcher.model.WeatherForcast
-import bums.lunatic.launcher.model.WeatherInfoManager
-import bums.lunatic.launcher.utils.Blog
-import io.realm.kotlin.UpdatePolicy
-import retrofit2.Call
-import retrofit2.Retrofit
-import retrofit2.converter.gson.GsonConverterFactory
-import retrofit2.create
-import retrofit2.http.GET
-import retrofit2.http.Path
-import retrofit2.http.Query
-
-class OpenWeatherGetter(context: Context, workerParams: WorkerParameters) : BaseGetter(context, workerParams) {
- companion object {
- val TAG = "OpenWeatherGetter"
- var lon: Double? = null // 경도
- var lat: Double? = null // 위도
- }
- //////////////////////////////////////////
- // weatherapi
- val VER_WEATHERAPI = "v1"
- val URI_WEATHERAPI = "https://api.weatherapi.com"
- val KEY_WEATHERAPI = "8133d83d23ab4175a4160624241909"
- val DAYS = 3
- //////////////////////////////////////////
-
- //////////////////////////////////////////
-
- override fun realWork(): Result {
- Blog.LOGE("${TAG} realWork()")
- // 위치 값 가져오기
- lat = LocationGetter.latitude
- lon = LocationGetter.longitude
- if (lat != null && lon != null) {
- getWeather(lat!!, lon!!)
- } else {
- Blog.LOGE("lat or lon is null")
- }
- return Result.success()
- }
-
- fun getWeather(latitude: Double, longitude: Double) {
- Blog.LOGE("into getWeather ${PrefString.weatherApiKey.get()}")
- ///saved weatherForcast
- Retrofit.Builder()
- .baseUrl(URI_WEATHERAPI)
- .addConverterFactory(GsonConverterFactory.create())
- .build()
- .create()
- .getForecast( // weatherApi
- ver = VER_WEATHERAPI,
- key = PrefString.weatherApiKey.get(),
- q = "$latitude,$longitude",
- days = (System.currentTimeMillis() % 5L).toInt().toString()
- )?.execute()?.let { response ->
-// BLog.LOGE("into getWeather after execute")
-// BLog.LOGE("weatherApi forecast response >>> $response")
- response.body()?.let { weatherInfo ->
- WeatherInfoManager.info = weatherInfo
- WeatherInfoManager.readyForSaving(lat ?: 0.0, lon ?: 0.0)
- // Realm에 저장
- WorkersDb.getRealm().writeBlocking {
- copyToRealm(weatherInfo, UpdatePolicy.ALL).also {
-// BLog.LOGE("saved weatherForcast >>> $it")
- }
- }
-// BLog.LOGE("saved weatherForcast forecastdayRealm.size >>> ${WorkersDb.getRealm().query().first().find()?.forecast?.forecastdayRealm?.size}")
-// BLog.LOGE("saved weatherForcast hour.count >>> ${WorkersDb.getRealm().query().count().find()}")
- }
- }
- }
-}
-
-interface RestrofitService {
- // weather_api
- @GET("/{ver}/forecast.json")
- fun getForecast(
- @Path("ver") ver: String,
- @Query("key") key: String,
- @Query("q") q: String,
- @Query("days") days: String
- ): Call?
-}
-
+//package bums.lunatic.launcher.workers
+//
+//import android.content.Context
+//import androidx.work.WorkerParameters
+//import bums.lunatic.launcher.helpers.PrefString
+//import bums.lunatic.launcher.model.WeatherForcast
+//import bums.lunatic.launcher.model.WeatherInfoManager
+//import bums.lunatic.launcher.utils.Blog
+//import io.realm.kotlin.UpdatePolicy
+//import io.realm.kotlin.types.RealmObject
+//import retrofit2.Call
+//import retrofit2.Retrofit
+//import retrofit2.converter.gson.GsonConverterFactory
+//import retrofit2.create
+//import retrofit2.http.GET
+//import retrofit2.http.Path
+//import retrofit2.http.Query
+//
+//class OpenWeatherGetter(context: Context) : BaseGetter(context) {
+// companion object {
+// val TAG = "OpenWeatherGetter"
+// var lon: Double? = null // 경도
+// var lat: Double? = null // 위도
+// }
+// //////////////////////////////////////////
+// // weatherapi
+// val VER_WEATHERAPI = "v1"
+// val URI_WEATHERAPI = "https://api.weatherapi.com"
+// val KEY_WEATHERAPI = "8133d83d23ab4175a4160624241909"
+// val DAYS = 3
+// //////////////////////////////////////////
+//
+// //////////////////////////////////////////
+//
+// override fun realWork(): List {
+// Blog.LOGE("${TAG} realWork()")
+// // 위치 값 가져오기
+// lat = LocationGetter.latitude
+// lon = LocationGetter.longitude
+// if (lat != null && lon != null) {
+// getWeather(lat!!, lon!!)
+// } else {
+// Blog.LOGE("lat or lon is null")
+// }
+// return Result.success()
+// }
+//
+// fun getWeather(latitude: Double, longitude: Double) {
+// Blog.LOGE("into getWeather ${PrefString.weatherApiKey.get()}")
+// ///saved weatherForcast
+// Retrofit.Builder()
+// .baseUrl(URI_WEATHERAPI)
+// .addConverterFactory(GsonConverterFactory.create())
+// .build()
+// .create()
+// .getForecast( // weatherApi
+// ver = VER_WEATHERAPI,
+// key = PrefString.weatherApiKey.get(),
+// q = "$latitude,$longitude",
+// days = (System.currentTimeMillis() % 5L).toInt().toString()
+// )?.execute()?.let { response ->
+//// BLog.LOGE("into getWeather after execute")
+//// BLog.LOGE("weatherApi forecast response >>> $response")
+// response.body()?.let { weatherInfo ->
+// WeatherInfoManager.info = weatherInfo
+// WeatherInfoManager.readyForSaving(lat ?: 0.0, lon ?: 0.0)
+// // Realm에 저장
+// WorkersDb.getRealm().writeBlocking {
+// copyToRealm(weatherInfo, UpdatePolicy.ALL).also {
+//// BLog.LOGE("saved weatherForcast >>> $it")
+// }
+// }
+//// BLog.LOGE("saved weatherForcast forecastdayRealm.size >>> ${WorkersDb.getRealm().query().first().find()?.forecast?.forecastdayRealm?.size}")
+//// BLog.LOGE("saved weatherForcast hour.count >>> ${WorkersDb.getRealm().query().count().find()}")
+// }
+// }
+// }
+//}
+//
+//interface RestrofitService {
+// // weather_api
+// @GET("/{ver}/forecast.json")
+// fun getForecast(
+// @Path("ver") ver: String,
+// @Query("key") key: String,
+// @Query("q") q: String,
+// @Query("days") days: String
+// ): Call?
+//}
+//
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/RecentCallGetter.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/RecentCallGetter.kt
index 79b981df..9f8bdb79 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/workers/RecentCallGetter.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/RecentCallGetter.kt
@@ -5,7 +5,6 @@ import android.content.Context
import android.provider.CallLog
import androidx.work.WorkerParameters
import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
-import bums.lunatic.launcher.apps.SimpleContact
import bums.lunatic.launcher.utils.beforeDay
import bums.lunatic.launcher.utils.getContactId
import com.google.gson.Gson
@@ -48,18 +47,16 @@ class RecentCall : RealmObject {
}
-class RecentCallGetter : BaseGetter {
+class RecentCallGetter(context: Context) : BaseGetter(context) {
companion object{
var dayRange = BaseGetter.defaultDay
val TAG = "RecentCallGetter"
val dateFormat = SimpleDateFormat("yyy/MM/dd-HH:mm:ss")
}
- constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
- }
@SuppressLint("RestrictedApi")
- override fun realWork(): Result {
-
+ override fun realWork(): List {
+ var temp = mutableListOf()
var dateParam = beforeDay(dayRange).toString()
var managedCursor = lActivity?.contentResolver?.query(
CallLog.Calls.CONTENT_URI, arrayOf(
@@ -69,8 +66,7 @@ class RecentCallGetter : BaseGetter {
CallLog.Calls.DURATION,
CallLog.Calls.CACHED_NAME,
), CallLog.Calls.DATE + " >= ? " , arrayOf(dateParam), CallLog.Calls.DATE + " desc")
- //+ CallLog.Calls.TYPE + " > ?"
- //, "2"
+
if(managedCursor != null && managedCursor.isClosed == false) {
try {
val number = managedCursor.getColumnIndex(CallLog.Calls.NUMBER)
@@ -97,8 +93,7 @@ class RecentCallGetter : BaseGetter {
CallLog.Calls.BLOCKED_TYPE -> { dir = "BLOCKED_TYPE" }
CallLog.Calls.ANSWERED_EXTERNALLY_TYPE -> { dir = "ANSWERED_EXTERNALLY_TYPE" }
}
-
- val call = RecentCall(
+ temp.add(RecentCall(
1,
callerName,
phNumber,
@@ -107,27 +102,8 @@ class RecentCallGetter : BaseGetter {
dateFormat.format(Date(callDayTime)),
callDayTime,
callDuration.toLong()
- )
- call.uniqK?.let {
-// BLog.LOGE("${TAG} call.uniqK? >>> ${call.uniqK}")
- WorkersDb.getRealm().writeBlocking {
- if(query().query("uniqK == $0", it).find().count() < 1) {
- this.copyToRealm(call, UpdatePolicy.ALL)
- var cId = getContactId(lActivity!!.contentResolver, call.number)
- if (cId!=null && cId.length > 0) {
- val result = query().query("id == $0", cId).find()
- if(result.size > 0){
- var contact = result.first()
- contact.touchCount = contact.touchCount + 1
- contact.lastedTouchDateTime = Math.max(contact.lastedTouchDateTime, callDayTime)
-// BLog.LOGE("${TAG} updatetouch info >>>> ${contact.touchCount}")
- }
- }
- }
-// BLog.LOGE("${TAG} updatetouch info >>>> BE ")
- }
-// BLog.LOGE("${TAG} updatetouch info >>>> E")
- }
+ ))
+
}
} catch (e: Exception) {
e.printStackTrace()
@@ -135,9 +111,7 @@ class RecentCallGetter : BaseGetter {
managedCursor.close()
}
}
- return Result.success().apply {
- dayRange = BaseGetter.defaultDay
- }
+ return temp
}
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/RecentSmsGetter.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/RecentSmsGetter.kt
index db8ee436..4d9546ad 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/workers/RecentSmsGetter.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/RecentSmsGetter.kt
@@ -23,20 +23,16 @@ import java.io.IOException
import java.io.InputStream
import java.io.InputStreamReader
-class RecentSmsGetter : BaseGetter {
+class RecentSmsGetter(context: Context) : BaseGetter(context) {
companion object {
var dayRange = BaseGetter.defaultDay
val SMS_WORK_TAG = "RecentSmsGetter"
}
- constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
-
- }
-
-
@SuppressLint("RestrictedApi")
- override fun realWork(): Result {
+ override fun realWork(): List {
+ var temp = mutableListOf()
var dateParam = beforeDay(dayRange).toString()
val managedCursor = lActivity?.contentResolver?.query(
Telephony.Sms.CONTENT_URI, arrayOf(
@@ -88,15 +84,8 @@ class RecentSmsGetter : BaseGetter {
)
log.isMms = false
// BLog.LOGE("RecentSmsGetter resultData put ${phNumber +"_"+ reciveDate} >>> ${log.toJson()}")
- log.sender = getContactName(applicationContext.contentResolver,phNumber) ?: ""
- WorkersDb.getRealm().apply {
- if (query("uniqKey == $0", log.uniqKey).find().size == 0) {
- writeBlocking {
- copyToRealm(log)
- }
- }
- }
-
+ log.sender = getContactName(context.applicationContext.contentResolver,phNumber) ?: ""
+ temp.add(log)
}
} catch (e: Exception) {
@@ -107,9 +96,7 @@ class RecentSmsGetter : BaseGetter {
if (lActivity?.contentResolver != null) {
MmsQueryHelper(lActivity?.contentResolver!!).query()
}
- return Result.success().apply {
- dayRange = BaseGetter.defaultDay
- }
+ return temp
}
}
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/RedditGetter.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/RedditGetter.kt
index 8343c2c8..d3f09b47 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/workers/RedditGetter.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/RedditGetter.kt
@@ -9,18 +9,17 @@ import bums.lunatic.launcher.model.RssDataType
import bums.lunatic.launcher.model.getRssData
import bums.lunatic.launcher.utils.RssList.feedJsons
import bums.lunatic.launcher.utils.RssList.feedJsons_nsfw
+import io.realm.kotlin.types.RealmObject
-class RedditGetter : BaseGetter {
+class RedditGetter(context: Context) : BaseGetter(context) {
companion object{
val REDDIT_WORK_TAG = "RedditGetter"
}
- constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
- }
@SuppressLint("RestrictedApi")
- override fun realWork(): Result {
- val temp = arrayListOf()
+ override fun realWork(): List {
+ temp.clear()
RssDataType.REDDIT.isOn { for (url in feedJsons) {
for (it in RssFeedsParser.getReddit(url,false)) {
if (it.pubDate() >= limitDateTime) {
@@ -37,8 +36,6 @@ class RedditGetter : BaseGetter {
}
} }
- return Result.success().apply {
- WorkersDb.insertBulkData(temp)
- }
+ return temp
}
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/RuliWebGetter.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/RuliWebGetter.kt
index c12612a8..db6e89b0 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/workers/RuliWebGetter.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/RuliWebGetter.kt
@@ -8,14 +8,13 @@ import bums.lunatic.launcher.model.RssDataType
import bums.lunatic.launcher.model.RuliWeb
import bums.lunatic.launcher.model.getRssData
import bums.lunatic.launcher.utils.Blog
+import io.realm.kotlin.types.RealmObject
import org.jsoup.Jsoup
-class RuliWebGetter : BaseGetter {
+class RuliWebGetter(context: Context) : BaseGetter(context) {
companion object {
val TAG = "RuliWebGetter"
}
- constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
- }
fun parseRuli(ruli_tr : org.jsoup.nodes.Element) {
Blog.LOGE("ruli_tr >>> ${ruli_tr.text()}")
@@ -61,30 +60,28 @@ class RuliWebGetter : BaseGetter {
}
@SuppressLint("RestrictedApi")
- override fun realWork(): Result {
+ override fun realWork(): List {
+ temp.clear()
RssDataType.RULIWEB.isOn {
- try {
- Blog.LOGE("realWork() ${this::class.simpleName}")
- val testUrl2 = arrayListOf("https://bbs.ruliweb.com/best/humor_only","https://bbs.ruliweb.com/best/humor_only/now?m=humor_only&t=default&page=2")
- testUrl2.forEach { url ->
- Jsoup.connect(url).timeout(5000).ignoreHttpErrors(true)
- .userAgent(USAGT)
- .header("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8")
- .header("accept-language", "ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7")
- .header("cache-control", "no-cache")
- .header("pragma", "no-cache")
- .ignoreContentType(true)
- .get().let { ruli ->
+ try {
+ Blog.LOGE("realWork() ${this::class.simpleName}")
+ val testUrl2 = arrayListOf("https://bbs.ruliweb.com/best/humor_only","https://bbs.ruliweb.com/best/humor_only/now?m=humor_only&t=default&page=2")
+ testUrl2.forEach { url ->
+ Jsoup.connect(url).timeout(5000).ignoreHttpErrors(true)
+ .userAgent(USAGT)
+ .header("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8")
+ .header("accept-language", "ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7")
+ .header("cache-control", "no-cache")
+ .header("pragma", "no-cache")
+ .ignoreContentType(true)
+ .get().let { ruli ->
// Blog.LOGE(TAG.plus("test ${testUrl2} >> ${ruli.title()}"))
- ruli.getElementsByClass("table_body blocktarget").forEach { ruli_tr ->
- parseRuli(ruli_tr)
+ ruli.getElementsByClass("table_body blocktarget").forEach { ruli_tr ->
+ parseRuli(ruli_tr)
+ }
}
- }
- }
- } catch (e:Exception){e.printStackTrace()}}
- return Result.success().apply {
-// BLog.LOGE("Ruli temp >>>> ${temp.size}")
- WorkersDb.insertBulkData(temp)
- }
+ }
+ } catch (e:Exception){e.printStackTrace()}}
+ return temp
}
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/TaskAggregator.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/TaskAggregator.kt
new file mode 100644
index 00000000..c86a2c12
--- /dev/null
+++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/TaskAggregator.kt
@@ -0,0 +1,130 @@
+package bums.lunatic.launcher.workers
+
+import android.content.Context
+import bums.lunatic.launcher.model.AppInfo
+import bums.lunatic.launcher.model.RssData
+import bums.lunatic.launcher.model.SimpleContact
+import bums.lunatic.launcher.utils.Blog
+import io.realm.kotlin.UpdatePolicy
+import kotlinx.coroutines.*
+
+object TaskAggregator {
+
+ // 1. 시스템 정보 통합 업데이트 (App, Contact, SMS 등)
+ // 로컬 작업이므로 Dispatchers.Default 사용
+ fun refreshSystemInfo(context: Context) {
+ CoroutineScope(Dispatchers.IO).launch {
+ val startTime = System.currentTimeMillis()
+
+ // 1. [병렬 수집] 시스템에서 최신 데이터 가져오기 (이때 카운트는 0인 상태)
+ val appsJob = async { AppInfoGetter(context).fetchData() }
+ val contactsJob = async { ContactInfoGetter(context).fetchData() }
+
+ val scannedApps: List = appsJob.await().filterIsInstance()
+ val scannedContacts: List = contactsJob.await().filterIsInstance()
+
+
+ val realm = WorkersDb.getRealm()
+
+ // 2. [일괄 저장 및 병합] 트랜잭션 시작
+ realm.write {
+
+ // --- [A] 앱 정보 병합 (AppInfo) ---
+ // DB에 있는 기존 앱들을 패키지명(Key) 기준으로 Map 생성
+ val existingAppsMap = query(AppInfo::class).find().associateBy { it.pkgName }
+ val activeAppPkgNames = HashSet()
+
+ scannedApps.forEach { newApp ->
+ val pkgName = newApp.pkgName ?: return@forEach
+ activeAppPkgNames.add(pkgName)
+
+ // 기존 데이터가 있으면 카운트 정보 이식
+ val oldApp = existingAppsMap[pkgName]
+ if (oldApp != null) {
+ newApp.clickCount = oldApp.clickCount
+ newApp.lastUseDate = oldApp.lastUseDate
+ // 즐겨찾기 여부 등 보존해야 할 다른 필드가 있다면 여기서 복사
+ // newApp.isFavorite = oldApp.isFavorite
+ }
+
+ // 병합된 데이터 저장 (덮어쓰기)
+ copyToRealm(newApp, UpdatePolicy.ALL)
+ }
+
+ // 삭제된 앱 처리 (시스템 스캔 목록에 없는 앱은 DB에서 삭제)
+ val appsToDelete = query(AppInfo::class).find().filter {
+ !activeAppPkgNames.contains(it.pkgName)
+ }
+ appsToDelete.forEach { delete(it) }
+
+
+ // --- [B] 연락처 정보 병합 (SimpleContact) ---
+ // DB에 있는 기존 연락처들을 ID(Key) 기준으로 Map 생성
+ // (SimpleContact의 PrimaryKey가 id라고 가정)
+ val existingContactsMap = query(SimpleContact::class).find().associateBy { it.id }
+ val activeContactIds = HashSet()
+
+ scannedContacts.forEach { newContact ->
+ // SimpleContact 타입 캐스팅 (fetchData 반환형이 List이므로)
+ val contact = newContact as? SimpleContact ?: return@forEach
+ val contactId = contact.id ?: return@forEach
+
+ activeContactIds.add(contactId)
+
+ val oldContact = existingContactsMap[contactId]
+ if (oldContact != null) {
+ contact.touchCount = oldContact.touchCount
+ contact.lastedTouchDateTime = oldContact.lastedTouchDateTime
+ }
+
+ copyToRealm(contact, UpdatePolicy.ALL)
+ }
+
+ // 삭제된 연락처 처리
+ val contactsToDelete = query(SimpleContact::class).find().filter {
+ !activeContactIds.contains(it.id)
+ }
+ contactsToDelete.forEach { delete(it) }
+
+
+ }
+
+ Blog.LOGE("SystemInfo Aggregation finished in ${System.currentTimeMillis() - startTime}ms.")
+ }
+ }
+
+ // 2. 뉴스 피드 통합 업데이트 (Ruliweb, Youtube, FMKorea 등)
+ // 네트워크 작업이므로 Dispatchers.IO 사용
+ fun refreshNewsFeeds(context: Context) {
+ CoroutineScope(Dispatchers.IO).launch {
+ val startTime = System.currentTimeMillis()
+
+ // 병렬로 네트워크 요청 쏘기 (시간 획기적으로 단축됨)
+ val jobs = listOf(
+ async { RuliWebGetter(context).fetchData() },
+ async { YoutubeGetter(context).fetchData() },
+ async { DCGetter(context).fetchData() },
+ async { FmKoreaGetter(context).fetchData() },
+ async { NewsFeedsGetter(context).fetchData() },
+ async { ClienGetter(context).fetchData() },
+ async { DotaxGetter(context).fetchData() },
+ async { RedditGetter(context).fetchData() },
+ )
+
+ // 결과 수집
+ val allFeeds = jobs.awaitAll().flatten()
+
+ // [일괄 저장]
+ if (allFeeds.isNotEmpty()) {
+ WorkersDb.getRealm().write {
+ // 1. 새 데이터 저장
+ allFeeds.forEach { feed ->
+ copyToRealm(feed, UpdatePolicy.ALL)
+ }
+ }
+ }
+
+ Blog.LOGE("NewsFeed Aggregation finished in ${System.currentTimeMillis() - startTime}ms. Items: ${allFeeds.size}")
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/TelegramBotGetter.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/TelegramBotGetter.kt
index 94e8fca7..abed570b 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/workers/TelegramBotGetter.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/TelegramBotGetter.kt
@@ -1,141 +1,141 @@
-package bums.lunatic.launcher.workers
-
-import android.annotation.SuppressLint
-import android.content.Context
-import androidx.work.WorkerParameters
-import com.google.android.gms.location.FusedLocationProviderClient
-
-
-class TelegramBotGetter : BaseGetter {
- companion object {
- val TAG = "TelegramBotGetter"
- }
- constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
- }
-
-
- @SuppressLint("RestrictedApi")
- override fun realWork(): Result {
-
- try {
-
-// try {
-// val url = "https://api.telegram.org/bot7934509464:AAE_xUbICxMdywLGnxo7BkeIqA1nVza4P9w/getUpdates"
-// //"https://api.telegram.org/bot7934509464:AAE_xUbICxMdywLGnxo7BkeIqA1nVza4P9w/sendMessage?chat_id=71476436&text=안녕하세요."
-// //7068729507
-// // OkHttp 클라이언트 객체 생성
-// val client = OkHttpClient.Builder().connectionPool(ConnectionPool(5,60,TimeUnit.SECONDS)).build()
+//package bums.lunatic.launcher.workers
//
-// // GET 요청 객체 생성
-// val builder: Request.Builder = Request.Builder().url(url).addHeader("Content-Type", "application/json").get()
+//import android.annotation.SuppressLint
+//import android.content.Context
+//import androidx.work.WorkerParameters
+//import com.google.android.gms.location.FusedLocationProviderClient
//
-// val request: Request = builder.build()
//
-// BLog.LOGE("telegram before request ")
-// // OkHttp 클라이언트로 GET 요청 객체 전송
-// val response: Response = client.newCall(request).execute()
-// if (response.isSuccessful()) {
-// // 응답 받아서 처리
-// val body: ResponseBody? = response.body()
-// if (body != null) {
-// val bodyString = body.string()
-// BLog.LOGE("bodyString >>>>\n${bodyString}")
-// Gson().fromJson(bodyString,TelegramBotUpdate::class.java)?.let { telegramUpdates ->
-// telegramUpdates.fill()
-// telegramUpdates.list.forEach {
-//// if (it.message?.text?.startsWith("/") == true) {
-// if((it.message?.text?.contains("where") == true) || (it.message?.text?.contains("어디") == true)) {
-// BLog.LOGE("it.message?.text?.contains(\"where\") == true) >>> ${it.message?.text?.contains("where") == true}")
-// BLog.LOGE("it.message?.text?.contains(\"어디\") == true) >>> ${it.message?.text?.contains("어디") == true}")
-// WorkersDb.getRealm().apply {
-// writeBlocking {
-// if (query("update_id == $0",it.update_id).find().size == 0) {
-// copyToRealm(it)
-// getLastLocation(context = applicationContext)
-// BLog.LOGE("telegram telegramUpdates >>>> ${Gson().toJson(it)}")
-// }
-// }
-// }
+//class TelegramBotGetter : BaseGetter {
+// companion object {
+// val TAG = "TelegramBotGetter"
+// }
+// constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
+// }
//
-// }
-// }
-// }
-// }
-// } else BLog.LOGE("telegram Error Occurred")
//
-// } catch (e: java.lang.Exception) {
-// e.printStackTrace()
-// }
- } catch (e:Exception){e.printStackTrace()}
- return Result.success().apply {
-
- }
- }
- var fusedLocationProviderClient: FusedLocationProviderClient? = null
- @SuppressLint("MissingPermission")
- private fun getLastLocation(context: Context) {
-// fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(context);
-// BLog.LOGE("Location getLastLocation")
-// fusedLocationProviderClient?.getLastLocation()?.addOnSuccessListener(object : OnSuccessListener {
-// override fun onSuccess(location: Location?) {
-// if (location != null) {
-// // Log the latitude and longitude
-// BLog.LOGE("Location Latitude: " + location.getLatitude())
-// BLog.LOGE("Location Longitude: " + location.getLongitude())
+// @SuppressLint("RestrictedApi")
+// override fun realWork(): Result {
//
-// // Use Geocoder to get detailed location information
-// try {
-// val geocoder = Geocoder(context, Locale.getDefault())
-// val addresses: List? = geocoder.getFromLocation(
-// location.getLatitude(),
-// location.getLongitude(),
-// 1
-// )
+// try {
//
-// addresses?.first()?.let {
-// it.getAddressLine(0)?.let {
-// Executors.newSingleThreadScheduledExecutor().schedule({
-// try {
-// //////-1002450229641
-// val url =
-// "https://api.telegram.org/bot7934509464:AAE_xUbICxMdywLGnxo7BkeIqA1nVza4P9w/sendMessage?chat_id=83268260&text=남편의현위치는${it}"
-// //7068729507
-// // OkHttp 클라이언트 객체 생성
-// val client = OkHttpClient.Builder()
-// .connectionPool(ConnectionPool(5, 60, TimeUnit.SECONDS))
-// .build()
+//// try {
+//// val url = "https://api.telegram.org/bot7934509464:AAE_xUbICxMdywLGnxo7BkeIqA1nVza4P9w/getUpdates"
+//// //"https://api.telegram.org/bot7934509464:AAE_xUbICxMdywLGnxo7BkeIqA1nVza4P9w/sendMessage?chat_id=71476436&text=안녕하세요."
+//// //7068729507
+//// // OkHttp 클라이언트 객체 생성
+//// val client = OkHttpClient.Builder().connectionPool(ConnectionPool(5,60,TimeUnit.SECONDS)).build()
+////
+//// // GET 요청 객체 생성
+//// val builder: Request.Builder = Request.Builder().url(url).addHeader("Content-Type", "application/json").get()
+////
+//// val request: Request = builder.build()
+////
+//// BLog.LOGE("telegram before request ")
+//// // OkHttp 클라이언트로 GET 요청 객체 전송
+//// val response: Response = client.newCall(request).execute()
+//// if (response.isSuccessful()) {
+//// // 응답 받아서 처리
+//// val body: ResponseBody? = response.body()
+//// if (body != null) {
+//// val bodyString = body.string()
+//// BLog.LOGE("bodyString >>>>\n${bodyString}")
+//// Gson().fromJson(bodyString,TelegramBotUpdate::class.java)?.let { telegramUpdates ->
+//// telegramUpdates.fill()
+//// telegramUpdates.list.forEach {
+////// if (it.message?.text?.startsWith("/") == true) {
+//// if((it.message?.text?.contains("where") == true) || (it.message?.text?.contains("어디") == true)) {
+//// BLog.LOGE("it.message?.text?.contains(\"where\") == true) >>> ${it.message?.text?.contains("where") == true}")
+//// BLog.LOGE("it.message?.text?.contains(\"어디\") == true) >>> ${it.message?.text?.contains("어디") == true}")
+//// WorkersDb.getRealm().apply {
+//// writeBlocking {
+//// if (query("update_id == $0",it.update_id).find().size == 0) {
+//// copyToRealm(it)
+//// getLastLocation(context = applicationContext)
+//// BLog.LOGE("telegram telegramUpdates >>>> ${Gson().toJson(it)}")
+//// }
+//// }
+//// }
+////
+//// }
+//// }
+//// }
+//// }
+//// } else BLog.LOGE("telegram Error Occurred")
+////
+//// } catch (e: java.lang.Exception) {
+//// e.printStackTrace()
+//// }
+// } catch (e:Exception){e.printStackTrace()}
+// return Result.success().apply {
//
-// // GET 요청 객체 생성
-// val builder: Request.Builder = Request.Builder().url(url)
-// .addHeader("Content-Type", "application/json").get()
-//
-// val request: Request = builder.build()
-//
-// BLog.LOGE("telegram before request ")
-// // OkHttp 클라이언트로 GET 요청 객체 전송
-// val response: Response = client.newCall(request).execute()
-// if (response.isSuccessful()) {
-// // 응답 받아서 처리
-// val body: ResponseBody? = response.body()
-// if (body != null) {
-//
-// }
-// } else BLog.LOGE("telegram Error Occurred")
-//
-// } catch (e: java.lang.Exception) {
-// e.printStackTrace()
-// }
-// }, 5, TimeUnit.SECONDS)
-// }
-// }
-// // Display location details on UI elements
-// // Log detailed location information
-// BLog.LOGE("Location Addresses: $addresses")
-// } catch (e: IOException) {
-// e.printStackTrace()
-// }
-// }
-// }
-// })
- }
-}
\ No newline at end of file
+// }
+// }
+// var fusedLocationProviderClient: FusedLocationProviderClient? = null
+// @SuppressLint("MissingPermission")
+// private fun getLastLocation(context: Context) {
+//// fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(context);
+//// BLog.LOGE("Location getLastLocation")
+//// fusedLocationProviderClient?.getLastLocation()?.addOnSuccessListener(object : OnSuccessListener {
+//// override fun onSuccess(location: Location?) {
+//// if (location != null) {
+//// // Log the latitude and longitude
+//// BLog.LOGE("Location Latitude: " + location.getLatitude())
+//// BLog.LOGE("Location Longitude: " + location.getLongitude())
+////
+//// // Use Geocoder to get detailed location information
+//// try {
+//// val geocoder = Geocoder(context, Locale.getDefault())
+//// val addresses: List? = geocoder.getFromLocation(
+//// location.getLatitude(),
+//// location.getLongitude(),
+//// 1
+//// )
+////
+//// addresses?.first()?.let {
+//// it.getAddressLine(0)?.let {
+//// Executors.newSingleThreadScheduledExecutor().schedule({
+//// try {
+//// //////-1002450229641
+//// val url =
+//// "https://api.telegram.org/bot7934509464:AAE_xUbICxMdywLGnxo7BkeIqA1nVza4P9w/sendMessage?chat_id=83268260&text=남편의현위치는${it}"
+//// //7068729507
+//// // OkHttp 클라이언트 객체 생성
+//// val client = OkHttpClient.Builder()
+//// .connectionPool(ConnectionPool(5, 60, TimeUnit.SECONDS))
+//// .build()
+////
+//// // GET 요청 객체 생성
+//// val builder: Request.Builder = Request.Builder().url(url)
+//// .addHeader("Content-Type", "application/json").get()
+////
+//// val request: Request = builder.build()
+////
+//// BLog.LOGE("telegram before request ")
+//// // OkHttp 클라이언트로 GET 요청 객체 전송
+//// val response: Response = client.newCall(request).execute()
+//// if (response.isSuccessful()) {
+//// // 응답 받아서 처리
+//// val body: ResponseBody? = response.body()
+//// if (body != null) {
+////
+//// }
+//// } else BLog.LOGE("telegram Error Occurred")
+////
+//// } catch (e: java.lang.Exception) {
+//// e.printStackTrace()
+//// }
+//// }, 5, TimeUnit.SECONDS)
+//// }
+//// }
+//// // Display location details on UI elements
+//// // Log detailed location information
+//// BLog.LOGE("Location Addresses: $addresses")
+//// } catch (e: IOException) {
+//// e.printStackTrace()
+//// }
+//// }
+//// }
+//// })
+// }
+//}
\ No newline at end of file
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/TheQooGetter.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/TheQooGetter.kt
index ee26c0c6..57603ab6 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/workers/TheQooGetter.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/TheQooGetter.kt
@@ -8,14 +8,14 @@ import bums.lunatic.launcher.model.TheQoo
import bums.lunatic.launcher.model.getHref
import bums.lunatic.launcher.model.getRssData
import bums.lunatic.launcher.model.getT
+import io.realm.kotlin.types.RealmObject
import org.jsoup.Jsoup
-class TheQooGetter : BaseGetter {
+class TheQooGetter(context: Context) : BaseGetter(context) {
companion object {
val TAG = "TheQooGetter"
}
- constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
- }
+
fun parseTQoo(tq_tr : org.jsoup.nodes.Element) {
// BLog.LOGE("tq_tr >>>> ${tq_tr}")
@@ -50,7 +50,7 @@ class TheQooGetter : BaseGetter {
}
@SuppressLint("RestrictedApi")
- override fun realWork(): Result {
+ override fun realWork(): List {
RssDataType.THEQOO.isOn {
try {
val testUrl2 = arrayListOf("https://theqoo.net/hot","https://theqoo.net/hot/category/512000937","https://theqoo.net/hot/category/24784","https://theqoo.net/hot/category/24788")
@@ -65,9 +65,6 @@ class TheQooGetter : BaseGetter {
}
}
} catch (e:Exception){e.printStackTrace()}}
- return Result.success().apply {
-// BLog.LOGE("theqoo temp >>>> ${temp.size}")
- WorkersDb.insertBulkData(temp)
- }
+ return temp
}
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/WorkersDb.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/WorkersDb.kt
index 99e7337c..52d60283 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/workers/WorkersDb.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/WorkersDb.kt
@@ -1,9 +1,9 @@
package bums.lunatic.launcher.workers
import bums.lunatic.launcher.BuildConfig
-import bums.lunatic.launcher.apps.SimpleContact
import bums.lunatic.launcher.common.letTrue
import bums.lunatic.launcher.model.AppInfo
+import bums.lunatic.launcher.model.AppUsageLog
import bums.lunatic.launcher.model.Astro
import bums.lunatic.launcher.model.BotCommandEentitie
import bums.lunatic.launcher.model.Condition
@@ -19,6 +19,7 @@ import bums.lunatic.launcher.model.NotificationItem
import bums.lunatic.launcher.model.RssData
import bums.lunatic.launcher.model.RssDataInterface
import bums.lunatic.launcher.model.RssDataType
+import bums.lunatic.launcher.model.SimpleContact
import bums.lunatic.launcher.model.TelegramBotUpdate
import bums.lunatic.launcher.model.TelegramChat
import bums.lunatic.launcher.model.TelegramData
@@ -45,6 +46,7 @@ import io.realm.kotlin.query.RealmQuery
import io.realm.kotlin.query.Sort
import io.realm.kotlin.types.BaseRealmObject
import io.realm.kotlin.types.TypedRealmObject
+import java.util.Calendar
import java.util.Locale
import java.util.regex.Pattern
import kotlin.reflect.KClass
@@ -57,16 +59,90 @@ class CustMigration : AutomaticSchemaMigration {
}
object WorkersDb {
-
+//RecentCall::class, RecentSms::class,
val clazz : Set> = setOf(
- RssData::class, NotificationItem::class, AppInfo::class,SimpleContact::class, RecentCall::class, RecentSms::class, CurrentPlayItem::class,
+ RssData::class, NotificationItem::class, AppInfo::class,SimpleContact::class, CurrentPlayItem::class,
TelegramBotUpdate::class, TelegramData::class, TelegramMessage::class, TelegramChat::class, BotCommandEentitie::class, TelegramFrom::class,
WeatherForcast::class, Location::class, Current::class, Forecast::class, Condition::class, Forecastday::class, Day::class, Astro::class, Hour::class,
LocationLog::class,
- LastInfo::class, HistoryItem::class, ReaderConfig::class, ContentsCollection::class, ContentsPageInfo::class,WidgetData::class
+ LastInfo::class, HistoryItem::class, ReaderConfig::class, ContentsCollection::class, ContentsPageInfo::class,
+ WidgetData::class,AppUsageLog::class
)
//,UserActionModel::class
+ // [추가] 앱/연락처 사용 시 로그 저장 (기존 updateAppUse 대신 이거 호출)
+ fun logAppUsage(key: String, type: String = "APP") {
+ val realm = getRealm()
+ val calendar = Calendar.getInstance()
+
+ realm.writeBlocking { // 비동기로 하려면 write { } 사용
+ // 1. 기존 카운트 증가 (기존 로직 유지)
+ // ... (AppInfo 조회 후 clickCount++ 하는 코드) ...
+
+ // 2. 상세 로그 저장 (추가된 부분)
+ copyToRealm(AppUsageLog().apply {
+ itemKey = key
+ itemType = type
+ timestamp = System.currentTimeMillis()
+ month = calendar.get(Calendar.MONTH)
+ dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH)
+ dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK)
+ hour = calendar.get(Calendar.HOUR_OF_DAY)
+ })
+ }
+ }
+
+ // [핵심] 현재 시간 상황에 맞는 추천 리스트 가져오기
+ fun getContextualRecommendations(limit: Int = 5): List {
+ val realm = getRealm()
+ val calendar = Calendar.getInstance()
+
+ val curMonth = calendar.get(Calendar.MONTH)
+ val curDay = calendar.get(Calendar.DAY_OF_MONTH)
+ val curDow = calendar.get(Calendar.DAY_OF_WEEK)
+ val curHour = calendar.get(Calendar.HOUR_OF_DAY)
+
+ // 최근 3개월 데이터만 조회 (너무 오래된 데이터는 노이즈가 됨)
+ val threeMonthsAgo = System.currentTimeMillis() - (90L * 24 * 60 * 60 * 1000)
+
+ // 쿼리: 최근 데이터만 가져와서 메모리에서 계산 (복잡한 가중치는 메모리 연산이 빠름)
+ val logs = realm.query("timestamp > $0", threeMonthsAgo).find()
+
+ // 점수 계산
+ val scores = HashMap()
+
+ for (log in logs) {
+ var score = 1.0 // 기본 점수 (최근에 썼다는 것 자체로 의미 있음)
+
+ // 1. 시간대 매칭 (가장 중요: 아침에 쓰던 앱은 아침에 쓴다)
+ if (log.hour == curHour) score += 3.0
+ else if (log.hour == curHour - 1 || log.hour == curHour + 1) score += 1.0
+
+ // 2. 요일 매칭 (주말/평일 패턴)
+ if (log.dayOfWeek == curDow) score += 2.0
+
+ // 3. "매월 1일" 같은 날짜 패턴 (가중치 매우 높음)
+ if (log.dayOfMonth == curDay) score += 5.0
+
+ // 4. (옵션) 매년 같은 달 같은 날? (생일 등)
+ if (log.month == curMonth && log.dayOfMonth == curDay) score += 10.0
+
+ // 5. 최신성 가중치 (최근 기록일수록 점수 높게)
+ val daysAgo = (System.currentTimeMillis() - log.timestamp) / (24 * 60 * 60 * 1000)
+ val decay = 1.0 / (daysAgo + 1) // 오늘이면 1, 9일전이면 0.1
+
+ // 최종 점수 누적
+ val finalScore = score * decay
+ scores[log.itemKey] = (scores[log.itemKey] ?: 0.0) + finalScore
+ }
+
+ // 점수 높은 순으로 정렬하여 상위 N개 반환
+ return scores.entries
+ .sortedByDescending { it.value }
+ .take(limit)
+ .map { it.key }
+ }
+
val schemaVersion : Long = BuildConfig.BuildDateTime
private var pRealm : Realm? = null
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/YoutubeGetter.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/YoutubeGetter.kt
index 5fcce0f1..a3c99429 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/workers/YoutubeGetter.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/YoutubeGetter.kt
@@ -10,21 +10,20 @@ import bums.lunatic.launcher.model.getRssData
import bums.lunatic.launcher.model.others.Youtube
import bums.lunatic.launcher.utils.RssList
import com.google.gson.Gson
+import io.realm.kotlin.types.RealmObject
import org.json.JSONObject
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
-class YoutubeGetter : BaseGetter {
+class YoutubeGetter(context: Context) : BaseGetter(context) {
companion object {
val YT_WORK_TAG = "YoutubeGetter"
}
var rssUrls = arrayListOf()
- constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
- }
@SuppressLint("RestrictedApi")
- override fun realWork(): Result {
+ override fun realWork(): List {
rssUrls.clear()
rssUrls.addAll(RssList.youtubeUrls)
RssDataType.YOUTUBE.isOn {
@@ -32,9 +31,7 @@ class YoutubeGetter : BaseGetter {
temp.addAll(ytChannel(Jsoup.connect(url).userAgent(USAGT).get()))
}
}
- return Result.success().apply {
- WorkersDb.insertBulkData(temp)
- }
+ return temp
}
fun ytChannel(doc: Document) : ArrayList {
diff --git a/app/src/main/res/layout/apps_child.xml b/app/src/main/res/layout/apps_child.xml
index 173f7f0b..91b4aefe 100644
--- a/app/src/main/res/layout/apps_child.xml
+++ b/app/src/main/res/layout/apps_child.xml
@@ -22,6 +22,7 @@
android:id="@+id/childTextview"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginLeft="5dp"
+ android:textColor="@color/white"
app:layout_constraintLeft_toRightOf="@id/appIconTwo"
app:layout_constraintRight_toRightOf="parent"
android:lines="2"
diff --git a/app/src/main/res/layout/apps_child_rec.xml b/app/src/main/res/layout/apps_child_rec.xml
new file mode 100644
index 00000000..91b4aefe
--- /dev/null
+++ b/app/src/main/res/layout/apps_child_rec.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/bottom_sheet_app_drawer.xml b/app/src/main/res/layout/bottom_sheet_app_drawer.xml
new file mode 100644
index 00000000..f507f530
--- /dev/null
+++ b/app/src/main/res/layout/bottom_sheet_app_drawer.xml
@@ -0,0 +1,180 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/contact_item.xml b/app/src/main/res/layout/contact_item.xml
index ed649386..72961a5a 100644
--- a/app/src/main/res/layout/contact_item.xml
+++ b/app/src/main/res/layout/contact_item.xml
@@ -10,6 +10,7 @@
+ android:layout_marginTop="10dp"/>
@@ -57,6 +58,7 @@
app:layout_constraintTop_toBottomOf="@id/fragment_container"
app:layout_constraintLeft_toRightOf="@id/back"
android:id="@+id/reload"
+ android:visibility="gone"
android:src="@drawable/ic_refresh"
tools:ignore="ContentDescription"
style="@style/CommonBottom"/>
@@ -68,6 +70,7 @@
app:layout_constraintLeft_toRightOf="@id/reload"
android:textColor="@color/white"
android:gravity="center"
+ android:visibility="gone"
android:textSize="@dimen/_12sp"
android:ellipsize="middle"
app:layout_constraintRight_toRightOf="parent"
@@ -79,6 +82,7 @@
app:layout_constraintRight_toLeftOf="@id/share"
app:layout_constraintBottom_toBottomOf="parent"
android:id="@+id/dl_video"
+ android:visibility="gone"
android:src="@drawable/dl_vid"
tools:ignore="ContentDescription"
style="@style/CommonBottom"/>
@@ -88,6 +92,7 @@
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginRight="60dp"
android:id="@+id/share"
+ android:visibility="gone"
android:foregroundTint="@color/white"
android:src="@drawable/ic_share"
tools:ignore="ContentDescription"
@@ -169,5 +174,15 @@
android:onClick="floatClick"
android:layout_width="wrap_content"
android:layout_height="20dp"/>
+
+
+
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 3748d81c..936030ce 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -21,4 +21,13 @@
- rounded
- 10dp
+
+
+
+
+
\ No newline at end of file