,,,....
This commit is contained in:
parent
dc6b95108f
commit
62f1e646ab
@ -101,6 +101,11 @@
|
|||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|screenLayout|layoutDirection|navigation"
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|screenLayout|layoutDirection|navigation"
|
||||||
android:windowSoftInputMode="adjustResize"
|
android:windowSoftInputMode="adjustResize"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.HOME" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.WEB_SEARCH"/>
|
<action android:name="android.intent.action.WEB_SEARCH"/>
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
@ -117,6 +122,7 @@
|
|||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<!-- <activity-->
|
<!-- <activity-->
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import android.os.Build
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
import android.view.GestureDetector
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import android.view.KeyEvent.ACTION_UP
|
import android.view.KeyEvent.ACTION_UP
|
||||||
import android.view.KeyEvent.KEYCODE_BUTTON_A
|
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.isVisible
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
|
import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
|
||||||
|
import bums.lunatic.launcher.apps.AppDrawerBottomSheet
|
||||||
import bums.lunatic.launcher.common.CommonActivity
|
import bums.lunatic.launcher.common.CommonActivity
|
||||||
import bums.lunatic.launcher.databinding.LauncherActivityBinding
|
import bums.lunatic.launcher.databinding.LauncherActivityBinding
|
||||||
import bums.lunatic.launcher.feeds.WidgetHost
|
import bums.lunatic.launcher.feeds.WidgetHost
|
||||||
@ -79,6 +81,97 @@ import java.util.Date
|
|||||||
|
|
||||||
|
|
||||||
open class LauncherActivity : CommonActivity() {
|
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
|
private lateinit var binding: LauncherActivityBinding
|
||||||
|
|
||||||
@ -405,14 +498,20 @@ open class LauncherActivity : CommonActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 두 뷰가 겹치는지 확인하는 헬퍼 함수
|
// 두 뷰가 겹치는지 확인하는 헬퍼 함수
|
||||||
private fun isViewOverlapping(v1: View, v2: View): Boolean {
|
private fun isViewOverlapping(dragView: View, targetView: View): Boolean {
|
||||||
val rect1 = android.graphics.Rect()
|
val targetRect = android.graphics.Rect()
|
||||||
v1.getGlobalVisibleRect(rect1)
|
targetView.getGlobalVisibleRect(targetRect)
|
||||||
|
|
||||||
val rect2 = android.graphics.Rect()
|
// 2. 드래그 중인 뷰의 화면상 절대 좌표 영역 구하기
|
||||||
v2.getGlobalVisibleRect(rect2)
|
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
|
private var appWidgetManager: AppWidgetManager? = null
|
||||||
@ -421,6 +520,13 @@ open class LauncherActivity : CommonActivity() {
|
|||||||
private val REQUEST_PICK_APPWIDGET = 100
|
private val REQUEST_PICK_APPWIDGET = 100
|
||||||
private val REQUEST_CREATE_APPWIDGET = 101
|
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")
|
@SuppressLint("NewApi", "MissingPermission", "ClickableViewAccessibility")
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
installSplashScreen()
|
installSplashScreen()
|
||||||
@ -446,6 +552,18 @@ open class LauncherActivity : CommonActivity() {
|
|||||||
DynamicColors.applyToActivityIfAvailable(this)
|
DynamicColors.applyToActivityIfAvailable(this)
|
||||||
binding = LauncherActivityBinding.inflate(layoutInflater)
|
binding = LauncherActivityBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
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)
|
HeadsetActionButtonReceiver.register(this)
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view, windowInsets ->
|
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view, windowInsets ->
|
||||||
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||||
@ -789,6 +907,15 @@ open class LauncherActivity : CommonActivity() {
|
|||||||
R.id.setting ->{
|
R.id.setting ->{
|
||||||
startActivity(Intent(this, SettingsActivity::class.java))
|
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 -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
binding.floatingActionMenu.close(false)
|
binding.floatingActionMenu.close(false)
|
||||||
|
|||||||
@ -45,6 +45,7 @@ import bums.lunatic.launcher.helpers.PrefBoolean
|
|||||||
import bums.lunatic.launcher.helpers.PrefLong
|
import bums.lunatic.launcher.helpers.PrefLong
|
||||||
import bums.lunatic.launcher.helpers.PrefString
|
import bums.lunatic.launcher.helpers.PrefString
|
||||||
import bums.lunatic.launcher.model.AppInfo
|
import bums.lunatic.launcher.model.AppInfo
|
||||||
|
import bums.lunatic.launcher.model.SimpleContact
|
||||||
import bums.lunatic.launcher.utils.Blog
|
import bums.lunatic.launcher.utils.Blog
|
||||||
import bums.lunatic.launcher.utils.JamoUtils
|
import bums.lunatic.launcher.utils.JamoUtils
|
||||||
import bums.lunatic.launcher.workers.WorkersDb
|
import bums.lunatic.launcher.workers.WorkersDb
|
||||||
|
|||||||
@ -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<AppInfo>()
|
||||||
|
private val contactList = arrayListOf<SimpleContact>() // 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<View>(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<View>(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<AppInfo>()
|
||||||
|
|
||||||
|
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<SimpleContact>()
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -38,6 +38,7 @@ import bums.lunatic.launcher.workers.WorkersDb
|
|||||||
import io.realm.kotlin.ext.query
|
import io.realm.kotlin.ext.query
|
||||||
import kotlinx.coroutines.MainScope
|
import kotlinx.coroutines.MainScope
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
||||||
internal class AppsAdapter(
|
internal class AppsAdapter(
|
||||||
@ -64,11 +65,7 @@ internal class AppsAdapter(
|
|||||||
holder.view.apply {
|
holder.view.apply {
|
||||||
childTextview.text = item.appName
|
childTextview.text = item.appName
|
||||||
appIconTwo.visibility = View.VISIBLE
|
appIconTwo.visibility = View.VISIBLE
|
||||||
|
loadIconAsync(appIconTwo, item.pkgName)
|
||||||
MainScope().async {
|
|
||||||
getDrawableIconForPackage(item.pkgName, packageManager.getApplicationIcon(item.pkgName!!)) {
|
|
||||||
appIconTwo.post { appIconTwo.setImageDrawable(it) }
|
|
||||||
} }
|
|
||||||
childTextview.apply {
|
childTextview.apply {
|
||||||
gravity = Gravity.CENTER
|
gravity = Gravity.CENTER
|
||||||
setTextSize(TypedValue.COMPLEX_UNIT_PX, lActivity!!.resources.getDimension(R.dimen.twelve))
|
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!!))
|
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
|
override fun getItemCount(): Int = oldList.size
|
||||||
|
|
||||||
inner class AppsViewHolder(var view: AppsChildBinding) : RecyclerView.ViewHolder(view.root)
|
inner class AppsViewHolder(var view: AppsChildBinding) : RecyclerView.ViewHolder(view.root)
|
||||||
@ -119,10 +139,17 @@ internal class AppsAdapter(
|
|||||||
/* update app list */
|
/* update app list */
|
||||||
fun updateData(newList: List<AppInfo>) {
|
fun updateData(newList: List<AppInfo>) {
|
||||||
val diffUtilResult = DiffUtil.calculateDiff(AppsDiffUtil(oldList, newList))
|
val diffUtilResult = DiffUtil.calculateDiff(AppsDiffUtil(oldList, newList))
|
||||||
//
|
|
||||||
diffUtilResult.dispatchUpdatesTo(this)
|
// [수정 전] dispatchUpdatesTo가 먼저 있어서 에러 발생함
|
||||||
|
// diffUtilResult.dispatchUpdatesTo(this)
|
||||||
|
// oldList.clear()
|
||||||
|
// oldList.addAll(newList)
|
||||||
|
|
||||||
|
// [수정 후] 반드시 리스트 데이터를 먼저 갱신하고 나서 알림을 보내야 합니다!
|
||||||
oldList.clear()
|
oldList.clear()
|
||||||
oldList.addAll(newList)
|
oldList.addAll(newList)
|
||||||
|
diffUtilResult.dispatchUpdatesTo(this) // <-- 순서 변경 (맨 뒤로)
|
||||||
|
|
||||||
newList.size.let {
|
newList.size.let {
|
||||||
appsCount?.text = it.toString()
|
appsCount?.text = it.toString()
|
||||||
appsSize = it
|
appsSize = it
|
||||||
|
|||||||
@ -27,6 +27,7 @@ import androidx.fragment.app.FragmentManager
|
|||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import bums.lunatic.launcher.databinding.ContactItemBinding
|
import bums.lunatic.launcher.databinding.ContactItemBinding
|
||||||
|
import bums.lunatic.launcher.model.SimpleContact
|
||||||
import bums.lunatic.launcher.utils.JamoUtils
|
import bums.lunatic.launcher.utils.JamoUtils
|
||||||
import io.realm.kotlin.types.RealmObject
|
import io.realm.kotlin.types.RealmObject
|
||||||
import io.realm.kotlin.types.annotations.PrimaryKey
|
import io.realm.kotlin.types.annotations.PrimaryKey
|
||||||
@ -80,9 +81,9 @@ internal class ContactAdapter (
|
|||||||
synchronized(oldList) {
|
synchronized(oldList) {
|
||||||
try {
|
try {
|
||||||
val diffUtilResult = DiffUtil.calculateDiff(ContactDiffUtil(oldList, newList))
|
val diffUtilResult = DiffUtil.calculateDiff(ContactDiffUtil(oldList, newList))
|
||||||
diffUtilResult.dispatchUpdatesTo(this)
|
|
||||||
oldList.clear()
|
oldList.clear()
|
||||||
oldList.addAll(newList)
|
oldList.addAll(newList)
|
||||||
|
diffUtilResult.dispatchUpdatesTo(this)
|
||||||
newList.size.let {
|
newList.size.let {
|
||||||
appsSize = it
|
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(
|
internal class ContactDiffUtil(
|
||||||
private val oldList: List<SimpleContact>, private val newList: List<SimpleContact>
|
private val oldList: List<SimpleContact>, private val newList: List<SimpleContact>
|
||||||
|
|||||||
@ -27,6 +27,7 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
|
import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
|
||||||
import bums.lunatic.launcher.databinding.ContactMenuBinding
|
import bums.lunatic.launcher.databinding.ContactMenuBinding
|
||||||
|
import bums.lunatic.launcher.model.SimpleContact
|
||||||
import bums.lunatic.launcher.utils.Blog
|
import bums.lunatic.launcher.utils.Blog
|
||||||
import bums.lunatic.launcher.workers.WorkersDb
|
import bums.lunatic.launcher.workers.WorkersDb
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
|
|||||||
@ -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 <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package bums.lunatic.launcher.apps
|
package bums.lunatic.launcher.apps
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.content.Context
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.content.res.Resources
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.graphics.drawable.BitmapDrawable
|
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import androidx.collection.LruCache
|
import android.util.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
|
|
||||||
|
|
||||||
|
class IconPackManager {
|
||||||
internal class IconPackManager {
|
|
||||||
|
|
||||||
@SuppressLint("DiscouragedApi")
|
|
||||||
companion object {
|
companion object {
|
||||||
|
// [추가] 메모리 캐시 (최대 4MB 또는 아이콘 100개 분량 등 설정)
|
||||||
private val settingsPrefs = lActivity!!.getSharedPreferences(PREFS_SETTINGS, 0)
|
private val iconCache = object : LruCache<String, Drawable>(100) {
|
||||||
private val icsPrefs = lActivity!!.getSharedPreferences(PREFS_PKGICS,0)
|
// 사이즈 측정 로직이 필요하면 sizeOf 오버라이드 (여기선 갯수 기준 100개)
|
||||||
private val packageName = settingsPrefs.getString(KEY_ICON_PACK, DEFAULT_ICON_PACK)
|
|
||||||
private var loaded = false
|
|
||||||
private val packagesDrawables = HashMap<String?, String?>()
|
|
||||||
private val packagesConponentNames = HashMap<String?, String?>()
|
|
||||||
private val backImages: MutableList<Bitmap> = 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<String, Drawable> = 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<String, Bitmap>(cacheSize) {
|
|
||||||
fun sizeOf(key: String?, value: Bitmap?): Int {
|
|
||||||
return if(value?.byteCount?.toInt() ?: 0 > 1024) {
|
|
||||||
value?.byteCount!!.div(1024)
|
|
||||||
} else { 0 }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadBitmap(drawableName: String): Bitmap? {
|
fun getDrawableIconForPackage(context: Context, packageName: String): Drawable? {
|
||||||
if (packageName != null && packageName.length > 0) {
|
// 1. 캐시에 있는지 확인
|
||||||
var bm = bitmapCache.get(packageName)
|
val cachedIcon = iconCache.get(packageName)
|
||||||
GlobalScope.async {
|
if (cachedIcon != null) {
|
||||||
bm?.let { ImageUtils.bitmapToBase64String(it)?.let {
|
return cachedIcon
|
||||||
icsPrefs.contains(packageName)
|
|
||||||
} }
|
|
||||||
}
|
|
||||||
if (bm != null) return bm
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadDrawable(drawableName: String): Drawable? {
|
// 2. 없으면 로딩 (기존 로직 수행)
|
||||||
iconPackRes!!.getIdentifier(drawableName, "drawable", packageName).let {
|
// ... (기존의 아이콘팩 로딩 또는 pm.getApplicationIcon 코드) ...
|
||||||
return if (it > 0) ResourcesCompat.getDrawable(iconPackRes!!, it, null)
|
val loadedIcon = try {
|
||||||
else null
|
context.packageManager.getApplicationIcon(packageName)
|
||||||
}
|
// 만약 아이콘팩 적용 로직이 있다면 여기서 변환 수행
|
||||||
|
} catch (e: Exception) {
|
||||||
|
context.resources.getDrawable(android.R.drawable.sym_def_app_icon, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun putAfterReturn(packages: String, drawable : Drawable?) : Drawable? {
|
// 3. 로딩된 아이콘 캐시에 저장
|
||||||
if (drawable != null) {
|
if (loadedIcon != null) {
|
||||||
appPackageIconDrawables.put(packages, drawable)
|
iconCache.put(packageName, loadedIcon)
|
||||||
(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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return loadedIcon
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 아이콘팩 변경 시 호출해줄 함수
|
||||||
|
fun clearCache() {
|
||||||
|
iconCache.evictAll()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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<RecommendedAppsAdapter.ViewHolder>() {
|
||||||
|
|
||||||
|
private val items = ArrayList<AppInfo>()
|
||||||
|
|
||||||
|
fun submitList(newItems: List<AppInfo>) {
|
||||||
|
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)
|
||||||
|
}
|
||||||
@ -20,29 +20,19 @@ import android.os.IBinder
|
|||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.work.CoroutineWorker
|
||||||
import androidx.work.ExistingPeriodicWorkPolicy
|
import androidx.work.ExistingPeriodicWorkPolicy
|
||||||
|
import androidx.work.ExistingWorkPolicy
|
||||||
|
import androidx.work.OneTimeWorkRequestBuilder
|
||||||
|
import androidx.work.OutOfQuotaPolicy
|
||||||
import androidx.work.PeriodicWorkRequestBuilder
|
import androidx.work.PeriodicWorkRequestBuilder
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
|
import androidx.work.WorkerParameters
|
||||||
import bums.lunatic.launcher.LauncherActivity
|
import bums.lunatic.launcher.LauncherActivity
|
||||||
import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
|
import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
|
||||||
import bums.lunatic.launcher.R
|
import bums.lunatic.launcher.R
|
||||||
import bums.lunatic.launcher.utils.Blog
|
import bums.lunatic.launcher.utils.Blog
|
||||||
import bums.lunatic.launcher.workers.ArcaGetter
|
import bums.lunatic.launcher.workers.TaskAggregator
|
||||||
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 com.yausername.youtubedl_android.YoutubeDL
|
import com.yausername.youtubedl_android.YoutubeDL
|
||||||
import com.yausername.youtubedl_android.YoutubeDLRequest
|
import com.yausername.youtubedl_android.YoutubeDLRequest
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@ -58,6 +48,22 @@ import java.util.UUID
|
|||||||
import java.util.concurrent.TimeUnit
|
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() {
|
class ForeGroundService : Service() {
|
||||||
companion object {
|
companion object {
|
||||||
val ACTION_SENDMSG = "ACTION_SEND_TO_LOVE"
|
val ACTION_SENDMSG = "ACTION_SEND_TO_LOVE"
|
||||||
@ -225,85 +231,26 @@ class ForeGroundService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun refreshFeeds() {
|
fun refreshFeeds() {
|
||||||
mWorkManager?.cancelAllWork()
|
val workManager = WorkManager.getInstance(applicationContext)
|
||||||
mWorkManager?.cancelAllWorkByTag(RuliWebGetter.TAG)
|
|
||||||
mWorkManager?.enqueueUniquePeriodicWork(
|
|
||||||
RuliWebGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
|
|
||||||
PeriodicWorkRequestBuilder<RuliWebGetter>(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
|
|
||||||
.addTag(RuliWebGetter.TAG)
|
|
||||||
.build())
|
|
||||||
|
|
||||||
mWorkManager?.cancelAllWorkByTag(FEDDS_WORK_TAG)
|
|
||||||
mWorkManager?.enqueueUniquePeriodicWork(
|
|
||||||
FEDDS_WORK_TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
|
|
||||||
PeriodicWorkRequestBuilder<NewsFeedsGetter>(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<YoutubeGetter>(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<RedditGetter>(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<FmKoreaGetter>(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<DotaxGetter>(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
|
|
||||||
.addTag(COMIC2_WORK_TAG)
|
|
||||||
.build())
|
|
||||||
mWorkManager?.cancelAllWorkByTag(ClienGetter.TAG)
|
|
||||||
mWorkManager?.enqueueUniquePeriodicWork(
|
|
||||||
ClienGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
|
|
||||||
PeriodicWorkRequestBuilder<ClienGetter>(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
|
|
||||||
.addTag(ClienGetter.TAG)
|
|
||||||
.build())
|
|
||||||
mWorkManager?.cancelAllWorkByTag(DCGetter.TAG)
|
|
||||||
mWorkManager?.enqueueUniquePeriodicWork(
|
|
||||||
DCGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
|
|
||||||
PeriodicWorkRequestBuilder<DCGetter>(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
|
|
||||||
.addTag(DCGetter.TAG)
|
|
||||||
.build())
|
|
||||||
mWorkManager?.cancelAllWorkByTag(TheQooGetter.TAG)
|
|
||||||
mWorkManager?.enqueueUniquePeriodicWork(
|
|
||||||
TheQooGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
|
|
||||||
PeriodicWorkRequestBuilder<TheQooGetter>(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
|
|
||||||
.addTag(TheQooGetter.TAG)
|
|
||||||
.build())
|
|
||||||
mWorkManager?.cancelAllWorkByTag(ArcaGetter.TAG)
|
|
||||||
mWorkManager?.enqueueUniquePeriodicWork(
|
|
||||||
ArcaGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
|
|
||||||
PeriodicWorkRequestBuilder<ArcaGetter>(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
|
|
||||||
.addTag(ArcaGetter.TAG)
|
|
||||||
.build())
|
|
||||||
mWorkManager?.cancelAllWorkByTag(LocationGetter.TAG)
|
|
||||||
mWorkManager?.enqueueUniquePeriodicWork(
|
|
||||||
LocationGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
|
|
||||||
PeriodicWorkRequestBuilder<LocationGetter>(PrefLong.locationTimePeriod.get(), TimeUnit.MINUTES)
|
|
||||||
.addTag(LocationGetter.TAG)
|
|
||||||
.build())
|
|
||||||
mWorkManager?.cancelAllWorkByTag(ServiceWatchdogWorker.TAG)
|
|
||||||
mWorkManager?.enqueueUniquePeriodicWork(
|
|
||||||
ServiceWatchdogWorker.TAG,
|
|
||||||
ExistingPeriodicWorkPolicy.REPLACE,
|
|
||||||
PeriodicWorkRequestBuilder<ServiceWatchdogWorker>(15, TimeUnit.MINUTES)
|
|
||||||
.addTag(ServiceWatchdogWorker.TAG)
|
|
||||||
.build())
|
|
||||||
|
|
||||||
|
TaskAggregator.refreshSystemInfo(applicationContext)
|
||||||
|
|
||||||
|
// 1. 시스템 정보: 자주 변경되지 않으므로 1~3시간 간격
|
||||||
|
val systemRequest = PeriodicWorkRequestBuilder<AggregatedSystemWorker>(PrefLong.longTimePeriod.get(120L), TimeUnit.MINUTES)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
// 2. 뉴스 피드: 사용자가 설정한 간격 (예: 1시간)
|
||||||
|
val newsRequest = PeriodicWorkRequestBuilder<AggregatedNewsWorker>(PrefLong.shortTimePeriod.get(20L), TimeUnit.MINUTES)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
// 기존의 수많은 enqueue 코드를 이 두 개로 대체
|
||||||
|
workManager.enqueueUniquePeriodicWork("AggregatedSystemWork", ExistingPeriodicWorkPolicy.KEEP, systemRequest)
|
||||||
|
workManager.enqueueUniquePeriodicWork("AggregatedNewsWork", ExistingPeriodicWorkPolicy.KEEP, newsRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fun workmanager() : WorkManager? {
|
fun workmanager() : WorkManager? {
|
||||||
if (mWorkManager == null && lActivity != null) {
|
if (mWorkManager == null && lActivity != null) {
|
||||||
mWorkManager = WorkManager.getInstance(lActivity!!)
|
mWorkManager = WorkManager.getInstance(lActivity!!)
|
||||||
|
|||||||
@ -18,4 +18,9 @@ class AppInfo : RealmObject {
|
|||||||
var category : String? = null
|
var category : String? = null
|
||||||
var currentInstalled : Boolean = false
|
var currentInstalled : Boolean = false
|
||||||
var isInstalled : Boolean = false
|
var isInstalled : Boolean = false
|
||||||
|
// [신규] 0: 항상 보이기(기본), 1: 검색 시만 보이기 (숨김)
|
||||||
|
var visibilityMode: Int = 0
|
||||||
|
|
||||||
|
// [신규] true면 추천 리스트에 절대 안 뜸
|
||||||
|
var blockRecommend: Boolean = false
|
||||||
}
|
}
|
||||||
@ -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시간제)
|
||||||
|
}
|
||||||
@ -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()
|
||||||
|
|
||||||
|
}
|
||||||
@ -45,7 +45,6 @@ import androidx.appcompat.widget.LinearLayoutCompat
|
|||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
|
import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
|
||||||
import bums.lunatic.launcher.R
|
import bums.lunatic.launcher.R
|
||||||
import bums.lunatic.launcher.apps.SimpleContact
|
|
||||||
import bums.lunatic.launcher.databinding.QuickAccessBinding
|
import bums.lunatic.launcher.databinding.QuickAccessBinding
|
||||||
import bums.lunatic.launcher.databinding.ShortcutMakerBinding
|
import bums.lunatic.launcher.databinding.ShortcutMakerBinding
|
||||||
import bums.lunatic.launcher.helpers.ColorPicker
|
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_PHONE
|
||||||
import bums.lunatic.launcher.helpers.Constants.Companion.SHORTCUT_TYPE_URL
|
import bums.lunatic.launcher.helpers.Constants.Companion.SHORTCUT_TYPE_URL
|
||||||
import bums.lunatic.launcher.model.AppInfo
|
import bums.lunatic.launcher.model.AppInfo
|
||||||
|
import bums.lunatic.launcher.model.SimpleContact
|
||||||
import bums.lunatic.launcher.utils.Blog
|
import bums.lunatic.launcher.utils.Blog
|
||||||
import bums.lunatic.launcher.workers.WorkersDb
|
import bums.lunatic.launcher.workers.WorkersDb
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
|
|||||||
@ -43,9 +43,9 @@ class PackageEventReceiver : BroadcastReceiver() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun startAppInfoGetter(context: Context) {
|
fun startAppInfoGetter(context: Context) {
|
||||||
var mWorkManager = WorkManager.getInstance(context)
|
// var mWorkManager = WorkManager.getInstance(context)
|
||||||
Executors.newSingleThreadScheduledExecutor().schedule({
|
// Executors.newSingleThreadScheduledExecutor().schedule({
|
||||||
mWorkManager.enqueue(OneTimeWorkRequest.from(AppInfoGetter::class.java))
|
// mWorkManager.enqueue(OneTimeWorkRequest.from(AppInfoGetter::class.java))
|
||||||
}, 5, TimeUnit.SECONDS)
|
// }, 5, TimeUnit.SECONDS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,7 +25,6 @@ import android.os.Bundle
|
|||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import bums.lunatic.launcher.BuildConfig
|
import bums.lunatic.launcher.BuildConfig
|
||||||
import bums.lunatic.launcher.R
|
import bums.lunatic.launcher.R
|
||||||
import bums.lunatic.launcher.apps.SimpleContact
|
|
||||||
import bums.lunatic.launcher.common.CommonActivity
|
import bums.lunatic.launcher.common.CommonActivity
|
||||||
import bums.lunatic.launcher.databinding.AboutBinding
|
import bums.lunatic.launcher.databinding.AboutBinding
|
||||||
import bums.lunatic.launcher.databinding.SettingsActivityBinding
|
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.PrefBoolean
|
||||||
import bums.lunatic.launcher.helpers.PrefHelper
|
import bums.lunatic.launcher.helpers.PrefHelper
|
||||||
import bums.lunatic.launcher.model.AppInfo
|
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.Apps
|
||||||
import bums.lunatic.launcher.settings.childs.HomeSettings
|
import bums.lunatic.launcher.settings.childs.HomeSettings
|
||||||
import bums.lunatic.launcher.settings.childs.Misc
|
import bums.lunatic.launcher.settings.childs.Misc
|
||||||
|
|||||||
@ -12,8 +12,10 @@ import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
|
|||||||
import bums.lunatic.launcher.apps.AppDrawer.Companion.appNamesPrefs
|
import bums.lunatic.launcher.apps.AppDrawer.Companion.appNamesPrefs
|
||||||
import bums.lunatic.launcher.model.AppInfo
|
import bums.lunatic.launcher.model.AppInfo
|
||||||
import bums.lunatic.launcher.utils.AlphabetToChosungMap
|
import bums.lunatic.launcher.utils.AlphabetToChosungMap
|
||||||
|
import bums.lunatic.launcher.utils.Blog
|
||||||
import bums.lunatic.launcher.utils.JamoUtils
|
import bums.lunatic.launcher.utils.JamoUtils
|
||||||
import io.realm.kotlin.ext.query
|
import io.realm.kotlin.ext.query
|
||||||
|
import io.realm.kotlin.types.RealmObject
|
||||||
import java.text.Normalizer
|
import java.text.Normalizer
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
@ -21,48 +23,49 @@ class AppInfoGetter : BaseGetter {
|
|||||||
companion object {
|
companion object {
|
||||||
val TAG = "AppInfoGetter"
|
val TAG = "AppInfoGetter"
|
||||||
}
|
}
|
||||||
constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
|
constructor(context: Context) : super(context) {
|
||||||
|
|
||||||
}
|
}
|
||||||
override fun realWork(): Result {
|
|
||||||
|
|
||||||
|
override fun realWork(): List<RealmObject> {
|
||||||
|
var result = mutableListOf<RealmObject>()
|
||||||
try {
|
try {
|
||||||
|
val packageManager = lActivity?.packageManager ?: return result
|
||||||
|
|
||||||
var packageManager = lActivity?.packageManager
|
// 1. 설치된 앱 목록 가져오기 (시스템 호출 1회)
|
||||||
var packageInfoList: MutableList<ResolveInfo> = mutableListOf()
|
val intent = Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LAUNCHER)
|
||||||
packageInfoList = (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
val resolveInfos = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
packageManager?.queryIntentActivities(
|
packageManager.queryIntentActivities(intent, PackageManager.ResolveInfoFlags.of(0))
|
||||||
Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LAUNCHER),
|
|
||||||
PackageManager.ResolveInfoFlags.of(0)
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
(packageManager?.queryIntentActivities(
|
packageManager.queryIntentActivities(intent, 0)
|
||||||
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<AppInfo>().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)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}!!
|
|
||||||
|
|
||||||
} catch (e : Exception) {e.printStackTrace()}
|
resolveInfos.forEach { ri ->
|
||||||
return Result.success()
|
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 {
|
fun appName(resolver: ResolveInfo): String {
|
||||||
return resolver.loadLabel(lActivity?.packageManager!!).toString().apply {
|
return resolver.loadLabel(lActivity?.packageManager!!).toString().apply {
|
||||||
appNamesPrefs?.edit()?.putString(resolver.activityInfo.packageName, this)?.apply()
|
appNamesPrefs?.edit()?.putString(resolver.activityInfo.packageName, this)?.apply()
|
||||||
|
|||||||
@ -1,108 +1,108 @@
|
|||||||
package bums.lunatic.launcher.workers
|
//package bums.lunatic.launcher.workers
|
||||||
|
//
|
||||||
//import bums.lunatic.launcher.workers.WorkersDb.blockKeyword
|
////import bums.lunatic.launcher.workers.WorkersDb.blockKeyword
|
||||||
import android.content.Context
|
//import android.content.Context
|
||||||
import androidx.work.WorkerParameters
|
//import androidx.work.WorkerParameters
|
||||||
import bums.lunatic.launcher.model.Arca
|
//import bums.lunatic.launcher.model.Arca
|
||||||
import bums.lunatic.launcher.model.RssDataInterface
|
//import bums.lunatic.launcher.model.RssDataInterface
|
||||||
import bums.lunatic.launcher.model.getT
|
//import bums.lunatic.launcher.model.getT
|
||||||
import bums.lunatic.launcher.utils.beforeOneDay
|
//import bums.lunatic.launcher.utils.beforeOneDay
|
||||||
import org.jsoup.nodes.Element
|
//import org.jsoup.nodes.Element
|
||||||
|
//
|
||||||
class ArcaGetter : BaseGetter {
|
//class ArcaGetter : BaseGetter {
|
||||||
companion object {
|
// companion object {
|
||||||
val TAG = "ArcaGetter"
|
// val TAG = "ArcaGetter"
|
||||||
}
|
// }
|
||||||
constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
|
// constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
|
||||||
|
//
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
override fun realWork(): Result {
|
// override fun realWork(): Result {
|
||||||
// RssDataType.ARCA.isOn {
|
//// 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 {
|
//// try {
|
||||||
//// Jsoup.connect(it)
|
//// Blog.LOGE("realWork() ${this::class.simpleName}")
|
||||||
//// .userAgent(USAGT)
|
//// temp.clear()
|
||||||
//// .header("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8")
|
//// val urls = arrayListOf(
|
||||||
//// .header("accept-language", "ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7")
|
//// "https://arca.live/b/singbung?mode=best",
|
||||||
//// .header("cache-control", "no-cache")
|
////// "https://arca.live/b/headline",
|
||||||
//// .header("pragma", "no-cache")
|
////// "https://arca.live/b/live",
|
||||||
//// .ignoreContentType(true)
|
//// "https://arca.live/b/namuhotnow",
|
||||||
//// .timeout(5000)
|
//// "https://arca.live/b/society",
|
||||||
//// .get().let { arca ->
|
////// "https://arca.live/b/replay",
|
||||||
////// BLog.LOGE("url >> ${it} >> ${arca}")
|
////// "https://arca.live/b/breaking"
|
||||||
//// arca.getElementsByClass("vrow hybrid").forEach { araca_li ->
|
//// )
|
||||||
//// if (araca_li.html().contains("title ") == true) {
|
//// urls.forEach {
|
||||||
//// parseArcaLi(araca_li).apply {
|
////// try {
|
||||||
//// this.forEach {
|
////// Jsoup.connect(it)
|
||||||
//// if (it.pubDate() > commicsDateTime) {
|
////// .userAgent(USAGT)
|
||||||
//// temp.add(it.getRssData())
|
////// .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) {
|
//// } catch (e: Exception) {
|
||||||
//// e.printStackTrace()
|
//// e.printStackTrace()
|
||||||
//// }
|
//// }
|
||||||
//
|
|
||||||
// }
|
|
||||||
//// Jsoup.connect("https://projrctjav.com").userAgent(USAGT).get().let { projectj ->
|
|
||||||
//// BLog.LOGE("projectj >>>>> ${projectj}")
|
|
||||||
//// }
|
//// }
|
||||||
// } catch (e: Exception) {
|
// return Result.success().apply {
|
||||||
// e.printStackTrace()
|
//// WorkersDb.insertBulkData(temp)
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
return Result.success().apply {
|
|
||||||
// WorkersDb.insertBulkData(temp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun parseArcaLi(aracaLi: Element) : ArrayList<RssDataInterface> {
|
|
||||||
var tempArray = arrayListOf<RssDataInterface>()
|
|
||||||
// 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}")
|
// private fun parseArcaLi(aracaLi: Element) : ArrayList<RssDataInterface> {
|
||||||
}
|
// var tempArray = arrayListOf<RssDataInterface>()
|
||||||
this.desc = desc
|
//// BLog.LOGE("aracaLi >>> ${aracaLi}")
|
||||||
this.dateTiem = dateTime
|
// var title = aracaLi.getElementsByClass("title hybrid-title").getT()
|
||||||
}.apply {
|
// var desc = aracaLi.getElementsByClass("badge").getT()
|
||||||
// BLog.LOGE("parseArcaLi >>>> ${this}")
|
// desc.plus(aracaLi.getElementsByClass("user-info ").getT())
|
||||||
if (this.pubDate() > beforeOneDay()) {
|
// var dateTime = aracaLi.getElementsByTag("time").attr("datetime")
|
||||||
tempArray.add(this)
|
// 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
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
//}
|
//}
|
||||||
}
|
|
||||||
return tempArray
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -8,10 +8,11 @@ import bums.lunatic.launcher.LunaticLauncher
|
|||||||
import bums.lunatic.launcher.model.RssData
|
import bums.lunatic.launcher.model.RssData
|
||||||
import bums.lunatic.launcher.utils.beforeDay
|
import bums.lunatic.launcher.utils.beforeDay
|
||||||
import bums.lunatic.launcher.utils.beforeOneDay
|
import bums.lunatic.launcher.utils.beforeOneDay
|
||||||
|
import io.realm.kotlin.types.RealmObject
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
open abstract class BaseGetter : Worker {
|
abstract class BaseGetter(internal val context: Context) {
|
||||||
protected companion object {
|
protected companion object {
|
||||||
var lastedUpdateTime = 0L
|
var lastedUpdateTime = 0L
|
||||||
val defaultDay = 3
|
val defaultDay = 3
|
||||||
@ -29,21 +30,8 @@ open abstract class BaseGetter : Worker {
|
|||||||
val commicsDateTime = beforeDay(1)
|
val commicsDateTime = beforeDay(1)
|
||||||
val temp = arrayListOf<RssData>()
|
val temp = arrayListOf<RssData>()
|
||||||
|
|
||||||
constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
|
abstract fun realWork() : List<RealmObject>
|
||||||
}
|
open suspend fun fetchData(): List<RealmObject> {
|
||||||
|
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
|
|
||||||
}
|
|
||||||
@ -1,149 +1,149 @@
|
|||||||
package bums.lunatic.launcher.workers
|
//package bums.lunatic.launcher.workers
|
||||||
|
//
|
||||||
import android.content.Context
|
//import android.content.Context
|
||||||
import android.net.Uri
|
//import android.net.Uri
|
||||||
import androidx.work.WorkerParameters
|
//import androidx.work.WorkerParameters
|
||||||
import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
|
//import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
|
||||||
import bums.lunatic.launcher.utils.Blog
|
//import bums.lunatic.launcher.utils.Blog
|
||||||
|
//import io.realm.kotlin.types.RealmObject
|
||||||
|
//
|
||||||
class CalendarGetter : BaseGetter {
|
//
|
||||||
companion object {
|
//class CalendarGetter : BaseGetter {
|
||||||
val TAG = "DCGetter"
|
// companion object {
|
||||||
}
|
// val TAG = "DCGetter"
|
||||||
constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
|
// }
|
||||||
|
// constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
|
||||||
}
|
//
|
||||||
override fun realWork(): Result {
|
// }
|
||||||
setCalendar()
|
//
|
||||||
return Result.success().apply {
|
//
|
||||||
|
// override fun realWork(): List<RealmObject> {
|
||||||
}
|
// return setCalendar()
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
fun setCalendar() {
|
// fun setCalendar() {
|
||||||
|
//
|
||||||
|
//
|
||||||
val calendars = Uri.parse("content://com.android.calendar/events")
|
// val calendars = Uri.parse("content://com.android.calendar/events")
|
||||||
|
//
|
||||||
val projection = arrayOf(
|
// val projection = arrayOf(
|
||||||
"calendar_id",
|
// "calendar_id",
|
||||||
// "htmlUri",
|
//// "htmlUri",
|
||||||
"title",
|
// "title",
|
||||||
// "eventLocation",
|
//// "eventLocation",
|
||||||
"description",
|
// "description",
|
||||||
// "eventStatus",
|
//// "eventStatus",
|
||||||
// "selfAttendeeStatus",
|
//// "selfAttendeeStatus",
|
||||||
// "commentsUri",
|
//// "commentsUri",
|
||||||
"dtstart",
|
// "dtstart",
|
||||||
"dtend",
|
// "dtend",
|
||||||
// "eventTimezone",
|
//// "eventTimezone",
|
||||||
// "duration",
|
//// "duration",
|
||||||
// "allDay",
|
//// "allDay",
|
||||||
// "visibility",
|
//// "visibility",
|
||||||
// "transparency",
|
//// "transparency",
|
||||||
// "hasAlarm",
|
//// "hasAlarm",
|
||||||
// "hasExtendedProperties",
|
//// "hasExtendedProperties",
|
||||||
// "rrule",
|
//// "rrule",
|
||||||
"rdate",
|
// "rdate",
|
||||||
// "exrule",
|
//// "exrule",
|
||||||
// "exdate",
|
//// "exdate",
|
||||||
// "originalEvent",
|
//// "originalEvent",
|
||||||
// "originalInstanceTime",
|
//// "originalInstanceTime",
|
||||||
// "originalAllDay",
|
//// "originalAllDay",
|
||||||
// "lastDate",
|
//// "lastDate",
|
||||||
// "hasAttendeeData",
|
//// "hasAttendeeData",
|
||||||
// "guestsCanModify",
|
//// "guestsCanModify",
|
||||||
// "guestsCanInviteOthers",
|
//// "guestsCanInviteOthers",
|
||||||
// "guestsCanSeeGuests",
|
//// "guestsCanSeeGuests",
|
||||||
// "organizer",
|
//// "organizer",
|
||||||
// "deleted"
|
//// "deleted"
|
||||||
)
|
// )
|
||||||
// val managedCursor: Cursor =
|
//// val managedCursor: Cursor =
|
||||||
lActivity?.contentResolver?.query(calendars, projection, null, null, null)?.let { managedCursor ->
|
// lActivity?.contentResolver?.query(calendars, projection, null, null, null)?.let { managedCursor ->
|
||||||
if (managedCursor.moveToFirst()) {
|
// if (managedCursor.moveToFirst()) {
|
||||||
val calendar_id = IntArray(managedCursor.count)
|
// val calendar_id = IntArray(managedCursor.count)
|
||||||
|
//
|
||||||
// val htmlUri = arrayOfNulls<String>(managedCursor.count)
|
//// val htmlUri = arrayOfNulls<String>(managedCursor.count)
|
||||||
val title = arrayOfNulls<String>(managedCursor.count)
|
// val title = arrayOfNulls<String>(managedCursor.count)
|
||||||
// val eventLocation = arrayOfNulls<String>(managedCursor.count)
|
//// val eventLocation = arrayOfNulls<String>(managedCursor.count)
|
||||||
val description = arrayOfNulls<String>(managedCursor.count)
|
// val description = arrayOfNulls<String>(managedCursor.count)
|
||||||
// val eventStatus = IntArray(managedCursor.count)
|
//// val eventStatus = IntArray(managedCursor.count)
|
||||||
// val selfAttendeeStatus = IntArray(managedCursor.count)
|
//// val selfAttendeeStatus = IntArray(managedCursor.count)
|
||||||
// val commentsUri = arrayOfNulls<String>(managedCursor.count)
|
//// val commentsUri = arrayOfNulls<String>(managedCursor.count)
|
||||||
val dtstart = arrayOfNulls<String>(managedCursor.count)
|
// val dtstart = arrayOfNulls<String>(managedCursor.count)
|
||||||
val dtend = arrayOfNulls<String>(managedCursor.count)
|
// val dtend = arrayOfNulls<String>(managedCursor.count)
|
||||||
// val eventTimezone = arrayOfNulls<String>(managedCursor.count)
|
//// val eventTimezone = arrayOfNulls<String>(managedCursor.count)
|
||||||
// val duration = arrayOfNulls<String>(managedCursor.count)
|
//// val duration = arrayOfNulls<String>(managedCursor.count)
|
||||||
// val allDay = IntArray(managedCursor.count)
|
//// val allDay = IntArray(managedCursor.count)
|
||||||
// val visibility = IntArray(managedCursor.count)
|
//// val visibility = IntArray(managedCursor.count)
|
||||||
// val transparency = IntArray(managedCursor.count)
|
//// val transparency = IntArray(managedCursor.count)
|
||||||
// val hasAlarm = IntArray(managedCursor.count)
|
//// val hasAlarm = IntArray(managedCursor.count)
|
||||||
// val hasExtendedProperties = IntArray(managedCursor.count)
|
//// val hasExtendedProperties = IntArray(managedCursor.count)
|
||||||
// val rrule = arrayOfNulls<String>(managedCursor.count)
|
//// val rrule = arrayOfNulls<String>(managedCursor.count)
|
||||||
val rdate = arrayOfNulls<String>(managedCursor.count)
|
// val rdate = arrayOfNulls<String>(managedCursor.count)
|
||||||
// val exrule = arrayOfNulls<String>(managedCursor.count)
|
//// val exrule = arrayOfNulls<String>(managedCursor.count)
|
||||||
// val exdate = arrayOfNulls<String>(managedCursor.count)
|
//// val exdate = arrayOfNulls<String>(managedCursor.count)
|
||||||
// val originalEvent = arrayOfNulls<String>(managedCursor.count)
|
//// val originalEvent = arrayOfNulls<String>(managedCursor.count)
|
||||||
// val originalInstanceTime = IntArray(managedCursor.count)
|
//// val originalInstanceTime = IntArray(managedCursor.count)
|
||||||
// val originalAllDay = IntArray(managedCursor.count)
|
//// val originalAllDay = IntArray(managedCursor.count)
|
||||||
// val lastDate = IntArray(managedCursor.count)
|
//// val lastDate = IntArray(managedCursor.count)
|
||||||
// val hasAttendeeData = IntArray(managedCursor.count)
|
//// val hasAttendeeData = IntArray(managedCursor.count)
|
||||||
// val guestsCanModify = IntArray(managedCursor.count)
|
//// val guestsCanModify = IntArray(managedCursor.count)
|
||||||
// val guestsCanInviteOthers = IntArray(managedCursor.count)
|
//// val guestsCanInviteOthers = IntArray(managedCursor.count)
|
||||||
// val guestsCanSeeGuests = IntArray(managedCursor.count)
|
//// val guestsCanSeeGuests = IntArray(managedCursor.count)
|
||||||
// val organizer = arrayOfNulls<String>(managedCursor.count)
|
//// val organizer = arrayOfNulls<String>(managedCursor.count)
|
||||||
// val deleted = IntArray(managedCursor.count)
|
//// val deleted = IntArray(managedCursor.count)
|
||||||
|
//
|
||||||
for (i in title.indices) {
|
// for (i in title.indices) {
|
||||||
calendar_id[i] = managedCursor.getInt(0)
|
// calendar_id[i] = managedCursor.getInt(0)
|
||||||
Blog.LOGE("Calendar ID : " + calendar_id[i])
|
// Blog.LOGE("Calendar ID : " + calendar_id[i])
|
||||||
// htmlUri[i] = managedCursor.getString(1)
|
//// htmlUri[i] = managedCursor.getString(1)
|
||||||
// Log.i("Calendar", "htmlUri : " + htmlUri[i])
|
//// Log.i("Calendar", "htmlUri : " + htmlUri[i])
|
||||||
title[i] = managedCursor.getString(1)
|
// title[i] = managedCursor.getString(1)
|
||||||
Blog.LOGE("Calendar title : " + title[i])
|
// Blog.LOGE("Calendar title : " + title[i])
|
||||||
// eventLocation[i] = managedCursor.getString(3)
|
//// eventLocation[i] = managedCursor.getString(3)
|
||||||
// Log.i("Calendar", "eventLocation : " + eventLocation[i])
|
//// Log.i("Calendar", "eventLocation : " + eventLocation[i])
|
||||||
description[i] = managedCursor.getString(2)
|
// description[i] = managedCursor.getString(2)
|
||||||
// eventStatus[i] = managedCursor.getInt(5)
|
//// eventStatus[i] = managedCursor.getInt(5)
|
||||||
// selfAttendeeStatus[i] = managedCursor.getInt(6)
|
//// selfAttendeeStatus[i] = managedCursor.getInt(6)
|
||||||
// commentsUri[i] = managedCursor.getString(7)
|
//// commentsUri[i] = managedCursor.getString(7)
|
||||||
dtstart[i] = managedCursor.getString(3)
|
// dtstart[i] = managedCursor.getString(3)
|
||||||
Blog.LOGE("Calendar dtstart : " + rdate[i])
|
// Blog.LOGE("Calendar dtstart : " + rdate[i])
|
||||||
dtend[i] = managedCursor.getString(4)
|
// dtend[i] = managedCursor.getString(4)
|
||||||
Blog.LOGE("Calendar dtend : " + rdate[i])
|
// Blog.LOGE("Calendar dtend : " + rdate[i])
|
||||||
// eventTimezone[i] = managedCursor.getString(10)
|
//// eventTimezone[i] = managedCursor.getString(10)
|
||||||
// duration[i] = managedCursor.getString(11)
|
//// duration[i] = managedCursor.getString(11)
|
||||||
// allDay[i] = managedCursor.getInt(12)
|
//// allDay[i] = managedCursor.getInt(12)
|
||||||
// visibility[i] = managedCursor.getInt(13)
|
//// visibility[i] = managedCursor.getInt(13)
|
||||||
// transparency[i] = managedCursor.getInt(14)
|
//// transparency[i] = managedCursor.getInt(14)
|
||||||
// hasAlarm[i] = managedCursor.getInt(15)
|
//// hasAlarm[i] = managedCursor.getInt(15)
|
||||||
// hasExtendedProperties[i] = managedCursor.getInt(16)
|
//// hasExtendedProperties[i] = managedCursor.getInt(16)
|
||||||
// rrule[i] = managedCursor.getString(17)
|
//// rrule[i] = managedCursor.getString(17)
|
||||||
rdate[i] = managedCursor.getString(5)
|
// rdate[i] = managedCursor.getString(5)
|
||||||
Blog.LOGE("Calendar rdate : " + rdate[i])
|
// Blog.LOGE("Calendar rdate : " + rdate[i])
|
||||||
// exrule[i] = managedCursor.getString(19)
|
//// exrule[i] = managedCursor.getString(19)
|
||||||
// exdate[i] = managedCursor.getString(20)
|
//// exdate[i] = managedCursor.getString(20)
|
||||||
// originalEvent[i] = managedCursor.getString(21)
|
//// originalEvent[i] = managedCursor.getString(21)
|
||||||
// originalInstanceTime[i] = managedCursor.getInt(22)
|
//// originalInstanceTime[i] = managedCursor.getInt(22)
|
||||||
// originalAllDay[i] = managedCursor.getInt(23)
|
//// originalAllDay[i] = managedCursor.getInt(23)
|
||||||
// lastDate[i] = managedCursor.getInt(24)
|
//// lastDate[i] = managedCursor.getInt(24)
|
||||||
// hasAttendeeData[i] = managedCursor.getInt(25)
|
//// hasAttendeeData[i] = managedCursor.getInt(25)
|
||||||
// guestsCanModify[i] = managedCursor.getInt(26)
|
//// guestsCanModify[i] = managedCursor.getInt(26)
|
||||||
// guestsCanInviteOthers[i] = managedCursor.getInt(27)
|
//// guestsCanInviteOthers[i] = managedCursor.getInt(27)
|
||||||
// guestsCanSeeGuests[i] = managedCursor.getInt(28)
|
//// guestsCanSeeGuests[i] = managedCursor.getInt(28)
|
||||||
// organizer[i] = managedCursor.getString(29)
|
//// organizer[i] = managedCursor.getString(29)
|
||||||
// deleted[i] = managedCursor.getInt(30)
|
//// deleted[i] = managedCursor.getInt(30)
|
||||||
|
//
|
||||||
if (title[i] != null) {
|
// if (title[i] != null) {
|
||||||
Blog.LOGE("title[i] ${title[i]}")
|
// Blog.LOGE("title[i] ${title[i]}")
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
managedCursor.moveToNext()
|
// managedCursor.moveToNext()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
managedCursor.close()
|
// managedCursor.close()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
}
|
//}
|
||||||
@ -9,14 +9,14 @@ import bums.lunatic.launcher.model.getHref
|
|||||||
import bums.lunatic.launcher.model.getRssData
|
import bums.lunatic.launcher.model.getRssData
|
||||||
import bums.lunatic.launcher.model.getT
|
import bums.lunatic.launcher.model.getT
|
||||||
import bums.lunatic.launcher.utils.Blog
|
import bums.lunatic.launcher.utils.Blog
|
||||||
|
import io.realm.kotlin.types.RealmObject
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
|
|
||||||
class ClienGetter : BaseGetter {
|
class ClienGetter(context: Context) : BaseGetter(context = context) {
|
||||||
companion object {
|
companion object {
|
||||||
val TAG = "ClienGetter"
|
val TAG = "ClienGetter"
|
||||||
}
|
}
|
||||||
constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
|
|
||||||
}
|
|
||||||
|
|
||||||
fun parseClien(div_clien : org.jsoup.nodes.Element) {
|
fun parseClien(div_clien : org.jsoup.nodes.Element) {
|
||||||
// BLog.LOGE("div_clien >>>> ${div_clien}")
|
// 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")
|
@SuppressLint("RestrictedApi")
|
||||||
override fun realWork(): Result {
|
override fun realWork(): List<RealmObject> {
|
||||||
RssDataType.CLIEN.isOn {
|
RssDataType.CLIEN.isOn {
|
||||||
Blog.LOGE("realWork() ${this::class.simpleName}")
|
Blog.LOGE("realWork() ${this::class.simpleName}")
|
||||||
try {
|
try {
|
||||||
@ -90,8 +69,6 @@ class ClienGetter : BaseGetter {
|
|||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Result.success().apply {
|
return temp
|
||||||
WorkersDb.insertBulkData(temp)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4,17 +4,18 @@ import android.content.Context
|
|||||||
import android.provider.ContactsContract
|
import android.provider.ContactsContract
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
|
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.ext.query
|
||||||
|
import io.realm.kotlin.types.RealmObject
|
||||||
|
|
||||||
class ContactInfoGetter : BaseGetter {
|
class ContactInfoGetter(context: Context) : BaseGetter(context) {
|
||||||
companion object {
|
companion object {
|
||||||
val TAG = "ContactInfoGetter"
|
val TAG = "ContactInfoGetter"
|
||||||
}
|
}
|
||||||
constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
|
|
||||||
|
|
||||||
}
|
override fun realWork(): List<RealmObject> {
|
||||||
override fun realWork(): Result {
|
var temp = mutableListOf<SimpleContact>()
|
||||||
val phoneUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI
|
val phoneUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI
|
||||||
val projection = arrayOf(
|
val projection = arrayOf(
|
||||||
ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
|
ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
|
||||||
@ -23,28 +24,31 @@ class ContactInfoGetter : BaseGetter {
|
|||||||
)
|
)
|
||||||
|
|
||||||
try {
|
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()) {
|
while (cursor.moveToNext()) {
|
||||||
val idx =cursor.getColumnIndex(projection[0])
|
val contactId = cursor.getString(idIdx)
|
||||||
val nameIndex = cursor.getColumnIndex(projection[1])
|
|
||||||
val numberIndex = cursor.getColumnIndex(projection[2])
|
val name = cursor.getString(nameIdx)
|
||||||
var contactId = cursor.getString(idx)
|
// 루프 내 불필요한 객체 생성 방지 및 null 처리
|
||||||
val name = cursor.getString(nameIndex)
|
val rawNumber = cursor.getString(numberIdx)
|
||||||
var number = cursor.getString(numberIndex)
|
val number = rawNumber?.replace("-", "") ?: ""
|
||||||
number = number.replace("-", "")
|
|
||||||
if (name?.length ?: 0 > 0 && number?.length ?: 0 > 0) {
|
if (!name.isNullOrEmpty() && number.isNotEmpty()) {
|
||||||
if (WorkersDb.getRealm().query<SimpleContact>("id == $0", contactId).find().size == 0) {
|
temp.add(SimpleContact(contactId, name, number))
|
||||||
WorkersDb.update(SimpleContact(contactId,name,number))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// 데이터 계열은 반드시 닫아줘야 한다.
|
|
||||||
cursor?.close()
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
return Result.success()
|
Blog.LOGE("ContactInfoGetter >>> ${temp.size}")
|
||||||
|
return temp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -9,15 +9,15 @@ import bums.lunatic.launcher.model.RssDataInterface
|
|||||||
import bums.lunatic.launcher.model.RssDataType
|
import bums.lunatic.launcher.model.RssDataType
|
||||||
import bums.lunatic.launcher.model.getRssData
|
import bums.lunatic.launcher.model.getRssData
|
||||||
import bums.lunatic.launcher.utils.Blog
|
import bums.lunatic.launcher.utils.Blog
|
||||||
|
import io.realm.kotlin.types.RealmObject
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
|
|
||||||
class DCGetter : BaseGetter {
|
class DCGetter(context: Context) : BaseGetter(context) {
|
||||||
companion object {
|
companion object {
|
||||||
val TAG = "DCGetter"
|
val TAG = "DCGetter"
|
||||||
}
|
}
|
||||||
constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
|
|
||||||
}
|
|
||||||
|
|
||||||
fun parseDcLi(dc_li : org.jsoup.nodes.Element) : ArrayList<RssDataInterface>{
|
fun parseDcLi(dc_li : org.jsoup.nodes.Element) : ArrayList<RssDataInterface>{
|
||||||
var temp = arrayListOf<RssDataInterface>()
|
var temp = arrayListOf<RssDataInterface>()
|
||||||
@ -71,7 +71,7 @@ class DCGetter : BaseGetter {
|
|||||||
}
|
}
|
||||||
val df = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
|
val df = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
override fun realWork(): Result {
|
override fun realWork(): List<RealmObject> {
|
||||||
// Blog.LOGE("${TAG} RssDataType.DCINSIDE.isOn >>>> ${PrefHelper.getBoolean(RssDataType.DCINSIDE.name,false)}")
|
// Blog.LOGE("${TAG} RssDataType.DCINSIDE.isOn >>>> ${PrefHelper.getBoolean(RssDataType.DCINSIDE.name,false)}")
|
||||||
RssDataType.DCINSIDE.isOn {
|
RssDataType.DCINSIDE.isOn {
|
||||||
Blog.LOGE("realWork() ${this::class.simpleName}")
|
Blog.LOGE("realWork() ${this::class.simpleName}")
|
||||||
@ -161,12 +161,6 @@ class DCGetter : BaseGetter {
|
|||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Result.success().apply {
|
return temp
|
||||||
try {
|
|
||||||
WorkersDb.insertBulkData(temp)
|
|
||||||
} catch (e : Exception) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6,30 +6,24 @@ import androidx.work.WorkerParameters
|
|||||||
import bums.lunatic.launcher.LauncherActivity
|
import bums.lunatic.launcher.LauncherActivity
|
||||||
import bums.lunatic.launcher.model.RssDataType
|
import bums.lunatic.launcher.model.RssDataType
|
||||||
import bums.lunatic.launcher.utils.Blog
|
import bums.lunatic.launcher.utils.Blog
|
||||||
|
import io.realm.kotlin.types.RealmObject
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
class DotaxGetter : BaseGetter {
|
class DotaxGetter(context: Context) : BaseGetter(context) {
|
||||||
companion object {
|
companion object {
|
||||||
val COMIC2_WORK_TAG = "ComicGetter2"
|
val COMIC2_WORK_TAG = "ComicGetter2"
|
||||||
}
|
}
|
||||||
constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
override fun realWork(): Result {
|
override fun realWork(): List<RealmObject> {
|
||||||
RssDataType.DOTAX.isOn {
|
RssDataType.DOTAX.isOn {
|
||||||
try {
|
try {
|
||||||
Blog.LOGE("realWork() ${this::class.simpleName}")
|
Blog.LOGE("realWork() ${this::class.simpleName}")
|
||||||
temp.clear()
|
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 ->
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
LauncherActivity.lActivity?.let {
|
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()}}
|
} catch (e : Exception) {e.printStackTrace()}}
|
||||||
return Result.success().apply {
|
return temp
|
||||||
WorkersDb.insertBulkData(temp)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -7,16 +7,14 @@ import bums.lunatic.launcher.model.FmKorea
|
|||||||
import bums.lunatic.launcher.model.RssDataType
|
import bums.lunatic.launcher.model.RssDataType
|
||||||
import bums.lunatic.launcher.model.getRssData
|
import bums.lunatic.launcher.model.getRssData
|
||||||
import bums.lunatic.launcher.utils.Blog
|
import bums.lunatic.launcher.utils.Blog
|
||||||
|
import io.realm.kotlin.types.RealmObject
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
class FmKoreaGetter : BaseGetter {
|
class FmKoreaGetter (context: Context): BaseGetter(context) {
|
||||||
companion object {
|
companion object {
|
||||||
val FM_WORK_TAG = "FmKoreaGetter"
|
val FM_WORK_TAG = "FmKoreaGetter"
|
||||||
}
|
}
|
||||||
constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fun extractDocumentSrl(url: String): String? {
|
fun extractDocumentSrl(url: String): String? {
|
||||||
val uri = java.net.URI(url)
|
val uri = java.net.URI(url)
|
||||||
@ -33,7 +31,8 @@ class FmKoreaGetter : BaseGetter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
override fun realWork(): Result {
|
override fun realWork(): List<RealmObject> {
|
||||||
|
temp.clear()
|
||||||
RssDataType.FMKORAE.isOn {
|
RssDataType.FMKORAE.isOn {
|
||||||
val now = Date()
|
val now = Date()
|
||||||
try {
|
try {
|
||||||
@ -97,8 +96,6 @@ class FmKoreaGetter : BaseGetter {
|
|||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Result.success().apply {
|
return temp
|
||||||
WorkersDb.insertBulkData(temp)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,54 +1,55 @@
|
|||||||
package bums.lunatic.launcher.workers
|
package bums.lunatic.launcher.workers
|
||||||
|
//
|
||||||
import android.annotation.SuppressLint
|
//import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
//import android.content.Context
|
||||||
import android.location.Location
|
//import android.location.Location
|
||||||
import androidx.work.WorkerParameters
|
//import androidx.work.WorkerParameters
|
||||||
import bums.lunatic.launcher.common.letTrue
|
//import bums.lunatic.launcher.common.letTrue
|
||||||
import bums.lunatic.launcher.helpers.PrefBoolean
|
//import bums.lunatic.launcher.helpers.PrefBoolean
|
||||||
import bums.lunatic.launcher.utils.Blog
|
//import bums.lunatic.launcher.utils.Blog
|
||||||
import bums.lunatic.launcher.workers.LocationUpdateService.Companion.pushLocation
|
//import bums.lunatic.launcher.workers.LocationUpdateService.Companion.pushLocation
|
||||||
import com.google.android.gms.location.LocationServices
|
//import com.google.android.gms.location.LocationServices
|
||||||
import com.google.android.gms.location.Priority
|
//import com.google.android.gms.location.Priority
|
||||||
import com.google.android.gms.tasks.CancellationTokenSource
|
//import com.google.android.gms.tasks.CancellationTokenSource
|
||||||
|
//import io.realm.kotlin.types.RealmObject
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
|
//
|
||||||
class LocationGetter(context: Context, workerParams: WorkerParameters) : BaseGetter(context, workerParams) {
|
//class LocationGetter(context: Context) : BaseGetter(context) {
|
||||||
companion object {
|
// companion object {
|
||||||
val TAG = "LocationGetter"
|
// val TAG = "LocationGetter"
|
||||||
var longitude: Double = 0.0
|
// var longitude: Double = 0.0
|
||||||
var latitude: Double = 0.0
|
// var latitude: Double = 0.0
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@SuppressLint("MissingPermission")
|
// @SuppressLint("MissingPermission")
|
||||||
override fun realWork(): Result {
|
// override fun realWork(): List<RealmObject> {
|
||||||
// Blog.LOGE("${OpenWeatherGetter.TAG} realWork()")
|
//// Blog.LOGE("${OpenWeatherGetter.TAG} realWork()")
|
||||||
|
//
|
||||||
LocationServices.getFusedLocationProviderClient(this.applicationContext)
|
// LocationServices.getFusedLocationProviderClient(context)
|
||||||
.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, CancellationTokenSource().token)
|
// .getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, CancellationTokenSource().token)
|
||||||
.addOnSuccessListener{ success: Location? ->
|
// .addOnSuccessListener{ success: Location? ->
|
||||||
success?.let {
|
// success?.let {
|
||||||
Blog.LOGE("Location >>> $it")
|
// Blog.LOGE("Location >>> $it")
|
||||||
Blog.LOGE("Location >>> (latitude)${it.longitude}/(longitude)${it.latitude}")
|
// Blog.LOGE("Location >>> (latitude)${it.longitude}/(longitude)${it.latitude}")
|
||||||
longitude = it.longitude
|
// longitude = it.longitude
|
||||||
latitude = it.latitude
|
// latitude = it.latitude
|
||||||
// runWeatherGetter()
|
//// runWeatherGetter()
|
||||||
PrefBoolean.location.get().letTrue {
|
// PrefBoolean.location.get().letTrue {
|
||||||
pushLocation(this.applicationContext,it.latitude, it.longitude)
|
// pushLocation(context,it.latitude, it.longitude)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}.addOnFailureListener{
|
// }.addOnFailureListener{
|
||||||
Blog.LOGE("Location error >>> $it")
|
// Blog.LOGE("Location error >>> $it")
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
return Result.success()
|
// return temp
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
|
//
|
||||||
val EARTH_RADIUS_METERS = 6371000
|
val EARTH_RADIUS_METERS = 6371000
|
||||||
val LATITUDE_DEGREE_PER_METER: Double = 1.0 / (2 * Math.PI * EARTH_RADIUS_METERS / 360)
|
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)
|
return doubleArrayOf(minLongitude, maxLongitude)
|
||||||
}
|
}
|
||||||
|
//
|
||||||
|
//
|
||||||
//https://jinkpark.tistory.com/296
|
////https://jinkpark.tistory.com/296
|
||||||
//https://develoyummer.tistory.com/103
|
////https://develoyummer.tistory.com/103
|
||||||
//https://ghj1001020.tistory.com/300
|
////https://ghj1001020.tistory.com/300
|
||||||
@ -18,8 +18,6 @@ import bums.lunatic.launcher.helpers.PrefString
|
|||||||
import bums.lunatic.launcher.model.LocationLog
|
import bums.lunatic.launcher.model.LocationLog
|
||||||
import bums.lunatic.launcher.utils.Blog
|
import bums.lunatic.launcher.utils.Blog
|
||||||
import bums.lunatic.launcher.utils.inRange
|
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.android.gms.location.LocationServices
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import io.realm.kotlin.ext.query
|
import io.realm.kotlin.ext.query
|
||||||
@ -42,6 +40,8 @@ import java.util.concurrent.TimeUnit
|
|||||||
class LocationUpdateService : Service(), LocationListener {
|
class LocationUpdateService : Service(), LocationListener {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
var longitude: Double = 0.0
|
||||||
|
var latitude: Double = 0.0
|
||||||
fun pushLocation(context: Context, lat :Double, long : Double) {
|
fun pushLocation(context: Context, lat :Double, long : Double) {
|
||||||
try {
|
try {
|
||||||
Blog.LOGE("Location >>> ${lat},${long}")
|
Blog.LOGE("Location >>> ${lat},${long}")
|
||||||
@ -179,7 +179,6 @@ class LocationUpdateService : Service(), LocationListener {
|
|||||||
).show()
|
).show()
|
||||||
longitude = location.longitude
|
longitude = location.longitude
|
||||||
latitude = location.latitude
|
latitude = location.latitude
|
||||||
// runWeatherGetter()
|
|
||||||
pushLocation(this.applicationContext, location.latitude, location.longitude)
|
pushLocation(this.applicationContext, location.latitude, location.longitude)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,17 +7,17 @@ import bums.lunatic.launcher.home.adapters.RssFeedsParser
|
|||||||
import bums.lunatic.launcher.model.RssDataType
|
import bums.lunatic.launcher.model.RssDataType
|
||||||
import bums.lunatic.launcher.model.getRssData
|
import bums.lunatic.launcher.model.getRssData
|
||||||
import bums.lunatic.launcher.utils.RssList
|
import bums.lunatic.launcher.utils.RssList
|
||||||
|
import io.realm.kotlin.types.RealmObject
|
||||||
|
|
||||||
class NewsFeedsGetter : BaseGetter {
|
class NewsFeedsGetter(context: Context) : BaseGetter(context) {
|
||||||
companion object {
|
companion object {
|
||||||
val FEDDS_WORK_TAG = "NewsFeedsGetter"
|
val FEDDS_WORK_TAG = "NewsFeedsGetter"
|
||||||
}
|
}
|
||||||
var feddsUrls = arrayListOf<String>()
|
var feddsUrls = arrayListOf<String>()
|
||||||
constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
override fun realWork(): Result {
|
override fun realWork(): List<RealmObject> {
|
||||||
RssDataType.NEWSFEED.isOn {
|
RssDataType.NEWSFEED.isOn {
|
||||||
feddsUrls.clear()
|
feddsUrls.clear()
|
||||||
feddsUrls.addAll(RssList.newsFeeds)
|
feddsUrls.addAll(RssList.newsFeeds)
|
||||||
@ -32,15 +32,7 @@ class NewsFeedsGetter : BaseGetter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return Result.success().apply {
|
return temp
|
||||||
WorkersDb.insertBulkData(temp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// temp.forEach { synchronized(rssSet){
|
|
||||||
// rssSet.put(it.originPage(), it)
|
|
||||||
// } }.run {
|
|
||||||
// rssSetTouchCount -= 1
|
|
||||||
// Result.success() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,89 +1,90 @@
|
|||||||
package bums.lunatic.launcher.workers
|
//package bums.lunatic.launcher.workers
|
||||||
|
//
|
||||||
import android.content.Context
|
//import android.content.Context
|
||||||
import androidx.work.WorkerParameters
|
//import androidx.work.WorkerParameters
|
||||||
import bums.lunatic.launcher.helpers.PrefString
|
//import bums.lunatic.launcher.helpers.PrefString
|
||||||
import bums.lunatic.launcher.model.WeatherForcast
|
//import bums.lunatic.launcher.model.WeatherForcast
|
||||||
import bums.lunatic.launcher.model.WeatherInfoManager
|
//import bums.lunatic.launcher.model.WeatherInfoManager
|
||||||
import bums.lunatic.launcher.utils.Blog
|
//import bums.lunatic.launcher.utils.Blog
|
||||||
import io.realm.kotlin.UpdatePolicy
|
//import io.realm.kotlin.UpdatePolicy
|
||||||
import retrofit2.Call
|
//import io.realm.kotlin.types.RealmObject
|
||||||
import retrofit2.Retrofit
|
//import retrofit2.Call
|
||||||
import retrofit2.converter.gson.GsonConverterFactory
|
//import retrofit2.Retrofit
|
||||||
import retrofit2.create
|
//import retrofit2.converter.gson.GsonConverterFactory
|
||||||
import retrofit2.http.GET
|
//import retrofit2.create
|
||||||
import retrofit2.http.Path
|
//import retrofit2.http.GET
|
||||||
import retrofit2.http.Query
|
//import retrofit2.http.Path
|
||||||
|
//import retrofit2.http.Query
|
||||||
class OpenWeatherGetter(context: Context, workerParams: WorkerParameters) : BaseGetter(context, workerParams) {
|
//
|
||||||
companion object {
|
//class OpenWeatherGetter(context: Context) : BaseGetter(context) {
|
||||||
val TAG = "OpenWeatherGetter"
|
// companion object {
|
||||||
var lon: Double? = null // 경도
|
// val TAG = "OpenWeatherGetter"
|
||||||
var lat: Double? = null // 위도
|
// var lon: Double? = null // 경도
|
||||||
}
|
// var lat: Double? = null // 위도
|
||||||
//////////////////////////////////////////
|
// }
|
||||||
// weatherapi
|
// //////////////////////////////////////////
|
||||||
val VER_WEATHERAPI = "v1"
|
// // weatherapi
|
||||||
val URI_WEATHERAPI = "https://api.weatherapi.com"
|
// val VER_WEATHERAPI = "v1"
|
||||||
val KEY_WEATHERAPI = "8133d83d23ab4175a4160624241909"
|
// val URI_WEATHERAPI = "https://api.weatherapi.com"
|
||||||
val DAYS = 3
|
// val KEY_WEATHERAPI = "8133d83d23ab4175a4160624241909"
|
||||||
//////////////////////////////////////////
|
// val DAYS = 3
|
||||||
|
// //////////////////////////////////////////
|
||||||
//////////////////////////////////////////
|
//
|
||||||
|
// //////////////////////////////////////////
|
||||||
override fun realWork(): Result {
|
//
|
||||||
Blog.LOGE("${TAG} realWork()")
|
// override fun realWork(): List<RealmObject> {
|
||||||
// 위치 값 가져오기
|
// Blog.LOGE("${TAG} realWork()")
|
||||||
lat = LocationGetter.latitude
|
// // 위치 값 가져오기
|
||||||
lon = LocationGetter.longitude
|
// lat = LocationGetter.latitude
|
||||||
if (lat != null && lon != null) {
|
// lon = LocationGetter.longitude
|
||||||
getWeather(lat!!, lon!!)
|
// if (lat != null && lon != null) {
|
||||||
} else {
|
// getWeather(lat!!, lon!!)
|
||||||
Blog.LOGE("lat or lon is null")
|
// } else {
|
||||||
}
|
// Blog.LOGE("lat or lon is null")
|
||||||
return Result.success()
|
// }
|
||||||
}
|
// return Result.success()
|
||||||
|
// }
|
||||||
fun getWeather(latitude: Double, longitude: Double) {
|
//
|
||||||
Blog.LOGE("into getWeather ${PrefString.weatherApiKey.get()}")
|
// fun getWeather(latitude: Double, longitude: Double) {
|
||||||
///saved weatherForcast
|
// Blog.LOGE("into getWeather ${PrefString.weatherApiKey.get()}")
|
||||||
Retrofit.Builder()
|
// ///saved weatherForcast
|
||||||
.baseUrl(URI_WEATHERAPI)
|
// Retrofit.Builder()
|
||||||
.addConverterFactory(GsonConverterFactory.create())
|
// .baseUrl(URI_WEATHERAPI)
|
||||||
.build()
|
// .addConverterFactory(GsonConverterFactory.create())
|
||||||
.create<RestrofitService>()
|
// .build()
|
||||||
.getForecast( // weatherApi
|
// .create<RestrofitService>()
|
||||||
ver = VER_WEATHERAPI,
|
// .getForecast( // weatherApi
|
||||||
key = PrefString.weatherApiKey.get(),
|
// ver = VER_WEATHERAPI,
|
||||||
q = "$latitude,$longitude",
|
// key = PrefString.weatherApiKey.get(),
|
||||||
days = (System.currentTimeMillis() % 5L).toInt().toString()
|
// q = "$latitude,$longitude",
|
||||||
)?.execute()?.let { response ->
|
// days = (System.currentTimeMillis() % 5L).toInt().toString()
|
||||||
// BLog.LOGE("into getWeather after execute")
|
// )?.execute()?.let { response ->
|
||||||
// BLog.LOGE("weatherApi forecast response >>> $response")
|
//// BLog.LOGE("into getWeather after execute")
|
||||||
response.body()?.let { weatherInfo ->
|
//// BLog.LOGE("weatherApi forecast response >>> $response")
|
||||||
WeatherInfoManager.info = weatherInfo
|
// response.body()?.let { weatherInfo ->
|
||||||
WeatherInfoManager.readyForSaving(lat ?: 0.0, lon ?: 0.0)
|
// WeatherInfoManager.info = weatherInfo
|
||||||
// Realm에 저장
|
// WeatherInfoManager.readyForSaving(lat ?: 0.0, lon ?: 0.0)
|
||||||
WorkersDb.getRealm().writeBlocking {
|
// // Realm에 저장
|
||||||
copyToRealm(weatherInfo, UpdatePolicy.ALL).also {
|
// WorkersDb.getRealm().writeBlocking {
|
||||||
// BLog.LOGE("saved weatherForcast >>> $it")
|
// copyToRealm(weatherInfo, UpdatePolicy.ALL).also {
|
||||||
}
|
//// BLog.LOGE("saved weatherForcast >>> $it")
|
||||||
}
|
// }
|
||||||
// BLog.LOGE("saved weatherForcast forecastdayRealm.size >>> ${WorkersDb.getRealm().query<WeatherForcast>().first().find()?.forecast?.forecastdayRealm?.size}")
|
// }
|
||||||
// BLog.LOGE("saved weatherForcast hour.count >>> ${WorkersDb.getRealm().query<Hour>().count().find()}")
|
//// BLog.LOGE("saved weatherForcast forecastdayRealm.size >>> ${WorkersDb.getRealm().query<WeatherForcast>().first().find()?.forecast?.forecastdayRealm?.size}")
|
||||||
}
|
//// BLog.LOGE("saved weatherForcast hour.count >>> ${WorkersDb.getRealm().query<Hour>().count().find()}")
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//}
|
||||||
interface RestrofitService {
|
//
|
||||||
// weather_api
|
//interface RestrofitService {
|
||||||
@GET("/{ver}/forecast.json")
|
// // weather_api
|
||||||
fun getForecast(
|
// @GET("/{ver}/forecast.json")
|
||||||
@Path("ver") ver: String,
|
// fun getForecast(
|
||||||
@Query("key") key: String,
|
// @Path("ver") ver: String,
|
||||||
@Query("q") q: String,
|
// @Query("key") key: String,
|
||||||
@Query("days") days: String
|
// @Query("q") q: String,
|
||||||
): Call<WeatherForcast?>?
|
// @Query("days") days: String
|
||||||
}
|
// ): Call<WeatherForcast?>?
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import android.content.Context
|
|||||||
import android.provider.CallLog
|
import android.provider.CallLog
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
|
import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
|
||||||
import bums.lunatic.launcher.apps.SimpleContact
|
|
||||||
import bums.lunatic.launcher.utils.beforeDay
|
import bums.lunatic.launcher.utils.beforeDay
|
||||||
import bums.lunatic.launcher.utils.getContactId
|
import bums.lunatic.launcher.utils.getContactId
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
@ -48,18 +47,16 @@ class RecentCall : RealmObject {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class RecentCallGetter : BaseGetter {
|
class RecentCallGetter(context: Context) : BaseGetter(context) {
|
||||||
companion object{
|
companion object{
|
||||||
var dayRange = BaseGetter.defaultDay
|
var dayRange = BaseGetter.defaultDay
|
||||||
val TAG = "RecentCallGetter"
|
val TAG = "RecentCallGetter"
|
||||||
val dateFormat = SimpleDateFormat("yyy/MM/dd-HH:mm:ss")
|
val dateFormat = SimpleDateFormat("yyy/MM/dd-HH:mm:ss")
|
||||||
}
|
}
|
||||||
constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
override fun realWork(): Result {
|
override fun realWork(): List<RealmObject> {
|
||||||
|
var temp = mutableListOf<RecentCall>()
|
||||||
var dateParam = beforeDay(dayRange).toString()
|
var dateParam = beforeDay(dayRange).toString()
|
||||||
var managedCursor = lActivity?.contentResolver?.query(
|
var managedCursor = lActivity?.contentResolver?.query(
|
||||||
CallLog.Calls.CONTENT_URI, arrayOf(
|
CallLog.Calls.CONTENT_URI, arrayOf(
|
||||||
@ -69,8 +66,7 @@ class RecentCallGetter : BaseGetter {
|
|||||||
CallLog.Calls.DURATION,
|
CallLog.Calls.DURATION,
|
||||||
CallLog.Calls.CACHED_NAME,
|
CallLog.Calls.CACHED_NAME,
|
||||||
), CallLog.Calls.DATE + " >= ? " , arrayOf<String>(dateParam), CallLog.Calls.DATE + " desc")
|
), CallLog.Calls.DATE + " >= ? " , arrayOf<String>(dateParam), CallLog.Calls.DATE + " desc")
|
||||||
//+ CallLog.Calls.TYPE + " > ?"
|
|
||||||
//, "2"
|
|
||||||
if(managedCursor != null && managedCursor.isClosed == false) {
|
if(managedCursor != null && managedCursor.isClosed == false) {
|
||||||
try {
|
try {
|
||||||
val number = managedCursor.getColumnIndex(CallLog.Calls.NUMBER)
|
val number = managedCursor.getColumnIndex(CallLog.Calls.NUMBER)
|
||||||
@ -97,8 +93,7 @@ class RecentCallGetter : BaseGetter {
|
|||||||
CallLog.Calls.BLOCKED_TYPE -> { dir = "BLOCKED_TYPE" }
|
CallLog.Calls.BLOCKED_TYPE -> { dir = "BLOCKED_TYPE" }
|
||||||
CallLog.Calls.ANSWERED_EXTERNALLY_TYPE -> { dir = "ANSWERED_EXTERNALLY_TYPE" }
|
CallLog.Calls.ANSWERED_EXTERNALLY_TYPE -> { dir = "ANSWERED_EXTERNALLY_TYPE" }
|
||||||
}
|
}
|
||||||
|
temp.add(RecentCall(
|
||||||
val call = RecentCall(
|
|
||||||
1,
|
1,
|
||||||
callerName,
|
callerName,
|
||||||
phNumber,
|
phNumber,
|
||||||
@ -107,27 +102,8 @@ class RecentCallGetter : BaseGetter {
|
|||||||
dateFormat.format(Date(callDayTime)),
|
dateFormat.format(Date(callDayTime)),
|
||||||
callDayTime,
|
callDayTime,
|
||||||
callDuration.toLong()
|
callDuration.toLong()
|
||||||
)
|
))
|
||||||
call.uniqK?.let {
|
|
||||||
// BLog.LOGE("${TAG} call.uniqK? >>> ${call.uniqK}")
|
|
||||||
WorkersDb.getRealm().writeBlocking {
|
|
||||||
if(query<RecentCall>().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<SimpleContact>().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) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
@ -135,9 +111,7 @@ class RecentCallGetter : BaseGetter {
|
|||||||
managedCursor.close()
|
managedCursor.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Result.success().apply {
|
return temp
|
||||||
dayRange = BaseGetter.defaultDay
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -23,20 +23,16 @@ import java.io.IOException
|
|||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
|
|
||||||
class RecentSmsGetter : BaseGetter {
|
class RecentSmsGetter(context: Context) : BaseGetter(context) {
|
||||||
companion object {
|
companion object {
|
||||||
var dayRange = BaseGetter.defaultDay
|
var dayRange = BaseGetter.defaultDay
|
||||||
val SMS_WORK_TAG = "RecentSmsGetter"
|
val SMS_WORK_TAG = "RecentSmsGetter"
|
||||||
}
|
}
|
||||||
constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
override fun realWork(): Result {
|
override fun realWork(): List<RealmObject> {
|
||||||
|
var temp = mutableListOf<RecentSms>()
|
||||||
var dateParam = beforeDay(dayRange).toString()
|
var dateParam = beforeDay(dayRange).toString()
|
||||||
val managedCursor = lActivity?.contentResolver?.query(
|
val managedCursor = lActivity?.contentResolver?.query(
|
||||||
Telephony.Sms.CONTENT_URI, arrayOf(
|
Telephony.Sms.CONTENT_URI, arrayOf(
|
||||||
@ -88,15 +84,8 @@ class RecentSmsGetter : BaseGetter {
|
|||||||
)
|
)
|
||||||
log.isMms = false
|
log.isMms = false
|
||||||
// BLog.LOGE("RecentSmsGetter resultData put ${phNumber +"_"+ reciveDate} >>> ${log.toJson()}")
|
// BLog.LOGE("RecentSmsGetter resultData put ${phNumber +"_"+ reciveDate} >>> ${log.toJson()}")
|
||||||
log.sender = getContactName(applicationContext.contentResolver,phNumber) ?: ""
|
log.sender = getContactName(context.applicationContext.contentResolver,phNumber) ?: ""
|
||||||
WorkersDb.getRealm().apply {
|
temp.add(log)
|
||||||
if (query<RecentSms>("uniqKey == $0", log.uniqKey).find().size == 0) {
|
|
||||||
writeBlocking {
|
|
||||||
copyToRealm(log)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|
||||||
@ -107,9 +96,7 @@ class RecentSmsGetter : BaseGetter {
|
|||||||
if (lActivity?.contentResolver != null) {
|
if (lActivity?.contentResolver != null) {
|
||||||
MmsQueryHelper(lActivity?.contentResolver!!).query()
|
MmsQueryHelper(lActivity?.contentResolver!!).query()
|
||||||
}
|
}
|
||||||
return Result.success().apply {
|
return temp
|
||||||
dayRange = BaseGetter.defaultDay
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,18 +9,17 @@ import bums.lunatic.launcher.model.RssDataType
|
|||||||
import bums.lunatic.launcher.model.getRssData
|
import bums.lunatic.launcher.model.getRssData
|
||||||
import bums.lunatic.launcher.utils.RssList.feedJsons
|
import bums.lunatic.launcher.utils.RssList.feedJsons
|
||||||
import bums.lunatic.launcher.utils.RssList.feedJsons_nsfw
|
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{
|
companion object{
|
||||||
val REDDIT_WORK_TAG = "RedditGetter"
|
val REDDIT_WORK_TAG = "RedditGetter"
|
||||||
}
|
}
|
||||||
constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
override fun realWork(): Result {
|
override fun realWork(): List<RealmObject> {
|
||||||
val temp = arrayListOf<RssData>()
|
temp.clear()
|
||||||
RssDataType.REDDIT.isOn { for (url in feedJsons) {
|
RssDataType.REDDIT.isOn { for (url in feedJsons) {
|
||||||
for (it in RssFeedsParser.getReddit(url,false)) {
|
for (it in RssFeedsParser.getReddit(url,false)) {
|
||||||
if (it.pubDate() >= limitDateTime) {
|
if (it.pubDate() >= limitDateTime) {
|
||||||
@ -37,8 +36,6 @@ class RedditGetter : BaseGetter {
|
|||||||
}
|
}
|
||||||
} }
|
} }
|
||||||
|
|
||||||
return Result.success().apply {
|
return temp
|
||||||
WorkersDb.insertBulkData(temp)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -8,14 +8,13 @@ import bums.lunatic.launcher.model.RssDataType
|
|||||||
import bums.lunatic.launcher.model.RuliWeb
|
import bums.lunatic.launcher.model.RuliWeb
|
||||||
import bums.lunatic.launcher.model.getRssData
|
import bums.lunatic.launcher.model.getRssData
|
||||||
import bums.lunatic.launcher.utils.Blog
|
import bums.lunatic.launcher.utils.Blog
|
||||||
|
import io.realm.kotlin.types.RealmObject
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
|
|
||||||
class RuliWebGetter : BaseGetter {
|
class RuliWebGetter(context: Context) : BaseGetter(context) {
|
||||||
companion object {
|
companion object {
|
||||||
val TAG = "RuliWebGetter"
|
val TAG = "RuliWebGetter"
|
||||||
}
|
}
|
||||||
constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
|
|
||||||
}
|
|
||||||
|
|
||||||
fun parseRuli(ruli_tr : org.jsoup.nodes.Element) {
|
fun parseRuli(ruli_tr : org.jsoup.nodes.Element) {
|
||||||
Blog.LOGE("ruli_tr >>> ${ruli_tr.text()}")
|
Blog.LOGE("ruli_tr >>> ${ruli_tr.text()}")
|
||||||
@ -61,7 +60,8 @@ class RuliWebGetter : BaseGetter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
override fun realWork(): Result {
|
override fun realWork(): List<RealmObject> {
|
||||||
|
temp.clear()
|
||||||
RssDataType.RULIWEB.isOn {
|
RssDataType.RULIWEB.isOn {
|
||||||
try {
|
try {
|
||||||
Blog.LOGE("realWork() ${this::class.simpleName}")
|
Blog.LOGE("realWork() ${this::class.simpleName}")
|
||||||
@ -82,9 +82,6 @@ class RuliWebGetter : BaseGetter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e:Exception){e.printStackTrace()}}
|
} catch (e:Exception){e.printStackTrace()}}
|
||||||
return Result.success().apply {
|
return temp
|
||||||
// BLog.LOGE("Ruli temp >>>> ${temp.size}")
|
|
||||||
WorkersDb.insertBulkData(temp)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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<AppInfo> = appsJob.await().filterIsInstance<AppInfo>()
|
||||||
|
val scannedContacts: List<SimpleContact> = contactsJob.await().filterIsInstance<SimpleContact>()
|
||||||
|
|
||||||
|
|
||||||
|
val realm = WorkersDb.getRealm()
|
||||||
|
|
||||||
|
// 2. [일괄 저장 및 병합] 트랜잭션 시작
|
||||||
|
realm.write {
|
||||||
|
|
||||||
|
// --- [A] 앱 정보 병합 (AppInfo) ---
|
||||||
|
// DB에 있는 기존 앱들을 패키지명(Key) 기준으로 Map 생성
|
||||||
|
val existingAppsMap = query<AppInfo>(AppInfo::class).find().associateBy { it.pkgName }
|
||||||
|
val activeAppPkgNames = HashSet<String>()
|
||||||
|
|
||||||
|
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>(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>(SimpleContact::class).find().associateBy { it.id }
|
||||||
|
val activeContactIds = HashSet<String>()
|
||||||
|
|
||||||
|
scannedContacts.forEach { newContact ->
|
||||||
|
// SimpleContact 타입 캐스팅 (fetchData 반환형이 List<RealmObject>이므로)
|
||||||
|
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>(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}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,141 +1,141 @@
|
|||||||
package bums.lunatic.launcher.workers
|
//package bums.lunatic.launcher.workers
|
||||||
|
//
|
||||||
import android.annotation.SuppressLint
|
//import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
//import android.content.Context
|
||||||
import androidx.work.WorkerParameters
|
//import androidx.work.WorkerParameters
|
||||||
import com.google.android.gms.location.FusedLocationProviderClient
|
//import com.google.android.gms.location.FusedLocationProviderClient
|
||||||
|
//
|
||||||
|
//
|
||||||
class TelegramBotGetter : BaseGetter {
|
//class TelegramBotGetter : BaseGetter {
|
||||||
companion object {
|
// companion object {
|
||||||
val TAG = "TelegramBotGetter"
|
// val TAG = "TelegramBotGetter"
|
||||||
}
|
// }
|
||||||
constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
|
// constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
@SuppressLint("RestrictedApi")
|
// @SuppressLint("RestrictedApi")
|
||||||
override fun realWork(): Result {
|
// override fun realWork(): Result {
|
||||||
|
//
|
||||||
try {
|
|
||||||
|
|
||||||
// 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()
|
|
||||||
//
|
//
|
||||||
// // GET 요청 객체 생성
|
//// try {
|
||||||
// val builder: Request.Builder = Request.Builder().url(url).addHeader("Content-Type", "application/json").get()
|
//// val url = "https://api.telegram.org/bot7934509464:AAE_xUbICxMdywLGnxo7BkeIqA1nVza4P9w/getUpdates"
|
||||||
//
|
//// //"https://api.telegram.org/bot7934509464:AAE_xUbICxMdywLGnxo7BkeIqA1nVza4P9w/sendMessage?chat_id=71476436&text=안녕하세요."
|
||||||
// val request: Request = builder.build()
|
//// //7068729507
|
||||||
//
|
//// // OkHttp 클라이언트 객체 생성
|
||||||
// BLog.LOGE("telegram before request ")
|
//// val client = OkHttpClient.Builder().connectionPool(ConnectionPool(5,60,TimeUnit.SECONDS)).build()
|
||||||
// // OkHttp 클라이언트로 GET 요청 객체 전송
|
////
|
||||||
// val response: Response = client.newCall(request).execute()
|
//// // GET 요청 객체 생성
|
||||||
// if (response.isSuccessful()) {
|
//// val builder: Request.Builder = Request.Builder().url(url).addHeader("Content-Type", "application/json").get()
|
||||||
// // 응답 받아서 처리
|
////
|
||||||
// val body: ResponseBody? = response.body()
|
//// val request: Request = builder.build()
|
||||||
// if (body != null) {
|
////
|
||||||
// val bodyString = body.string()
|
//// BLog.LOGE("telegram before request ")
|
||||||
// BLog.LOGE("bodyString >>>>\n${bodyString}")
|
//// // OkHttp 클라이언트로 GET 요청 객체 전송
|
||||||
// Gson().fromJson<TelegramBotUpdate>(bodyString,TelegramBotUpdate::class.java)?.let { telegramUpdates ->
|
//// val response: Response = client.newCall(request).execute()
|
||||||
// telegramUpdates.fill()
|
//// if (response.isSuccessful()) {
|
||||||
// telegramUpdates.list.forEach {
|
//// // 응답 받아서 처리
|
||||||
//// if (it.message?.text?.startsWith("/") == true) {
|
//// val body: ResponseBody? = response.body()
|
||||||
// if((it.message?.text?.contains("where") == true) || (it.message?.text?.contains("어디") == true)) {
|
//// if (body != null) {
|
||||||
// BLog.LOGE("it.message?.text?.contains(\"where\") == true) >>> ${it.message?.text?.contains("where") == true}")
|
//// val bodyString = body.string()
|
||||||
// BLog.LOGE("it.message?.text?.contains(\"어디\") == true) >>> ${it.message?.text?.contains("어디") == true}")
|
//// BLog.LOGE("bodyString >>>>\n${bodyString}")
|
||||||
// WorkersDb.getRealm().apply {
|
//// Gson().fromJson<TelegramBotUpdate>(bodyString,TelegramBotUpdate::class.java)?.let { telegramUpdates ->
|
||||||
// writeBlocking {
|
//// telegramUpdates.fill()
|
||||||
// if (query<TelegramData>("update_id == $0",it.update_id).find().size == 0) {
|
//// telegramUpdates.list.forEach {
|
||||||
// copyToRealm(it)
|
////// if (it.message?.text?.startsWith("/") == true) {
|
||||||
// getLastLocation(context = applicationContext)
|
//// if((it.message?.text?.contains("where") == true) || (it.message?.text?.contains("어디") == true)) {
|
||||||
// BLog.LOGE("telegram telegramUpdates >>>> ${Gson().toJson(it)}")
|
//// 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<TelegramData>("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 {
|
||||||
//
|
//
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
// var fusedLocationProviderClient: FusedLocationProviderClient? = null
|
||||||
|
// @SuppressLint("MissingPermission")
|
||||||
|
// private fun getLastLocation(context: Context) {
|
||||||
|
//// fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(context);
|
||||||
|
//// BLog.LOGE("Location getLastLocation")
|
||||||
|
//// fusedLocationProviderClient?.getLastLocation()?.addOnSuccessListener(object : OnSuccessListener<Location?> {
|
||||||
|
//// 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<Address>? = 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()
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// })
|
||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
// } 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<Location?> {
|
|
||||||
// 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<Address>? = 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()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -8,14 +8,14 @@ import bums.lunatic.launcher.model.TheQoo
|
|||||||
import bums.lunatic.launcher.model.getHref
|
import bums.lunatic.launcher.model.getHref
|
||||||
import bums.lunatic.launcher.model.getRssData
|
import bums.lunatic.launcher.model.getRssData
|
||||||
import bums.lunatic.launcher.model.getT
|
import bums.lunatic.launcher.model.getT
|
||||||
|
import io.realm.kotlin.types.RealmObject
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
|
|
||||||
class TheQooGetter : BaseGetter {
|
class TheQooGetter(context: Context) : BaseGetter(context) {
|
||||||
companion object {
|
companion object {
|
||||||
val TAG = "TheQooGetter"
|
val TAG = "TheQooGetter"
|
||||||
}
|
}
|
||||||
constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
|
|
||||||
}
|
|
||||||
|
|
||||||
fun parseTQoo(tq_tr : org.jsoup.nodes.Element) {
|
fun parseTQoo(tq_tr : org.jsoup.nodes.Element) {
|
||||||
// BLog.LOGE("tq_tr >>>> ${tq_tr}")
|
// BLog.LOGE("tq_tr >>>> ${tq_tr}")
|
||||||
@ -50,7 +50,7 @@ class TheQooGetter : BaseGetter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
override fun realWork(): Result {
|
override fun realWork(): List<RealmObject> {
|
||||||
RssDataType.THEQOO.isOn {
|
RssDataType.THEQOO.isOn {
|
||||||
try {
|
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")
|
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()}}
|
} catch (e:Exception){e.printStackTrace()}}
|
||||||
return Result.success().apply {
|
return temp
|
||||||
// BLog.LOGE("theqoo temp >>>> ${temp.size}")
|
|
||||||
WorkersDb.insertBulkData(temp)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,9 +1,9 @@
|
|||||||
package bums.lunatic.launcher.workers
|
package bums.lunatic.launcher.workers
|
||||||
|
|
||||||
import bums.lunatic.launcher.BuildConfig
|
import bums.lunatic.launcher.BuildConfig
|
||||||
import bums.lunatic.launcher.apps.SimpleContact
|
|
||||||
import bums.lunatic.launcher.common.letTrue
|
import bums.lunatic.launcher.common.letTrue
|
||||||
import bums.lunatic.launcher.model.AppInfo
|
import bums.lunatic.launcher.model.AppInfo
|
||||||
|
import bums.lunatic.launcher.model.AppUsageLog
|
||||||
import bums.lunatic.launcher.model.Astro
|
import bums.lunatic.launcher.model.Astro
|
||||||
import bums.lunatic.launcher.model.BotCommandEentitie
|
import bums.lunatic.launcher.model.BotCommandEentitie
|
||||||
import bums.lunatic.launcher.model.Condition
|
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.RssData
|
||||||
import bums.lunatic.launcher.model.RssDataInterface
|
import bums.lunatic.launcher.model.RssDataInterface
|
||||||
import bums.lunatic.launcher.model.RssDataType
|
import bums.lunatic.launcher.model.RssDataType
|
||||||
|
import bums.lunatic.launcher.model.SimpleContact
|
||||||
import bums.lunatic.launcher.model.TelegramBotUpdate
|
import bums.lunatic.launcher.model.TelegramBotUpdate
|
||||||
import bums.lunatic.launcher.model.TelegramChat
|
import bums.lunatic.launcher.model.TelegramChat
|
||||||
import bums.lunatic.launcher.model.TelegramData
|
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.query.Sort
|
||||||
import io.realm.kotlin.types.BaseRealmObject
|
import io.realm.kotlin.types.BaseRealmObject
|
||||||
import io.realm.kotlin.types.TypedRealmObject
|
import io.realm.kotlin.types.TypedRealmObject
|
||||||
|
import java.util.Calendar
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
@ -57,16 +59,90 @@ class CustMigration : AutomaticSchemaMigration {
|
|||||||
}
|
}
|
||||||
object WorkersDb {
|
object WorkersDb {
|
||||||
|
|
||||||
|
//RecentCall::class, RecentSms::class,
|
||||||
val clazz : Set<KClass<out BaseRealmObject>> = setOf(
|
val clazz : Set<KClass<out BaseRealmObject>> = 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,
|
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,
|
WeatherForcast::class, Location::class, Current::class, Forecast::class, Condition::class, Forecastday::class, Day::class, Astro::class, Hour::class,
|
||||||
LocationLog::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
|
//,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<String> {
|
||||||
|
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<AppUsageLog>("timestamp > $0", threeMonthsAgo).find()
|
||||||
|
|
||||||
|
// 점수 계산
|
||||||
|
val scores = HashMap<String, Double>()
|
||||||
|
|
||||||
|
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
|
val schemaVersion : Long = BuildConfig.BuildDateTime
|
||||||
|
|
||||||
private var pRealm : Realm? = null
|
private var pRealm : Realm? = null
|
||||||
|
|||||||
@ -10,21 +10,20 @@ import bums.lunatic.launcher.model.getRssData
|
|||||||
import bums.lunatic.launcher.model.others.Youtube
|
import bums.lunatic.launcher.model.others.Youtube
|
||||||
import bums.lunatic.launcher.utils.RssList
|
import bums.lunatic.launcher.utils.RssList
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
|
import io.realm.kotlin.types.RealmObject
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import org.jsoup.nodes.Document
|
import org.jsoup.nodes.Document
|
||||||
|
|
||||||
class YoutubeGetter : BaseGetter {
|
class YoutubeGetter(context: Context) : BaseGetter(context) {
|
||||||
companion object {
|
companion object {
|
||||||
val YT_WORK_TAG = "YoutubeGetter"
|
val YT_WORK_TAG = "YoutubeGetter"
|
||||||
}
|
}
|
||||||
var rssUrls = arrayListOf<String>()
|
var rssUrls = arrayListOf<String>()
|
||||||
constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
override fun realWork(): Result {
|
override fun realWork(): List<RealmObject> {
|
||||||
rssUrls.clear()
|
rssUrls.clear()
|
||||||
rssUrls.addAll(RssList.youtubeUrls)
|
rssUrls.addAll(RssList.youtubeUrls)
|
||||||
RssDataType.YOUTUBE.isOn {
|
RssDataType.YOUTUBE.isOn {
|
||||||
@ -32,9 +31,7 @@ class YoutubeGetter : BaseGetter {
|
|||||||
temp.addAll(ytChannel(Jsoup.connect(url).userAgent(USAGT).get()))
|
temp.addAll(ytChannel(Jsoup.connect(url).userAgent(USAGT).get()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Result.success().apply {
|
return temp
|
||||||
WorkersDb.insertBulkData(temp)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ytChannel(doc: Document) : ArrayList<RssData> {
|
fun ytChannel(doc: Document) : ArrayList<RssData> {
|
||||||
|
|||||||
@ -22,6 +22,7 @@
|
|||||||
android:id="@+id/childTextview"
|
android:id="@+id/childTextview"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
android:layout_marginLeft="5dp"
|
android:layout_marginLeft="5dp"
|
||||||
|
android:textColor="@color/white"
|
||||||
app:layout_constraintLeft_toRightOf="@id/appIconTwo"
|
app:layout_constraintLeft_toRightOf="@id/appIconTwo"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
android:lines="2"
|
android:lines="2"
|
||||||
|
|||||||
34
app/src/main/res/layout/apps_child_rec.xml
Normal file
34
app/src/main/res/layout/apps_child_rec.xml
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="150dp"
|
||||||
|
android:layout_height="45dp"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:background="@drawable/apps_bg"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
|
android:id="@+id/appIconTwo"
|
||||||
|
android:visibility="visible"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
android:layout_width="@dimen/forty"
|
||||||
|
android:layout_height="@dimen/forty"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
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"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_marginBottom="@dimen/four"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
180
app/src/main/res/layout/bottom_sheet_app_drawer.xml
Normal file
180
app/src/main/res/layout/bottom_sheet_app_drawer.xml
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@drawable/rounded_bg_top"
|
||||||
|
android:paddingTop="10dp">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/drag_handle"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="4dp"
|
||||||
|
android:background="#E0E0E0"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/searchInput"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:layout_margin="@dimen/default_layout_margin"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:background="@drawable/base_bg"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:hint="앱 검색"
|
||||||
|
android:imeOptions="actionSearch"
|
||||||
|
android:singleLine="true"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/drag_handle"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"/>
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageButton
|
||||||
|
android:id="@+id/reset"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/rounded_bg"
|
||||||
|
android:padding="@dimen/eight"
|
||||||
|
android:tint="@color/white"
|
||||||
|
android:layout_marginEnd="5dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/searchInput"
|
||||||
|
app:layout_constraintRight_toRightOf="@id/searchInput"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/searchInput"
|
||||||
|
app:srcCompat="@drawable/ic_refresh" />
|
||||||
|
|
||||||
|
<androidx.core.widget.NestedScrollView
|
||||||
|
android:id="@+id/nestedScrollView"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:fillViewport="true"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/searchInput"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingBottom="20dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/appsCount"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingLeft="15dp"
|
||||||
|
android:paddingRight="15dp"
|
||||||
|
android:text="0 Apps"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"/>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title_recommend"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:paddingLeft="15dp"
|
||||||
|
android:paddingRight="15dp"
|
||||||
|
android:text="앱 목록"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"/>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recAppsList"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="100dp"
|
||||||
|
android:background="@android:color/transparent"
|
||||||
|
android:overScrollMode="never"
|
||||||
|
android:nestedScrollingEnabled="false"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/title_recommend"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title_apps"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:paddingLeft="15dp"
|
||||||
|
android:paddingRight="15dp"
|
||||||
|
android:text="앱 목록"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/recAppsList"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"/>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/appsList"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="100dp"
|
||||||
|
android:background="@android:color/transparent"
|
||||||
|
android:overScrollMode="never"
|
||||||
|
android:nestedScrollingEnabled="false"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/title_apps"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title_webs"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:paddingLeft="15dp"
|
||||||
|
android:paddingRight="15dp"
|
||||||
|
android:text="빠른 검색"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/appsList"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/quickSearch"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/title_webs">
|
||||||
|
|
||||||
|
<bums.lunatic.launcher.view.CircleImageView
|
||||||
|
android:id="@+id/search_nmap"
|
||||||
|
style="@style/SearchIcons"
|
||||||
|
android:src="@drawable/navermap"/>
|
||||||
|
<bums.lunatic.launcher.view.CircleImageView
|
||||||
|
android:id="@+id/search_google"
|
||||||
|
style="@style/SearchIcons"
|
||||||
|
android:src="@drawable/google"/>
|
||||||
|
<bums.lunatic.launcher.view.CircleImageView
|
||||||
|
android:id="@+id/search_naver"
|
||||||
|
style="@style/SearchIcons"
|
||||||
|
android:src="@drawable/naver"/>
|
||||||
|
<bums.lunatic.launcher.view.CircleImageView
|
||||||
|
android:id="@+id/search_youtube"
|
||||||
|
style="@style/SearchIcons"
|
||||||
|
android:src="@drawable/youtube"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title_contact"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:paddingLeft="15dp"
|
||||||
|
android:paddingRight="15dp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:text="연락처"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/quickSearch"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"/>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/contactList"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="90dp"
|
||||||
|
android:nestedScrollingEnabled="false"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/title_contact"/>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@ -10,6 +10,7 @@
|
|||||||
<com.google.android.material.textview.MaterialTextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
android:id="@+id/name"
|
android:id="@+id/name"
|
||||||
android:visibility="visible"
|
android:visibility="visible"
|
||||||
|
android:textColor="@color/white"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
@ -21,6 +22,7 @@
|
|||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
android:id="@+id/number"
|
android:id="@+id/number"
|
||||||
|
android:textColor="@color/white"
|
||||||
app:layout_constraintTop_toBottomOf="@id/name"
|
app:layout_constraintTop_toBottomOf="@id/name"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
|||||||
@ -21,9 +21,9 @@
|
|||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/delete_zone"
|
android:id="@+id/delete_zone"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="50dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="50dp"
|
||||||
android:text="삭제하려면 여기에 놓으세요"
|
android:text="삭제"
|
||||||
android:textColor="#FFFFFF"
|
android:textColor="#FFFFFF"
|
||||||
android:background="#99FF0000"
|
android:background="#99FF0000"
|
||||||
android:padding="20dp"
|
android:padding="20dp"
|
||||||
@ -32,7 +32,7 @@
|
|||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
android:layout_marginTop="50dp"/>
|
android:layout_marginTop="10dp"/>
|
||||||
|
|
||||||
<androidx.fragment.app.FragmentContainerView
|
<androidx.fragment.app.FragmentContainerView
|
||||||
android:id="@+id/fragment_container"
|
android:id="@+id/fragment_container"
|
||||||
@ -49,6 +49,7 @@
|
|||||||
app:layout_constraintTop_toBottomOf="@id/fragment_container"
|
app:layout_constraintTop_toBottomOf="@id/fragment_container"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
android:id="@+id/back"
|
android:id="@+id/back"
|
||||||
|
android:visibility="gone"
|
||||||
android:src="@drawable/back_vector"
|
android:src="@drawable/back_vector"
|
||||||
tools:ignore="ContentDescription"
|
tools:ignore="ContentDescription"
|
||||||
style="@style/CommonBottom" />
|
style="@style/CommonBottom" />
|
||||||
@ -57,6 +58,7 @@
|
|||||||
app:layout_constraintTop_toBottomOf="@id/fragment_container"
|
app:layout_constraintTop_toBottomOf="@id/fragment_container"
|
||||||
app:layout_constraintLeft_toRightOf="@id/back"
|
app:layout_constraintLeft_toRightOf="@id/back"
|
||||||
android:id="@+id/reload"
|
android:id="@+id/reload"
|
||||||
|
android:visibility="gone"
|
||||||
android:src="@drawable/ic_refresh"
|
android:src="@drawable/ic_refresh"
|
||||||
tools:ignore="ContentDescription"
|
tools:ignore="ContentDescription"
|
||||||
style="@style/CommonBottom"/>
|
style="@style/CommonBottom"/>
|
||||||
@ -68,6 +70,7 @@
|
|||||||
app:layout_constraintLeft_toRightOf="@id/reload"
|
app:layout_constraintLeft_toRightOf="@id/reload"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
|
android:visibility="gone"
|
||||||
android:textSize="@dimen/_12sp"
|
android:textSize="@dimen/_12sp"
|
||||||
android:ellipsize="middle"
|
android:ellipsize="middle"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
@ -79,6 +82,7 @@
|
|||||||
app:layout_constraintRight_toLeftOf="@id/share"
|
app:layout_constraintRight_toLeftOf="@id/share"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
android:id="@+id/dl_video"
|
android:id="@+id/dl_video"
|
||||||
|
android:visibility="gone"
|
||||||
android:src="@drawable/dl_vid"
|
android:src="@drawable/dl_vid"
|
||||||
tools:ignore="ContentDescription"
|
tools:ignore="ContentDescription"
|
||||||
style="@style/CommonBottom"/>
|
style="@style/CommonBottom"/>
|
||||||
@ -88,6 +92,7 @@
|
|||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
android:layout_marginRight="60dp"
|
android:layout_marginRight="60dp"
|
||||||
android:id="@+id/share"
|
android:id="@+id/share"
|
||||||
|
android:visibility="gone"
|
||||||
android:foregroundTint="@color/white"
|
android:foregroundTint="@color/white"
|
||||||
android:src="@drawable/ic_share"
|
android:src="@drawable/ic_share"
|
||||||
tools:ignore="ContentDescription"
|
tools:ignore="ContentDescription"
|
||||||
@ -169,5 +174,15 @@
|
|||||||
android:onClick="floatClick"
|
android:onClick="floatClick"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="20dp"/>
|
android:layout_height="20dp"/>
|
||||||
|
|
||||||
|
<bums.lunatic.launcher.view.FloatingActionButton
|
||||||
|
app:fab_label="close"
|
||||||
|
android:id="@+id/close"
|
||||||
|
app:fab_showShadow="true"
|
||||||
|
app:fab_size="mini"
|
||||||
|
android:onClick="floatClick"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="20dp"/>
|
||||||
|
|
||||||
</bums.lunatic.launcher.view.FloatingActionMenu>
|
</bums.lunatic.launcher.view.FloatingActionMenu>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|||||||
@ -21,4 +21,13 @@
|
|||||||
<item name="cornerFamily">rounded</item>
|
<item name="cornerFamily">rounded</item>
|
||||||
<item name="cornerSize">10dp</item>
|
<item name="cornerSize">10dp</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="CustomBottomSheetDialogTheme" parent="Theme.Design.Light.BottomSheetDialog">
|
||||||
|
<item name="bottomSheetStyle">@style/CustomBottomSheetStyle</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="CustomBottomSheetStyle" parent="Widget.Design.BottomSheet.Modal">
|
||||||
|
<item name="android:background">@android:color/transparent</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
Loading…
x
Reference in New Issue
Block a user