This commit is contained in:
2026-03-16 15:22:49 +09:00
parent a9fdf9ac9f
commit 1383781dc6
35 changed files with 830 additions and 0 deletions
+1
View File
@@ -136,6 +136,7 @@ dependencies {
"include" to listOf("*.aar", "*.jar"),
)))
implementation(project(":gdrive"))
implementation("com.google.android.gms:play-services-wearable:19.0.0")
val kotlinVersion: String? by extra
val realmVersion = "2.0.0"
implementation ("androidx.appcompat:appcompat:1.7.1")
+4
View File
@@ -146,6 +146,10 @@
android:enabled="true"
android:exported="false" />
<service
android:name=".workers.WatchGestureService"
android:enabled="true"
android:exported="false" />
<service
android:name=".workers.TorrentService"
@@ -0,0 +1,120 @@
(function() {
var retryCount = 0;
var maxRetries = 2;
function tryExtract() {
try {
const newUrl = window.location.origin + window.location.pathname;
const hostname = window.location.hostname;
const contentsType = hostname.includes("book") ? "booktoki" :
hostname.includes("mana") ? "manatoki" :
hostname.includes("new") ? "newtoki" : "web";
// 1. 목차(List) 감지 로직
const listBody = document.querySelector(".list-body");
const novelContent = document.querySelector("#novel_content");
// [중요] 두 핵심 요소가 모두 없으면 재시도 결정
if (!listBody && !novelContent) {
if (retryCount < maxRetries) {
retryCount++;
console.log("DEBUG_LOG: [JS] 요소를 찾지 못해 재시도 중... (" + retryCount + ")");
setTimeout(tryExtract, 500); // 0.5초 후 다시 실행
return;
} else {
console.log("DEBUG_LOG: [JS] 최대 재시도 횟수 초과");
window.webkit.messageHandlers.ContentsRcv.postMessage(JSON.stringify({
'type': 'JP',
'currentUrl' : newUrl
}));
}
return;
}
// --- 1. 목차 추출 실행 ---
if (listBody !== null) {
const contentsArray = [];
const children = listBody.children;
const bookTitleEl = document.querySelector('.view-title span');
const bookTitle = bookTitleEl ? bookTitleEl.innerText.trim() : "Unknown";
const pagination = document.querySelector(".pagination");
let nextPagingUrl = null;
if (pagination) {
const activeLi = pagination.querySelector("li.active");
if (activeLi && activeLi.nextElementSibling) {
const nextA = activeLi.nextElementSibling.querySelector("a");
// 다음 엘리먼트가 존재하고, 'disabled' 클래스가 없으며, href가 있는 경우
if (nextA && nextA.href && !activeLi.nextElementSibling.classList.contains("disabled")) {
nextPagingUrl = nextA.href;
}
}
}
for (let i = 0; i < children.length; i++) {
try {
const wrNumEl = children[i].getElementsByClassName('wr-num')[0];
const wrSubjectA = children[i].getElementsByClassName('wr-subject')[0]?.getElementsByTagName('a')[0];
if (!wrNumEl || !wrSubjectA) continue;
let pageUrl = new URL(wrSubjectA.href, window.location.origin).pathname;
let paths = pageUrl.split('/').filter(p => p);
if (paths.length > 0) {
var lastPart = paths[paths.length - 1];
var isOnlyNumber = lastPart && Array.from(lastPart).every(ch => ch >= '0' && ch <= '9');
if (!isOnlyNumber) paths.pop();
}
pageUrl = "/" + paths.join('/');
contentsArray.push({
'chapterID': parseInt(wrNumEl.textContent.replace(/[^0-9]/g, "")),
'chapterNum': parseInt(wrNumEl.textContent.replace(/[^0-9]/g, "")),
'pathUrl': pageUrl,
'contentsType': contentsType,
'bookPageUrl': window.location.pathname,
'chapterTitle': wrSubjectA.innerText.split('\n').pop().trim(),
'bookTitle': bookTitle,
'currentUrl' : newUrl
});
} catch (e) {}
}
if (contentsArray.length > 0) {
window.webkit.messageHandlers.ContentsRcv.postMessage(JSON.stringify({
'type': 'getListResult',
'contentsType': contentsType,
'bookTitle': bookTitle,
'bookPageUrl': window.location.pathname,
'pages': contentsArray,
'currentUrl' : newUrl,
'nextPagingUrl': nextPagingUrl
}));
return; // 전송 성공 시 종료
}
}
// --- 2. 본문 추출 실행 ---
if (novelContent !== null) {
const titleEl = document.querySelector(".page-desc");
const chapterTitle = titleEl ? titleEl.innerText.trim() : "";
const contents = novelContent.innerText.trim();
if (contents.length > 100) {
window.webkit.messageHandlers.ContentsRcv.postMessage(JSON.stringify({
'type': 'BookContents',
'chapterTitle': chapterTitle,
'bookContents': contents,
'pageUrl' : window.location.pathname,
'currentUrl' : newUrl
}));
}
}
} catch (e) {
console.log("Unified Script Error: " + e);
}
}
// 최초 실행
tryExtract();
})();
@@ -0,0 +1,25 @@
package bums.lunatic.launcher.workers
import bums.lunatic.launcher.utils.Blog
import com.google.android.gms.wearable.MessageEvent
import com.google.android.gms.wearable.WearableListenerService
class WatchGestureService : WearableListenerService() {
override fun onMessageReceived(messageEvent: MessageEvent) {
when (messageEvent.path) {
"/gesture/next" -> {
// 런처의 다음 페이지로 이동하거나, 이북 앱에 키 이벤트 전송
handleGestureAction("NEXT")
}
"/gesture/prev" -> {
handleGestureAction("PREV")
}
}
}
private fun handleGestureAction(action: String) {
// Broadcast를 런처 메인 Activity로 쏘거나,
// 직접 AccessibilityService를 호출하여 시스템 이벤트를 발생시킵니다.
Blog.LOGE("WatchGesture", "Gesture Received: $action")
}
}