Skip to content
This repository was archived by the owner on Mar 23, 2026. It is now read-only.

Commit ee3a56a

Browse files
committed
최근 앱 작업표시줄, 앱 즐겨찾기 창의 앱 아이콘 모양 커스텀 추가
1 parent e7c7569 commit ee3a56a

8 files changed

Lines changed: 370 additions & 33 deletions

File tree

app/src/main/java/com/minsoo/ultranavbar/core/NavbarAppsPanel.kt

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ class NavbarAppsPanel(
208208
val paddingPx = context.dpToPx(PANEL_PADDING_DP)
209209
val iconSizePx = context.dpToPx(ICON_SIZE_DP)
210210
val iconPaddingPx = context.dpToPx(ICON_PADDING_DP)
211+
val iconShape = SettingsManager.getInstance(context).recentAppsTaskbarIconShape
211212

212213
// 네비바와 동일한 배경색: 라이트=WHITE, 다크=BLACK
213214
val dark = isDarkMode()
@@ -244,7 +245,14 @@ class NavbarAppsPanel(
244245
var cellIndex = 0
245246
for (item in items) {
246247
val isShortcut = item.startsWith("shortcut:")
247-
val cellView = createAppCell(item, isShortcut, iconSizePx, iconPaddingPx, textColor)
248+
val cellView = createAppCell(
249+
item,
250+
isShortcut,
251+
iconSizePx,
252+
iconPaddingPx,
253+
textColor,
254+
iconShape
255+
)
248256
val col = cellIndex % GRID_COLUMNS
249257
val row = cellIndex / GRID_COLUMNS
250258
cellView.layoutParams = GridLayout.LayoutParams(
@@ -280,7 +288,8 @@ class NavbarAppsPanel(
280288
isShortcut: Boolean,
281289
iconSizePx: Int,
282290
paddingPx: Int,
283-
textColor: Int
291+
textColor: Int,
292+
iconShape: SettingsManager.RecentAppsTaskbarIconShape
284293
): View {
285294
val pm = context.packageManager
286295
val packageName = if (isShortcut) item.removePrefix("shortcut:") else item
@@ -312,12 +321,7 @@ class NavbarAppsPanel(
312321
setImageDrawable(icon)
313322
scaleType = ImageView.ScaleType.CENTER_CROP
314323
layoutParams = LinearLayout.LayoutParams(iconSizePx, iconSizePx)
315-
outlineProvider = object : ViewOutlineProvider() {
316-
override fun getOutline(view: View, outline: Outline) {
317-
outline.setOval(0, 0, view.width, view.height)
318-
}
319-
}
320-
clipToOutline = true
324+
applyIconShape(this, iconShape)
321325
}
322326

323327
val labelView = TextView(context).apply {
@@ -343,6 +347,37 @@ class NavbarAppsPanel(
343347
return cell
344348
}
345349

350+
private fun applyIconShape(
351+
iconView: ImageView,
352+
iconShape: SettingsManager.RecentAppsTaskbarIconShape
353+
) {
354+
iconView.outlineProvider = object : ViewOutlineProvider() {
355+
override fun getOutline(view: View, outline: Outline) {
356+
val width = view.width
357+
val height = view.height
358+
if (width <= 0 || height <= 0) return
359+
360+
when (iconShape) {
361+
SettingsManager.RecentAppsTaskbarIconShape.CIRCLE -> {
362+
outline.setOval(0, 0, width, height)
363+
}
364+
SettingsManager.RecentAppsTaskbarIconShape.SQUARE -> {
365+
outline.setRect(0, 0, width, height)
366+
}
367+
SettingsManager.RecentAppsTaskbarIconShape.SQUIRCLE -> {
368+
val radius = minOf(width, height) * 0.38f
369+
outline.setRoundRect(0, 0, width, height, radius)
370+
}
371+
SettingsManager.RecentAppsTaskbarIconShape.ROUNDED_RECT -> {
372+
val radius = minOf(width, height) * 0.22f
373+
outline.setRoundRect(0, 0, width, height, radius)
374+
}
375+
}
376+
}
377+
}
378+
iconView.clipToOutline = true
379+
}
380+
346381
@SuppressLint("ClickableViewAccessibility")
347382
private fun setupCellTouchListener(
348383
cell: View,

app/src/main/java/com/minsoo/ultranavbar/core/RecentAppsTaskbar.kt

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@ import android.view.View
1414
import android.view.ViewOutlineProvider
1515
import android.widget.ImageView
1616
import android.widget.LinearLayout
17+
import com.minsoo.ultranavbar.settings.SettingsManager
1718
import kotlin.math.abs
1819
import kotlin.math.sqrt
1920

2021
/**
2122
* 최근 앱 작업 표시줄 UI 관리
2223
*
23-
* 중앙 LinearLayout에 원형 앱 아이콘 표시
24+
* 중앙 LinearLayout에 앱 아이콘 표시
2425
* 탭: 앱 전환
2526
* 길게 누른 뒤 드래그: 분할화면 실행
2627
*/
@@ -46,11 +47,16 @@ class RecentAppsTaskbar(
4647
fun onDragStart(iconView: ImageView, screenX: Float, screenY: Float) // 드래그 시작 (아이콘 정보 전달)
4748
fun onDragEnd() // 드래그 종료
4849
fun shouldIgnoreTouch(toolType: Int): Boolean
50+
fun isSplitDragAllowed(): Boolean
4951
}
5052

5153
/** 분할화면 드래그 활성화 여부 (false면 탭만 가능) */
5254
var splitScreenEnabled: Boolean = false
5355

56+
/** 최근 앱 아이콘 모양 */
57+
var iconShape: SettingsManager.RecentAppsTaskbarIconShape =
58+
SettingsManager.RecentAppsTaskbarIconShape.SQUARE
59+
5460
private var centerGroup: LinearLayout? = null
5561
private val iconViews = mutableListOf<ImageView>()
5662
private var currentApps = listOf<RecentAppsManager.RecentAppInfo>()
@@ -100,7 +106,7 @@ class RecentAppsTaskbar(
100106
// 새 아이콘 추가
101107
val sizePx = context.dpToPx(Constants.Dimension.TASKBAR_ICON_SIZE_DP)
102108
for (app in apps) {
103-
val iconView = createCircularIconView(app, sizePx)
109+
val iconView = createIconView(app, sizePx)
104110
setupTouchListener(iconView, app)
105111
group.addView(iconView)
106112
iconViews.add(iconView)
@@ -126,13 +132,20 @@ class RecentAppsTaskbar(
126132
}
127133

128134
/**
129-
* 원형 아이콘 뷰 생성
135+
* 아이콘 뷰 생성
130136
*/
131-
private fun createCircularIconView(
137+
private fun createIconView(
132138
app: RecentAppsManager.RecentAppInfo,
133139
sizePx: Int
134140
): ImageView {
135141
val spacingPx = context.dpToPx(Constants.Dimension.TASKBAR_ICON_SPACING_DP / 2)
142+
val shapeMode = iconShape
143+
val cornerRadiusPx = when (shapeMode) {
144+
SettingsManager.RecentAppsTaskbarIconShape.CIRCLE -> sizePx / 2f
145+
SettingsManager.RecentAppsTaskbarIconShape.SQUARE -> 0f
146+
SettingsManager.RecentAppsTaskbarIconShape.SQUIRCLE -> sizePx * 0.38f
147+
SettingsManager.RecentAppsTaskbarIconShape.ROUNDED_RECT -> sizePx * 0.22f
148+
}
136149

137150
return ImageView(context).apply {
138151
val params = LinearLayout.LayoutParams(sizePx, sizePx).apply {
@@ -144,10 +157,26 @@ class RecentAppsTaskbar(
144157
setImageDrawable(app.icon)
145158
contentDescription = app.label
146159

147-
// 원형 클리핑
160+
// 모양별 클리핑
148161
outlineProvider = object : ViewOutlineProvider() {
149162
override fun getOutline(view: View, outline: Outline) {
150-
outline.setOval(0, 0, view.width, view.height)
163+
val width = view.width
164+
val height = view.height
165+
if (width <= 0 || height <= 0) {
166+
return
167+
}
168+
when (shapeMode) {
169+
SettingsManager.RecentAppsTaskbarIconShape.CIRCLE -> {
170+
outline.setOval(0, 0, width, height)
171+
}
172+
SettingsManager.RecentAppsTaskbarIconShape.SQUARE -> {
173+
outline.setRect(0, 0, width, height)
174+
}
175+
SettingsManager.RecentAppsTaskbarIconShape.SQUIRCLE,
176+
SettingsManager.RecentAppsTaskbarIconShape.ROUNDED_RECT -> {
177+
outline.setRoundRect(0, 0, width, height, cornerRadiusPx)
178+
}
179+
}
151180
}
152181
}
153182
clipToOutline = true
@@ -158,7 +187,20 @@ class RecentAppsTaskbar(
158187
// Ripple 효과
159188
val rippleColor = ColorStateList.valueOf(0x33808080)
160189
val maskDrawable = GradientDrawable().apply {
161-
shape = GradientDrawable.OVAL
190+
when (shapeMode) {
191+
SettingsManager.RecentAppsTaskbarIconShape.CIRCLE -> {
192+
shape = GradientDrawable.OVAL
193+
}
194+
SettingsManager.RecentAppsTaskbarIconShape.SQUARE -> {
195+
shape = GradientDrawable.RECTANGLE
196+
cornerRadius = 0f
197+
}
198+
SettingsManager.RecentAppsTaskbarIconShape.SQUIRCLE,
199+
SettingsManager.RecentAppsTaskbarIconShape.ROUNDED_RECT -> {
200+
shape = GradientDrawable.RECTANGLE
201+
cornerRadius = cornerRadiusPx
202+
}
203+
}
162204
setColor(android.graphics.Color.GRAY)
163205
}
164206
foreground = RippleDrawable(rippleColor, null, maskDrawable)
@@ -184,7 +226,7 @@ class RecentAppsTaskbar(
184226
val splitTriggerPx = context.dpToPx(SPLIT_TRIGGER_DP)
185227

186228
val longPressRunnable = Runnable {
187-
if (!splitScreenEnabled || hasMoved) {
229+
if (!splitScreenEnabled || hasMoved || !listener.isSplitDragAllowed()) {
188230
return@Runnable
189231
}
190232
longPressTriggered = true

app/src/main/java/com/minsoo/ultranavbar/overlay/NavBarOverlay.kt

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,11 @@ class NavBarOverlay(private val service: NavBarAccessibilityService) {
253253
override fun shouldIgnoreTouch(toolType: Int): Boolean {
254254
return settings.ignoreStylus && toolType == MotionEvent.TOOL_TYPE_STYLUS
255255
}
256+
257+
override fun isSplitDragAllowed(): Boolean {
258+
val launchContext = service.getSplitLaunchContext()
259+
return !launchContext.isOnHomeScreen && settings.splitScreenTaskbarEnabled
260+
}
256261
}
257262

258263
private val navbarAppsPanelListener = object : NavbarAppsPanel.PanelActionListener {
@@ -503,6 +508,7 @@ class NavBarOverlay(private val service: NavBarAccessibilityService) {
503508
recentAppsManager = RecentAppsManager(context, recentAppsListener)
504509
recentAppsTaskbar = RecentAppsTaskbar(context, taskbarListener).apply {
505510
splitScreenEnabled = settings.splitScreenTaskbarEnabled
511+
iconShape = settings.recentAppsTaskbarIconShape
506512
}
507513
}
508514

@@ -691,8 +697,7 @@ class NavBarOverlay(private val service: NavBarAccessibilityService) {
691697
bar.addView(centerView, centerParams)
692698
centerGroupView = centerView
693699

694-
// 홈화면이면 숨김
695-
if (isOnHomeScreen || isRecentsVisible) {
700+
if (!shouldShowTaskbar()) {
696701
centerView.visibility = View.GONE
697702
}
698703
}
@@ -1236,8 +1241,12 @@ class NavBarOverlay(private val service: NavBarAccessibilityService) {
12361241
clearHomeExitSuppression()
12371242
Log.d(TAG, "Home screen state: true")
12381243

1239-
// 홈화면에서는 최근 앱 작업 표시줄 숨김 (애니메이션 적용)
1240-
animateTaskbarExit()
1244+
if (settings.recentAppsTaskbarShowOnHome) {
1245+
syncTaskbarVisibility(animate = true)
1246+
} else {
1247+
// 홈화면에서는 최근 앱 작업 표시줄 숨김 (애니메이션 적용)
1248+
animateTaskbarExit()
1249+
}
12411250

12421251
// 언락 페이드 중에는 배경 전환하지 않음 (언락 완료 후 자동 업데이트)
12431252
if (isUnlockPending || isUnlockFadeRunning || isUnlockFadeSuppressed) {
@@ -1283,7 +1292,11 @@ class NavBarOverlay(private val service: NavBarAccessibilityService) {
12831292

12841293
// 홈 -> 앱 전환 시 진입 애니메이션이 항상 보이도록
12851294
// 한 프레임 리셋 후 재생
1286-
playTaskbarEntryFromHomeIfNeeded()
1295+
if (settings.recentAppsTaskbarShowOnHome) {
1296+
syncTaskbarVisibility(animate = true)
1297+
} else {
1298+
playTaskbarEntryFromHomeIfNeeded()
1299+
}
12871300

12881301
updateNavBarBackground()
12891302
}
@@ -1679,7 +1692,7 @@ class NavBarOverlay(private val service: NavBarAccessibilityService) {
16791692
isRecentsVisible = false
16801693
updateNavBarBackground()
16811694
if (isOnHomeScreen) {
1682-
hideTaskbarImmediate()
1695+
syncTaskbarVisibility(animate = true)
16831696
} else {
16841697
val task = Runnable {
16851698
pendingRecentsClose = null
@@ -1717,7 +1730,8 @@ class NavBarOverlay(private val service: NavBarAccessibilityService) {
17171730

17181731
private fun shouldShowTaskbar(): Boolean {
17191732
if (!settings.recentAppsTaskbarEnabled) return false
1720-
if (isOnHomeScreen || isRecentsVisible || isHomeExitPending) return false
1733+
if (isRecentsVisible || isHomeExitPending) return false
1734+
if (isOnHomeScreen) return settings.recentAppsTaskbarShowOnHome
17211735
if (currentPackage.isEmpty()) return false
17221736
if (cachedLauncherPackages.contains(currentPackage)) return false
17231737
return true
@@ -2228,19 +2242,13 @@ class NavBarOverlay(private val service: NavBarAccessibilityService) {
22282242
val iconSize = iconView.width
22292243
if (iconSize <= 0) return
22302244

2231-
// 아이콘 drawable 복사 + 원형 클리핑 적용
2245+
// 아이콘 drawable 복사 + 모양 클리핑 적용
22322246
val drawable = iconView.drawable?.constantState?.newDrawable()?.mutate() ?: return
22332247

22342248
val icon = ImageView(context).apply {
22352249
setImageDrawable(drawable)
22362250
scaleType = ImageView.ScaleType.CENTER_CROP
2237-
// 원형 클리핑 (원본 아이콘과 동일)
2238-
outlineProvider = object : android.view.ViewOutlineProvider() {
2239-
override fun getOutline(view: View, outline: android.graphics.Outline) {
2240-
outline.setOval(0, 0, view.width, view.height)
2241-
}
2242-
}
2243-
clipToOutline = true
2251+
applyDragIconShape(this, settings.recentAppsTaskbarIconShape)
22442252
}
22452253
dragIconView = icon
22462254

@@ -2287,6 +2295,37 @@ class NavBarOverlay(private val service: NavBarAccessibilityService) {
22872295
}
22882296
}
22892297

2298+
private fun applyDragIconShape(
2299+
icon: ImageView,
2300+
shapeMode: SettingsManager.RecentAppsTaskbarIconShape
2301+
) {
2302+
icon.outlineProvider = object : android.view.ViewOutlineProvider() {
2303+
override fun getOutline(view: View, outline: android.graphics.Outline) {
2304+
val width = view.width
2305+
val height = view.height
2306+
if (width <= 0 || height <= 0) return
2307+
2308+
when (shapeMode) {
2309+
SettingsManager.RecentAppsTaskbarIconShape.CIRCLE -> {
2310+
outline.setOval(0, 0, width, height)
2311+
}
2312+
SettingsManager.RecentAppsTaskbarIconShape.SQUARE -> {
2313+
outline.setRect(0, 0, width, height)
2314+
}
2315+
SettingsManager.RecentAppsTaskbarIconShape.SQUIRCLE -> {
2316+
val radius = minOf(width, height) * 0.38f
2317+
outline.setRoundRect(0, 0, width, height, radius)
2318+
}
2319+
SettingsManager.RecentAppsTaskbarIconShape.ROUNDED_RECT -> {
2320+
val radius = minOf(width, height) * 0.22f
2321+
outline.setRoundRect(0, 0, width, height, radius)
2322+
}
2323+
}
2324+
}
2325+
}
2326+
icon.clipToOutline = true
2327+
}
2328+
22902329
/**
22912330
* 드래그 중 아이콘 위치/스케일 업데이트
22922331
* dragFreeMove=true: X/Y 모두 손가락 추적 (앱 즐겨찾기)
@@ -2371,12 +2410,14 @@ class NavBarOverlay(private val service: NavBarAccessibilityService) {
23712410
recentAppsManager = RecentAppsManager(context, recentAppsListener)
23722411
recentAppsTaskbar = RecentAppsTaskbar(context, taskbarListener).apply {
23732412
splitScreenEnabled = settings.splitScreenTaskbarEnabled
2413+
iconShape = settings.recentAppsTaskbarIconShape
23742414
}
23752415
recentAppsManager?.setLauncherPackages(cachedLauncherPackages)
23762416
recentAppsManager?.loadInitialRecentApps()
23772417
} else {
23782418
// 설정 변경 시 분할화면 플래그 업데이트
23792419
recentAppsTaskbar?.splitScreenEnabled = settings.splitScreenTaskbarEnabled
2420+
recentAppsTaskbar?.iconShape = settings.recentAppsTaskbarIconShape
23802421
}
23812422
} else {
23822423
recentAppsManager?.clear()
@@ -2410,6 +2451,7 @@ class NavBarOverlay(private val service: NavBarAccessibilityService) {
24102451
}
24112452

24122453
updateNavBarBackground()
2454+
syncTaskbarVisibility(animate = false)
24132455
buttonManager.updatePanelButtonState(isPanelOpen())
24142456
}
24152457

0 commit comments

Comments
 (0)