|
|
|
@@ -1,25 +1,11 @@
|
|
|
|
|
/*
|
|
|
|
|
* Lunar Launcher
|
|
|
|
|
* Copyright (C) 2022 Md Rasel Hossain
|
|
|
|
|
*
|
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
package bums.lunatic.launcher
|
|
|
|
|
|
|
|
|
|
//import kr.lunaticbum.utils.ui.DisplayUtil
|
|
|
|
|
import android.annotation.SuppressLint
|
|
|
|
|
import android.app.Activity
|
|
|
|
|
import android.app.SearchManager
|
|
|
|
|
import android.appwidget.AppWidgetHost
|
|
|
|
|
import android.appwidget.AppWidgetHostView
|
|
|
|
|
import android.appwidget.AppWidgetManager
|
|
|
|
|
import android.content.Context
|
|
|
|
|
import android.content.Intent
|
|
|
|
|
import android.content.res.Configuration
|
|
|
|
@@ -42,6 +28,7 @@ import android.view.MotionEvent
|
|
|
|
|
import android.view.PointerIcon
|
|
|
|
|
import android.view.View
|
|
|
|
|
import android.view.WindowManager
|
|
|
|
|
import android.widget.FrameLayout
|
|
|
|
|
import androidx.activity.OnBackPressedCallback
|
|
|
|
|
import androidx.annotation.RequiresApi
|
|
|
|
|
import androidx.core.net.toUri
|
|
|
|
@@ -53,6 +40,7 @@ import androidx.core.view.updatePadding
|
|
|
|
|
import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
|
|
|
|
|
import bums.lunatic.launcher.common.CommonActivity
|
|
|
|
|
import bums.lunatic.launcher.databinding.LauncherActivityBinding
|
|
|
|
|
import bums.lunatic.launcher.feeds.WidgetHost
|
|
|
|
|
import bums.lunatic.launcher.helpers.ForeGroundService
|
|
|
|
|
import bums.lunatic.launcher.helpers.HeadsetActionButtonReceiver
|
|
|
|
|
import bums.lunatic.launcher.home.GeckoWeb
|
|
|
|
@@ -61,6 +49,7 @@ import bums.lunatic.launcher.home.adapters.BookmarkDetailFragment
|
|
|
|
|
import bums.lunatic.launcher.home.adapters.BookmarkPagerFragment
|
|
|
|
|
import bums.lunatic.launcher.model.RssData
|
|
|
|
|
import bums.lunatic.launcher.model.RssDataType
|
|
|
|
|
import bums.lunatic.launcher.model.WidgetData
|
|
|
|
|
import bums.lunatic.launcher.receiver.NLService
|
|
|
|
|
import bums.lunatic.launcher.settings.SettingsActivity
|
|
|
|
|
import bums.lunatic.launcher.tokiz.Comics
|
|
|
|
@@ -75,6 +64,7 @@ import com.google.android.material.color.DynamicColors
|
|
|
|
|
import com.yausername.ffmpeg.FFmpeg
|
|
|
|
|
import com.yausername.youtubedl_android.YoutubeDL
|
|
|
|
|
import com.yausername.youtubedl_android.YoutubeDLException
|
|
|
|
|
import io.realm.kotlin.UpdatePolicy
|
|
|
|
|
import io.realm.kotlin.ext.query
|
|
|
|
|
import kotlinx.coroutines.CoroutineScope
|
|
|
|
|
import kotlinx.coroutines.Dispatchers
|
|
|
|
@@ -396,7 +386,40 @@ open class LauncherActivity : CommonActivity() {
|
|
|
|
|
.commit()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
private fun deleteWidget(view: View) {
|
|
|
|
|
val widgetId = view.tag as? Int ?: return
|
|
|
|
|
|
|
|
|
|
// 1. 화면에서 제거
|
|
|
|
|
binding.widgetContainer.removeView(view)
|
|
|
|
|
|
|
|
|
|
// 2. 시스템 호스트에서 제거 (ID 삭제)
|
|
|
|
|
appWidgetHost?.deleteAppWidgetId(widgetId)
|
|
|
|
|
|
|
|
|
|
// 3. DB에서 제거
|
|
|
|
|
WorkersDb.getRealm().writeBlocking {
|
|
|
|
|
val item = query<WidgetData>("appWidgetId == $0", widgetId).first().find()
|
|
|
|
|
if (item != null) {
|
|
|
|
|
delete(item)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 두 뷰가 겹치는지 확인하는 헬퍼 함수
|
|
|
|
|
private fun isViewOverlapping(v1: View, v2: View): Boolean {
|
|
|
|
|
val rect1 = android.graphics.Rect()
|
|
|
|
|
v1.getGlobalVisibleRect(rect1)
|
|
|
|
|
|
|
|
|
|
val rect2 = android.graphics.Rect()
|
|
|
|
|
v2.getGlobalVisibleRect(rect2)
|
|
|
|
|
|
|
|
|
|
return android.graphics.Rect.intersects(rect1, rect2)
|
|
|
|
|
}
|
|
|
|
|
// 위젯 관련 변수
|
|
|
|
|
private var appWidgetManager: AppWidgetManager? = null
|
|
|
|
|
private var appWidgetHost: AppWidgetHost? = null
|
|
|
|
|
private val APPWIDGET_HOST_ID = 1024
|
|
|
|
|
private val REQUEST_PICK_APPWIDGET = 100
|
|
|
|
|
private val REQUEST_CREATE_APPWIDGET = 101
|
|
|
|
|
|
|
|
|
|
@SuppressLint("NewApi", "MissingPermission", "ClickableViewAccessibility")
|
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
|
|
@@ -434,7 +457,7 @@ open class LauncherActivity : CommonActivity() {
|
|
|
|
|
updateLocationService()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
showContents(binding.feeds.id)
|
|
|
|
|
// showContents(binding.feeds.id)
|
|
|
|
|
binding.floatingActionMenu.setOnTouchListener { v: View, e: MotionEvent ->
|
|
|
|
|
if (binding.floatingActionMenu.isOpened) {
|
|
|
|
|
binding.floatingActionMenu.close(true)
|
|
|
|
@@ -464,9 +487,268 @@ open class LauncherActivity : CommonActivity() {
|
|
|
|
|
startActivity(shareIntent)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
appWidgetManager = AppWidgetManager.getInstance(this)
|
|
|
|
|
appWidgetHost = WidgetHost(applicationContext, APPWIDGET_HOST_ID)
|
|
|
|
|
appWidgetHost?.startListening()
|
|
|
|
|
|
|
|
|
|
// 2. 저장된 위젯 복구
|
|
|
|
|
restoreWidgets()
|
|
|
|
|
|
|
|
|
|
// 3. 바탕화면 롱클릭 시 위젯 추가 메뉴 띄우기 (예시)
|
|
|
|
|
binding.widgetContainer.setOnLongClickListener {
|
|
|
|
|
selectWidget()
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
scaleDetector = android.view.ScaleGestureDetector(this, object : android.view.ScaleGestureDetector.SimpleOnScaleGestureListener() {
|
|
|
|
|
override fun onScale(detector: android.view.ScaleGestureDetector): Boolean {
|
|
|
|
|
currentDragView?.let { view ->
|
|
|
|
|
val params = view.layoutParams as FrameLayout.LayoutParams
|
|
|
|
|
|
|
|
|
|
// 기존 크기에 스케일 팩터(확대/축소 비율)를 곱해서 새로운 크기 계산
|
|
|
|
|
val newWidth = (params.width * detector.scaleFactor).toInt()
|
|
|
|
|
val newHeight = (params.height * detector.scaleFactor).toInt()
|
|
|
|
|
|
|
|
|
|
// 최소 크기 제한 (너무 작아지지 않게 방지, 예: 100px)
|
|
|
|
|
params.width = newWidth.coerceAtLeast(100)
|
|
|
|
|
params.height = newHeight.coerceAtLeast(100)
|
|
|
|
|
|
|
|
|
|
view.layoutParams = params
|
|
|
|
|
|
|
|
|
|
// AppWidgetHostView에 크기 변경 알림 (위젯 내용물 재배치 유도)
|
|
|
|
|
(view as? AppWidgetHostView)?.updateAppWidgetSize(null, params.width, params.height, params.width, params.height)
|
|
|
|
|
|
|
|
|
|
return true // 이벤트를 처리했음을 알림
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 위젯 선택기 실행
|
|
|
|
|
fun selectWidget() {
|
|
|
|
|
val appWidgetId = appWidgetHost?.allocateAppWidgetId()
|
|
|
|
|
val pickIntent = Intent(AppWidgetManager.ACTION_APPWIDGET_PICK).apply {
|
|
|
|
|
putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
|
|
|
|
|
putParcelableArrayListExtra(AppWidgetManager.EXTRA_CUSTOM_INFO, ArrayList())
|
|
|
|
|
putParcelableArrayListExtra(AppWidgetManager.EXTRA_CUSTOM_EXTRAS, ArrayList())
|
|
|
|
|
}
|
|
|
|
|
startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
|
|
|
|
super.onActivityResult(requestCode, resultCode, data)
|
|
|
|
|
if (resultCode == Activity.RESULT_OK) {
|
|
|
|
|
when (requestCode) {
|
|
|
|
|
REQUEST_PICK_APPWIDGET -> configureWidget(data)
|
|
|
|
|
REQUEST_CREATE_APPWIDGET -> createWidget(data)
|
|
|
|
|
}
|
|
|
|
|
} else if (resultCode == Activity.RESULT_CANCELED && data != null) {
|
|
|
|
|
val appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1)
|
|
|
|
|
if (appWidgetId != -1) appWidgetHost?.deleteAppWidgetId(appWidgetId)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun configureWidget(data: Intent?) {
|
|
|
|
|
val extras = data?.extras
|
|
|
|
|
val appWidgetId = extras?.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) ?: -1
|
|
|
|
|
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)
|
|
|
|
|
// }
|
|
|
|
|
appWidgetHost?.startAppWidgetConfigureActivityForResult(
|
|
|
|
|
this,
|
|
|
|
|
appWidgetId,
|
|
|
|
|
0,
|
|
|
|
|
REQUEST_CREATE_APPWIDGET,
|
|
|
|
|
null
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
createWidget(data)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LauncherActivity 클래스 멤버 변수 추가
|
|
|
|
|
private var isDraggingWidget = false
|
|
|
|
|
private var currentDragView: View? = null
|
|
|
|
|
private var lastTouchX = 0f
|
|
|
|
|
private var lastTouchY = 0f
|
|
|
|
|
|
|
|
|
|
// 위젯 뷰 생성 및 화면 배치
|
|
|
|
|
private fun createWidget(data: Intent?, savedWidgetData: WidgetData? = null) {
|
|
|
|
|
val appWidgetId = savedWidgetData?.appWidgetId ?: data?.extras?.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) ?: return
|
|
|
|
|
val appWidgetInfo = appWidgetManager?.getAppWidgetInfo(appWidgetId) ?: return
|
|
|
|
|
|
|
|
|
|
val hostView = appWidgetHost?.createView(applicationContext, appWidgetId, appWidgetInfo)
|
|
|
|
|
hostView?.setAppWidget(appWidgetId, appWidgetInfo)
|
|
|
|
|
|
|
|
|
|
// 크기 및 위치 설정
|
|
|
|
|
val width = savedWidgetData?.width ?: appWidgetInfo.minWidth
|
|
|
|
|
val height = savedWidgetData?.height ?: appWidgetInfo.minHeight
|
|
|
|
|
val params = FrameLayout.LayoutParams(width, height)
|
|
|
|
|
hostView?.layoutParams = params
|
|
|
|
|
|
|
|
|
|
if (savedWidgetData != null) {
|
|
|
|
|
hostView?.x = savedWidgetData.x
|
|
|
|
|
hostView?.y = savedWidgetData.y
|
|
|
|
|
} else {
|
|
|
|
|
// 새 위젯은 겹치지 않게 약간 랜덤한 위치에 배치 (예: 50~400 사이)
|
|
|
|
|
hostView?.x = (50..400).random().toFloat()
|
|
|
|
|
hostView?.y = (200..600).random().toFloat()
|
|
|
|
|
}
|
|
|
|
|
hostView?.tag = appWidgetId
|
|
|
|
|
|
|
|
|
|
// 드래그 리스너 등록
|
|
|
|
|
hostView?.setOnLongClickListener { view ->
|
|
|
|
|
isDraggingWidget = true
|
|
|
|
|
currentDragView = view
|
|
|
|
|
lastTouchX = 0f
|
|
|
|
|
lastTouchY = 0f
|
|
|
|
|
|
|
|
|
|
// [추가] 드래그 시작 시 삭제 영역 표시
|
|
|
|
|
binding.deleteZone.visibility = View.VISIBLE
|
|
|
|
|
|
|
|
|
|
// 시각적 피드백
|
|
|
|
|
view.animate().scaleX(1.05f).scaleY(1.05f).setDuration(100).start()
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
binding.widgetContainer.addView(hostView)
|
|
|
|
|
|
|
|
|
|
// 새로 추가된 위젯이라면 DB에 저장
|
|
|
|
|
if (savedWidgetData == null) {
|
|
|
|
|
saveWidgetState(appWidgetId, hostView!!.x, hostView.y, width, height, appWidgetInfo.provider.className)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 위젯 정보 DB 저장
|
|
|
|
|
private fun saveWidgetState(id: Int, x: Float, y: Float, w: Int, h: Int, name: String) {
|
|
|
|
|
WorkersDb.getRealm().writeBlocking {
|
|
|
|
|
copyToRealm(WidgetData().apply {
|
|
|
|
|
this.appWidgetId = id
|
|
|
|
|
this.x = x
|
|
|
|
|
this.y = y
|
|
|
|
|
this.width = w
|
|
|
|
|
this.height = h
|
|
|
|
|
this.className = name
|
|
|
|
|
}, UpdatePolicy.ALL)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DB에서 위젯 복구
|
|
|
|
|
private fun restoreWidgets() {
|
|
|
|
|
val widgets = WorkersDb.getRealm().query<WidgetData>().find()
|
|
|
|
|
widgets.forEach { widgetData ->
|
|
|
|
|
createWidget(null, widgetData)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
lateinit var scaleDetector: android.view.ScaleGestureDetector
|
|
|
|
|
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
|
|
|
|
|
if (ev == null) return super.dispatchTouchEvent(ev)
|
|
|
|
|
|
|
|
|
|
if (isDraggingWidget && currentDragView != null) {
|
|
|
|
|
// 1. 스케일(크기 조절) 감지기에게 이벤트 전달
|
|
|
|
|
scaleDetector.onTouchEvent(ev)
|
|
|
|
|
|
|
|
|
|
// 2. 크기 조절 중이면 드래그(이동) 로직 건너뛰기
|
|
|
|
|
if (scaleDetector.isInProgress) {
|
|
|
|
|
// 스케일 중에는 좌표 초기화 (드래그 튀는 현상 방지)
|
|
|
|
|
lastTouchX = ev.rawX
|
|
|
|
|
lastTouchY = ev.rawY
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3. 기존 이동(드래그) 로직
|
|
|
|
|
when (ev.actionMasked) { // actionMasked 사용 권장 (멀티터치 지원)
|
|
|
|
|
MotionEvent.ACTION_MOVE -> {
|
|
|
|
|
// 손가락이 하나일 때만 이동 허용
|
|
|
|
|
// [추가] 삭제 영역과 겹치는지 확인하여 시각적 피드백 (예: 빨갛게 변함)
|
|
|
|
|
if (isViewOverlapping(currentDragView!!, binding.deleteZone)) {
|
|
|
|
|
binding.deleteZone.setBackgroundColor(android.graphics.Color.RED)
|
|
|
|
|
binding.deleteZone.text = "손을 떼면 삭제됩니다"
|
|
|
|
|
} else {
|
|
|
|
|
binding.deleteZone.setBackgroundColor(0x99FF0000.toInt()) // 반투명 빨강 (원래 색)
|
|
|
|
|
binding.deleteZone.text = "삭제하려면 여기에 놓으세요"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ev.pointerCount == 1 && lastTouchX != 0f && lastTouchY != 0f) {
|
|
|
|
|
val dx = ev.rawX - lastTouchX
|
|
|
|
|
val dy = ev.rawY - lastTouchY
|
|
|
|
|
currentDragView!!.x += dx
|
|
|
|
|
currentDragView!!.y += dy
|
|
|
|
|
}
|
|
|
|
|
lastTouchX = ev.rawX
|
|
|
|
|
lastTouchY = ev.rawY
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
|
|
|
|
// 드래그/리사이즈 종료
|
|
|
|
|
if (isViewOverlapping(currentDragView!!, binding.deleteZone)) {
|
|
|
|
|
// 삭제 수행
|
|
|
|
|
deleteWidget(currentDragView!!)
|
|
|
|
|
} else {
|
|
|
|
|
// 삭제가 아니라면 원래대로 복구 및 위치 저장 (기존 로직)
|
|
|
|
|
currentDragView?.let { view ->
|
|
|
|
|
view.animate().scaleX(1.0f).scaleY(1.0f).setDuration(100).start()
|
|
|
|
|
val widgetId = view.tag as? Int ?: -1
|
|
|
|
|
if (widgetId != -1) {
|
|
|
|
|
saveWidgetState(widgetId, view.x, view.y, view.layoutParams.width, view.layoutParams.height, "")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// [추가] 삭제 영역 숨기기 및 초기화
|
|
|
|
|
binding.deleteZone.visibility = View.GONE
|
|
|
|
|
binding.deleteZone.setBackgroundColor(0x99FF0000.toInt())
|
|
|
|
|
|
|
|
|
|
isDraggingWidget = false
|
|
|
|
|
currentDragView = null
|
|
|
|
|
lastTouchX = 0f
|
|
|
|
|
lastTouchY = 0f
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
// ACTION_POINTER_DOWN: 두 번째 손가락 터치 시 좌표 튐 방지
|
|
|
|
|
MotionEvent.ACTION_POINTER_DOWN -> {
|
|
|
|
|
lastTouchX = ev.rawX
|
|
|
|
|
lastTouchY = ev.rawY
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return super.dispatchTouchEvent(ev)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 드래그 앤 드롭 리스너 (자유 배치 핵심)
|
|
|
|
|
inner class WidgetTouchListener(val view: View, val widgetId: Int) : View.OnTouchListener {
|
|
|
|
|
var dX = 0f
|
|
|
|
|
var dY = 0f
|
|
|
|
|
var startClickTime: Long = 0
|
|
|
|
|
|
|
|
|
|
override fun onTouch(v: View, event: MotionEvent): Boolean {
|
|
|
|
|
when (event.action) {
|
|
|
|
|
MotionEvent.ACTION_DOWN -> {
|
|
|
|
|
dX = v.x - event.rawX
|
|
|
|
|
dY = v.y - event.rawY
|
|
|
|
|
startClickTime = System.currentTimeMillis()
|
|
|
|
|
}
|
|
|
|
|
MotionEvent.ACTION_MOVE -> {
|
|
|
|
|
v.animate().x(event.rawX + dX).y(event.rawY + dY).setDuration(0).start()
|
|
|
|
|
}
|
|
|
|
|
MotionEvent.ACTION_UP -> {
|
|
|
|
|
if (System.currentTimeMillis() - startClickTime < 200) return false // 클릭으로 간주
|
|
|
|
|
// 위치 업데이트 저장
|
|
|
|
|
val w = v.width
|
|
|
|
|
val h = v.height
|
|
|
|
|
// className을 가져오기 위해 기존 데이터 조회 필요 (간소화를 위해 여기선 생략하거나 로컬 변수 사용)
|
|
|
|
|
saveWidgetState(widgetId, v.x, v.y, w, h, "")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun showContents(id : Int) {
|
|
|
|
|
binding.fragmentContainer.visibility = View.VISIBLE
|
|
|
|
|
when(id) {
|
|
|
|
|
R.id.feeds -> {
|
|
|
|
|
supportFragmentManager.beginTransaction()
|
|
|
|
@@ -533,23 +815,26 @@ open class LauncherActivity : CommonActivity() {
|
|
|
|
|
return super.onTouchEvent(event)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun onStart() {
|
|
|
|
|
super.onStart()
|
|
|
|
|
appWidgetHost?.startListening() // [필수] 여기서 리스닝 시작
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun onStop() {
|
|
|
|
|
super.onStop()
|
|
|
|
|
appWidgetHost?.stopListening() // [필수] 여기서 리스닝 중지 (onDestroy 대신 여기 추천)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun onDestroy() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
sRuntime?.shutdown()
|
|
|
|
|
sRuntime = null
|
|
|
|
|
} catch (e: Exception) {e.printStackTrace()
|
|
|
|
|
}
|
|
|
|
|
} catch (e: Exception) { e.printStackTrace() }
|
|
|
|
|
|
|
|
|
|
// appWidgetHost?.stopListening() // 이 줄은 제거하고 onStop으로 이동
|
|
|
|
|
super.onDestroy()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun onStart() {
|
|
|
|
|
super.onStart()
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@RequiresApi(Build.VERSION_CODES.O_MR1)
|
|
|
|
|
override fun onResume() {
|
|
|
|
|
super.onResume()
|
|
|
|
|