diff --git a/app/src/main/assets/extensions/my_extension/messaging.js b/app/src/main/assets/extensions/my_extension/messaging.js index 2765df7d..3d781fec 100644 --- a/app/src/main/assets/extensions/my_extension/messaging.js +++ b/app/src/main/assets/extensions/my_extension/messaging.js @@ -55,22 +55,203 @@ port.onMessage.addListener(response => { var type= response["type"]; switch (type) { - case "CLICK_PREV_CHAPTER" : { - const links = document.querySelectorAll("a"); - let found = false; - for (let link of links) { - if (link.textContent.includes("이전화 보기")) { - link.click(); - found = true; - break; - } - } - if (!found) { - port.postMessage({ type: "MSG", msg: "이전화 버튼을 찾을 수 없습니다." }); - } - break; + case "SEEK_NEXT": // 5초 앞으로 + { + let btn = + document.querySelector('button[aria-label="10초 빨리 감기"]') || + document.querySelector('button[aria-label="뒤로 10초"]') || + document.querySelector('.ytp-right-controls .ytp-button[aria-label*="뒤로"]'); + + if (btn) { + btn.click(); + return; } - case "CLICK_NEXT_CHAPTER" : { + } + break; + case "PLAY_PAUSE": // 5초 뒤로 + { + if (document.location.href.search("youtube") > -1) { + let btn = + document.querySelector('button[aria-label="동영상 재생"]') || + document.querySelector('button[aria-label="동영상 일시중지"]') ; + + if (btn) { + btn.click(); + return; + } + } + + } break; + case "SEEK_PREV": // 5초 뒤로 + { + if (document.location.href.search("youtube") > -1) { + let btn = + document.querySelector('button[aria-label="10초 되감기"]') || + document.querySelector('button[aria-label="앞으로 10초"]') || + document.querySelector('.ytp-right-controls .ytp-button[aria-label*="앞으로"]'); + + if (btn) { + btn.click(); + return; + } + } + + } break; + case "PREV_SHORTS": { + if (document.location.href.search("shorts") > -1) { + // 1. 유튜브 쇼츠의 스크롤 컨테이너들 후보 (버전에 따라 다름) + const container = document.getElementById('shorts-container') || + document.querySelector('ytd-shorts') || + document.querySelector('#items.ytd-vertical-list-renderer'); + + if (container) { + // 컨테이너 내부에서 현재 뷰포트 높이만큼 스크롤 + container.scrollBy({ + top: window.innerHeight * -1, + behavior: 'smooth' + }); + } else { + // 컨테이너를 못 찾을 경우, 키보드 'J'나 'ArrowDown'을 시뮬레이션하는 게 가장 정확합니다. + const event = new KeyboardEvent('keydown', { + key: 'ArrowUp', + keyCode: 40, + code: 'ArrowUp', + which: 40, + bubbles: true + }); + document.dispatchEvent(event); + } + }else { + let btn = document.querySelector('.ytp-prev-button'); + + if (!btn) { + btn = document.querySelector('button[aria-label="이전 동영상"], button[aria-label="이전"]'); + } + + if (!btn) { + btn = Array.from(document.querySelectorAll('[aria-label]')) + .find(el => /이전/.test(el.getAttribute('aria-label'))); + } + + if (btn) { + btn.click(); + } else { + console.log('이전 영상 버튼을 찾지 못했습니다.'); + } + } + break; + } + + case "NEXT_SHORTS": { + if (document.location.href.search("shorts") > -1) { + // 1. 유튜브 쇼츠의 스크롤 컨테이너들 후보 (버전에 따라 다름) + const container = document.getElementById('shorts-container') || + document.querySelector('ytd-shorts') || + document.querySelector('#items.ytd-vertical-list-renderer'); + + if (container) { + // 컨테이너 내부에서 현재 뷰포트 높이만큼 스크롤 + container.scrollBy({ + top: window.innerHeight, + behavior: 'smooth' + }); + } else { + // 컨테이너를 못 찾을 경우, 키보드 'J'나 'ArrowDown'을 시뮬레이션하는 게 가장 정확합니다. + const event = new KeyboardEvent('keydown', { + key: 'ArrowDown', + keyCode: 40, + code: 'ArrowDown', + which: 40, + bubbles: true + }); + document.dispatchEvent(event); + } + } else { + // 1) 예전/기본 플레이어용 클래스 + let btn = document.querySelector('.ytp-next-button'); + + // 2) aria-label 기반 (언어별로 텍스트 다를 수 있음) + if (!btn) { + btn = document.querySelector('button[aria-label="다음 동영상"], button[aria-label="다음"]'); + } + + // 3) 혹시 아이콘 버튼이 다른 태그일 수도 있으니 좀 더 느슨하게 + if (!btn) { + btn = Array.from(document.querySelectorAll('[aria-label]')) + .find(el => /다음/.test(el.getAttribute('aria-label'))); + } + + if (btn) { + btn.click(); + } else { + console.log('다음 영상 버튼을 찾지 못했습니다.'); + } + } + break; + } + case "CLICK_PREV_CHAPTER" : { + const links = document.querySelectorAll("a"); + let found = false; + for (let link of links) { + if (link.textContent.includes("이전화 보기")) { + link.click(); + found = true; + break; + } + } + if (!found) { + port.postMessage({ type: "MSG", msg: "이전화 버튼을 찾을 수 없습니다." }); + } + break; + } + case "MARKER": { + const isDown = response["isDown"]; + const scrollRatio = 0.6; // 이동 비율 (30%) + const viewportHeight = window.innerHeight; + const distance = viewportHeight * scrollRatio; + const startY = window.scrollY; + + const id = 'scroll-ghost-layer'; + let ghost = document.getElementById(id); + if (ghost) ghost.remove(); + + ghost = document.createElement('div'); + ghost.id = id; + + // 💡 isDown에 따라 그라데이션 방향과 보더 위치를 반전 + const gradDir = isDown ? 'to top' : 'to bottom'; + const borderStyle = isDown ? 'border-top: 3px solid rgba(0,122,255,0.4);' : 'border-bottom: 3px solid rgba(0,122,255,0.3);'; + + ghost.style.cssText = ` + position: absolute; + top: ${startY}px; + left: 0; + width: 100%; + height: ${viewportHeight}px; + background: linear-gradient(${gradDir}, rgba(0,122,255,0.5), transparent); + ${borderStyle} + z-index: 2147483647; + pointer-events: none; + transition: opacity 0.8s ease-out; + opacity: 1; + `; + document.body.appendChild(ghost); + + // 실제 스크롤 수행 + const targetY = isDown ? startY + distance : startY - distance; + window.scrollTo({ + top: targetY, + behavior: 'smooth' + }); + + // 레이어 제거 + setTimeout(() => { + ghost.style.opacity = '0'; + setTimeout(() => ghost.remove(), 800); + }, 1000); + break; + } + case "CLICK_NEXT_CHAPTER" : { const links = document.querySelectorAll("a"); let found = false; for (let link of links) { @@ -83,17 +264,17 @@ port.onMessage.addListener(response => { if (!found) { port.postMessage({ type: "MSG", msg: "다음화 버튼을 찾을 수 없습니다." }); } - break; + break; } case "fetchAllImages": { - const targetSrc = response["targetSrc"]; - const imageUrls = findAllRelatedImages(targetSrc); - // 찾은 이미지 URL 목록을 'allImagesFound' 타입으로 네이티브 앱에 다시 전송 - sendMessage({ - type: "allImagesFound", - urls: imageUrls - }); - break; + const targetSrc = response["targetSrc"]; + const imageUrls = findAllRelatedImages(targetSrc); + // 찾은 이미지 URL 목록을 'allImagesFound' 타입으로 네이티브 앱에 다시 전송 + sendMessage({ + type: "allImagesFound", + urls: imageUrls + }); + break; } case "search" : { var keyword = response["keyword"]; @@ -131,7 +312,7 @@ port.onMessage.addListener(response => { const elements = document.querySelectorAll(".main-content"); // 또는 getElementsByClassName("item") const matched = Array.from(elements).filter(el => - el.textContent.includes(keyword) // 대소문자 구분 + el.textContent.includes(keyword) // 대소문자 구분 ); @@ -284,15 +465,15 @@ if(document.querySelector("#html_encoder_div")) { ); } function removeSpecificGifs() { - const images = document.getElementsByTagName('img'); - for (let i = images.length - 1; i >= 0; i--) { - const src = images[i].src; - // 요청하신 특정 도메인과 gif 패턴, 쿼리 파라미터를 체크 - if (src.includes('tokinbtoki') && src.includes('.gif') && src.includes('_=')) { - images[i].parentNode.removeChild(images[i]); - } - } - } + const images = document.getElementsByTagName('img'); + for (let i = images.length - 1; i >= 0; i--) { + const src = images[i].src; + // 요청하신 특정 도메인과 gif 패턴, 쿼리 파라미터를 체크 + if (src.includes('tokinbtoki') && src.includes('.gif') && src.includes('_=')) { + images[i].parentNode.removeChild(images[i]); + } + } +} function getList(children) { // port.postMessage("Start of getList!" + children); @@ -395,7 +576,7 @@ function scrollByPercentUpDown(isToDown , max) { window.scrollTo({ top: currentScroll + (moveAmount * isToDown) , behavior: "smooth" }); } function loadComplete() { - try {port.postMessage(JSON.stringify({type: "NotRegistered"}));}catch (e) {} + try {port.postMessage(JSON.stringify({type: "NotRegistered"}));}catch (e) {} } function hideBook() { @@ -492,7 +673,7 @@ function scrollToLazyImg(fastMode) { // 한 번에 이동할 픽셀 const step = fastMode ? 600 : 200; // 반복 간격(ms) (느릴수록 로딩에 더 여유 생김) - const delay = fastMode ? 200 : 600; + const delay = fastMode ? 300 : 500; // 스크롤 현재 위치 추적 let currentY = window.scrollY; let maxY = Math.max( @@ -521,7 +702,7 @@ function scrollToLazyImg(fastMode) { // 직접 메시지 보내거나, 원하는 최종 동작 실행 // 예: window.postMessage('saveToObsidian', '*'); } - }, 800); + }, delay); } } @@ -555,7 +736,7 @@ function gotoNext() { var targetElement = null; document.querySelectorAll('.btn-group')?.forEach((el) => { - var pageLinks =el?.querySelectorAll('a'); + var pageLinks =el?.querySelectorAll('a'); if (pageLinks) { pageLinks.forEach(function(link) { diff --git a/app/src/main/kotlin/bums/lunatic/launcher/LunaticLauncher.kt b/app/src/main/kotlin/bums/lunatic/launcher/LunaticLauncher.kt index 565f7051..e261ea52 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/LunaticLauncher.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/LunaticLauncher.kt @@ -20,7 +20,14 @@ package bums.lunatic.launcher import android.app.Application import android.content.ComponentCallbacks2 +import android.content.Context import android.database.sqlite.SQLiteDatabase +import android.text.SpannableStringBuilder +import android.text.style.RelativeSizeSpan +import android.view.View +import android.view.View.inflate +import android.widget.TextView +import android.widget.Toast import bums.lunatic.launcher.helpers.HourlyLogWriter import bums.lunatic.launcher.helpers.PrefHelper import bums.lunatic.launcher.home.Base64ImageCache @@ -39,6 +46,7 @@ import java.io.File import java.util.concurrent.TimeUnit + internal class LunaticLauncher : Application() { companion object { var appContext : LunaticLauncher? = null @@ -49,6 +57,21 @@ internal class LunaticLauncher : Application() { return sRuntime } var privateCompletedDir : File? = null + + var toast : Toast? = null + fun Context.toast(msg: String) { + val biggerText = SpannableStringBuilder(msg) + biggerText.setSpan(RelativeSizeSpan(1.6f), 0, msg.length, 0) + val view: View = inflate(this, R.layout.simple_toast, null) + view.findViewById(R.id.text).text = biggerText + if (toast==null) { + toast = Toast(this) + toast?.duration = Toast.LENGTH_SHORT + toast?.cancel() + } + toast?.view = view + toast?.show() + } } private fun initGeckoRuntime() { diff --git a/app/src/main/kotlin/bums/lunatic/launcher/helpers/ForeGroundService.kt b/app/src/main/kotlin/bums/lunatic/launcher/helpers/ForeGroundService.kt index 660c0d60..9751de95 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/helpers/ForeGroundService.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/helpers/ForeGroundService.kt @@ -170,7 +170,7 @@ class ForeGroundService : Service() { set(value) { field = value if (value == null) { - startForeGround(max= 0, progress = 0, vibrator = true) + startForeGround(max= 0, progress = 0, vibrator = false) } } @@ -190,7 +190,7 @@ class ForeGroundService : Service() { currentProcessId = UUID.randomUUID().toString() YoutubeDL.getInstance() .execute(command, currentProcessId) { progress, est, str -> - startForeGround(100, progress.toInt(),str, true) + startForeGround(100, progress.toInt(),str, false) if (progress >= 100) { targetUrls.remove(url) currentProcessId = null @@ -210,7 +210,7 @@ class ForeGroundService : Service() { } } - fun startForeGround(max : Int = 0, progress : Int = 0, str : String? = "실행중입니다.", vibrator : Boolean? = false) { + fun startForeGround(max : Int = 0, progress : Int = 0, str : String? = "실행중입니다.", vibrator : Boolean = false) { val currentChannelId = if (vibrator == true) "${CHANNEL_ID}_vibrate" else "${CHANNEL_ID}_silent" diff --git a/app/src/main/kotlin/bums/lunatic/launcher/home/GeckoWeb.kt b/app/src/main/kotlin/bums/lunatic/launcher/home/GeckoWeb.kt index a1b8e4a4..78f0d65e 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/home/GeckoWeb.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/home/GeckoWeb.kt @@ -7,6 +7,8 @@ import android.content.Context import android.content.Intent import android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP import android.content.Intent.FLAG_ACTIVITY_NEW_TASK +import android.graphics.Color +import android.graphics.drawable.GradientDrawable import android.net.Uri import android.os.Build import android.text.SpannableStringBuilder @@ -30,6 +32,7 @@ import androidx.core.net.toUri import androidx.core.view.isVisible import bums.lunatic.launcher.BookmarkUploader import bums.lunatic.launcher.LunaticLauncher.Companion.getRuntime +import bums.lunatic.launcher.LunaticLauncher.Companion.toast import bums.lunatic.launcher.R import bums.lunatic.launcher.helpers.ForeGroundService import bums.lunatic.launcher.helpers.ForeGroundService.Companion.ACTION_VIDEO_DOWNLOAD @@ -306,6 +309,16 @@ open class GeckoWeb @JvmOverloads constructor( } } } + fun nextShorts() { + + sendJsonMsg("NEXT_SHORTS") + saveCurrentSessionState() + } + fun prevShorts() { + + sendJsonMsg("PREV_SHORTS") + saveCurrentSessionState() + } // [Navigation Delegate] private val navigationDelegate = object : GeckoSession.NavigationDelegate { @@ -605,25 +618,62 @@ open class GeckoWeb @JvmOverloads constructor( var HH = 0.3 fun pageDown() { val session = this.session ?: return - val pzc = session.panZoomController - - // 💡 현재 눈에 보이는 GeckoView의 높이를 가져옵니다. - val viewHeight = this.height.toDouble().times(HH) - - // 세로 방향(y)으로 뷰 높이만큼 아래로 스크롤 (픽셀 단위) - // 세 번째 인자는 애니메이션 여부입니다. - pzc.scrollBy(ScreenLength.fromPixels(0.0), ScreenLength.fromPixels(viewHeight), - PanZoomController.SCROLL_BEHAVIOR_SMOOTH) + sendJsonMsg("MARKER",Pair("isDown",true)) +// val pzc = session.panZoomController +// +// // 💡 현재 눈에 보이는 GeckoView의 높이를 가져옵니다. +// val viewHeight = this.height.toDouble().times(HH) +// +// // 세로 방향(y)으로 뷰 높이만큼 아래로 스크롤 (픽셀 단위) +// // 세 번째 인자는 애니메이션 여부입니다. +// pzc.scrollBy(ScreenLength.fromPixels(0.0), ScreenLength.fromPixels(viewHeight), +// PanZoomController.SCROLL_BEHAVIOR_SMOOTH) +// +// showGuideLine((this.height - viewHeight).toInt()) } fun pageUp() { val session = this.session ?: return - val pzc = session.panZoomController - val viewHeight = this.height.toDouble().times(HH) + sendJsonMsg("MARKER",Pair("isDown",false)) +// val pzc = session.panZoomController +// val viewHeight = this.height.toDouble().times(HH) +// +// // 위로 스크롤 +// pzc.scrollBy(ScreenLength.fromPixels(0.0), ScreenLength.fromPixels(-viewHeight), +// PanZoomController.SCROLL_BEHAVIOR_SMOOTH) +// +// showGuideLine(viewHeight.toInt()) + } - // 위로 스크롤 - pzc.scrollBy(ScreenLength.fromPixels(0.0), ScreenLength.fromPixels(-viewHeight), - PanZoomController.SCROLL_BEHAVIOR_SMOOTH) + private val guideView: View by lazy { + View(context).apply { + layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, 150.dpToPx()) // 150dp 정도의 넓은 영역 + // 위는 투명하고 아래로 갈수록 강조색이 보이는 그라데이션 + background = GradientDrawable( + GradientDrawable.Orientation.TOP_BOTTOM, + intArrayOf(0x00FF0000, 0x40FF0000) // 투명 -> 반투명 빨강 + ) + visibility = GONE + } + } + + private fun showGuideLine(yPosition: Int) { + sendJsonMsg("MARKER",Pair("yPos",yPosition)) +// if (guideView.parent == null) this.addView(guideView) +// +// guideView.apply { +// // PageDown 시에는 이전 바닥 지점(yPosition)에 뷰의 상단을 맞춤 +// // PageUp 시에는 이전 천장 지점(yPosition)에 뷰의 하단을 맞춤 +// translationY = if (isDown) (yPosition - 150.dpToPx()).toFloat() else yPosition.toFloat() +// visibility = VISIBLE +// alpha = 1f +// +// animate() +// .alpha(0f) +// .setDuration(800) // 너무 길면 거슬리니 0.8초 정도가 적당합니다. +// .withEndAction { visibility = GONE } +// .start() +// } } // File/Download Helpers @@ -712,19 +762,7 @@ open class GeckoWeb @JvmOverloads constructor( private fun getFilterF() = String(Base64.decode("aHR0cHM6Ly9pamF2dG9ycmVudC5jb20=", Base64.DEFAULT)) - var toast : Toast? = null - private fun Context.toast(msg: String) { - val biggerText = SpannableStringBuilder(msg) - biggerText.setSpan(RelativeSizeSpan(1.6f), 0, msg.length, 0) - val view: View = inflate(this, R.layout.simple_toast, null) - view.findViewById(R.id.text).text = biggerText - if (toast==null) { - toast = Toast(this) - toast?.duration = Toast.LENGTH_SHORT - } - toast?.view = view - toast?.show() - } + private fun View.post(action: () -> Unit) = this.post(Runnable(action)) fun onPause() { @@ -738,4 +776,18 @@ open class GeckoWeb @JvmOverloads constructor( restoreSessionState() session?.setActive(true) } + + // dp 변환 확장 함수 + private fun Int.dpToPx(): Int = (this * context.resources.displayMetrics.density).toInt() + fun prev10() { + sendJsonMsg("SEEK_PREV") + } + + fun next10() { + sendJsonMsg("SEEK_NEXT") + } + + fun play_pause() { + sendJsonMsg("PLAY_PAUSE") + } } \ No newline at end of file diff --git a/app/src/main/kotlin/bums/lunatic/launcher/home/RssHome.kt b/app/src/main/kotlin/bums/lunatic/launcher/home/RssHome.kt index 44a61569..8e4fb700 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/home/RssHome.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/home/RssHome.kt @@ -116,7 +116,7 @@ internal class RssHome : RemoteGestureFragment() , KeyEventHandler { override fun onRemoteLeft(isDouble : Boolean) { if (binding.lunaticBrowser.isVisible) { - doNextPage() + binding.lunaticBrowser.geckoWeb.goBack() } else { } @@ -124,7 +124,7 @@ internal class RssHome : RemoteGestureFragment() , KeyEventHandler { override fun onRemoteRight(isDouble : Boolean) { if (binding.lunaticBrowser.isVisible) { - binding.lunaticBrowser.geckoWeb.goBack() + doNextPage() } else { } @@ -1076,12 +1076,4 @@ internal class RssHome : RemoteGestureFragment() , KeyEventHandler { } fun randomOrNull() : RssData? = lasted.randomOrNull() -} -var toast: Toast? = null -fun Context.toast(string: String) { - if (toast == null) { - toast = Toast.makeText(this,string,Toast.LENGTH_SHORT) - } - toast?.setText(string) - toast?.show() } \ No newline at end of file diff --git a/app/src/main/kotlin/bums/lunatic/launcher/home/SystemStatusFragment.kt b/app/src/main/kotlin/bums/lunatic/launcher/home/SystemStatusFragment.kt index 8f2b85c2..d92f60de 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/home/SystemStatusFragment.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/home/SystemStatusFragment.kt @@ -41,6 +41,7 @@ import java.util.Calendar import com.google.common.util.concurrent.ListenableFuture import androidx.camera.lifecycle.ProcessCameraProvider import androidx.core.content.ContextCompat +import bums.lunatic.launcher.LunaticLauncher.Companion.toast class SystemStatusFragment : Fragment() { diff --git a/app/src/main/kotlin/bums/lunatic/launcher/home/tokiz/TokiFragment.kt b/app/src/main/kotlin/bums/lunatic/launcher/home/tokiz/TokiFragment.kt index 2819ebfa..c2f9251e 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/home/tokiz/TokiFragment.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/home/tokiz/TokiFragment.kt @@ -1,8 +1,10 @@ package bums.lunatic.launcher.home.tokiz +import android.content.Context import android.content.DialogInterface import android.content.Intent import android.content.res.Configuration +import android.media.AudioManager import android.net.Uri import android.os.Bundle import android.os.Handler @@ -31,6 +33,7 @@ import androidx.appcompat.app.AlertDialog import androidx.core.net.toUri import androidx.core.view.isVisible import androidx.fragment.app.Fragment +import bums.lunatic.launcher.LunaticLauncher.Companion.toast import bums.lunatic.launcher.R import bums.lunatic.launcher.common.RemoteGestureFragment import bums.lunatic.launcher.databinding.BooktokiBinding @@ -38,7 +41,6 @@ import bums.lunatic.launcher.home.GeckoWeb import bums.lunatic.launcher.home.GeckoWeb.JxEvent import bums.lunatic.launcher.home.KeyEventHandler import bums.lunatic.launcher.home.NeoRssActivity -import bums.lunatic.launcher.home.toast import bums.lunatic.launcher.home.tokiz.view.PagedTextLayout import bums.lunatic.launcher.home.tokiz.view.PagedTextViewInterface import bums.lunatic.launcher.utils.Blog @@ -238,7 +240,12 @@ class TokiFragment : RemoteGestureFragment(), PagedTextViewInterface,KeyEventHan } override fun onRemoteCenterClick() { - super.onRemoteCenterClick() + if (contentsType.contains("youtube")) { + binding.lunaticBrowser.geckoWeb.play_pause() + binding.lunaticBrowser.geckoWeb.saveCurrentSessionState() + } else { + super.onRemoteCenterClick() + } } override fun onRemoteCenterDoubleClick() { @@ -253,6 +260,11 @@ class TokiFragment : RemoteGestureFragment(), PagedTextViewInterface,KeyEventHan } else if (contentsType.contains("comics")) { if (contentsType == "comics") sendViewerTouch("right") else actionNextEvent() } + else if (contentsType.contains("youtube")) { + binding.lunaticBrowser.geckoWeb.next10() + } + + } override fun onRemoteLeft(isDouble : Boolean) { @@ -260,6 +272,8 @@ class TokiFragment : RemoteGestureFragment(), PagedTextViewInterface,KeyEventHan actionPrevEvent(isDouble) } else if (contentsType.contains("comics")) { if (contentsType == "comics") sendViewerTouch("left") else actionNextEvent() + }else if (contentsType.contains("youtube")) { + binding.lunaticBrowser.geckoWeb.prev10() } } @@ -267,12 +281,15 @@ class TokiFragment : RemoteGestureFragment(), PagedTextViewInterface,KeyEventHan if (binding.pagedLayer.isVisible) { } else { - if (binding.lunaticBrowser.geckoWeb.scrollState > 0) { - + if (contentsType.contains("youtube")) { + binding.lunaticBrowser.geckoWeb.nextShorts() } else { - binding.lunaticBrowser.geckoWeb.pageDown() - } + if (binding.lunaticBrowser.geckoWeb.scrollState > 0) { + } else { + binding.lunaticBrowser.geckoWeb.pageDown() + } + } } } @@ -280,10 +297,14 @@ class TokiFragment : RemoteGestureFragment(), PagedTextViewInterface,KeyEventHan if (binding.pagedLayer.isVisible) { } else { - if (binding.lunaticBrowser.geckoWeb.scrollState < 0) { - + if (contentsType.contains("youtube")) { + binding.lunaticBrowser.geckoWeb.prevShorts() } else { - binding.lunaticBrowser.geckoWeb.pageUp() + if (binding.lunaticBrowser.geckoWeb.scrollState < 0) { + + } else { + binding.lunaticBrowser.geckoWeb.pageUp() + } } } } @@ -526,13 +547,17 @@ class TokiFragment : RemoteGestureFragment(), PagedTextViewInterface,KeyEventHan } } - + private var originalVolume: Int = -1 + private val audioManager by lazy { + requireContext().getSystemService(Context.AUDIO_SERVICE) as AudioManager + } override fun onHiddenChanged(hidden: Boolean) { super.onHiddenChanged(hidden) saveContinuation = false if (hidden) { // 💡 화면에서 사라질 때: 타이머 중지 및 애니메이션 중지 binding.lunaticBrowser.geckoWeb?.onPause() + // 일반 WebView라면: webView.onPause() 및 webView.pauseTimers() } else { // 💡 다시 나타날 때: 다시 시작 @@ -541,6 +566,8 @@ class TokiFragment : RemoteGestureFragment(), PagedTextViewInterface,KeyEventHan } } + + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -551,7 +578,10 @@ class TokiFragment : RemoteGestureFragment(), PagedTextViewInterface,KeyEventHan // 💡 1. Toki용 레이아웃 설정 binding.lunaticBrowser.setupForToki() + originalVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) + // 미디어 볼륨을 0으로 설정 (FLAG_SHOW_UI를 0으로 주면 볼륨 바가 뜨지 않음) + audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0) // 💡 2. GeckoWeb 접근 및 설정 val geckoWeb = binding.lunaticBrowser.geckoWeb @@ -740,7 +770,21 @@ class TokiFragment : RemoteGestureFragment(), PagedTextViewInterface,KeyEventHan override fun onResume() { super.onResume() + originalVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) + if (contentsType.contains("youtube")) { + // 미디어 볼륨을 0으로 설정 (FLAG_SHOW_UI를 0으로 주면 볼륨 바가 뜨지 않음) + audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0) + } + Blog.LOGE("RssHome 활성화: 미디어 볼륨 0 설정 (이전 볼륨: $originalVolume)") + } + + override fun onPause() { + super.onPause() + if (originalVolume != -1 && contentsType.contains("youtube")) { + audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, originalVolume, 0) + Blog.LOGE("RssHome 비활성화: 미디어 볼륨 복구 ($originalVolume)") + } } fun getLastinfo() : LastInfo? { @@ -1013,21 +1057,7 @@ class TokiFragment : RemoteGestureFragment(), PagedTextViewInterface,KeyEventHan fun showToast(origin: String) { - activity?.runOnUiThread { - val toast = Toast(requireContext()) - toast.duration = Toast.LENGTH_SHORT - val biggerText = SpannableStringBuilder(origin) - biggerText.setSpan(RelativeSizeSpan(1.6f), 0, origin.length, 0) - val view: View = inflate(requireContext(), R.layout.simple_toast, null) - view.findViewById(R.id.text).text = biggerText - toast.view = view - toast.show() -// Toast.makeText( -// baseContext, -// biggerText, -// Toast.LENGTH_SHORT -// ).show() - } + activity?.toast(origin) } var delayed = 3500L + Math.abs(Random.nextLong().rem(9999L)) diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/TorrentManager.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/TorrentManager.kt index 58e2f2e7..f8c01d24 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/workers/TorrentManager.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/TorrentManager.kt @@ -27,7 +27,7 @@ import android.net.NetworkCapabilities import android.net.NetworkRequest import android.os.* import androidx.annotation.RequiresApi -import bums.lunatic.launcher.home.toast +import bums.lunatic.launcher.LunaticLauncher.Companion.toast import bums.lunatic.launcher.utils.Blog import com.frostwire.jlibtorrent.swig.error_code import com.frostwire.jlibtorrent.swig.libtorrent