This commit is contained in:
lunaticbum 2026-03-23 18:03:45 +09:00
parent 9aee32e42d
commit 112012ce0c
8 changed files with 383 additions and 104 deletions

View File

@ -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) {

View File

@ -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<TextView>(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() {

View File

@ -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"

View File

@ -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<String, Any?>("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<String, Any?>("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<String, Any?>("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<TextView>(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")
}
}

View File

@ -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()
}

View File

@ -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() {

View File

@ -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<TextView>(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))

View File

@ -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