..
This commit is contained in:
parent
62f1e646ab
commit
98fd382ed4
@ -55,7 +55,9 @@
|
|||||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.PACKAGE_USAGE_STATS"
|
||||||
|
tools:ignore="ProtectedPermissions" />
|
||||||
|
|
||||||
<!-- <queries>-->
|
<!-- <queries>-->
|
||||||
<!-- <intent>-->
|
<!-- <intent>-->
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package bums.lunatic.launcher
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.app.AppOpsManager
|
||||||
import android.app.SearchManager
|
import android.app.SearchManager
|
||||||
import android.appwidget.AppWidgetHost
|
import android.appwidget.AppWidgetHost
|
||||||
import android.appwidget.AppWidgetHostView
|
import android.appwidget.AppWidgetHostView
|
||||||
@ -9,11 +10,13 @@ import android.appwidget.AppWidgetManager
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
|
import android.graphics.Color
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
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.provider.Settings
|
||||||
import android.view.GestureDetector
|
import android.view.GestureDetector
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import android.view.KeyEvent.ACTION_UP
|
import android.view.KeyEvent.ACTION_UP
|
||||||
@ -35,6 +38,7 @@ import androidx.annotation.RequiresApi
|
|||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
|
import androidx.core.view.WindowCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
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
|
||||||
@ -62,6 +66,7 @@ import bums.lunatic.launcher.tokiz.Webtoons
|
|||||||
import bums.lunatic.launcher.tokiz.YouTube
|
import bums.lunatic.launcher.tokiz.YouTube
|
||||||
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 bums.lunatic.launcher.workers.WorkersDb.syncSystemUsageStats
|
||||||
import com.google.android.material.color.DynamicColors
|
import com.google.android.material.color.DynamicColors
|
||||||
import com.yausername.ffmpeg.FFmpeg
|
import com.yausername.ffmpeg.FFmpeg
|
||||||
import com.yausername.youtubedl_android.YoutubeDL
|
import com.yausername.youtubedl_android.YoutubeDL
|
||||||
@ -126,8 +131,8 @@ open class LauncherActivity : CommonActivity() {
|
|||||||
// 2. 더블 클릭 감지 (빈 공간)
|
// 2. 더블 클릭 감지 (빈 공간)
|
||||||
override fun onDoubleTap(e: MotionEvent): Boolean {
|
override fun onDoubleTap(e: MotionEvent): Boolean {
|
||||||
// 더블 클릭 액션
|
// 더블 클릭 액션
|
||||||
showToast("더블 클릭: 설정 열기")
|
// showToast("더블 클릭: 설정 열기")
|
||||||
// 예: startActivity(Intent(this@LauncherActivity, SettingsActivity::class.java))
|
openApp("org.telegram.messenger")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +147,7 @@ open class LauncherActivity : CommonActivity() {
|
|||||||
override fun onLongPress(e: MotionEvent) {
|
override fun onLongPress(e: MotionEvent) {
|
||||||
// 위젯 추가 메뉴 등을 띄우려면 여기서 처리
|
// 위젯 추가 메뉴 등을 띄우려면 여기서 처리
|
||||||
// 주의: 위젯 위에서 롱프레스하면 위젯 드래그가 먼저 작동하도록 설계해야 함
|
// 주의: 위젯 위에서 롱프레스하면 위젯 드래그가 먼저 작동하도록 설계해야 함
|
||||||
showToast("바탕화면 롱프레스: 위젯 추가")
|
// showToast("바탕화면 롱프레스: 위젯 추가")
|
||||||
selectWidget() // 기존에 만든 위젯 추가 함수 호출
|
selectWidget() // 기존에 만든 위젯 추가 함수 호출
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,13 +161,27 @@ open class LauncherActivity : CommonActivity() {
|
|||||||
fun showToast(msg: String) {
|
fun showToast(msg: String) {
|
||||||
android.widget.Toast.makeText(this, msg, android.widget.Toast.LENGTH_SHORT).show()
|
android.widget.Toast.makeText(this, msg, android.widget.Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
|
fun openApp(packageName: String) {
|
||||||
|
val intent = packageManager.getLaunchIntentForPackage(packageName)
|
||||||
|
|
||||||
|
if (intent != null) {
|
||||||
|
// 앱이 설치되어 있으면 실행
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
startActivity(intent)
|
||||||
|
} else {
|
||||||
|
// 앱이 없으면 플레이스토어로 이동
|
||||||
|
val playStoreIntent = Intent(Intent.ACTION_VIEW).apply {
|
||||||
|
data = Uri.parse("market://details?id=$packageName")
|
||||||
|
}
|
||||||
|
startActivity(playStoreIntent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 각 스와이프 동작 정의
|
// 각 스와이프 동작 정의
|
||||||
fun onSwipeUp() {
|
fun onSwipeUp() {
|
||||||
showAppDrawer() // 지난번에 만든 바텀시트 앱서랍 열기
|
openApp("com.google.android.apps.bard")
|
||||||
}
|
}
|
||||||
fun onSwipeDown() {
|
fun onSwipeDown() {
|
||||||
// 알림창 내리기 등
|
|
||||||
try {
|
try {
|
||||||
val service = getSystemService("statusbar")
|
val service = getSystemService("statusbar")
|
||||||
val statusbarManager = Class.forName("android.app.StatusBarManager")
|
val statusbarManager = Class.forName("android.app.StatusBarManager")
|
||||||
@ -170,8 +189,14 @@ open class LauncherActivity : CommonActivity() {
|
|||||||
expand.invoke(service)
|
expand.invoke(service)
|
||||||
} catch (e: Exception) { e.printStackTrace() }
|
} catch (e: Exception) { e.printStackTrace() }
|
||||||
}
|
}
|
||||||
fun onSwipeLeft() { /* 페이지 이동 등 */ }
|
|
||||||
fun onSwipeRight() { /* 페이지 이동 등 */ }
|
fun onSwipeLeft() {
|
||||||
|
showAppDrawer()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onSwipeRight() {
|
||||||
|
showContents(R.id.feeds)
|
||||||
|
}
|
||||||
|
|
||||||
private lateinit var binding: LauncherActivityBinding
|
private lateinit var binding: LauncherActivityBinding
|
||||||
|
|
||||||
@ -543,7 +568,14 @@ open class LauncherActivity : CommonActivity() {
|
|||||||
|
|
||||||
val intent = Intent(this, ForeGroundService::class.java)
|
val intent = Intent(this, ForeGroundService::class.java)
|
||||||
this.startForegroundService(intent)
|
this.startForegroundService(intent)
|
||||||
|
// 1. 시스템 바 공간을 앱이 차지하도록 설정 (상태바 뒤로 레이아웃 확장)
|
||||||
|
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||||
|
|
||||||
|
// 2. 상태바 색상을 투명하게 변경 (필요한 경우)
|
||||||
|
window.statusBarColor = Color.TRANSPARENT
|
||||||
|
|
||||||
|
// (선택 사항) 내비게이션 바도 투명하게 하고 싶다면
|
||||||
|
window.navigationBarColor = Color.TRANSPARENT
|
||||||
val nlService = Intent(this, NLService::class.java)
|
val nlService = Intent(this, NLService::class.java)
|
||||||
this.startService(nlService)
|
this.startService(nlService)
|
||||||
|
|
||||||
@ -783,11 +815,11 @@ open class LauncherActivity : CommonActivity() {
|
|||||||
// 손가락이 하나일 때만 이동 허용
|
// 손가락이 하나일 때만 이동 허용
|
||||||
// [추가] 삭제 영역과 겹치는지 확인하여 시각적 피드백 (예: 빨갛게 변함)
|
// [추가] 삭제 영역과 겹치는지 확인하여 시각적 피드백 (예: 빨갛게 변함)
|
||||||
if (isViewOverlapping(currentDragView!!, binding.deleteZone)) {
|
if (isViewOverlapping(currentDragView!!, binding.deleteZone)) {
|
||||||
binding.deleteZone.setBackgroundColor(android.graphics.Color.RED)
|
binding.deleteZone.setBackgroundResource(R.drawable.bg_circle_emoji_red)
|
||||||
binding.deleteZone.text = "손을 떼면 삭제됩니다"
|
// binding.deleteZone.text = "\uD83D\uDDD1\uFE0F"
|
||||||
} else {
|
} else {
|
||||||
binding.deleteZone.setBackgroundColor(0x99FF0000.toInt()) // 반투명 빨강 (원래 색)
|
binding.deleteZone.setBackgroundResource(R.drawable.bg_circle_emoji)
|
||||||
binding.deleteZone.text = "삭제하려면 여기에 놓으세요"
|
// binding.deleteZone.text = "\uD83D\uDDD1\uFE0F"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ev.pointerCount == 1 && lastTouchX != 0f && lastTouchY != 0f) {
|
if (ev.pointerCount == 1 && lastTouchX != 0f && lastTouchY != 0f) {
|
||||||
@ -866,7 +898,10 @@ open class LauncherActivity : CommonActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun showContents(id : Int) {
|
fun showContents(id : Int) {
|
||||||
|
binding.fragmentLayer.visibility = View.VISIBLE
|
||||||
binding.fragmentContainer.visibility = View.VISIBLE
|
binding.fragmentContainer.visibility = View.VISIBLE
|
||||||
|
binding.controllPanel.visibility = View.VISIBLE
|
||||||
|
binding.floatingActionMenu.visibility = View.VISIBLE
|
||||||
when(id) {
|
when(id) {
|
||||||
R.id.feeds -> {
|
R.id.feeds -> {
|
||||||
supportFragmentManager.beginTransaction()
|
supportFragmentManager.beginTransaction()
|
||||||
@ -912,7 +947,10 @@ open class LauncherActivity : CommonActivity() {
|
|||||||
supportFragmentManager.beginTransaction()
|
supportFragmentManager.beginTransaction()
|
||||||
.remove(it)
|
.remove(it)
|
||||||
.commit()
|
.commit()
|
||||||
|
binding.fragmentLayer.visibility = View.GONE
|
||||||
binding.fragmentContainer.visibility = View.GONE
|
binding.fragmentContainer.visibility = View.GONE
|
||||||
|
binding.controllPanel.visibility = View.GONE
|
||||||
|
binding.floatingActionMenu.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -962,10 +1000,31 @@ open class LauncherActivity : CommonActivity() {
|
|||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun checkUsageStatsPermission(): Boolean {
|
||||||
|
val appOps = getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
|
||||||
|
val mode = appOps.checkOpNoThrow(
|
||||||
|
AppOpsManager.OPSTR_GET_USAGE_STATS,
|
||||||
|
android.os.Process.myUid(),
|
||||||
|
packageName
|
||||||
|
)
|
||||||
|
return mode == AppOpsManager.MODE_ALLOWED
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun requestPermission() {
|
||||||
|
if (!checkUsageStatsPermission()) {
|
||||||
|
startActivity(Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.O_MR1)
|
@RequiresApi(Build.VERSION_CODES.O_MR1)
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
Blog.LOGE("LauncherActivity onResume")
|
Blog.LOGE("LauncherActivity onResume")
|
||||||
|
if (checkUsageStatsPermission()) {
|
||||||
|
WorkersDb.syncSystemUsageStats(applicationContext)
|
||||||
|
} else {
|
||||||
|
requestPermission()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun openSearch() {
|
private fun openSearch() {
|
||||||
@ -988,6 +1047,7 @@ open class LauncherActivity : CommonActivity() {
|
|||||||
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
|
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
|
||||||
override fun handleOnBackPressed() {
|
override fun handleOnBackPressed() {
|
||||||
val currentFragment = supportFragmentManager.findFragmentById(R.id.fragment_container)
|
val currentFragment = supportFragmentManager.findFragmentById(R.id.fragment_container)
|
||||||
|
if (currentFragment == null) showContents(R.id.close)
|
||||||
when(currentFragment) {
|
when(currentFragment) {
|
||||||
is RssHome ->{
|
is RssHome ->{
|
||||||
if (currentFragment.binding.layoutRssSummary.root.isVisible) {
|
if (currentFragment.binding.layoutRssSummary.root.isVisible) {
|
||||||
@ -1002,6 +1062,9 @@ open class LauncherActivity : CommonActivity() {
|
|||||||
is Novels -> {
|
is Novels -> {
|
||||||
currentFragment.actionNextEvent(false)
|
currentFragment.actionNextEvent(false)
|
||||||
}
|
}
|
||||||
|
else -> {
|
||||||
|
showContents(R.id.close)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -48,6 +48,8 @@ import bums.lunatic.launcher.model.AppInfo
|
|||||||
import bums.lunatic.launcher.model.SimpleContact
|
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.UsageLogType
|
||||||
|
import bums.lunatic.launcher.workers.UsageUpdateType
|
||||||
import bums.lunatic.launcher.workers.WorkersDb
|
import bums.lunatic.launcher.workers.WorkersDb
|
||||||
import io.realm.kotlin.ext.query
|
import io.realm.kotlin.ext.query
|
||||||
import io.realm.kotlin.query.RealmResults
|
import io.realm.kotlin.query.RealmResults
|
||||||
@ -99,7 +101,7 @@ class AppDrawer : CommonActivity() {
|
|||||||
layoutType = settingsPrefs!!.getInt(KEY_APPS_LAYOUT, 0)
|
layoutType = settingsPrefs!!.getInt(KEY_APPS_LAYOUT, 0)
|
||||||
|
|
||||||
appsAdapter = AppsAdapter(packageManager!!, supportFragmentManager, binding.appsCount)
|
appsAdapter = AppsAdapter(packageManager!!, supportFragmentManager, binding.appsCount)
|
||||||
contactAdapter = ContactAdapter(packageManager!!, supportFragmentManager)
|
contactAdapter = ContactAdapter(applicationContext, supportFragmentManager)
|
||||||
|
|
||||||
binding.appsCount.visibility = if (settingsPrefs!!.getBoolean(KEY_APPS_COUNT, true)) VISIBLE else GONE
|
binding.appsCount.visibility = if (settingsPrefs!!.getBoolean(KEY_APPS_COUNT, true)) VISIBLE else GONE
|
||||||
binding.searchNmap.setOnClickListener {
|
binding.searchNmap.setOnClickListener {
|
||||||
@ -244,7 +246,7 @@ class AppDrawer : CommonActivity() {
|
|||||||
val mapIntent = Intent(Intent.ACTION_VIEW, gmmIntentUri)
|
val mapIntent = Intent(Intent.ACTION_VIEW, gmmIntentUri)
|
||||||
pakage?.let {
|
pakage?.let {
|
||||||
mapIntent.setPackage(pakage)
|
mapIntent.setPackage(pakage)
|
||||||
WorkersDb.updateAppUse(pakage)
|
WorkersDb.logAppUsage(pakage, UsageLogType.APP, UsageUpdateType.DATETIME)
|
||||||
}
|
}
|
||||||
startActivity(mapIntent)
|
startActivity(mapIntent)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package bums.lunatic.launcher.apps
|
package bums.lunatic.launcher.apps
|
||||||
|
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
|
import android.app.SearchManager
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@ -21,6 +22,8 @@ import bums.lunatic.launcher.model.AppInfo
|
|||||||
import bums.lunatic.launcher.model.SimpleContact
|
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.UsageLogType
|
||||||
|
import bums.lunatic.launcher.workers.UsageUpdateType
|
||||||
import bums.lunatic.launcher.workers.WorkersDb
|
import bums.lunatic.launcher.workers.WorkersDb
|
||||||
import com.google.android.gms.common.wrappers.PackageManagerWrapper
|
import com.google.android.gms.common.wrappers.PackageManagerWrapper
|
||||||
import com.google.android.gms.common.wrappers.Wrappers.packageManager
|
import com.google.android.gms.common.wrappers.Wrappers.packageManager
|
||||||
@ -110,12 +113,12 @@ class AppDrawerBottomSheet : BottomSheetDialogFragment() {
|
|||||||
bottomSheet.layoutParams = layoutParams
|
bottomSheet.layoutParams = layoutParams
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private var recAdapter: AppsAdapter? = null
|
private var recAdapter: RecommendedAppsAdapter? = null
|
||||||
private fun setupAdapters() {
|
private fun setupAdapters() {
|
||||||
// 기존 Activity의 packageManager 대신 requireContext().packageManager 사용
|
// 기존 Activity의 packageManager 대신 requireContext().packageManager 사용
|
||||||
val pm = requireContext().packageManager
|
val pm = requireContext().packageManager
|
||||||
appsAdapter = AppsAdapter(pm, childFragmentManager, binding.appsCount)
|
appsAdapter = AppsAdapter(pm, childFragmentManager, binding.appsCount)
|
||||||
contactAdapter = ContactAdapter(pm, childFragmentManager)
|
contactAdapter = ContactAdapter(requireContext(), childFragmentManager)
|
||||||
|
|
||||||
// 가로 그리드 개수 4~5개 정도로 조정 (기존 2개였으면 그대로 유지)
|
// 가로 그리드 개수 4~5개 정도로 조정 (기존 2개였으면 그대로 유지)
|
||||||
binding.appsList.layoutManager = GridLayoutManager(context, 3,GridLayoutManager.HORIZONTAL,false)
|
binding.appsList.layoutManager = GridLayoutManager(context, 3,GridLayoutManager.HORIZONTAL,false)
|
||||||
@ -126,7 +129,7 @@ class AppDrawerBottomSheet : BottomSheetDialogFragment() {
|
|||||||
binding.contactList.layoutManager = GridLayoutManager(context, 3,GridLayoutManager.HORIZONTAL,false)
|
binding.contactList.layoutManager = GridLayoutManager(context, 3,GridLayoutManager.HORIZONTAL,false)
|
||||||
binding.contactList.adapter = contactAdapter
|
binding.contactList.adapter = contactAdapter
|
||||||
|
|
||||||
recAdapter = AppsAdapter(pm, childFragmentManager,null)
|
recAdapter = RecommendedAppsAdapter(requireContext(),pm,childFragmentManager)
|
||||||
binding.recAppsList.layoutManager = GridLayoutManager(context, 1,GridLayoutManager.HORIZONTAL,false)
|
binding.recAppsList.layoutManager = GridLayoutManager(context, 1,GridLayoutManager.HORIZONTAL,false)
|
||||||
binding.recAppsList.adapter = recAdapter
|
binding.recAppsList.adapter = recAdapter
|
||||||
}
|
}
|
||||||
@ -163,7 +166,12 @@ class AppDrawerBottomSheet : BottomSheetDialogFragment() {
|
|||||||
openSearchApps("https://www.youtube.com/results?search_query=${getInputText()}", "com.google.android.youtube")
|
openSearchApps("https://www.youtube.com/results?search_query=${getInputText()}", "com.google.android.youtube")
|
||||||
}
|
}
|
||||||
binding.searchGoogle.setOnClickListener {
|
binding.searchGoogle.setOnClickListener {
|
||||||
openSearchApps("https://www.google.com/search?q=${getInputText()}", "com.android.chrome")
|
val intent = Intent(Intent.ACTION_WEB_SEARCH).apply {
|
||||||
|
putExtra(SearchManager.QUERY, getInputText()) // 질문 전달
|
||||||
|
}
|
||||||
|
if (intent.resolveActivity(requireContext().packageManager) != null) {
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
binding.searchNaver.setOnClickListener {
|
binding.searchNaver.setOnClickListener {
|
||||||
openSearchApps("https://search.naver.com/search.naver?where=nexearch&query=${getInputText()}", "com.nhn.android.search")
|
openSearchApps("https://search.naver.com/search.naver?where=nexearch&query=${getInputText()}", "com.nhn.android.search")
|
||||||
@ -178,7 +186,7 @@ class AppDrawerBottomSheet : BottomSheetDialogFragment() {
|
|||||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(schemeString))
|
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(schemeString))
|
||||||
packageName?.let {
|
packageName?.let {
|
||||||
intent.setPackage(it)
|
intent.setPackage(it)
|
||||||
WorkersDb.updateAppUse(it)
|
WorkersDb.logAppUsage(packageName, UsageLogType.APP, UsageUpdateType.DATETIME)
|
||||||
}
|
}
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
dismiss() // 실행 후 닫기
|
dismiss() // 실행 후 닫기
|
||||||
@ -211,13 +219,24 @@ class AppDrawerBottomSheet : BottomSheetDialogFragment() {
|
|||||||
val pm = requireContext().packageManager
|
val pm = requireContext().packageManager
|
||||||
|
|
||||||
// 1. [추천 로직] 에러가 나도 앱 목록 로딩은 진행되도록 try-catch 분리
|
// 1. [추천 로직] 에러가 나도 앱 목록 로딩은 진행되도록 try-catch 분리
|
||||||
val recommendedPkgNames = try {
|
val scoredItems = WorkersDb.getContextualRecommendations(limit = 8)
|
||||||
if (keyword.isNullOrEmpty()) {
|
|
||||||
WorkersDb.getContextualRecommendations(limit = 5)
|
val unifiedList = mutableListOf<RecommendationItem>()
|
||||||
} else {
|
|
||||||
emptyList() // 검색 중에는 추천 안 함
|
for (item in scoredItems) {
|
||||||
|
if (item.type == "APP") {
|
||||||
|
val app = realm.query<AppInfo>("pkgName == $0", item.key).first().find()
|
||||||
|
if (app != null && !app.blockRecommend) {
|
||||||
|
unifiedList.add(RecommendationItem.AppItem(realm.copyFromRealm(app)))
|
||||||
|
}
|
||||||
|
} else if (item.type == "CONTACT") {
|
||||||
|
// 연락처 ID나 전화번호로 조회 (Log 저장 시 key가 무엇인지에 따라 다름)
|
||||||
|
val contact = realm.query<SimpleContact>("id == $0", item.key).first().find()
|
||||||
|
if (contact != null) {
|
||||||
|
unifiedList.add(RecommendationItem.ContactItem(realm.copyFromRealm(contact)))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) { emptyList() }
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 2. [쿼리 구성]
|
// 2. [쿼리 구성]
|
||||||
@ -254,12 +273,9 @@ class AppDrawerBottomSheet : BottomSheetDialogFragment() {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// C. [추천 앱 객체 추출]
|
|
||||||
val recAppList = recommendedPkgNames.mapNotNull { pkg ->
|
|
||||||
allApps.find { it.pkgName == pkg }
|
|
||||||
}
|
|
||||||
|
|
||||||
val mainAppList = allApps
|
|
||||||
|
val mainAppList = allApps.filter { it.visibilityMode == 0}
|
||||||
|
|
||||||
var contactQuery = realm.query<SimpleContact>()
|
var contactQuery = realm.query<SimpleContact>()
|
||||||
|
|
||||||
@ -282,16 +298,15 @@ class AppDrawerBottomSheet : BottomSheetDialogFragment() {
|
|||||||
val contactsResult = contactQuery.find()
|
val contactsResult = contactQuery.find()
|
||||||
val contactsList = contactsResult.map { realm.copyFromRealm(it) }
|
val contactsList = contactsResult.map { realm.copyFromRealm(it) }
|
||||||
|
|
||||||
|
Blog.LOGE("unifiedList >>> ${unifiedList.size}")
|
||||||
|
|
||||||
// 6. [UI 업데이트]
|
// 6. [UI 업데이트]
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
if (recAppList.isNotEmpty() && keyword.isNullOrEmpty()) {
|
|
||||||
|
if (unifiedList.isNotEmpty()) {
|
||||||
binding.titleRecommend.visibility = View.VISIBLE
|
binding.titleRecommend.visibility = View.VISIBLE
|
||||||
binding.recAppsList.visibility = View.VISIBLE
|
binding.recAppsList.visibility = View.VISIBLE
|
||||||
recAdapter?.updateData(recAppList)
|
recAdapter?.submitList(unifiedList)
|
||||||
} else {
|
|
||||||
// 검색 중이거나 데이터 없으면 숨김
|
|
||||||
binding.titleRecommend.visibility = View.GONE
|
|
||||||
binding.recAppsList.visibility = View.GONE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 전체 앱 리스트 업데이트
|
// 2. 전체 앱 리스트 업데이트
|
||||||
|
|||||||
@ -54,6 +54,8 @@ import bums.lunatic.launcher.helpers.UniUtils.Companion.screenHeight
|
|||||||
import bums.lunatic.launcher.helpers.UniUtils.Companion.screenWidth
|
import bums.lunatic.launcher.helpers.UniUtils.Companion.screenWidth
|
||||||
import bums.lunatic.launcher.model.AppInfo
|
import bums.lunatic.launcher.model.AppInfo
|
||||||
import bums.lunatic.launcher.utils.JamoUtils
|
import bums.lunatic.launcher.utils.JamoUtils
|
||||||
|
import bums.lunatic.launcher.workers.UsageLogType
|
||||||
|
import bums.lunatic.launcher.workers.UsageUpdateType
|
||||||
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
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
@ -126,29 +128,20 @@ internal class AppMenu : BottomSheetDialogFragment() {
|
|||||||
"최종 실행 일시 : ".plus(SimpleDateFormat("yyyy-MM-dd HH:mm").format(Date(app.lastUseDate)))
|
"최종 실행 일시 : ".plus(SimpleDateFormat("yyyy-MM-dd HH:mm").format(Date(app.lastUseDate)))
|
||||||
app.currentInstalled = true
|
app.currentInstalled = true
|
||||||
binding.alterName.setText(app.koreanName)
|
binding.alterName.setText(app.koreanName)
|
||||||
// app.clickCount = app.clickCount + 15
|
binding.recommend.isChecked = app.blockRecommend
|
||||||
|
binding.listVisible.isChecked = app.visibilityMode == 1
|
||||||
// app.lastUseDate = Math.max(app.lastUseDate, System.currentTimeMillis())
|
|
||||||
// app.clickCount = app.clickCount + 15
|
|
||||||
// app.lastUseDate = Math.max(app.lastUseDate, System.currentTimeMillis())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun update() {
|
|
||||||
WorkersDb.getRealm().apply {
|
binding.totalTouch.setOnClickListener {
|
||||||
writeBlocking {
|
WorkersDb.logAppUsage(packageName, UsageLogType.APP,UsageUpdateType.COUNT)
|
||||||
var result = query<AppInfo>("pkgName == $0",packageName).find()
|
|
||||||
if(result.size > 0) {
|
|
||||||
val app = result.first()
|
|
||||||
app.clickCount = app.clickCount + 15
|
|
||||||
}
|
}
|
||||||
|
binding.lastTouchDate.setOnClickListener {
|
||||||
|
WorkersDb.logAppUsage(packageName, UsageLogType.APP,UsageUpdateType.DATETIME)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
binding.totalTouch.setOnClickListener { update() }
|
|
||||||
binding.lastTouchDate.setOnClickListener { update() }
|
|
||||||
|
|
||||||
binding.alterName.doOnTextChanged { text, start, before, count ->
|
binding.alterName.doOnTextChanged { text, start, before, count ->
|
||||||
WorkersDb.getRealm().apply {
|
WorkersDb.getRealm().apply {
|
||||||
@ -168,6 +161,7 @@ internal class AppMenu : BottomSheetDialogFragment() {
|
|||||||
hint = defAppName
|
hint = defAppName
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.appPackage.text = packageName
|
binding.appPackage.text = packageName
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
@ -189,6 +183,25 @@ internal class AppMenu : BottomSheetDialogFragment() {
|
|||||||
binding.appInfo.setOnClickListener { appInfo() }
|
binding.appInfo.setOnClickListener { appInfo() }
|
||||||
binding.appShare.setOnClickListener { share() }
|
binding.appShare.setOnClickListener { share() }
|
||||||
binding.appUninstall.setOnClickListener { uninstall() }
|
binding.appUninstall.setOnClickListener { uninstall() }
|
||||||
|
binding.listVisible.setOnClickListener {
|
||||||
|
WorkersDb.getRealm().writeBlocking {
|
||||||
|
var result = query<AppInfo>("pkgName == $0",packageName).find()
|
||||||
|
if (result.size > 0) {
|
||||||
|
var appInfo = result.first()
|
||||||
|
appInfo.visibilityMode = if (binding.listVisible.isChecked) 1 else 0
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.recommend.setOnClickListener {
|
||||||
|
WorkersDb.getRealm().writeBlocking {
|
||||||
|
var result = query<AppInfo>("pkgName == $0",packageName).find()
|
||||||
|
if(result.size > 0){
|
||||||
|
var appInfo = result.first()
|
||||||
|
appInfo.blockRecommend = binding.recommend.isChecked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -30,21 +30,52 @@ import androidx.recyclerview.widget.DiffUtil
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
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.IconPackManager.Companion.getDrawableIconForPackage
|
|
||||||
import bums.lunatic.launcher.databinding.AppsChildBinding
|
import bums.lunatic.launcher.databinding.AppsChildBinding
|
||||||
import bums.lunatic.launcher.model.AppInfo
|
import bums.lunatic.launcher.model.AppInfo
|
||||||
import bums.lunatic.launcher.utils.Blog
|
import bums.lunatic.launcher.utils.Blog
|
||||||
|
import bums.lunatic.launcher.workers.UsageLogType
|
||||||
|
import bums.lunatic.launcher.workers.UsageUpdateType
|
||||||
import bums.lunatic.launcher.workers.WorkersDb
|
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.async
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class AppsViewHolder(var view: AppsChildBinding) : RecyclerView.ViewHolder(view.root) {
|
||||||
|
fun bind(
|
||||||
|
packageManager: PackageManager,
|
||||||
|
fragmentManager: FragmentManager,
|
||||||
|
appinfo: AppInfo) {
|
||||||
|
view.apply {
|
||||||
|
childTextview.text = appinfo.appName
|
||||||
|
appIconTwo.visibility = View.VISIBLE
|
||||||
|
loadIconAsync(appIconTwo, appinfo.pkgName)
|
||||||
|
childTextview.apply {
|
||||||
|
gravity = Gravity.CENTER
|
||||||
|
setTextSize(TypedValue.COMPLEX_UNIT_PX, lActivity!!.resources.getDimension(R.dimen.twelve))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
view.root.apply {
|
||||||
|
/* on click - open app */
|
||||||
|
setOnClickListener {
|
||||||
|
appinfo.pkgName?.let { WorkersDb.logAppUsage(it, UsageLogType.APP,datetime = UsageUpdateType.DATETIME) }
|
||||||
|
context.startActivity(packageManager.getLaunchIntentForPackage(appinfo.pkgName!!))
|
||||||
|
}
|
||||||
|
|
||||||
|
/* on long click - open app menu */
|
||||||
|
setOnLongClickListener {
|
||||||
|
appinfo.pkgName?.let { WorkersDb.logAppUsage(it, UsageLogType.APP,datetime = UsageUpdateType.JC) }
|
||||||
|
AppMenu().apply {
|
||||||
|
}.show(fragmentManager, appinfo.pkgName)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal class AppsAdapter(
|
internal class AppsAdapter(
|
||||||
private val packageManager: PackageManager,
|
private val packageManager: PackageManager,
|
||||||
private val fragmentManager: FragmentManager,
|
private val fragmentManager: FragmentManager,
|
||||||
private val appsCount: TextView?) : RecyclerView.Adapter<AppsAdapter.AppsViewHolder>() {
|
private val appsCount: TextView?) : RecyclerView.Adapter<AppsViewHolder>() {
|
||||||
|
|
||||||
private var oldList = mutableListOf<AppInfo>()
|
private var oldList = mutableListOf<AppInfo>()
|
||||||
// private var appGravity: Int = Gravity.CENTER
|
// private var appGravity: Int = Gravity.CENTER
|
||||||
@ -58,94 +89,18 @@ internal class AppsAdapter(
|
|||||||
|
|
||||||
override fun onBindViewHolder(holder: AppsViewHolder, i: Int) {
|
override fun onBindViewHolder(holder: AppsViewHolder, i: Int) {
|
||||||
val item = oldList[i]
|
val item = oldList[i]
|
||||||
|
holder.bind(packageManager,fragmentManager,item)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
holder.view.apply {
|
|
||||||
childTextview.text = item.appName
|
|
||||||
appIconTwo.visibility = View.VISIBLE
|
|
||||||
loadIconAsync(appIconTwo, item.pkgName)
|
|
||||||
childTextview.apply {
|
|
||||||
gravity = Gravity.CENTER
|
|
||||||
setTextSize(TypedValue.COMPLEX_UNIT_PX, lActivity!!.resources.getDimension(R.dimen.twelve))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.view.root.apply {
|
|
||||||
/* on click - open app */
|
|
||||||
setOnClickListener {
|
|
||||||
WorkersDb.getRealm().apply {
|
|
||||||
writeBlocking {
|
|
||||||
Blog.LOGE("item.pkgName >>>> ${item.pkgName
|
|
||||||
}")
|
|
||||||
var result = query<AppInfo>("pkgName == $0",item.pkgName).find()
|
|
||||||
if(result.size > 0) {
|
|
||||||
val app = result.first()
|
|
||||||
app.clickCount = app.clickCount + 1
|
|
||||||
app.lastUseDate = Math.max(app.lastUseDate, System.currentTimeMillis())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
item.pkgName?.let { WorkersDb.logAppUsage(it,"APP") }
|
|
||||||
context.startActivity(packageManager.getLaunchIntentForPackage(item.pkgName!!))
|
|
||||||
}
|
|
||||||
|
|
||||||
/* on long click - open app menu */
|
|
||||||
setOnLongClickListener {
|
|
||||||
WorkersDb.getRealm().apply {
|
|
||||||
writeBlocking {
|
|
||||||
var result = query<AppInfo>("pkgName == $0",item.pkgName).find()
|
|
||||||
if(result.size > 0) {
|
|
||||||
val app = result.first()
|
|
||||||
app.clickCount = app.clickCount + 15
|
|
||||||
// app.lastUseDate = Math.max(app.lastUseDate, System.currentTimeMillis())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AppMenu().apply {
|
|
||||||
}.show(fragmentManager, item.pkgName)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
/* 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))
|
||||||
|
|
||||||
// [수정 전] dispatchUpdatesTo가 먼저 있어서 에러 발생함
|
|
||||||
// diffUtilResult.dispatchUpdatesTo(this)
|
|
||||||
// oldList.clear()
|
|
||||||
// oldList.addAll(newList)
|
|
||||||
|
|
||||||
// [수정 후] 반드시 리스트 데이터를 먼저 갱신하고 나서 알림을 보내야 합니다!
|
|
||||||
oldList.clear()
|
oldList.clear()
|
||||||
oldList.addAll(newList)
|
oldList.addAll(newList)
|
||||||
diffUtilResult.dispatchUpdatesTo(this) // <-- 순서 변경 (맨 뒤로)
|
diffUtilResult.dispatchUpdatesTo(this) // <-- 순서 변경 (맨 뒤로)
|
||||||
@ -171,3 +126,25 @@ internal class AppsDiffUtil(
|
|||||||
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean =
|
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean =
|
||||||
oldList[oldItemPosition] == newList[newItemPosition]
|
oldList[oldItemPosition] == newList[newItemPosition]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -19,7 +19,9 @@
|
|||||||
package bums.lunatic.launcher.apps
|
package bums.lunatic.launcher.apps
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.pm.PackageManager
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
@ -28,14 +30,42 @@ 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.model.SimpleContact
|
||||||
import bums.lunatic.launcher.utils.JamoUtils
|
import bums.lunatic.launcher.workers.UsageLogType
|
||||||
import io.realm.kotlin.types.RealmObject
|
import bums.lunatic.launcher.workers.UsageUpdateType
|
||||||
import io.realm.kotlin.types.annotations.PrimaryKey
|
import bums.lunatic.launcher.workers.WorkersDb
|
||||||
|
|
||||||
|
class ContactViewHolder(var view: ContactItemBinding) : RecyclerView.ViewHolder(view.root) {
|
||||||
|
fun bind(context: Context, fragmentManager: FragmentManager , simpleContact: SimpleContact) {
|
||||||
|
view.apply {
|
||||||
|
name.text = simpleContact.name
|
||||||
|
number.text= simpleContact.phoneNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
view.root.apply {
|
||||||
|
/* on click - open app */
|
||||||
|
setOnClickListener {
|
||||||
|
var intent = Intent(Intent.ACTION_DIAL);
|
||||||
|
intent.setData(Uri.parse("tel:" + simpleContact.phoneNumber));
|
||||||
|
context.startActivity(intent);
|
||||||
|
simpleContact.id?.let {
|
||||||
|
WorkersDb.logAppUsage(it, UsageLogType.CONTACT, UsageUpdateType.DATETIME)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* on long click - open app menu */
|
||||||
|
setOnLongClickListener {
|
||||||
|
simpleContact.id?.let {
|
||||||
|
WorkersDb.logAppUsage(it, UsageLogType.CONTACT, UsageUpdateType.JC)
|
||||||
|
}
|
||||||
|
ContactMenu().show(fragmentManager, simpleContact.id.toString())
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
internal class ContactAdapter (
|
internal class ContactAdapter (
|
||||||
private val packageManager: PackageManager,
|
private val context: Context,
|
||||||
private val fragmentManager: FragmentManager) : RecyclerView.Adapter<ContactAdapter.ContactViewHolder>() {
|
private val fragmentManager: FragmentManager) : RecyclerView.Adapter<ContactViewHolder>() {
|
||||||
|
|
||||||
private var oldList = mutableListOf<SimpleContact>()
|
private var oldList = mutableListOf<SimpleContact>()
|
||||||
private var appGravity: Int = Gravity.CENTER
|
private var appGravity: Int = Gravity.CENTER
|
||||||
@ -49,32 +79,12 @@ internal class ContactAdapter (
|
|||||||
|
|
||||||
override fun onBindViewHolder(holder: ContactViewHolder, i: Int) {
|
override fun onBindViewHolder(holder: ContactViewHolder, i: Int) {
|
||||||
val item = oldList[i]
|
val item = oldList[i]
|
||||||
// BLog.LOGE("name >>> ${item.name} :: ${item.touchCount} :: ${RecentCallGetter.dateFormat.format(
|
holder.bind(context, fragmentManager,item)
|
||||||
// Date(item.lastedTouchDateTime)
|
|
||||||
// )}")
|
|
||||||
|
|
||||||
holder.view.apply {
|
|
||||||
name.text = item.name
|
|
||||||
number.text= item.phoneNumber
|
|
||||||
}
|
|
||||||
holder.view.root.apply {
|
|
||||||
/* on click - open app */
|
|
||||||
setOnClickListener {
|
|
||||||
ContactMenu().show(fragmentManager, item.id.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
/* on long click - open app menu */
|
|
||||||
setOnLongClickListener {
|
|
||||||
// BLog.LOGE("item.id.toString() >> ${item.id.toString()}")
|
|
||||||
ContactMenu().show(fragmentManager, item.id.toString())
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int = oldList.size
|
override fun getItemCount(): Int = oldList.size
|
||||||
|
|
||||||
inner class ContactViewHolder(var view: ContactItemBinding) : RecyclerView.ViewHolder(view.root)
|
|
||||||
|
|
||||||
/* update app list */
|
/* update app list */
|
||||||
fun updateData(newList: List<SimpleContact>) {
|
fun updateData(newList: List<SimpleContact>) {
|
||||||
|
|||||||
@ -29,6 +29,8 @@ 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.model.SimpleContact
|
||||||
import bums.lunatic.launcher.utils.Blog
|
import bums.lunatic.launcher.utils.Blog
|
||||||
|
import bums.lunatic.launcher.workers.UsageLogType
|
||||||
|
import bums.lunatic.launcher.workers.UsageUpdateType
|
||||||
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
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
@ -62,19 +64,14 @@ internal class ContactMenu : BottomSheetDialogFragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun update() {
|
|
||||||
WorkersDb.getRealm().writeBlocking {
|
|
||||||
if (contactId != null && contactId.length ?: 0 > 0) {
|
binding.totalTouch.setOnClickListener {
|
||||||
val result = query<SimpleContact>().query("id == $0", contactId).find()
|
WorkersDb.logAppUsage(contactId, UsageLogType.CONTACT,UsageUpdateType.COUNT)
|
||||||
if(result.size > 0){
|
|
||||||
var contact = result.first()
|
|
||||||
contact.touchCount = contact.touchCount + 15
|
|
||||||
}
|
}
|
||||||
|
binding.lastTouchDate.setOnClickListener {
|
||||||
|
WorkersDb.logAppUsage(contactId, UsageLogType.CONTACT,UsageUpdateType.DATETIME)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
binding.totalTouch.setOnClickListener { update() }
|
|
||||||
binding.lastTouchDate.setOnClickListener { update() }
|
|
||||||
|
|
||||||
val resolver = lActivity!!.contentResolver
|
val resolver = lActivity!!.contentResolver
|
||||||
val phoneUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI
|
val phoneUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI
|
||||||
@ -121,6 +118,28 @@ internal class ContactMenu : BottomSheetDialogFragment() {
|
|||||||
binding.detailedInfo.setOnClickListener { detailedInfo() }
|
binding.detailedInfo.setOnClickListener { detailedInfo() }
|
||||||
binding.call.setOnClickListener { callPhone() }
|
binding.call.setOnClickListener { callPhone() }
|
||||||
binding.sms.setOnClickListener { sendSms() }
|
binding.sms.setOnClickListener { sendSms() }
|
||||||
|
binding.listVisible.setOnClickListener {
|
||||||
|
if (contactId != null && contactId.length ?: 0 > 0) {
|
||||||
|
WorkersDb.getRealm().writeBlocking {
|
||||||
|
val result = query<SimpleContact>().query("id == $0", contactId).find()
|
||||||
|
if (result.size > 0) {
|
||||||
|
var contact = result.first()
|
||||||
|
contact.visibilityMode = if (binding.listVisible.isChecked) 1 else 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.recommend.setOnClickListener {
|
||||||
|
if (contactId != null && contactId.length ?: 0 > 0) {
|
||||||
|
WorkersDb.getRealm().writeBlocking {
|
||||||
|
val result = query<SimpleContact>().query("id == $0", contactId).find()
|
||||||
|
if(result.size > 0){
|
||||||
|
var contact = result.first()
|
||||||
|
contact.blockRecommend = binding.recommend.isChecked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// binding.activityBrowser.setOnClickListener { activityBrowser() }
|
// binding.activityBrowser.setOnClickListener { activityBrowser() }
|
||||||
// binding.appStore.setOnClickListener { appStore() }
|
// binding.appStore.setOnClickListener { appStore() }
|
||||||
// binding.appFreeform.setOnClickListener { freeform() }
|
// binding.appFreeform.setOnClickListener { freeform() }
|
||||||
|
|||||||
@ -1,68 +1,68 @@
|
|||||||
package bums.lunatic.launcher.apps
|
package bums.lunatic.launcher.apps
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.fragment.app.FragmentManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import bums.lunatic.launcher.databinding.AppsChildBinding // 기존 레이아웃 재사용 (또는 별도 레이아웃 생성)
|
import bums.lunatic.launcher.databinding.AppsChildBinding
|
||||||
import bums.lunatic.launcher.databinding.AppsChildRecBinding
|
import bums.lunatic.launcher.databinding.AppsChildRecBinding
|
||||||
|
import bums.lunatic.launcher.databinding.ContactItemBinding
|
||||||
import bums.lunatic.launcher.model.AppInfo
|
import bums.lunatic.launcher.model.AppInfo
|
||||||
import bums.lunatic.launcher.workers.WorkersDb
|
import bums.lunatic.launcher.model.SimpleContact
|
||||||
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>() {
|
sealed class RecommendationItem {
|
||||||
|
// 앱을 감싸는 클래스
|
||||||
|
data class AppItem(val appInfo: AppInfo) : RecommendationItem()
|
||||||
|
|
||||||
private val items = ArrayList<AppInfo>()
|
// 연락처를 감싸는 클래스
|
||||||
|
data class ContactItem(val contact: SimpleContact) : RecommendationItem()
|
||||||
|
}
|
||||||
|
|
||||||
fun submitList(newItems: List<AppInfo>) {
|
|
||||||
|
class RecommendedAppsAdapter(
|
||||||
|
private val context: Context,
|
||||||
|
private val packageManager: PackageManager,
|
||||||
|
private val fragmentManager: FragmentManager) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
|
|
||||||
|
private val items = ArrayList<RecommendationItem>()
|
||||||
|
|
||||||
|
fun submitList(newItems: List<RecommendationItem>) {
|
||||||
items.clear()
|
items.clear()
|
||||||
items.addAll(newItems)
|
items.addAll(newItems)
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
companion object {
|
||||||
// 기존 apps_child.xml을 재사용하거나, 아이콘만 보여주는 새로운 xml을 만들어도 됨
|
const val TYPE_APP = 0
|
||||||
val binding = AppsChildRecBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
const val TYPE_CONTACT = 1
|
||||||
return ViewHolder(binding)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
override fun getItemViewType(position: Int): Int {
|
||||||
val item = items[position]
|
return when (items[position]) {
|
||||||
|
is RecommendationItem.AppItem -> TYPE_APP
|
||||||
// 앱 이름 (추천 영역에서는 숨기거나 작게 표시 가능)
|
is RecommendationItem.ContactItem -> TYPE_CONTACT
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
// 클릭 이벤트
|
return (if (viewType == TYPE_APP) {
|
||||||
holder.itemView.setOnClickListener {
|
AppsViewHolder(AppsChildBinding.inflate(LayoutInflater.from(parent.context), parent, false))
|
||||||
try {
|
} else {
|
||||||
val intent = pm.getLaunchIntentForPackage(item.pkgName ?: "")
|
ContactViewHolder(ContactItemBinding.inflate(LayoutInflater.from(parent.context), parent, false))
|
||||||
if (intent != null) {
|
}) as RecyclerView.ViewHolder
|
||||||
holder.itemView.context.startActivity(intent)
|
}
|
||||||
// [중요] 사용 로그 저장 -> 추천 정확도 상승
|
|
||||||
WorkersDb.logAppUsage(item.pkgName ?: "", "APP")
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
|
when (val item = items[position]) {
|
||||||
|
is RecommendationItem.AppItem -> {
|
||||||
|
(holder as AppsViewHolder).bind(packageManager,fragmentManager,item.appInfo)
|
||||||
|
}
|
||||||
|
is RecommendationItem.ContactItem -> {
|
||||||
|
(holder as ContactViewHolder).bind(context, fragmentManager,item.contact)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) { e.printStackTrace() }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int = items.size
|
override fun getItemCount(): Int = items.size
|
||||||
|
|
||||||
class ViewHolder(val binding: AppsChildRecBinding) : RecyclerView.ViewHolder(binding.root)
|
|
||||||
}
|
}
|
||||||
@ -127,19 +127,12 @@ class LocationUpdateService : Service(), LocationListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected var locationManager: LocationManager? = null
|
protected var locationManager: LocationManager? = null
|
||||||
var checkGPS = false
|
var checkGPS = false
|
||||||
var checkNetwork = false
|
var checkNetwork = false
|
||||||
|
|
||||||
// boolean canGetLocation = false;
|
|
||||||
var loc: Location? = null
|
|
||||||
|
|
||||||
|
|
||||||
override fun onBind(intent: Intent?): IBinder? {
|
override fun onBind(intent: Intent?): IBinder? {
|
||||||
TODO("Not yet implemented")
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLocationChanged(p0: Location) {
|
override fun onLocationChanged(p0: Location) {
|
||||||
|
|||||||
@ -43,6 +43,8 @@ object TaskAggregator {
|
|||||||
if (oldApp != null) {
|
if (oldApp != null) {
|
||||||
newApp.clickCount = oldApp.clickCount
|
newApp.clickCount = oldApp.clickCount
|
||||||
newApp.lastUseDate = oldApp.lastUseDate
|
newApp.lastUseDate = oldApp.lastUseDate
|
||||||
|
newApp.visibilityMode = oldApp.visibilityMode
|
||||||
|
newApp.blockRecommend = oldApp.blockRecommend
|
||||||
// 즐겨찾기 여부 등 보존해야 할 다른 필드가 있다면 여기서 복사
|
// 즐겨찾기 여부 등 보존해야 할 다른 필드가 있다면 여기서 복사
|
||||||
// newApp.isFavorite = oldApp.isFavorite
|
// newApp.isFavorite = oldApp.isFavorite
|
||||||
}
|
}
|
||||||
@ -75,16 +77,18 @@ object TaskAggregator {
|
|||||||
if (oldContact != null) {
|
if (oldContact != null) {
|
||||||
contact.touchCount = oldContact.touchCount
|
contact.touchCount = oldContact.touchCount
|
||||||
contact.lastedTouchDateTime = oldContact.lastedTouchDateTime
|
contact.lastedTouchDateTime = oldContact.lastedTouchDateTime
|
||||||
|
contact.visibilityMode = oldContact.visibilityMode
|
||||||
|
contact.blockRecommend = oldContact.blockRecommend
|
||||||
}
|
}
|
||||||
|
|
||||||
copyToRealm(contact, UpdatePolicy.ALL)
|
copyToRealm(contact, UpdatePolicy.ALL)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 삭제된 연락처 처리
|
// 삭제된 연락처 처리
|
||||||
val contactsToDelete = query<SimpleContact>(SimpleContact::class).find().filter {
|
// val contactsToDelete = query<SimpleContact>(SimpleContact::class).find().filter {
|
||||||
!activeContactIds.contains(it.id)
|
// !activeContactIds.contains(it.id)
|
||||||
}
|
// }
|
||||||
contactsToDelete.forEach { delete(it) }
|
// contactsToDelete.forEach { delete(it) }
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
package bums.lunatic.launcher.workers
|
package bums.lunatic.launcher.workers
|
||||||
|
|
||||||
|
import android.app.usage.UsageStatsManager
|
||||||
|
import android.content.Context
|
||||||
import bums.lunatic.launcher.BuildConfig
|
import bums.lunatic.launcher.BuildConfig
|
||||||
import bums.lunatic.launcher.common.letTrue
|
import bums.lunatic.launcher.common.letTrue
|
||||||
import bums.lunatic.launcher.model.AppInfo
|
import bums.lunatic.launcher.model.AppInfo
|
||||||
@ -57,6 +59,10 @@ class CustMigration : AutomaticSchemaMigration {
|
|||||||
Blog.LOGE(migrationContext.newRealm.configuration.schemaVersion.toString())
|
Blog.LOGE(migrationContext.newRealm.configuration.schemaVersion.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class ScoredItem(val key: String, val type: String, val score: Double)
|
||||||
|
enum class UsageLogType { APP, CONTACT };
|
||||||
|
enum class UsageUpdateType { JC, COUNT, DATETIME };
|
||||||
object WorkersDb {
|
object WorkersDb {
|
||||||
|
|
||||||
//RecentCall::class, RecentSms::class,
|
//RecentCall::class, RecentSms::class,
|
||||||
@ -70,19 +76,61 @@ object WorkersDb {
|
|||||||
)
|
)
|
||||||
//,UserActionModel::class
|
//,UserActionModel::class
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// [추가] 앱/연락처 사용 시 로그 저장 (기존 updateAppUse 대신 이거 호출)
|
// [추가] 앱/연락처 사용 시 로그 저장 (기존 updateAppUse 대신 이거 호출)
|
||||||
fun logAppUsage(key: String, type: String = "APP") {
|
fun logAppUsage(key: String, type: UsageLogType = UsageLogType.APP, datetime: UsageUpdateType) {
|
||||||
val realm = getRealm()
|
val realm = getRealm()
|
||||||
val calendar = Calendar.getInstance()
|
val calendar = Calendar.getInstance()
|
||||||
|
|
||||||
realm.writeBlocking { // 비동기로 하려면 write { } 사용
|
realm.writeBlocking {
|
||||||
// 1. 기존 카운트 증가 (기존 로직 유지)
|
when(type) {
|
||||||
// ... (AppInfo 조회 후 clickCount++ 하는 코드) ...
|
UsageLogType.APP -> {
|
||||||
|
var result = query<AppInfo>("pkgName == $0",key).find()
|
||||||
|
if(result.isNotEmpty()) {
|
||||||
|
val app = result.first()
|
||||||
|
when(datetime) {
|
||||||
|
UsageUpdateType.JC -> {
|
||||||
|
app.clickCount += 1
|
||||||
|
}
|
||||||
|
UsageUpdateType.COUNT -> {
|
||||||
|
app.clickCount += 15
|
||||||
|
}
|
||||||
|
UsageUpdateType.DATETIME -> {
|
||||||
|
app.clickCount += 1
|
||||||
|
app.lastUseDate = System.currentTimeMillis()
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
|
||||||
// 2. 상세 로그 저장 (추가된 부분)
|
}
|
||||||
|
}
|
||||||
|
UsageLogType.CONTACT -> {
|
||||||
|
val results = query<SimpleContact>().query("id == $0", key).find()
|
||||||
|
if(results.isNotEmpty()) {
|
||||||
|
val result = results.first()
|
||||||
|
when(datetime) {
|
||||||
|
UsageUpdateType.JC -> {
|
||||||
|
result.touchCount += 1
|
||||||
|
}
|
||||||
|
UsageUpdateType.COUNT -> {
|
||||||
|
result.touchCount += 15
|
||||||
|
}
|
||||||
|
UsageUpdateType.DATETIME -> {
|
||||||
|
result.touchCount += 1
|
||||||
|
result.lastedTouchDateTime = System.currentTimeMillis()
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (datetime.equals(UsageUpdateType.JC) == false) {
|
||||||
copyToRealm(AppUsageLog().apply {
|
copyToRealm(AppUsageLog().apply {
|
||||||
itemKey = key
|
itemKey = key
|
||||||
itemType = type
|
itemType = type.name
|
||||||
timestamp = System.currentTimeMillis()
|
timestamp = System.currentTimeMillis()
|
||||||
month = calendar.get(Calendar.MONTH)
|
month = calendar.get(Calendar.MONTH)
|
||||||
dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH)
|
dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH)
|
||||||
@ -91,9 +139,10 @@ object WorkersDb {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// [핵심] 현재 시간 상황에 맞는 추천 리스트 가져오기
|
// [핵심] 현재 시간 상황에 맞는 추천 리스트 가져오기
|
||||||
fun getContextualRecommendations(limit: Int = 5): List<String> {
|
fun getContextualRecommendations(limit: Int = 8): List<ScoredItem> {
|
||||||
val realm = getRealm()
|
val realm = getRealm()
|
||||||
val calendar = Calendar.getInstance()
|
val calendar = Calendar.getInstance()
|
||||||
|
|
||||||
@ -103,13 +152,14 @@ object WorkersDb {
|
|||||||
val curHour = calendar.get(Calendar.HOUR_OF_DAY)
|
val curHour = calendar.get(Calendar.HOUR_OF_DAY)
|
||||||
|
|
||||||
// 최근 3개월 데이터만 조회 (너무 오래된 데이터는 노이즈가 됨)
|
// 최근 3개월 데이터만 조회 (너무 오래된 데이터는 노이즈가 됨)
|
||||||
val threeMonthsAgo = System.currentTimeMillis() - (90L * 24 * 60 * 60 * 1000)
|
val threeMonthsAgo = System.currentTimeMillis() - (365L * 24 * 60 * 60 * 1000)
|
||||||
|
|
||||||
// 쿼리: 최근 데이터만 가져와서 메모리에서 계산 (복잡한 가중치는 메모리 연산이 빠름)
|
// 쿼리: 최근 데이터만 가져와서 메모리에서 계산 (복잡한 가중치는 메모리 연산이 빠름)
|
||||||
val logs = realm.query<AppUsageLog>("timestamp > $0", threeMonthsAgo).find()
|
val logs = realm.query<AppUsageLog>("timestamp > $0", threeMonthsAgo).find()
|
||||||
|
|
||||||
// 점수 계산
|
|
||||||
val scores = HashMap<String, Double>()
|
val scores = HashMap<String, Double>()
|
||||||
|
val types = HashMap<String, String>()
|
||||||
|
// 점수 계산
|
||||||
|
|
||||||
for (log in logs) {
|
for (log in logs) {
|
||||||
var score = 1.0 // 기본 점수 (최근에 썼다는 것 자체로 의미 있음)
|
var score = 1.0 // 기본 점수 (최근에 썼다는 것 자체로 의미 있음)
|
||||||
@ -134,13 +184,17 @@ object WorkersDb {
|
|||||||
// 최종 점수 누적
|
// 최종 점수 누적
|
||||||
val finalScore = score * decay
|
val finalScore = score * decay
|
||||||
scores[log.itemKey] = (scores[log.itemKey] ?: 0.0) + finalScore
|
scores[log.itemKey] = (scores[log.itemKey] ?: 0.0) + finalScore
|
||||||
|
types[log.itemKey] = log.itemType
|
||||||
}
|
}
|
||||||
|
|
||||||
// 점수 높은 순으로 정렬하여 상위 N개 반환
|
// 점수 높은 순으로 정렬하여 상위 N개 반환
|
||||||
return scores.entries
|
return scores.entries
|
||||||
.sortedByDescending { it.value }
|
.sortedByDescending { it.value }
|
||||||
.take(limit)
|
.take(limit)
|
||||||
.map { it.key }
|
.map {
|
||||||
|
// 키, 타입, 점수를 묶어서 반환
|
||||||
|
ScoredItem(it.key, types[it.key] ?: "APP", it.value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val schemaVersion : Long = BuildConfig.BuildDateTime
|
val schemaVersion : Long = BuildConfig.BuildDateTime
|
||||||
@ -274,16 +328,61 @@ object WorkersDb {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateAppUse(pkg : String) {
|
fun syncSystemUsageStats(context: Context) {
|
||||||
getRealm().writeBlocking {
|
val usm = context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
|
||||||
val result = query<AppInfo>().query("pkgName == $0",pkg).find()
|
val calendar = Calendar.getInstance()
|
||||||
if(result.size > 0) {
|
val endTime = calendar.timeInMillis
|
||||||
val appInfo = result.first()
|
val startTime = endTime - (1000 * 60 * 60 * 24) // 최근 24시간 조회
|
||||||
appInfo.clickCount = appInfo.clickCount + 1
|
|
||||||
appInfo.lastUseDate = System.currentTimeMillis()
|
// 1. 시스템에서 사용 기록 조회
|
||||||
|
val usageStatsList = usm.queryUsageStats(
|
||||||
|
UsageStatsManager.INTERVAL_DAILY,
|
||||||
|
startTime,
|
||||||
|
endTime
|
||||||
|
)
|
||||||
|
|
||||||
|
if (usageStatsList.isNullOrEmpty()) return
|
||||||
|
|
||||||
|
val realm = getRealm() // WorkersDb의 getRealm() 활용
|
||||||
|
|
||||||
|
realm.writeBlocking {
|
||||||
|
for (stats in usageStatsList) {
|
||||||
|
val pkgName = stats.packageName
|
||||||
|
val lastUsedTime = stats.lastTimeUsed
|
||||||
|
|
||||||
|
// 2. 우리 DB(AppInfo)에 저장된 시간보다 최신인지 확인
|
||||||
|
val appInfo = query<AppInfo>("pkgName == $0", pkgName).first().find()
|
||||||
|
|
||||||
|
if (appInfo != null) {
|
||||||
|
// 시스템 기록이 내 DB 기록보다 더 최신이면 -> 런처 밖에서 실행된 것임!
|
||||||
|
if (lastUsedTime > appInfo.lastUseDate) {
|
||||||
|
|
||||||
|
// A. AppInfo 갱신
|
||||||
|
appInfo.lastUseDate = lastUsedTime
|
||||||
|
// (선택) 외부 실행도 카운트에 포함할지 결정
|
||||||
|
appInfo.clickCount += 1
|
||||||
|
|
||||||
|
// B. AppUsageLog에 로그 추가 (외부 실행 로그)
|
||||||
|
// 주의: 너무 많은 로그가 쌓일 수 있으므로 중복 체크 필요
|
||||||
|
val existingLog = query<AppUsageLog>(
|
||||||
|
"itemKey == $0 AND timestamp == $1",
|
||||||
|
pkgName, lastUsedTime
|
||||||
|
).find()
|
||||||
|
|
||||||
|
if (existingLog.isEmpty()) {
|
||||||
|
copyToRealm(AppUsageLog().apply {
|
||||||
|
itemKey = pkgName
|
||||||
|
itemType = "APP"
|
||||||
|
timestamp = lastUsedTime
|
||||||
|
// month, day 등 날짜 정보 계산해서 넣기...
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun push(loc: LocationLog) {
|
fun push(loc: LocationLog) {
|
||||||
getRealm().writeBlocking {
|
getRealm().writeBlocking {
|
||||||
|
|||||||
7
app/src/main/res/drawable/bg_circle_emoji.xml
Normal file
7
app/src/main/res/drawable/bg_circle_emoji.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:color="#42000000"> <item>
|
||||||
|
<shape android:shape="oval">
|
||||||
|
<solid android:color="#F0F0F0"/> </shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
7
app/src/main/res/drawable/bg_circle_emoji_red.xml
Normal file
7
app/src/main/res/drawable/bg_circle_emoji_red.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:color="#42FF0000"> <item>
|
||||||
|
<shape android:shape="oval">
|
||||||
|
<solid android:color="#F00000"/> </shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
9
app/src/main/res/drawable/search.xml
Normal file
9
app/src/main/res/drawable/search.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:pathData="M784,840 L532,588q-30,24 -69,38t-83,14q-109,0 -184.5,-75.5T120,380q0,-109 75.5,-184.5T380,120q109,0 184.5,75.5T640,380q0,44 -14,83t-38,69l252,252 -56,56ZM380,560q75,0 127.5,-52.5T560,380q0,-75 -52.5,-127.5T380,200q-75,0 -127.5,52.5T200,380q0,75 52.5,127.5T380,560Z"
|
||||||
|
android:fillColor="#e3e3e3"/>
|
||||||
|
</vector>
|
||||||
@ -33,6 +33,7 @@
|
|||||||
android:padding="@dimen/eight"
|
android:padding="@dimen/eight"
|
||||||
android:inputType="textNoSuggestions"
|
android:inputType="textNoSuggestions"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
|
||||||
android:id="@+id/lastTouchDate"
|
android:id="@+id/lastTouchDate"
|
||||||
@ -45,6 +46,23 @@
|
|||||||
android:padding="@dimen/eight"
|
android:padding="@dimen/eight"
|
||||||
android:inputType="textNoSuggestions"
|
android:inputType="textNoSuggestions"
|
||||||
/>
|
/>
|
||||||
|
<CheckBox
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/totalTouch"
|
||||||
|
android:id="@+id/listVisible"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
app:layout_constraintRight_toLeftOf="@id/recommend"
|
||||||
|
android:text="검색만 보임"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
<CheckBox
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/totalTouch"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
android:id="@+id/recommend"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:text="추천에 제외"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:inputType="none"
|
android:inputType="none"
|
||||||
android:id="@+id/alterName"
|
android:id="@+id/alterName"
|
||||||
|
|||||||
@ -31,7 +31,7 @@
|
|||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
app:layout_constraintTop_toBottomOf="@id/drag_handle"
|
app:layout_constraintTop_toBottomOf="@id/drag_handle"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent"/>
|
app:layout_constraintRight_toLeftOf="@+id/search_google"/>
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageButton
|
<androidx.appcompat.widget.AppCompatImageButton
|
||||||
android:id="@+id/reset"
|
android:id="@+id/reset"
|
||||||
@ -46,6 +46,20 @@
|
|||||||
app:layout_constraintTop_toTopOf="@id/searchInput"
|
app:layout_constraintTop_toTopOf="@id/searchInput"
|
||||||
app:srcCompat="@drawable/ic_refresh" />
|
app:srcCompat="@drawable/ic_refresh" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageButton
|
||||||
|
android:id="@+id/search_google"
|
||||||
|
android:layout_width="45dp"
|
||||||
|
android:layout_height="45dp"
|
||||||
|
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_constraintLeft_toRightOf="@+id/searchInput"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/searchInput"
|
||||||
|
app:srcCompat="@drawable/search" />
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView
|
<androidx.core.widget.NestedScrollView
|
||||||
android:id="@+id/nestedScrollView"
|
android:id="@+id/nestedScrollView"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
@ -87,7 +101,7 @@
|
|||||||
android:id="@+id/recAppsList"
|
android:id="@+id/recAppsList"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="100dp"
|
android:minHeight="50dp"
|
||||||
android:background="@android:color/transparent"
|
android:background="@android:color/transparent"
|
||||||
android:overScrollMode="never"
|
android:overScrollMode="never"
|
||||||
android:nestedScrollingEnabled="false"
|
android:nestedScrollingEnabled="false"
|
||||||
@ -140,10 +154,6 @@
|
|||||||
android:id="@+id/search_nmap"
|
android:id="@+id/search_nmap"
|
||||||
style="@style/SearchIcons"
|
style="@style/SearchIcons"
|
||||||
android:src="@drawable/navermap"/>
|
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
|
<bums.lunatic.launcher.view.CircleImageView
|
||||||
android:id="@+id/search_naver"
|
android:id="@+id/search_naver"
|
||||||
style="@style/SearchIcons"
|
style="@style/SearchIcons"
|
||||||
|
|||||||
@ -8,7 +8,6 @@
|
|||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusableInTouchMode="true">
|
android:focusableInTouchMode="true">
|
||||||
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/appName"
|
android:id="@+id/appName"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
@ -22,6 +21,7 @@
|
|||||||
android:padding="@dimen/eight"
|
android:padding="@dimen/eight"
|
||||||
android:inputType="textNoSuggestions"
|
android:inputType="textNoSuggestions"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/totalTouch"
|
android:id="@+id/totalTouch"
|
||||||
android:layout_margin="20dp"
|
android:layout_margin="20dp"
|
||||||
@ -34,7 +34,6 @@
|
|||||||
android:inputType="textNoSuggestions"
|
android:inputType="textNoSuggestions"
|
||||||
/>
|
/>
|
||||||
<TextView
|
<TextView
|
||||||
|
|
||||||
android:id="@+id/lastTouchDate"
|
android:id="@+id/lastTouchDate"
|
||||||
android:layout_margin="20dp"
|
android:layout_margin="20dp"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
@ -45,6 +44,23 @@
|
|||||||
android:padding="@dimen/eight"
|
android:padding="@dimen/eight"
|
||||||
android:inputType="textNoSuggestions"
|
android:inputType="textNoSuggestions"
|
||||||
/>
|
/>
|
||||||
|
<CheckBox
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/totalTouch"
|
||||||
|
android:id="@+id/listVisible"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
app:layout_constraintRight_toLeftOf="@id/recommend"
|
||||||
|
android:text="검색만 보임"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
<CheckBox
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/totalTouch"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
android:id="@+id/recommend"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:text="추천에 제외"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:textSize="30dp"
|
android:textSize="30dp"
|
||||||
android:id="@+id/phoneNumber"
|
android:id="@+id/phoneNumber"
|
||||||
@ -54,7 +70,7 @@
|
|||||||
android:layout_marginTop="@dimen/eight"
|
android:layout_marginTop="@dimen/eight"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/totalTouch" />
|
app:layout_constraintTop_toBottomOf="@+id/listVisible" />
|
||||||
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@ -63,38 +79,41 @@
|
|||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
<bums.lunatic.launcher.view.CircleImageView
|
<TextView
|
||||||
app:civ_circle_background_color="#77FFFFFF"
|
app:autoSizeTextType="uniform"
|
||||||
app:civ_border_width="8dp"
|
|
||||||
app:civ_border_color="#77FFFFFF"
|
|
||||||
android:adjustViewBounds="true"
|
|
||||||
android:layout_width="80dp"
|
|
||||||
android:layout_height="80dp"
|
|
||||||
android:id="@+id/detailedInfo"
|
android:id="@+id/detailedInfo"
|
||||||
android:layout_margin="@dimen/eight"
|
|
||||||
android:src="@drawable/contact"
|
|
||||||
/>
|
|
||||||
<bums.lunatic.launcher.view.CircleImageView
|
|
||||||
app:civ_circle_background_color="#77FFFFFF"
|
|
||||||
app:civ_border_width="8dp"
|
|
||||||
app:civ_border_color="#77FFFFFF"
|
|
||||||
android:adjustViewBounds="true"
|
|
||||||
android:layout_width="80dp"
|
android:layout_width="80dp"
|
||||||
android:layout_height="80dp"
|
android:layout_height="80dp"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:layout_margin="10dp"
|
||||||
|
android:text="🗃️"
|
||||||
|
android:background="@drawable/bg_circle_emoji"
|
||||||
|
android:gravity="center"
|
||||||
|
android:visibility="visible"
|
||||||
|
/>
|
||||||
|
<TextView
|
||||||
|
app:autoSizeTextType="uniform"
|
||||||
android:id="@+id/sms"
|
android:id="@+id/sms"
|
||||||
android:layout_margin="@dimen/eight"
|
android:padding="10dp"
|
||||||
android:src="@drawable/message"
|
android:layout_margin="10dp"
|
||||||
/>
|
|
||||||
<bums.lunatic.launcher.view.CircleImageView
|
|
||||||
app:civ_circle_background_color="#77FFFFFF"
|
|
||||||
app:civ_border_width="8dp"
|
|
||||||
app:civ_border_color="#77FFFFFF"
|
|
||||||
android:adjustViewBounds="true"
|
|
||||||
android:layout_width="80dp"
|
android:layout_width="80dp"
|
||||||
android:layout_height="80dp"
|
android:layout_height="80dp"
|
||||||
|
android:text="✉️"
|
||||||
|
android:background="@drawable/bg_circle_emoji"
|
||||||
|
android:gravity="center"
|
||||||
|
android:visibility="visible"
|
||||||
|
/>
|
||||||
|
<TextView
|
||||||
|
app:autoSizeTextType="uniform"
|
||||||
android:id="@+id/call"
|
android:id="@+id/call"
|
||||||
android:layout_margin="@dimen/eight"
|
android:padding="10dp"
|
||||||
android:src="@drawable/phonecall"
|
android:layout_margin="10dp"
|
||||||
|
android:layout_width="80dp"
|
||||||
|
android:layout_height="80dp"
|
||||||
|
android:text="📱"
|
||||||
|
android:background="@drawable/bg_circle_emoji"
|
||||||
|
android:gravity="center"
|
||||||
|
android:visibility="visible"
|
||||||
/>
|
/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|||||||
@ -20,36 +20,45 @@
|
|||||||
app:layout_constraintEnd_toEndOf="parent" />
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
app:autoSizeTextType="uniform"
|
||||||
android:id="@+id/delete_zone"
|
android:id="@+id/delete_zone"
|
||||||
android:layout_width="50dp"
|
android:layout_width="50dp"
|
||||||
android:layout_height="50dp"
|
android:layout_height="50dp"
|
||||||
android:text="삭제"
|
android:text="🗑️"
|
||||||
android:textColor="#FFFFFF"
|
android:background="@drawable/bg_circle_emoji"
|
||||||
android:background="#99FF0000"
|
|
||||||
android:padding="20dp"
|
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
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="10dp"/>
|
android:layout_marginTop="10dp"/>
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/fragment_layer"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:visibility="visible"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" >
|
||||||
<androidx.fragment.app.FragmentContainerView
|
<androidx.fragment.app.FragmentContainerView
|
||||||
android:id="@+id/fragment_container"
|
android:id="@+id/fragment_container"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
android:layout_width="0dp"
|
android:layout_weight="1"
|
||||||
android:layout_height="0dp"
|
android:layout_width="match_parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
android:layout_height="0dp" />
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
<LinearLayout
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
android:id="@+id/controll_panel"
|
||||||
app:layout_constraintBottom_toTopOf="@id/current_address"/>
|
android:visibility="gone"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="40dp">
|
||||||
<ImageButton
|
<ImageButton
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
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" />
|
||||||
@ -58,19 +67,18 @@
|
|||||||
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"/>
|
||||||
<TextView
|
<TextView
|
||||||
android:text="asdasdsadasd"
|
android:text="asdasdsadasd"
|
||||||
|
android:layout_weight="1"
|
||||||
android:id="@+id/current_address"
|
android:id="@+id/current_address"
|
||||||
app:layout_constraintTop_toTopOf="@id/back"
|
app:layout_constraintTop_toTopOf="@id/back"
|
||||||
app:layout_constraintRight_toLeftOf="@id/dl_video"
|
app:layout_constraintRight_toLeftOf="@id/dl_video"
|
||||||
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"
|
||||||
@ -82,7 +90,6 @@
|
|||||||
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"/>
|
||||||
@ -92,14 +99,16 @@
|
|||||||
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"
|
||||||
style="@style/CommonBottom"/>
|
style="@style/CommonBottom"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
<bums.lunatic.launcher.view.FloatingActionMenu
|
<bums.lunatic.launcher.view.FloatingActionMenu
|
||||||
android:id="@+id/floating_action_menu"
|
android:id="@+id/floating_action_menu"
|
||||||
android:layout_margin="5dp"
|
android:layout_margin="5dp"
|
||||||
|
android:visibility="gone"
|
||||||
app:menu_colorNormal="#80FF0000"
|
app:menu_colorNormal="#80FF0000"
|
||||||
app:menu_fab_size="mini"
|
app:menu_fab_size="mini"
|
||||||
app:menu_icon="@drawable/ic_add"
|
app:menu_icon="@drawable/ic_add"
|
||||||
|
|||||||
@ -17,7 +17,7 @@
|
|||||||
android:adjustViewBounds="true"
|
android:adjustViewBounds="true"
|
||||||
android:visibility="visible"
|
android:visibility="visible"
|
||||||
android:background="@null"
|
android:background="@null"
|
||||||
android:src="@drawable/ic_search"
|
android:src="@drawable/search"
|
||||||
android:layout_width="30dp"
|
android:layout_width="30dp"
|
||||||
android:tint="@color/finestSilver"
|
android:tint="@color/finestSilver"
|
||||||
android:foregroundTint="@color/finestSilver"
|
android:foregroundTint="@color/finestSilver"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user