...
This commit is contained in:
parent
614e244be7
commit
83546e5e10
@ -3,6 +3,7 @@ package bums.lunatic.launcher.apps
|
||||
import android.app.Dialog
|
||||
import android.app.SearchManager
|
||||
import android.content.Intent
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
@ -28,6 +29,7 @@ import bums.lunatic.launcher.model.AppInfo
|
||||
import bums.lunatic.launcher.model.SimpleContact
|
||||
import bums.lunatic.launcher.utils.Blog
|
||||
import bums.lunatic.launcher.utils.CategoryGrouper
|
||||
import bums.lunatic.launcher.utils.CategoryManualMapper
|
||||
import bums.lunatic.launcher.utils.EnToKo
|
||||
import bums.lunatic.launcher.utils.JamoUtils
|
||||
import bums.lunatic.launcher.utils.SimpleTransliterater
|
||||
@ -97,18 +99,7 @@ class AppDrawerBottomSheet : BottomSheetDialogFragment() {
|
||||
return binding.root
|
||||
}
|
||||
|
||||
private val categoryMap = mapOf(
|
||||
"전체" to "ALL",
|
||||
"게임" to "GAME",
|
||||
"생산성" to "PRODUCTIVITY",
|
||||
"소셜" to "SOCIAL",
|
||||
"오디오" to "AUDIO",
|
||||
"비디오" to "VIDEO",
|
||||
"이미지" to "IMAGE",
|
||||
"지도" to "MAPS",
|
||||
"뉴스" to "NEWS",
|
||||
"기타" to "UNDEFINED" // 혹은 UNKNOWN
|
||||
)
|
||||
|
||||
|
||||
private var currentScope: String = CategoryGrouper.SCOPE_ALL
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
@ -127,7 +118,7 @@ class AppDrawerBottomSheet : BottomSheetDialogFragment() {
|
||||
private var currentCategoryKey: String = "ALL"
|
||||
private fun setupCategorySpinner() {
|
||||
// 1. 스피너에 들어갈 데이터(표시 이름들) 준비
|
||||
val displayList = categoryMap.keys.toList()
|
||||
val displayList = CategoryManualMapper.CATEGORY_MAP.keys.toList()
|
||||
|
||||
// 2. 어댑터 설정 (기본 안드로이드 레이아웃 사용)
|
||||
val adapter = object : ArrayAdapter<String>(
|
||||
@ -154,7 +145,7 @@ class AppDrawerBottomSheet : BottomSheetDialogFragment() {
|
||||
binding.categorySpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||
val selectedDisplayName = displayList[position]
|
||||
currentCategoryKey = categoryMap[selectedDisplayName] ?: "ALL"
|
||||
currentCategoryKey = CategoryManualMapper.CATEGORY_MAP[selectedDisplayName] ?: "ALL"
|
||||
|
||||
// 선택 변경 시 목록 갱신 (검색어 유지)
|
||||
fetchApps(binding.searchInput.text.toString())
|
||||
@ -421,6 +412,8 @@ class AppDrawerBottomSheet : BottomSheetDialogFragment() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 앱 목록을 불러오는 함수
|
||||
* - 검색어(keyword)가 있으면 필터링을 수행합니다.
|
||||
@ -467,7 +460,15 @@ class AppDrawerBottomSheet : BottomSheetDialogFragment() {
|
||||
.sort("lastUseDate", Sort.DESCENDING)
|
||||
.find()
|
||||
|
||||
val scopedAppsList = baseAppQuery.map { realm.copyFromRealm(it) }
|
||||
val scopedAppsList = baseAppQuery.map {
|
||||
val appCopy = realm.copyFromRealm(it)
|
||||
|
||||
// 💡 수동 매퍼를 사용하여 카테고리 강제 재할당
|
||||
// it.systemCategoryInt는 AppInfo 모델에 시스템 카테고리 값이 저장되어 있다고 가정함
|
||||
val fixedCategory = CategoryManualMapper.getFixedCategory(it.pkgName ?:"", it.appName, it.systemCategoryInt)
|
||||
appCopy.category = fixedCategory
|
||||
appCopy
|
||||
}
|
||||
.filter { appInfo ->
|
||||
val isLaunchable = try {
|
||||
pm.getLaunchIntentForPackage(appInfo.pkgName ?: "") != null
|
||||
@ -479,7 +480,6 @@ class AppDrawerBottomSheet : BottomSheetDialogFragment() {
|
||||
val isCategoryMatch = if (currentCategoryKey == "ALL") {
|
||||
true
|
||||
} else {
|
||||
// AppInfoGetter에서 저장한 값("GAME", "AUDIO" 등)과 비교
|
||||
appInfo.category == currentCategoryKey
|
||||
}
|
||||
|
||||
|
||||
@ -16,6 +16,7 @@ class AppInfo : RealmObject {
|
||||
var clickCount : Int = 0
|
||||
var lastUseDate : Long = 0L
|
||||
var category : String? = null
|
||||
var systemCategoryInt : Int = 0
|
||||
var currentInstalled : Boolean = false
|
||||
var isInstalled : Boolean = false
|
||||
// [신규] 0: 항상 보이기(기본), 1: 검색 시만 보이기 (숨김)
|
||||
|
||||
@ -0,0 +1,166 @@
|
||||
package bums.lunatic.launcher.utils
|
||||
|
||||
import android.content.pm.ApplicationInfo
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* 로그 분석 및 키워드 기반 카테고리 자동 분류 유틸리티
|
||||
*/
|
||||
object CategoryManualMapper {
|
||||
val CATEGORY_MAP = mapOf(
|
||||
"전체" to "ALL",
|
||||
"게임" to "GAME",
|
||||
"생산성" to "PRODUCTIVITY",
|
||||
"소셜" to "SOCIAL",
|
||||
"오디오" to "MUSIC",
|
||||
"쇼핑" to "SHOPPING",
|
||||
"금융" to "FINANCE",
|
||||
"비디오" to "VIDEO",
|
||||
"이미지" to "IMAGE",
|
||||
"지도" to "MAPS",
|
||||
"뉴스" to "NEWS",
|
||||
"도구" to "TOOLS",
|
||||
"기타" to "UNDEFINED"
|
||||
)
|
||||
|
||||
// 1. 고정 패키지명 매핑 (가장 높은 우선순위)
|
||||
private val manualMapping = mapOf(
|
||||
"com.sec.android.app.myfiles" to "TOOLS", // 삼성 내 파일
|
||||
"com.google.android.apps.nbu.files" to "TOOLS", // 구글 Files
|
||||
// VIDEO 카테고리
|
||||
"com.netflix.mediaclient" to "VIDEO",
|
||||
"net.cj.cjhv.gs.tving" to "VIDEO",
|
||||
"com.google.android.youtube" to "VIDEO",
|
||||
"com.disney.disneyplus" to "VIDEO",
|
||||
"com.coupang.mobile.play" to "VIDEO",
|
||||
"kr.co.captv.pooqV2" to "VIDEO", // wavve
|
||||
"com.synology.dsvideo" to "VIDEO",
|
||||
"org.videolan.vlc" to "VIDEO",
|
||||
"com.mxtech.videoplayer.ad" to "VIDEO",
|
||||
"com.gretech.gomplayerko" to "VIDEO",
|
||||
"com.google.android.videos" to "VIDEO", // Google TV
|
||||
|
||||
// MAPS / TRAVEL 카테고리
|
||||
"com.nhn.android.nmap" to "MAPS",
|
||||
"net.daum.android.map" to "MAPS",
|
||||
"com.google.android.apps.maps" to "MAPS",
|
||||
"com.skt.tmap.ku" to "MAPS",
|
||||
"com.locnall.KimGiSa" to "MAPS", // 카카오내비
|
||||
"com.agoda.mobile.consumer" to "MAPS",
|
||||
"com.airbnb.android" to "MAPS",
|
||||
"com.mrt.ducati" to "MAPS", // 마이리얼트립
|
||||
|
||||
// FINANCE 카테고리
|
||||
"viva.republica.toss" to "FINANCE",
|
||||
"com.kakaobank.channel" to "FINANCE",
|
||||
"com.btckorea.bithumb" to "FINANCE",
|
||||
"com.dunamu.exchange" to "FINANCE", // 업비트
|
||||
"com.truefriend.ministock" to "FINANCE",
|
||||
"com.truefriend.neosmartarenewal" to "FINANCE",
|
||||
"com.hyundaicard.appcard" to "FINANCE",
|
||||
"kr.co.samsungcard.mpocket" to "FINANCE",
|
||||
"com.kbcard.cxh.appcard" to "FINANCE",
|
||||
"com.kbstar.kbbank" to "FINANCE",
|
||||
"nh.smart.banking" to "FINANCE",
|
||||
"com.kbankwith.smartbank" to "FINANCE",
|
||||
|
||||
// SHOPPING 카테고리
|
||||
"com.coupang.mobile" to "SHOPPING",
|
||||
"com.coupang.mobile.eats" to "SHOPPING",
|
||||
"com.ebay.kr.gmarket" to "SHOPPING",
|
||||
"com.ebay.kr.auction" to "SHOPPING",
|
||||
"com.elevenst" to "SHOPPING",
|
||||
"net.giosis.shopping.sg" to "SHOPPING", // Qoo10
|
||||
"com.alibaba.aliexpresshd" to "SHOPPING",
|
||||
"com.einnovation.temu" to "SHOPPING",
|
||||
|
||||
// GAME 카테고리
|
||||
"com.sundaytoz.mobile.anisachun.google.service" to "GAME",
|
||||
"com.gof.global" to "GAME", // 화이트아웃서바이벌
|
||||
"com.tap4fun.odin.kingdomguard" to "GAME",
|
||||
"com.dreamgames.royalmatch" to "GAME",
|
||||
"com.ragequitgames.tomorrow" to "GAME"
|
||||
)
|
||||
|
||||
// 2. 키워드 기반 카테고리 매핑 (두 번째 우선순위)
|
||||
private val keywordMapping = mapOf(
|
||||
"VIDEO" to listOf(
|
||||
"video", "player", "movie", "tv", "cinema", "streaming", "media", "netflix",
|
||||
"비디오", "플레이어", "영화", "티브이", "시네마", "스트리밍", "넷플릭스", "티빙", "유튜브"
|
||||
),
|
||||
"GAME" to listOf(
|
||||
"game", "vulkan", "unity", "unreal", "nexon", "netmarble", "lineage",
|
||||
"게임", "넥슨", "넷마블", "카카오게임", "애니팡", "전투", "전략", "퍼즐", "RPG"
|
||||
),
|
||||
"FINANCE" to listOf(
|
||||
"bank", "card", "finance", "pay", "stock", "invest", "kb", "shinhan", "woori", "hana",
|
||||
"은행", "카드", "금융", "페이", "증권", "주식", "투자", "뱅크", "뱅킹", "보험", "자산"
|
||||
),
|
||||
"MUSIC" to listOf(
|
||||
"music", "audio", "sound", "radio", "melody", "streaming", "melon", "bugs",
|
||||
"뮤직", "음악", "오디오", "사운드", "라디오", "멜로디", "멜론", "벅스", "지니"
|
||||
),
|
||||
"MAPS" to listOf(
|
||||
"map", "navi", "taxi", "transport", "bus", "subway", "metro", "navigation",
|
||||
"지도", "내비", "택시", "교통", "버스", "지하철", "네비", "길찾기"
|
||||
),
|
||||
"IMAGE" to listOf(
|
||||
"photo", "gallery", "image", "camera", "editor", "snap", "pic",
|
||||
"사진", "갤러리", "이미지", "카메라", "편집", "스냅", "뷰어"
|
||||
),
|
||||
"SOCIAL" to listOf(
|
||||
"talk", "messenger", "chat", "social", "sns", "community", "kakao", "telegram",
|
||||
"톡", "메신저", "채팅", "소셜", "커뮤니티", "카카오", "텔레그램", "밴드", "카페"
|
||||
),
|
||||
"SHOPPING" to listOf(
|
||||
"shop", "mall", "market", "delivery", "eats", "order", "commerce", "coupang",
|
||||
"쇼핑", "몰", "마켓", "배달", "이츠", "주문", "커머스", "쿠팡", "마트", "백화점"
|
||||
),
|
||||
"TOOLS" to listOf(
|
||||
"file", "manager", "explorer", "cleaner", "storage", "calculator", "clock", "calendar",
|
||||
"파일", "관리자", "탐색기", "클리너", "저장소", "계산기", "시계", "달력", "메모"
|
||||
),
|
||||
)
|
||||
|
||||
/**
|
||||
* 패키지명, 앱 이름, 시스템 카테고리를 종합하여 최적의 카테고리를 반환합니다.
|
||||
*/
|
||||
fun getFixedCategory(packageName: String?, appName: String?, systemCategory: Int): String {
|
||||
// ---------------------------------------------------------
|
||||
// [단계 1] 이전의 수동 맵(manualMapping) 확인
|
||||
// ---------------------------------------------------------
|
||||
manualMapping[packageName]?.let { return it }
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// [단계 2] 시스템 분류(systemCategory)가 명확한지 확인
|
||||
// ---------------------------------------------------------
|
||||
val systemType = when (systemCategory) {
|
||||
ApplicationInfo.CATEGORY_GAME -> "GAME"
|
||||
ApplicationInfo.CATEGORY_VIDEO -> "VIDEO"
|
||||
ApplicationInfo.CATEGORY_MAPS -> "MAPS"
|
||||
ApplicationInfo.CATEGORY_AUDIO -> "MUSIC"
|
||||
ApplicationInfo.CATEGORY_PRODUCTIVITY -> "PRODUCTIVITY"
|
||||
ApplicationInfo.CATEGORY_SOCIAL -> "SOCIAL"
|
||||
ApplicationInfo.CATEGORY_NEWS -> "NEWS"
|
||||
ApplicationInfo.CATEGORY_IMAGE -> "IMAGE"
|
||||
else -> null
|
||||
}
|
||||
if (systemType != null) return systemType
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// [단계 3] 시스템 분류가 없는 경우 키워드 매핑 수행
|
||||
// ---------------------------------------------------------
|
||||
val pkg = packageName?.lowercase(Locale.ROOT) ?: ""
|
||||
val name = appName?.lowercase(Locale.ROOT)?.replace(" ", "") ?: ""
|
||||
|
||||
for ((category, keywords) in keywordMapping) {
|
||||
for (keyword in keywords) {
|
||||
if (pkg.contains(keyword) || name.contains(keyword)) {
|
||||
return category
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "UNDEFINED"
|
||||
}
|
||||
}
|
||||
@ -47,6 +47,7 @@ class AppInfoGetter : BaseGetter {
|
||||
this.searchIndex = SimpleTransliterater.makeSearchIndex(appName)
|
||||
this.pkgName = pkgName
|
||||
this.category = getCategory(ri.activityInfo.applicationInfo.category)
|
||||
this.systemCategoryInt = ri.activityInfo.applicationInfo.category
|
||||
this.alphaCho = AlphabetToChosungMap.getCho(appName)
|
||||
this.appNameChosung = JamoUtils.split(appName).joinToString("")
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user