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"]; var type= response["type"];
switch (type) { switch (type) {
case "CLICK_PREV_CHAPTER" : { case "SEEK_NEXT": // 5초 앞으로
const links = document.querySelectorAll("a"); {
let found = false; let btn =
for (let link of links) { document.querySelector('button[aria-label="10초 빨리 감기"]') ||
if (link.textContent.includes("이전화 보기")) { document.querySelector('button[aria-label="뒤로 10초"]') ||
link.click(); document.querySelector('.ytp-right-controls .ytp-button[aria-label*="뒤로"]');
found = true;
break; if (btn) {
} btn.click();
} return;
if (!found) {
port.postMessage({ type: "MSG", msg: "이전화 버튼을 찾을 수 없습니다." });
}
break;
} }
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"); const links = document.querySelectorAll("a");
let found = false; let found = false;
for (let link of links) { for (let link of links) {
@ -83,17 +264,17 @@ port.onMessage.addListener(response => {
if (!found) { if (!found) {
port.postMessage({ type: "MSG", msg: "다음화 버튼을 찾을 수 없습니다." }); port.postMessage({ type: "MSG", msg: "다음화 버튼을 찾을 수 없습니다." });
} }
break; break;
} }
case "fetchAllImages": { case "fetchAllImages": {
const targetSrc = response["targetSrc"]; const targetSrc = response["targetSrc"];
const imageUrls = findAllRelatedImages(targetSrc); const imageUrls = findAllRelatedImages(targetSrc);
// 찾은 이미지 URL 목록을 'allImagesFound' 타입으로 네이티브 앱에 다시 전송 // 찾은 이미지 URL 목록을 'allImagesFound' 타입으로 네이티브 앱에 다시 전송
sendMessage({ sendMessage({
type: "allImagesFound", type: "allImagesFound",
urls: imageUrls urls: imageUrls
}); });
break; break;
} }
case "search" : { case "search" : {
var keyword = response["keyword"]; var keyword = response["keyword"];
@ -131,7 +312,7 @@ port.onMessage.addListener(response => {
const elements = document.querySelectorAll(".main-content"); // 또는 getElementsByClassName("item") const elements = document.querySelectorAll(".main-content"); // 또는 getElementsByClassName("item")
const matched = Array.from(elements).filter(el => 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() { function removeSpecificGifs() {
const images = document.getElementsByTagName('img'); const images = document.getElementsByTagName('img');
for (let i = images.length - 1; i >= 0; i--) { for (let i = images.length - 1; i >= 0; i--) {
const src = images[i].src; const src = images[i].src;
// 요청하신 특정 도메인과 gif 패턴, 쿼리 파라미터를 체크 // 요청하신 특정 도메인과 gif 패턴, 쿼리 파라미터를 체크
if (src.includes('tokinbtoki') && src.includes('.gif') && src.includes('_=')) { if (src.includes('tokinbtoki') && src.includes('.gif') && src.includes('_=')) {
images[i].parentNode.removeChild(images[i]); images[i].parentNode.removeChild(images[i]);
} }
} }
} }
function getList(children) { function getList(children) {
// port.postMessage("Start of getList!" + children); // port.postMessage("Start of getList!" + children);
@ -395,7 +576,7 @@ function scrollByPercentUpDown(isToDown , max) {
window.scrollTo({ top: currentScroll + (moveAmount * isToDown) , behavior: "smooth" }); window.scrollTo({ top: currentScroll + (moveAmount * isToDown) , behavior: "smooth" });
} }
function loadComplete() { function loadComplete() {
try {port.postMessage(JSON.stringify({type: "NotRegistered"}));}catch (e) {} try {port.postMessage(JSON.stringify({type: "NotRegistered"}));}catch (e) {}
} }
function hideBook() { function hideBook() {
@ -492,7 +673,7 @@ function scrollToLazyImg(fastMode) {
// 한 번에 이동할 픽셀 // 한 번에 이동할 픽셀
const step = fastMode ? 600 : 200; const step = fastMode ? 600 : 200;
// 반복 간격(ms) (느릴수록 로딩에 더 여유 생김) // 반복 간격(ms) (느릴수록 로딩에 더 여유 생김)
const delay = fastMode ? 200 : 600; const delay = fastMode ? 300 : 500;
// 스크롤 현재 위치 추적 // 스크롤 현재 위치 추적
let currentY = window.scrollY; let currentY = window.scrollY;
let maxY = Math.max( let maxY = Math.max(
@ -521,7 +702,7 @@ function scrollToLazyImg(fastMode) {
// 직접 메시지 보내거나, 원하는 최종 동작 실행 // 직접 메시지 보내거나, 원하는 최종 동작 실행
// 예: window.postMessage('saveToObsidian', '*'); // 예: window.postMessage('saveToObsidian', '*');
} }
}, 800); }, delay);
} }
} }
@ -555,7 +736,7 @@ function gotoNext() {
var targetElement = null; var targetElement = null;
document.querySelectorAll('.btn-group')?.forEach((el) => { document.querySelectorAll('.btn-group')?.forEach((el) => {
var pageLinks =el?.querySelectorAll('a'); var pageLinks =el?.querySelectorAll('a');
if (pageLinks) { if (pageLinks) {
pageLinks.forEach(function(link) { pageLinks.forEach(function(link) {

View File

@ -20,7 +20,14 @@ package bums.lunatic.launcher
import android.app.Application import android.app.Application
import android.content.ComponentCallbacks2 import android.content.ComponentCallbacks2
import android.content.Context
import android.database.sqlite.SQLiteDatabase 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.HourlyLogWriter
import bums.lunatic.launcher.helpers.PrefHelper import bums.lunatic.launcher.helpers.PrefHelper
import bums.lunatic.launcher.home.Base64ImageCache import bums.lunatic.launcher.home.Base64ImageCache
@ -39,6 +46,7 @@ import java.io.File
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
internal class LunaticLauncher : Application() { internal class LunaticLauncher : Application() {
companion object { companion object {
var appContext : LunaticLauncher? = null var appContext : LunaticLauncher? = null
@ -49,6 +57,21 @@ internal class LunaticLauncher : Application() {
return sRuntime return sRuntime
} }
var privateCompletedDir : File? = null 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() { private fun initGeckoRuntime() {

View File

@ -170,7 +170,7 @@ class ForeGroundService : Service() {
set(value) { set(value) {
field = value field = value
if (value == null) { 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() currentProcessId = UUID.randomUUID().toString()
YoutubeDL.getInstance() YoutubeDL.getInstance()
.execute(command, currentProcessId) { progress, est, str -> .execute(command, currentProcessId) { progress, est, str ->
startForeGround(100, progress.toInt(),str, true) startForeGround(100, progress.toInt(),str, false)
if (progress >= 100) { if (progress >= 100) {
targetUrls.remove(url) targetUrls.remove(url)
currentProcessId = null 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" 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
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP import android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
@ -30,6 +32,7 @@ import androidx.core.net.toUri
import androidx.core.view.isVisible import androidx.core.view.isVisible
import bums.lunatic.launcher.BookmarkUploader import bums.lunatic.launcher.BookmarkUploader
import bums.lunatic.launcher.LunaticLauncher.Companion.getRuntime import bums.lunatic.launcher.LunaticLauncher.Companion.getRuntime
import bums.lunatic.launcher.LunaticLauncher.Companion.toast
import bums.lunatic.launcher.R import bums.lunatic.launcher.R
import bums.lunatic.launcher.helpers.ForeGroundService import bums.lunatic.launcher.helpers.ForeGroundService
import bums.lunatic.launcher.helpers.ForeGroundService.Companion.ACTION_VIDEO_DOWNLOAD 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] // [Navigation Delegate]
private val navigationDelegate = object : GeckoSession.NavigationDelegate { private val navigationDelegate = object : GeckoSession.NavigationDelegate {
@ -605,25 +618,62 @@ open class GeckoWeb @JvmOverloads constructor(
var HH = 0.3 var HH = 0.3
fun pageDown() { fun pageDown() {
val session = this.session ?: return val session = this.session ?: return
val pzc = session.panZoomController sendJsonMsg("MARKER",Pair<String, Any?>("isDown",true))
// val pzc = session.panZoomController
// 💡 현재 눈에 보이는 GeckoView의 높이를 가져옵니다. //
val viewHeight = this.height.toDouble().times(HH) // // 💡 현재 눈에 보이는 GeckoView의 높이를 가져옵니다.
// val viewHeight = this.height.toDouble().times(HH)
// 세로 방향(y)으로 뷰 높이만큼 아래로 스크롤 (픽셀 단위) //
// 세 번째 인자는 애니메이션 여부입니다. // // 세로 방향(y)으로 뷰 높이만큼 아래로 스크롤 (픽셀 단위)
pzc.scrollBy(ScreenLength.fromPixels(0.0), ScreenLength.fromPixels(viewHeight), // // 세 번째 인자는 애니메이션 여부입니다.
PanZoomController.SCROLL_BEHAVIOR_SMOOTH) // pzc.scrollBy(ScreenLength.fromPixels(0.0), ScreenLength.fromPixels(viewHeight),
// PanZoomController.SCROLL_BEHAVIOR_SMOOTH)
//
// showGuideLine((this.height - viewHeight).toInt())
} }
fun pageUp() { fun pageUp() {
val session = this.session ?: return val session = this.session ?: return
val pzc = session.panZoomController sendJsonMsg("MARKER",Pair<String, Any?>("isDown",false))
val viewHeight = this.height.toDouble().times(HH) // 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())
}
// 위로 스크롤 private val guideView: View by lazy {
pzc.scrollBy(ScreenLength.fromPixels(0.0), ScreenLength.fromPixels(-viewHeight), View(context).apply {
PanZoomController.SCROLL_BEHAVIOR_SMOOTH) 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 // File/Download Helpers
@ -712,19 +762,7 @@ open class GeckoWeb @JvmOverloads constructor(
private fun getFilterF() = String(Base64.decode("aHR0cHM6Ly9pamF2dG9ycmVudC5jb20=", Base64.DEFAULT)) 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)) private fun View.post(action: () -> Unit) = this.post(Runnable(action))
fun onPause() { fun onPause() {
@ -738,4 +776,18 @@ open class GeckoWeb @JvmOverloads constructor(
restoreSessionState() restoreSessionState()
session?.setActive(true) 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) { override fun onRemoteLeft(isDouble : Boolean) {
if (binding.lunaticBrowser.isVisible) { if (binding.lunaticBrowser.isVisible) {
doNextPage() binding.lunaticBrowser.geckoWeb.goBack()
} else { } else {
} }
@ -124,7 +124,7 @@ internal class RssHome : RemoteGestureFragment() , KeyEventHandler {
override fun onRemoteRight(isDouble : Boolean) { override fun onRemoteRight(isDouble : Boolean) {
if (binding.lunaticBrowser.isVisible) { if (binding.lunaticBrowser.isVisible) {
binding.lunaticBrowser.geckoWeb.goBack() doNextPage()
} else { } else {
} }
@ -1077,11 +1077,3 @@ internal class RssHome : RemoteGestureFragment() , KeyEventHandler {
fun randomOrNull() : RssData? = lasted.randomOrNull() 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 com.google.common.util.concurrent.ListenableFuture
import androidx.camera.lifecycle.ProcessCameraProvider import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import bums.lunatic.launcher.LunaticLauncher.Companion.toast
class SystemStatusFragment : Fragment() { class SystemStatusFragment : Fragment() {

View File

@ -1,8 +1,10 @@
package bums.lunatic.launcher.home.tokiz package bums.lunatic.launcher.home.tokiz
import android.content.Context
import android.content.DialogInterface import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.content.res.Configuration import android.content.res.Configuration
import android.media.AudioManager
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
@ -31,6 +33,7 @@ import androidx.appcompat.app.AlertDialog
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import bums.lunatic.launcher.LunaticLauncher.Companion.toast
import bums.lunatic.launcher.R import bums.lunatic.launcher.R
import bums.lunatic.launcher.common.RemoteGestureFragment import bums.lunatic.launcher.common.RemoteGestureFragment
import bums.lunatic.launcher.databinding.BooktokiBinding 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.GeckoWeb.JxEvent
import bums.lunatic.launcher.home.KeyEventHandler import bums.lunatic.launcher.home.KeyEventHandler
import bums.lunatic.launcher.home.NeoRssActivity 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.PagedTextLayout
import bums.lunatic.launcher.home.tokiz.view.PagedTextViewInterface import bums.lunatic.launcher.home.tokiz.view.PagedTextViewInterface
import bums.lunatic.launcher.utils.Blog import bums.lunatic.launcher.utils.Blog
@ -238,7 +240,12 @@ class TokiFragment : RemoteGestureFragment(), PagedTextViewInterface,KeyEventHan
} }
override fun onRemoteCenterClick() { override fun onRemoteCenterClick() {
super.onRemoteCenterClick() if (contentsType.contains("youtube")) {
binding.lunaticBrowser.geckoWeb.play_pause()
binding.lunaticBrowser.geckoWeb.saveCurrentSessionState()
} else {
super.onRemoteCenterClick()
}
} }
override fun onRemoteCenterDoubleClick() { override fun onRemoteCenterDoubleClick() {
@ -253,6 +260,11 @@ class TokiFragment : RemoteGestureFragment(), PagedTextViewInterface,KeyEventHan
} else if (contentsType.contains("comics")) { } else if (contentsType.contains("comics")) {
if (contentsType == "comics") sendViewerTouch("right") else actionNextEvent() if (contentsType == "comics") sendViewerTouch("right") else actionNextEvent()
} }
else if (contentsType.contains("youtube")) {
binding.lunaticBrowser.geckoWeb.next10()
}
} }
override fun onRemoteLeft(isDouble : Boolean) { override fun onRemoteLeft(isDouble : Boolean) {
@ -260,6 +272,8 @@ class TokiFragment : RemoteGestureFragment(), PagedTextViewInterface,KeyEventHan
actionPrevEvent(isDouble) actionPrevEvent(isDouble)
} else if (contentsType.contains("comics")) { } else if (contentsType.contains("comics")) {
if (contentsType == "comics") sendViewerTouch("left") else actionNextEvent() 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) { if (binding.pagedLayer.isVisible) {
} else { } else {
if (binding.lunaticBrowser.geckoWeb.scrollState > 0) { if (contentsType.contains("youtube")) {
binding.lunaticBrowser.geckoWeb.nextShorts()
} else { } 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) { if (binding.pagedLayer.isVisible) {
} else { } else {
if (binding.lunaticBrowser.geckoWeb.scrollState < 0) { if (contentsType.contains("youtube")) {
binding.lunaticBrowser.geckoWeb.prevShorts()
} else { } 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) { override fun onHiddenChanged(hidden: Boolean) {
super.onHiddenChanged(hidden) super.onHiddenChanged(hidden)
saveContinuation = false saveContinuation = false
if (hidden) { if (hidden) {
// 💡 화면에서 사라질 때: 타이머 중지 및 애니메이션 중지 // 💡 화면에서 사라질 때: 타이머 중지 및 애니메이션 중지
binding.lunaticBrowser.geckoWeb?.onPause() binding.lunaticBrowser.geckoWeb?.onPause()
// 일반 WebView라면: webView.onPause() 및 webView.pauseTimers() // 일반 WebView라면: webView.onPause() 및 webView.pauseTimers()
} else { } else {
// 💡 다시 나타날 때: 다시 시작 // 💡 다시 나타날 때: 다시 시작
@ -541,6 +566,8 @@ class TokiFragment : RemoteGestureFragment(), PagedTextViewInterface,KeyEventHan
} }
} }
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
@ -551,7 +578,10 @@ class TokiFragment : RemoteGestureFragment(), PagedTextViewInterface,KeyEventHan
// 💡 1. Toki용 레이아웃 설정 // 💡 1. Toki용 레이아웃 설정
binding.lunaticBrowser.setupForToki() binding.lunaticBrowser.setupForToki()
originalVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
// 미디어 볼륨을 0으로 설정 (FLAG_SHOW_UI를 0으로 주면 볼륨 바가 뜨지 않음)
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0)
// 💡 2. GeckoWeb 접근 및 설정 // 💡 2. GeckoWeb 접근 및 설정
val geckoWeb = binding.lunaticBrowser.geckoWeb val geckoWeb = binding.lunaticBrowser.geckoWeb
@ -740,7 +770,21 @@ class TokiFragment : RemoteGestureFragment(), PagedTextViewInterface,KeyEventHan
override fun onResume() { override fun onResume() {
super.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? { fun getLastinfo() : LastInfo? {
@ -1013,21 +1057,7 @@ class TokiFragment : RemoteGestureFragment(), PagedTextViewInterface,KeyEventHan
fun showToast(origin: String) { fun showToast(origin: String) {
activity?.runOnUiThread { activity?.toast(origin)
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()
}
} }
var delayed = 3500L + Math.abs(Random.nextLong().rem(9999L)) 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.net.NetworkRequest
import android.os.* import android.os.*
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import bums.lunatic.launcher.home.toast import bums.lunatic.launcher.LunaticLauncher.Companion.toast
import bums.lunatic.launcher.utils.Blog import bums.lunatic.launcher.utils.Blog
import com.frostwire.jlibtorrent.swig.error_code import com.frostwire.jlibtorrent.swig.error_code
import com.frostwire.jlibtorrent.swig.libtorrent import com.frostwire.jlibtorrent.swig.libtorrent