diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 564d2c85..57a1bcae 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -99,6 +99,8 @@ android:screenOrientation="userPortrait" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|screenLayout|layoutDirection|navigation" android:windowSoftInputMode="adjustResize" + android:noHistory="true" + android:excludeFromRecents="true" android:exported="false"> @@ -118,7 +120,9 @@ > ${intent}") - if(intent?.action?.equals(Intent.ACTION_SEND) == true && - intent?.hasExtra(Intent.EXTRA_TEXT) == true) { - intent?.getStringExtra(Intent.EXTRA_TEXT)?.let { - if(it.startsWith("http") == false) { - it.split("http").forEach { string -> - if(string.startsWith("http")) { - try { - string.toUri()?.let { uri -> - Blog.LOGE("onNewIntent string uri.lastPathSegment >>>>> ${uri.host}") - Blog.LOGE("onNewIntent string uri.lastPathSegment >>>>> ${uri.lastPathSegment}") - } - } catch (e: Exception) { +// if(intent?.action?.equals(Intent.ACTION_SEND) == true && +// intent?.hasExtra(Intent.EXTRA_TEXT) == true) { +// intent?.getStringExtra(Intent.EXTRA_TEXT)?.let { +// if(it.startsWith("http") == false) { +// it.split("http").forEach { string -> +// if(string.startsWith("http")) { +// try { +// string.toUri()?.let { uri -> +// Blog.LOGE("onNewIntent string uri.lastPathSegment >>>>> ${uri.host}") +// Blog.LOGE("onNewIntent string uri.lastPathSegment >>>>> ${uri.lastPathSegment}") +// } +// } catch (e: Exception) { +// +// } +// } +// } +// } else { +// try { +// it.toUri()?.let { uri -> +// Blog.LOGE("onNewIntent it uri.lastPathSegment >>>>> ${uri.host}") +// Blog.LOGE("onNewIntent it uri.lastPathSegment >>>>> ${uri.lastPathSegment}") +// } +// } catch (e: Exception) { +// +// } +// } +// } +// } else if (intent?.action == Intent.ACTION_WEB_SEARCH) { +// openWithIntent(intent) +// } else { - } - } - } - } else { - try { - it.toUri()?.let { uri -> - Blog.LOGE("onNewIntent it uri.lastPathSegment >>>>> ${uri.host}") - Blog.LOGE("onNewIntent it uri.lastPathSegment >>>>> ${uri.lastPathSegment}") - } - } catch (e: Exception) { +// Blog.LOGE("onNewIntent intent?.hasExtra >> ${intent?.hasExtra(Intent.EXTRA_STREAM)}") +//&& intent.hasExtra("android.intent.extra.EXTRA_START_REASON") && intent.getStringExtra( +// "android.intent.extra.EXTRA_START_REASON" +// ).equals("startDockOrHome") +// ) - } - } + try { + val fragment = supportFragmentManager.findFragmentByTag(AppDrawerBottomSheet.TAG) + Blog.LOGE("fragment >>> $fragment") + if (fragment is AppDrawerBottomSheet) { + fragment.dismiss() } - } else if (intent?.action == Intent.ACTION_WEB_SEARCH) { - openWithIntent(intent) - } else { - Blog.LOGE("onNewIntent intent?.hasExtra >> ${intent?.hasExtra(Intent.EXTRA_STREAM)}") - - if (intent?.action?.equals(Intent.ACTION_MAIN) == true && intent.categories.contains( - Intent.CATEGORY_HOME - ) && intent.hasExtra("android.intent.extra.EXTRA_START_REASON") && intent.getStringExtra( - "android.intent.extra.EXTRA_START_REASON" - ).equals("startDockOrHome") - ) { + Intent.CATEGORY_HOME)) { } else { intent?.extras?.keySet()?.forEach { @@ -287,7 +297,11 @@ open class LauncherActivity : CommonActivity() { } } + } catch (e : Exception) { + e.printStackTrace() } + +// } super.onNewIntent(intent) } @@ -341,12 +355,13 @@ open class LauncherActivity : CommonActivity() { } // 위젯 관련 변수 private var appWidgetManager: AppWidgetManager? = null - private var appWidgetHost: AppWidgetHost? = null + private var appWidgetHost: WidgetHost? = null private val APPWIDGET_HOST_ID = 1024 private val REQUEST_PICK_APPWIDGET = 100 private val REQUEST_CREATE_APPWIDGET = 101 + fun showAppDrawer() { val bottomSheet = AppDrawerBottomSheet.newInstance() bottomSheet.show(supportFragmentManager, AppDrawerBottomSheet.TAG) @@ -357,15 +372,20 @@ open class LauncherActivity : CommonActivity() { override fun onCreate(savedInstanceState: Bundle?) { installSplashScreen() super.onCreate(savedInstanceState) - try { - YoutubeDL.getInstance().init(this) - FFmpeg.getInstance().init(this); - CoroutineScope(Dispatchers.IO).launch { - YoutubeDL.getInstance().updateYoutubeDL(this@LauncherActivity) - } - } catch (e: YoutubeDLException) { - Blog.LOGE("failed to initialize youtubedl-android", e) - } +// try { +// YoutubeDL.getInstance().init(this) +// FFmpeg.getInstance().init(this); +// CoroutineScope(Dispatchers.IO).launch { +// try { +// YoutubeDL.getInstance().updateYoutubeDL(this@LauncherActivity) +// }catch (e : Exception) { +// +// } +// +// } +// } catch (e: YoutubeDLException) { +// Blog.LOGE("failed to initialize youtubedl-android", e) +// } val intent = Intent(this, ForeGroundService::class.java) this.startForegroundService(intent) @@ -376,7 +396,7 @@ open class LauncherActivity : CommonActivity() { window.statusBarColor = Color.TRANSPARENT // (선택 사항) 내비게이션 바도 투명하게 하고 싶다면 - window.navigationBarColor = Color.TRANSPARENT + window.navigationBarColor = Color.TRANSPARENT val nlService = Intent(this, NLService::class.java) this.startService(nlService) @@ -444,7 +464,7 @@ open class LauncherActivity : CommonActivity() { view.layoutParams = params // AppWidgetHostView에 크기 변경 알림 (위젯 내용물 재배치 유도) - (view as? AppWidgetHostView)?.updateAppWidgetSize(null, params.width, params.height, params.width, params.height) + return true // 이벤트를 처리했음을 알림 } @@ -453,6 +473,29 @@ open class LauncherActivity : CommonActivity() { }) } + private fun updateWidgetOptions(appWidgetId: Int, hostView: AppWidgetHostView) { + val width = hostView.layoutParams.width + val height = hostView.layoutParams.height + + // DP 단위로 변환 (시스템은 픽셀이 아니라 DP를 기준으로 레이아웃을 결정함) + val density = resources.displayMetrics.density + val widthDp = (width / density).toInt() + val heightDp = (height / density).toInt() + + val options = Bundle().apply { + putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, widthDp) + putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, heightDp) + putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, widthDp) + putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, heightDp) + } + + // 시스템 매니저에 현재 위젯의 새로운 크기 정보를 동기화 + appWidgetManager?.updateAppWidgetOptions(appWidgetId, options) + + // 호스트 뷰에게도 크기가 변했으니 다시 그리라고 명령 (콘텐츠 유실 방지) + hostView.updateAppWidgetSize(options, widthDp, heightDp, widthDp, heightDp) + } + override fun onPause() { super.onPause() @@ -508,6 +551,30 @@ open class LauncherActivity : CommonActivity() { private var lastTouchX = 0f private var lastTouchY = 0f + private fun reconfigureWidget(widgetId: Int) { + val appWidgetInfo = appWidgetManager?.getAppWidgetInfo(widgetId) + + // 1. 해당 위젯이 설정 액티비티를 가지고 있는지 확인 + if (appWidgetInfo?.configure != null) { + try { + // 2. AppWidgetHost를 통해 설정 액티비티 실행 + // REQUEST_CREATE_APPWIDGET을 재사용하거나 별도의 코드를 지정하세요. + appWidgetHost?.startAppWidgetConfigureActivityForResult( + this, + widgetId, + 0, + REQUEST_CREATE_APPWIDGET, + null + ) + } catch (e: Exception) { + e.printStackTrace() + showToast("설정 화면을 열 수 없습니다.") + } + } else { + showToast("이 위젯은 설정을 지원하지 않습니다.") + } + } + // 위젯 뷰 생성 및 화면 배치 private fun createWidget(data: Intent?, savedWidgetData: WidgetData? = null) { val appWidgetId = savedWidgetData?.appWidgetId ?: data?.extras?.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) ?: return @@ -551,9 +618,12 @@ open class LauncherActivity : CommonActivity() { // 새로 추가된 위젯이라면 DB에 저장 if (savedWidgetData == null) { saveWidgetState(appWidgetId, hostView!!.x, hostView.y, width, height, appWidgetInfo.provider.className) + } else { + } } + // 위젯 정보 DB 저장 private fun saveWidgetState(id: Int, x: Float, y: Float, w: Int, h: Int, name: String) { WorkersDb.getRealm().writeBlocking { @@ -575,7 +645,7 @@ open class LauncherActivity : CommonActivity() { createWidget(null, widgetData) } } - lateinit var scaleDetector: android.view.ScaleGestureDetector + lateinit var scaleDetector: android.view.ScaleGestureDetector override fun dispatchTouchEvent(ev: MotionEvent?): Boolean { if (ev == null) return super.dispatchTouchEvent(ev) @@ -846,3 +916,4 @@ open class LauncherActivity : CommonActivity() { // lActivity?.startActivity(mapIntent) //} // + diff --git a/app/src/main/kotlin/bums/lunatic/launcher/WidgetWorkspaceLayout.kt b/app/src/main/kotlin/bums/lunatic/launcher/WidgetWorkspaceLayout.kt new file mode 100644 index 00000000..cdf416fd --- /dev/null +++ b/app/src/main/kotlin/bums/lunatic/launcher/WidgetWorkspaceLayout.kt @@ -0,0 +1,250 @@ +package bums.lunatic.launcher + +import android.annotation.SuppressLint +import android.app.Activity +import android.appwidget.AppWidgetHost +import android.appwidget.AppWidgetHostView +import android.appwidget.AppWidgetManager +import android.content.Context +import android.content.Intent +import android.graphics.Rect +import android.os.Bundle +import android.util.AttributeSet +import android.view.MotionEvent +import android.view.ScaleGestureDetector +import android.view.View +import android.widget.FrameLayout +import androidx.activity.ComponentActivity +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts +import bums.lunatic.launcher.model.WidgetData + +interface OnWidgetUpdateListener { + fun onStateChanged(widgetId: Int, x: Float, y: Float, width: Int, height: Int) + fun onDeleted(widgetId: Int) + fun onLongPress(view: View) // 추가 메뉴(설정 등) 띄우기용 +} + +class WidgetWorkspaceLayout @JvmOverloads constructor( + context: android.content.Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 +) : FrameLayout(context, attrs, defStyleAttr) { + + private val APPWIDGET_HOST_ID = 1024 + private var appWidgetManager = AppWidgetManager.getInstance(context) + private var appWidgetHost = AppWidgetHost(context, APPWIDGET_HOST_ID) + + private var isDragging = false + private var currentView: View? = null + private var lastX = 0f + private var lastY = 0f + + private var deleteZone: View? = null + + // Activity로부터 전달받을 결과 처리기 (캡슐화의 핵심) + private lateinit var pickLauncher: ActivityResultLauncher + private lateinit var configLauncher: ActivityResultLauncher + + private var updateListener: OnWidgetUpdateListener? = null + + // 1. 뷰가 화면에 붙을 때 호스트 리스닝 시작 + override fun onAttachedToWindow() { + super.onAttachedToWindow() + appWidgetHost.startListening() + setupResultLaunchers() // 결과 처리기 초기화 + } + + // 2. 뷰가 떨어질 때 리스닝 중지 (메모리 누수 방지) + override fun onDetachedFromWindow() { + super.onDetachedFromWindow() + appWidgetHost.stopListening() + } + + // 3. Activity Result 처리를 뷰 내부로 가져오기 + private fun setupResultLaunchers() { + val activity = context as? ComponentActivity ?: return + val registry = activity.activityResultRegistry + + // 위젯 선택 결과 처리 + pickLauncher = registry.register("pick_widget", ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode == Activity.RESULT_OK) { + handleWidgetPickResult(result.data) + } + } + + // 위젯 설정 결과 처리 + configLauncher = registry.register("config_widget", ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode == Activity.RESULT_OK) { + createWidgetView(result.data?.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) ?: -1) + } + } + } + + // 4. 새 위젯 추가 프로세스 시작 (Activity 호출 필요 없음) + fun addNewWidget() { + val appWidgetId = appWidgetHost.allocateAppWidgetId() + val pickIntent = Intent(AppWidgetManager.ACTION_APPWIDGET_PICK).apply { + putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) + } + pickLauncher.launch(pickIntent) + } + + private fun handleWidgetPickResult(data: Intent?) { + val appWidgetId = data?.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) ?: return + val appWidgetInfo = appWidgetManager.getAppWidgetInfo(appWidgetId) + + if (appWidgetInfo?.configure != null) { + // 설정창이 있는 위젯이면 설정창 실행 + val intent = Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE).apply { + component = appWidgetInfo.configure + putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) + } + configLauncher.launch(intent) + } else { + // 설정창 없으면 바로 생성 + createWidgetView(appWidgetId) + } + } + + // 5. 위젯 뷰 생성 및 레이아웃 배치 로직 + fun createWidgetView(appWidgetId: Int, savedData: WidgetData? = null) { + val info = appWidgetManager.getAppWidgetInfo(appWidgetId) ?: return + val hostView = appWidgetHost.createView(context, appWidgetId, info) + hostView.setAppWidget(appWidgetId, info) + hostView.tag = appWidgetId + + // 위치 및 크기 설정 (savedData가 있으면 복구, 없으면 초기화) + val params = LayoutParams( + savedData?.width ?: info.minWidth, + savedData?.height ?: info.minHeight + ) + hostView.layoutParams = params + hostView.x = savedData?.x ?: 100f + hostView.y = savedData?.y ?: 100f + + addView(hostView) + + // 새 위젯이라면 DB 저장 알림 + if (savedData == null) { + updateListener?.onStateChanged(appWidgetId, hostView.x, hostView.y, params.width, params.height) + } + } + + private val scaleDetector = ScaleGestureDetector(context, object : ScaleGestureDetector.SimpleOnScaleGestureListener() { + override fun onScale(detector: ScaleGestureDetector): Boolean { + currentView?.let { view -> + val params = view.layoutParams + params.width = (params.width * detector.scaleFactor).toInt().coerceAtLeast(150) + params.height = (params.height * detector.scaleFactor).toInt().coerceAtLeast(150) + view.layoutParams = params + return true + } + return false + } + }) + fun setup(listener: OnWidgetUpdateListener, deleteZoneView: View) { + this.updateListener = listener + this.deleteZone = deleteZoneView + } + + // 자식 위젯의 클릭을 방해하지 않으면서 드래그 시점에 이벤트를 뺏어옴 + override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { + if (ev.action == MotionEvent.ACTION_DOWN) { + // 자식 뷰(위젯) 중 터치 지점에 있는 것을 찾음 + currentView = findChildByPos(ev.x, ev.y) + } + return isDragging || super.onInterceptTouchEvent(ev) + } + + @SuppressLint("ClickableViewAccessibility") + override fun onTouchEvent(event: MotionEvent): Boolean { + scaleDetector.onTouchEvent(event) + if (scaleDetector.isInProgress) return true + + when (event.actionMasked) { + MotionEvent.ACTION_MOVE -> { + if (isDragging && currentView != null) { + if (lastX != 0f && lastY != 0f) { + currentView!!.x += event.rawX - lastX + currentView!!.y += event.rawY - lastY + } + lastX = event.rawX + lastY = event.rawY + + // 삭제 영역 피드백 (UI 로직도 여기서 처리) + checkDeleteZoneFeedback() + } + } + MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { + finalizeWidgetAction() + } + } + return true + } + + private fun finalizeWidgetAction() { + currentView?.let { view -> + val widgetId = view.tag as? Int ?: return@let + + if (isInsideDeleteZone(view)) { + updateListener?.onDeleted(widgetId) + removeView(view) + } else { + // 시스템 옵션 동기화 (콘텐츠 유실 방지 핵심) + syncSystemOptions(widgetId, view as AppWidgetHostView) + + // 액티비티에는 "저장해라"라고 결과만 통보 + updateListener?.onStateChanged(widgetId, view.x, view.y, view.width, view.height) + } + + view.animate().scaleX(1.0f).scaleY(1.0f).setDuration(100).start() + } + + deleteZone?.visibility = View.GONE + isDragging = false + currentView = null + lastX = 0f + lastY = 0f + } + + // 위젯 롱클릭 시 드래그 모드 진입 (Activity에서 호출할 함수) + fun startDrag(view: View) { + this.currentView = view + this.isDragging = true + this.deleteZone?.visibility = View.VISIBLE + view.animate().scaleX(1.05f).scaleY(1.05f).setDuration(100).start() + } + + private fun syncSystemOptions(widgetId: Int, hostView: AppWidgetHostView) { + val density = resources.displayMetrics.density + val wDp = (hostView.width / density).toInt() + val hDp = (hostView.height / density).toInt() + + val options = Bundle().apply { + putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, wDp) + putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, hDp) + } + appWidgetManager?.updateAppWidgetOptions(widgetId, options) + hostView.updateAppWidgetSize(options, wDp, hDp, wDp, hDp) + } + + private fun isInsideDeleteZone(view: View): Boolean { + if (deleteZone == null) return false + val rect = Rect() + deleteZone!!.getGlobalVisibleRect(rect) + return rect.contains(view.x.toInt() + view.width / 2, view.y.toInt() + view.height / 2) + } + + private fun checkDeleteZoneFeedback() { + // 삭제 영역 위일 때 배경색 변경 등 피드백 로직 + } + + private fun findChildByPos(x: Float, y: Float): View? { + for (i in childCount - 1 downTo 0) { + val child = getChildAt(i) + if (x >= child.x && x <= child.x + child.width && y >= child.y && y <= child.y + child.height) { + return child + } + } + return null + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/bums/lunatic/launcher/home/NeoRssActivity.kt b/app/src/main/kotlin/bums/lunatic/launcher/home/NeoRssActivity.kt index 93564b05..900b3121 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/home/NeoRssActivity.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/home/NeoRssActivity.kt @@ -298,67 +298,67 @@ open class NeoRssActivity : CommonActivity() { showContents(v.id) } - override fun onNewIntent(intent: Intent) { - Blog.LOGE("onNewIntent intent >> ${intent}") - if(intent?.action?.equals(Intent.ACTION_SEND) == true && - intent?.hasExtra(Intent.EXTRA_TEXT) == true) { - intent?.getStringExtra(Intent.EXTRA_TEXT)?.let { - if(it.startsWith("http") == false) { - it.split("http").forEach { string -> - if(string.startsWith("http")) { - try { - string.toUri()?.let { uri -> - Blog.LOGE("onNewIntent string uri.lastPathSegment >>>>> ${uri.host}") - Blog.LOGE("onNewIntent string uri.lastPathSegment >>>>> ${uri.lastPathSegment}") - } - } catch (e: Exception) { - - } - } - } - } else { - try { - it.toUri()?.let { uri -> - Blog.LOGE("onNewIntent it uri.lastPathSegment >>>>> ${uri.host}") - Blog.LOGE("onNewIntent it uri.lastPathSegment >>>>> ${uri.lastPathSegment}") - } - } catch (e: Exception) { - - } - } - } - } else if (intent?.action == Intent.ACTION_WEB_SEARCH) { - openWithIntent(intent) - } else { - Blog.LOGE("onNewIntent intent?.hasExtra >> ${intent?.hasExtra(Intent.EXTRA_STREAM)}") - - - if (intent?.action?.equals(Intent.ACTION_MAIN) == true && intent.categories.contains( - Intent.CATEGORY_HOME - ) && intent.hasExtra("android.intent.extra.EXTRA_START_REASON") && intent.getStringExtra( - "android.intent.extra.EXTRA_START_REASON" - ).equals("startDockOrHome") - ) { - - } else { - intent?.extras?.keySet()?.forEach { - try { - Blog.LOGE( - "onNewIntent :: key >> ${it} :: value >> ${ - intent?.extras?.getString( - it - ) - }" - ) - } catch (e: Exception) { - e.printStackTrace() - } - - } - } - } - super.onNewIntent(intent) - } +// override fun onNewIntent(intent: Intent) { +// Blog.LOGE("onNewIntent intent >> ${intent}") +// if(intent?.action?.equals(Intent.ACTION_SEND) == true && +// intent?.hasExtra(Intent.EXTRA_TEXT) == true) { +// intent?.getStringExtra(Intent.EXTRA_TEXT)?.let { +// if(it.startsWith("http") == false) { +// it.split("http").forEach { string -> +// if(string.startsWith("http")) { +// try { +// string.toUri()?.let { uri -> +// Blog.LOGE("onNewIntent string uri.lastPathSegment >>>>> ${uri.host}") +// Blog.LOGE("onNewIntent string uri.lastPathSegment >>>>> ${uri.lastPathSegment}") +// } +// } catch (e: Exception) { +// +// } +// } +// } +// } else { +// try { +// it.toUri()?.let { uri -> +// Blog.LOGE("onNewIntent it uri.lastPathSegment >>>>> ${uri.host}") +// Blog.LOGE("onNewIntent it uri.lastPathSegment >>>>> ${uri.lastPathSegment}") +// } +// } catch (e: Exception) { +// +// } +// } +// } +// } else if (intent?.action == Intent.ACTION_WEB_SEARCH) { +// openWithIntent(intent) +// } else { +// Blog.LOGE("onNewIntent intent?.hasExtra >> ${intent?.hasExtra(Intent.EXTRA_STREAM)}") +// +// +// if (intent?.action?.equals(Intent.ACTION_MAIN) == true && intent.categories.contains( +// Intent.CATEGORY_HOME +// ) && intent.hasExtra("android.intent.extra.EXTRA_START_REASON") && intent.getStringExtra( +// "android.intent.extra.EXTRA_START_REASON" +// ).equals("startDockOrHome") +// ) { +// +// } else { +// intent?.extras?.keySet()?.forEach { +// try { +// Blog.LOGE( +// "onNewIntent :: key >> ${it} :: value >> ${ +// intent?.extras?.getString( +// it +// ) +// }" +// ) +// } catch (e: Exception) { +// e.printStackTrace() +// } +// +// } +// } +// } +// super.onNewIntent(intent) +// } private fun openWithIntent(intent: Intent){ Blog.LOGE("intent >> ${intent}") @@ -382,24 +382,25 @@ open class NeoRssActivity : CommonActivity() { super.onCreate(savedInstanceState) try { YoutubeDL.getInstance().init(this) - FFmpeg.getInstance().init(this); + FFmpeg.getInstance().init(this) CoroutineScope(Dispatchers.IO).launch { - YoutubeDL.getInstance().updateYoutubeDL(this@NeoRssActivity) + try { + YoutubeDL.getInstance().updateYoutubeDL(this@NeoRssActivity) + } catch (e: YoutubeDLException) { + Blog.LOGE("failed to initialize youtubedl-android", e) + } } } catch (e: YoutubeDLException) { Blog.LOGE("failed to initialize youtubedl-android", e) } - val intent = Intent(this, ForeGroundService::class.java) - this.startForegroundService(intent) -// 1. 시스템 바 공간을 앱이 차지하도록 설정 (상태바 뒤로 레이아웃 확장) WindowCompat.setDecorFitsSystemWindows(window, false) // 2. 상태바 색상을 투명하게 변경 (필요한 경우) window.statusBarColor = Color.TRANSPARENT // (선택 사항) 내비게이션 바도 투명하게 하고 싶다면 - window.navigationBarColor = Color.TRANSPARENT + window.navigationBarColor = Color.TRANSPARENT getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); @@ -522,7 +523,9 @@ open class NeoRssActivity : CommonActivity() { binding.controllPanel.visibility = View.GONE binding.floatingActionMenu.visibility = View.GONE } - + sRuntime?.shutdown() + sRuntime = null + finish() } else -> {} } @@ -568,7 +571,12 @@ open class NeoRssActivity : CommonActivity() { super.onDestroy() } + override fun onUserLeaveHint() { + showContents(R.id.close) + + super.onUserLeaveHint() + } @RequiresApi(Build.VERSION_CODES.O_MR1) override fun onResume() {