This commit is contained in:
lunaticbum 2025-08-13 17:02:15 +09:00
parent 012fcd7b9b
commit 8f3dc93ec1
15 changed files with 1089 additions and 617 deletions

View File

@ -100,6 +100,7 @@
<category android:name="android.intent.category.DEFAULT" />
<action android:name="android.intent.action.MAIN"/>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
@ -130,14 +131,14 @@
<!-- </intent-filter>-->
<!-- </activity>-->
<!-- <activity-->
<!-- android:name=".tokiz.Settings"-->
<!-- android:label="@string/lunar_settings"-->
<!-- android:launchMode="singleInstance"-->
<!-- android:excludeFromRecents="true"-->
<!-- android:exported="true">-->
<activity
android:name=".tokiz.Settings"
android:label="@string/lunar_settings"
android:launchMode="singleInstance"
android:excludeFromRecents="true"
android:exported="true">
<!-- </activity>-->
</activity>
<!-- <activity-->
<!-- android:name=".settings.SettingsActivity"-->
<!-- android:label="@string/lunar_settings"-->

View File

@ -5,6 +5,27 @@ port.onMessage.addListener(response => {
var type= response["type"];
switch (type) {
case "search" : {
var keyword = response["keyword"];
if (location.href.search("bigkinds.or.kr") > -1) {
document.querySelector('input[id^="total-search-key"]').value = keyword
} else if (location.href.search("www.google") > -1) {
document.querySelector('form[action^="/search"]').querySelector("textarea").value = keyword
} else if(location.href.search("naver.com") > -1) {
document.querySelector('input[id^="query"]').value = keyword
} else if(location.href.search("namu.wiki") > -1) {
document.querySelector('input[type^="search"]').value = keyword
}
}
case "searchDo" : {
if (location.href.search("bigkinds.or.kr") > -1) {
document.querySelector('a[class^="bk_keywordSearchBtn"]').click()
} else if (location.href.search("www.google") > -1) {
document.querySelector('form[action^="/search"]').submit()
} else if(location.href.search("naver.com") > -1) {
document.querySelector('[class^="sch_btn_search"]').click()
}
}
case "getList": {
try {
// var listBody = null
@ -39,6 +60,7 @@ port.onMessage.addListener(response => {
}break;
case "scrollDown":{
autoScrollAndSave()
var max = response["max"]
var current = response["current"]
var isUpDown = response["isUpDown"]
@ -74,7 +96,7 @@ port.onMessage.addListener(response => {
}
break;
case "saveContent":{
scrollToLazyImg()
scrollToLazyImg(true === response["fast"])
}
default:
port.postMessage(`Received: ${JSON.stringify(response)}`);
@ -235,12 +257,12 @@ document.addEventListener('DOMContentLoaded', function () {
})
function scrollToLazyImg() {
function scrollToLazyImg(fastMode) {
(function(autoScrollAndSave){
// 한 번에 이동할 픽셀
const step = 200;
const step = fastMode ? 600 : 200;
// 반복 간격(ms) (느릴수록 로딩에 더 여유 생김)
const delay = 600;
const delay = fastMode ? 200 : 600;
// 스크롤 현재 위치 추적
let currentY = window.scrollY;
let maxY = Math.max(
@ -322,357 +344,62 @@ function isNewerThanOneDay(dateStr) {
return (now - date) < oneDayMs;
}
function autoScrollAndSave(senContents) {
if (location.href.search("nate.com") > -1 && document.querySelectorAll("#ad_sponsorBar")) {
if (location.href.startsWith("https://news.nate.com")) {
var string = location.href.toString()
string = string.replace("https://","https://m.");
location.href = string
}
document.querySelectorAll('[class^="float-adv-wrap"]').forEach(e => e.remove())
document.querySelectorAll('[id^="ad_"]').forEach(e => e.remove())
document.querySelectorAll("#ad_sponsorBar").forEach((element) => {element.remove();});
document.querySelectorAll("#newsSidebar").forEach((element) => {element.remove();});
document.querySelectorAll("#header").forEach((element) => {element.remove();});
document.querySelectorAll('iframe[id^="ad"]').forEach(e => e.remove())
document.querySelectorAll("#module_ssul").forEach((element) => {element.remove();});
document.querySelectorAll('[class^="boxtype1 thisTimeNews"]').forEach(e => e.remove())
document.querySelectorAll("#mediaFooter").forEach((element) => {element.remove();});
document.querySelectorAll('[class^="news_cmt"]').forEach(e => e.remove())
document.querySelectorAll('section[class^="rwd_right"]').forEach(e => e.remove())
// 도메인에 맞는 handler 실행
const matchedRule = domainRules.find(rule => rule.test(location.href));
if (matchedRule) {
matchedRule.handler();
}
// 공통 광고 요소 제거는 항상 실행
handleCommon();
window.scrollTo({ top: 2, behavior: 'smooth' });
if (mainContentsEl == null) {
mainContentsEl = document.body.outerHTML
}
if (senContents) {
sendMessage({type: "MainContentsEl", contents: mainContentsEl.outerHTML, currentPage: location.href});
}
}
}
// 도메인 규칙 배열
const domainRules = [
{ test: url => url.includes("nate.com") && document.querySelectorAll("#ad_sponsorBar").length > 0, handler: handleNate },
{ test: url => url.includes("zdnet.co.kr"), handler: handleZdnet },
{ test: url => url.includes("inews24"), handler: handleInews24 },
{ test: url => url.includes("starnewskorea"), handler: handleStarnewsKorea },
{ test: url => url.includes("khan.co.kr"), handler: handleKhan },
{ test: url => url.includes("m.health.chosun.com"), handler: handleHealthChosun },
{ test: url => url.includes("betanews"), handler: handleBetanews },
{ test: url => url.includes("newsis.com"), handler: handleNewsis },
{ test: url => url.includes("seoul.co.kr"), handler: handleSeoul },
{ test: url => url.includes("ytn.co.kr"), handler: handleYtn },
{ test: url => url.includes("nocutnews.co.kr"), handler: handleNocutnews },
{ test: url => url.includes("sedaily.com"), handler: handleSedaily },
{ test: url => url.includes("digitaltoday"), handler: handleDigitaltoday },
{ test: url => url.includes("kormedi"), handler: handleKormedi },
{ test: url => url.includes("chosun.com"), handler: handleChosun },
{ test: url => url.includes("traveltimes"), handler: handleTraveltimes },
{ test: url => url.includes("theqoo.net") && document.querySelectorAll('[class^="m-list m-element"]').length > 0, handler: handleTheqoo },
{ test: url => url.includes("doctorsnews"), handler: handleDoctorsnews },
{ test: url => url.includes("dcinside.com") && document.querySelectorAll('[class^="container"]').length > 0, handler: handleDcinside },
{ test: url => url.includes("fmkorea.com") && document.querySelectorAll('[class^="bd bd_mobile"]').length > 0, handler: handleFmkorea },
{ test: url => url.includes("torrentzota"), handler: handleToreentZota},
{ test: url => url.includes("acrofan.com") && document.querySelectorAll('[id^="wide"]').length > 0, handler: handleAcrofan },
{ test: url => url.includes("yna.co.kr") && document.querySelectorAll('[class^="wrapper"]').length > 0, handler: handleYna },
{ test: url => url.includes("clien") && document.querySelectorAll('[class^="content_view"]').length > 0, handler: handleClien },
{ test: url => url.includes("toki") && (document.querySelectorAll('[id^="id_mbv"]').length > 0 || document.querySelectorAll('[class^="basic-banner"]').length > 0), handler: handleToki },
];
if (location.href.search("khan.co.kr") > -1) {
document.querySelectorAll('[class^="banner-"]').forEach(e => e.remove())
document.querySelectorAll('[class^="google-auto-placed"]').forEach(e => e.remove())
document.querySelectorAll('[class^="relationList"]').forEach(e => e.remove())
document.querySelectorAll('[class^="reporter_news"]').forEach(e => e.remove())
document.querySelectorAll('[class^="box"]').forEach(e => e.remove())
document.querySelectorAll('footer').forEach(e => e.remove())
document.querySelectorAll('iframe').forEach(e => e.remove())
document.querySelectorAll('[class^="bottom-wrap"]').forEach(e => e.remove())
document.querySelectorAll('aside[class^="list-wrap"]').forEach(e => e.remove())
document.querySelectorAll('div[style^="width:100%;height:250px;text-align:center;margin-bottom:20px;overflow:hidden;"]').forEach(e => e.remove())
function handleCommon() {
// 공통 광고 제거
if (document.querySelector(".top_google_ad_space")) document.querySelector(".top_google_ad_space").remove();
document.querySelectorAll(".adv-group, [id^='div-gpt-ad'], [id^='div_adnmore_area'], [class^='adv-groupno'], [class^='code-block'], .ad-template").forEach(e => e.remove());
if (document.querySelector('#xpromo-bottom-sheet')) document.querySelector('#xpromo-bottom-sheet').remove();
}
if (location.href.search("betanews") > -1) {
document.querySelectorAll('[class^="banner"]').forEach(e => e.remove())
document.querySelectorAll('aside[class^="list-wrap"]').forEach(e => e.remove())
document.querySelectorAll('section[class^="bottom"]').forEach(e => e.remove())
document.querySelectorAll('footer').forEach(e => e.remove())
}
if (location.href.search("newsis.com") > -1) {
document.querySelectorAll('div[style^="width: 300px; margin: 0 auto 25px;"]').forEach(e => e.remove())
document.querySelectorAll('div[class^="listStyle"]').forEach(e => e.remove())
document.querySelectorAll('div[class^="headtopBanner"]').forEach(e => e.remove())
document.querySelectorAll('div[id^="ad_"]').forEach(e => e.remove())
document.querySelectorAll('div[id^="adbay"]').forEach(e => e.remove())
document.querySelectorAll('div[class^="article"]').forEach(function (e) {
e.querySelectorAll("iframe").forEach(e => e.remove())
})
document.querySelectorAll('div[class^="Float"]').forEach(e => e.remove())
document.querySelectorAll('div[class^="bxcn"]').forEach(e => e.remove())
document.querySelectorAll('div[class^="A1"]').forEach(e => e.remove())
document.querySelectorAll('div[id^="news_body_end"]').forEach(e => e.remove())
document.querySelectorAll('div[class^="linkNews"]').forEach(e => e.remove())
document.querySelectorAll('div[id^="footer"]').forEach(e => e.remove())
}
if (location.href.search("seoul.co.kr") > -1) {
document.querySelectorAll('div[class^="rowAd"]').forEach(e => e.remove())
document.querySelectorAll('div[class^="ad"]').forEach(e => e.remove())
document.querySelectorAll('[class^="articleBottomNews"]').forEach(e => e.remove())
document.querySelectorAll('[class^="sub-layoutBorder"]').forEach(e => e.remove())
document.querySelectorAll('[class^="m-sectionLayout"]').forEach(e => e.remove())
document.querySelectorAll('[id^="rollingAdDiv"]').forEach(e => e.remove())
document.querySelectorAll('footer').forEach(e => e.remove())
document.querySelectorAll('[class^="banner"]').forEach(e => e.remove())
document.querySelectorAll('[class^="joinPopup"]').forEach(e => e.remove())
}
if (location.href.search("ytn.co.kr") > -1) {
document.querySelectorAll('div[class^="footer"]').forEach(e => e.remove())
document.querySelectorAll('div[style^="width: 100%; background-color: rgb(255, 255, 255); display: flex; justify-content: center;"]').forEach(e => e.remove())
document.querySelectorAll('div[style^="width:100%"]').forEach(e => e.remove())
document.querySelectorAll('div[class^="ad_"]').forEach(e => e.remove())
document.querySelectorAll('div[class^="bx_hot"]').forEach(e => e.remove())
document.querySelectorAll('div[class^="bx_ad"]').forEach(e => e.remove())
document.querySelectorAll('div[class^="bx_pro"]').forEach(e => e.remove())
document.querySelectorAll('div[class^="bx_main"]').forEach(e => e.remove())
document.querySelectorAll('div[class^="bx_sns"]').forEach(e => e.remove())
document.querySelectorAll('div[id^="popularArea"]').forEach(e => e.remove())
}
if (location.href.search("nocutnews.co.kr")> -1) {
document.querySelectorAll('div[style^="width:300px; margin:0 auto 25px;"]').forEach(e => e.remove())
document.querySelectorAll('div[class^="ct2"]').forEach(e => e.remove())
document.querySelectorAll('div[class^="footer"]').forEach(e => e.remove())
document.querySelectorAll('div[class^="con_b"]').forEach(e => e.remove())
document.querySelectorAll('div[class^="v_like"]').forEach(e => e.remove())
document.querySelectorAll('div[id^="divCommonRelated"]').forEach(e => e.remove())
document.querySelectorAll('iframe').forEach(e => e.remove())
}
if (location.href.search("sedaily.com") > -1) {
document.querySelectorAll('div[class^="article"]').forEach(function (e) {
e.querySelectorAll("iframe").forEach(e => e.remove())
})
document.querySelectorAll('div[class^="banner"]').forEach(e => e.remove())
document.querySelectorAll('div[class^=" banner"]').forEach(e => e.remove())
document.querySelectorAll('div[class^="col-right"]').forEach(e => e.remove())
document.querySelectorAll('footer[class^="footer"]').forEach(e => e.remove())
}
if (location.href.search("digitaltoday") > -1) {
document.querySelectorAll('[class^="aedi"]').forEach(e => e.remove())
document.querySelectorAll('[id^="layer-popups"]').forEach(e => e.remove())
document.querySelectorAll('[id^="enters"]').forEach(e => e.remove())
document.querySelectorAll('[class^="view-toast"]').forEach(e => e.remove())
document.querySelectorAll('footer[id^="user"]').forEach(e => e.remove())
document.querySelectorAll('[class^="clearfix"]').forEach(e => e.remove())
document.querySelectorAll('[id^="bottom-sticky"]').forEach(e => e.remove())
if (location.href.search("kormedi") > -1) {
document.querySelectorAll('[class^="entry-content-after"]').forEach(e => e.remove())
document.querySelectorAll('[class^="code-block"]').forEach(e => e.remove())
document.querySelectorAll('[class^="post-featured"]').forEach(e => e.remove())
document.querySelectorAll('footer[class^="cs"]').forEach(e => e.remove())
document.querySelectorAll('ins[class^="adsbygoogle"]').forEach(e => e.remove())
}
}
if (location.href.search("chosun.com") > -1) {
document.querySelectorAll('[class^="arcad-wrapper"]').forEach(e => e.remove())
document.querySelectorAll('[class^="flex-chain-wrapper"]').forEach(e => e.remove())
document.querySelectorAll('footer[class^="layout"]').forEach(e => e.remove())
document.querySelectorAll('aside[class^="layout"]').forEach(e => e.remove())
document.querySelectorAll('ins').forEach(e => e.remove())
}
if (location.href.search("traveltimes") > -1) {
document.querySelectorAll('ins').forEach(e => e.remove())
document.querySelectorAll('[class^="teads"]').forEach(e => e.remove())
document.querySelectorAll('[class^="tdn"]').forEach(e => e.remove())
document.querySelectorAll('aside[class^="grid"]').forEach(e => e.remove())
document.querySelectorAll('footer[id^="user"]').forEach(e => e.remove())
document.querySelectorAll('[class^="clearfix"]').forEach(e => e.remove())
}
if (location.href.search("theqoo.net") > -1 && document.querySelectorAll('[class^="m-list m-element"]')) {
document.querySelectorAll('[class^="m-list m-element"]').forEach(e => e.remove())
document.querySelectorAll('[class^="button_area"]').forEach(e => e.remove())
document.querySelectorAll('[class^="board_content_google_ad"]').forEach(e => e.remove())
document.querySelectorAll('[class^="clearfix list-header"]').forEach(e => e.remove())
document.querySelectorAll('[class^="clearfix list-footer"]').forEach(e => e.remove())
document.querySelectorAll('[class^="main-footer"]').forEach(e => e.remove())
document.querySelectorAll('ins[class^="adsbygoogle"]').forEach(e => e.remove())
mainContentsEl = document.querySelector('div[id="grid-content"]')
}
if(location.href.search("doctorsnews") > -1) {
document.querySelectorAll('[class^="clearfix"]').forEach(e => e.remove())
document.querySelectorAll('aside').forEach(e => e.remove())
document.querySelectorAll('[class^="banner_"]').forEach(e => e.remove())
}
if (location.href.search("dcinside.com") > -1 && document.querySelectorAll('[class^="container"]')) {
document.querySelectorAll('[id^="view_btn_area"]').forEach(e => e.remove())
document.querySelectorAll('[class^="trend-rank"]').forEach(e => e.remove())
document.querySelectorAll('[class^="view-btm-con"]').forEach(e => e.remove())
document.querySelectorAll('[class^="md-tit-box"]').forEach(e => e.remove())
document.querySelectorAll('[class^="gall-detail-lst"]').forEach(e => e.remove())
document.querySelectorAll('[class^="outside-search-box"]').forEach(e => e.remove())
document.querySelectorAll('[class^="footer ftlong"]').forEach(e => e.remove())
document.querySelectorAll('[class^="adv-group"]').forEach(e => e.remove())
document.querySelectorAll('li[style^="cursor:default;"]').forEach(e => e.remove())
mainContentsEl = document.querySelector('div[class="container"]')
}
if (location.href.search("fmkorea.com") > -1 && document.querySelectorAll('[class^="bd bd_mobile"]')) {
document.querySelectorAll('[class^="fmad_wrapper fmad_naver_power_link"]').forEach(e => e.remove())
document.querySelectorAll('[class^="ad ad_wrapper"]').forEach(e => e.remove())
document.querySelectorAll('[class^="bd_lst_wrp"]').forEach(e => e.remove())
document.querySelectorAll('[class^="m_top_hotdeal"]').forEach(e => e.remove())
document.querySelectorAll('[style^="margin-top:2px;line-height:0;"]').forEach(e => e.remove())
mainContentsEl = document.querySelector('div[class="bd bd_mobile"]')
}
if (location.hostname.search("acrofan.com") > -1 && document.querySelectorAll('[id^="wide"]')) {
document.querySelectorAll('[class^="header"]').forEach(e => e.remove())
document.querySelectorAll('[class^="footer"]').forEach(e => e.remove())
document.querySelectorAll('[id^="tabmenu"]').forEach(e => e.remove())
mainContentsEl = document.querySelector('div[class="wide"]')
}
if (location.hostname.search("yna.co.kr") > -1 && document.querySelectorAll('[class^="wrapper"]')) {
document.querySelectorAll('[class^="aside-box426 sticky"]').forEach(e => e.remove())
document.querySelectorAll('aside[class^="aside-box"]').forEach(e => e.remove())
document.querySelectorAll('[class^="section02"]').forEach(e => e.remove())
document.querySelectorAll('[class^="content03 wide"]').forEach(e => e.remove())
document.querySelectorAll('[id="footer"]').forEach(e => e.remove())
mainContentsEl = document.querySelector('div[id="container"]')
}
if (location.hostname.search("clien") > -1 && document.querySelectorAll('[class^="content_view"]')) {
document.querySelectorAll('[class^="view_top"]').forEach(e => e.remove())
document.querySelectorAll('[id^="naverAd"]').forEach(e => e.remove())
document.querySelectorAll('[class^="content_view_list"]').forEach(e => e.remove())
document.querySelectorAll('[class^="footer_wrap"]').forEach(e => e.remove())
mainContentsEl = document.querySelector('div[id="content_view"]')
}
if (location.hostname.search("toki") > -1 && document.querySelectorAll('[id^="id_mbv"]')) {
document.querySelectorAll('[id^="id_mbv"]').forEach(e => e.remove())
}
if (location.hostname.search("toki") > -1 && document.querySelectorAll('[class^="basic-banner"]')) {
document.querySelectorAll('[class^="basic-banner"]').forEach(e => e.remove())
}
if (document.querySelector(".top_google_ad_space")) {
document.querySelector(".top_google_ad_space").remove()
}
if (document.querySelectorAll(".adv-group")) {
document.querySelectorAll(".adv-group").forEach(e =>
e.remove()
)
}
if (document.querySelectorAll('[id^="div-gpt-ad"]')) {
document.querySelectorAll('[id^="div-gpt-ad"]').forEach(e =>
e.remove()
)
}
if (document.location.href.search("reddit") > -1) {
if (document.querySelector('#xpromo-bottom-sheet')) {
document.querySelector('#xpromo-bottom-sheet').remove()
}
}
if (document.querySelectorAll('[id^="div_adnmore_area"]')) {
document.querySelectorAll('[id^="div_adnmore_area"]').forEach(e =>
e.remove()
)
}
if (document.querySelectorAll('[id^="div_adnmore_area"]')) {
document.querySelectorAll('[id^="div_adnmore_area"]').forEach(e =>
e.remove()
)
}
if (document.querySelectorAll('[class^="adv-groupno"]')) {
document.querySelectorAll('[class^="adv-groupno"]').forEach(e =>
e.remove()
)
}
if (document.querySelectorAll('[class^="code-block"]')) {
document.querySelectorAll('[class^="code-block"]').forEach(e => e.remove())
}
if (document.querySelectorAll('.ad-template')) {
document.querySelectorAll('.ad-template').forEach(function (e) {
e.remove()
})
}
if (location.href.search("arca.live") > -1 && document.querySelectorAll('[class^="vrow hybrid"]')) {
const tempArray = [];
document.querySelectorAll('[class^="vrow hybrid"]').forEach(function (aracaLi) {
if (aracaLi.innerHTML.search("title ") > -1) {
// title.hybrid-title 클래스가 붙은 첫 번째 요소 텍스트 추출
let titleEl = aracaLi.querySelector('.title.hybrid-title');
let title = titleEl ? titleEl.textContent.trim() : '';
// badge 클래스 텍스트와 user-info 클래스 텍스트 합치기
let descBadge = aracaLi.querySelector('.badge');
let descUserInfo = aracaLi.querySelector('.user-info');
let desc = (descBadge ? descBadge.textContent.trim() : '')
if (desc.length > 0) {
desc = desc + ","
}
desc = desc + (descUserInfo ? descUserInfo.textContent.trim() : '');
if (descUserInfo) {
desc = desc + ","
}
// time 태그 datetime 속성 값
let timeEl = aracaLi.querySelector('time');
let dateTime = timeEl ? timeEl.getAttribute('datetime') : '';
// console.log("dateTime >>> "+ dateTime)
// const dateObj = new Date(dateTime);
// console.log(dateObj.getFullYear()); // 2025
// console.log(dateObj.getMonth() + 1); // 7 (월은 0부터 시작하므로 +1)
// console.log(dateObj.getDate()); // 30
// console.log(dateObj.getHours()); // 13 (현지 시간 기준)
// console.log(dateObj.getMinutes()); // 45
// console.log(dateObj.getSeconds());
// console.log(dateObj);
// img 태그 src 값
let imgEl = aracaLi.querySelector('img');
let thumbnail = imgEl ? imgEl.getAttribute('src') : '';
// 링크 만들기
let link = 'https://arca.live';
if (titleEl && titleEl.getAttribute('href')) {
link += titleEl.getAttribute('href');
} else {
let aEl = aracaLi.querySelector('a');
if (aEl && aEl.getAttribute('href')) {
link += aEl.getAttribute('href');
}
}
if (title.length > 0 && link.length > 20) {
// thumbnail 절대경로 처리 (프로토콜이 없는 경우 https: 붙이기)
if (thumbnail.startsWith('//')) {
thumbnail = 'https:' + thumbnail;
}
// 날짜가 하루 이내인지 확인하는 함수 사용
tempArray.push({
title: title.replace(/\s+/g, ' ').trim(),
description: desc.replace(/\s+/g, ' ').trim() + "ARCA",
pubDate: new Date(dateTime).getTime(),
originPage: link,
"category": "ARCA",
thumbnail: thumbnail
});
}
}
})
if (tempArray.length > 0) {
console.log(tempArray);
sendMessage(
{
type: "PRIVATES",
privates: tempArray,
currentPage: location.href
}
);
location.href = "about:blank;"
}
}
if (document.querySelectorAll('[class^="popupBanner_w popupOpen"]')) {
document.querySelectorAll('[class^="popupBanner_w popupOpen"]').forEach(function (e) {
e.remove()
})
}
if (document.querySelectorAll('iframe')) {
document.querySelectorAll('iframe').forEach(function (e) {
if (e.getAttribute("src") != null && (e.getAttribute("src").search("ads") > -1 || e.getAttribute("src").search("coupang") > -1)) {
e.remove()
}
})
}
if (location.href.search("x.com") > -1) {
var mainClass = document.querySelector('section').className
document.querySelector('section').querySelectorAll('div[class="' + mainClass + '"]').forEach(function (e) {
if (e.hasAttribute("data-testid") && e.querySelector('video')) {
e.querySelectorAll('[poster]').forEach(function (e) {
})
console.log(e.innerHTML)
}
})
}
document.querySelectorAll('iframe').forEach(e => {
const src = e.getAttribute("src");
if (src != null && (src.includes("ads") || src.includes("coupang"))) e.remove();
});
if (document.querySelectorAll('[data-banner^="coupang-"]')) {
document.querySelectorAll('[data-banner^="coupang-"]').forEach(e => e.remove())
@ -690,6 +417,127 @@ function autoScrollAndSave(senContents) {
if (document.querySelectorAll('[class^="science-banner-area"]')) {
document.querySelectorAll('[class^="science-banner-area"]').forEach(e => e.remove())
}
if (document.querySelectorAll('[class^="col-md-4 mb-4 video-item"]').length > 1) {
var datas = []
document.querySelectorAll('[class^="col-md-4 mb-4 video-item"]').forEach(function (e) {
var date = 0
try {
const dateString = e.querySelector('[class^="mb-2"]').querySelector("a").textContent.trim();
const [day, month, year] = dateString.split("/").map(Number);
date = new Date(year, month - 1, day).getTime();
} catch (e) {
}
var actor = ""
try {
actor = e.querySelector('[class^="mb-1"]').getAttribute("alt").trim()
} catch (e) {
}
var desc = ""
try {
e.querySelectorAll('[class^="badge badge-"]').forEach(function (e) {
try {
if (Number(e.textContent) > 0) {
} else {
if (desc.length > 0) {
desc += ","
}
desc += e.textContent.trim()
}
} catch (e) {
}
}
)
} catch (e) {
}
var thumb = ""
try {
thumb = e.querySelector("td").querySelector("a").getAttribute('data-link')
try {
var sets = e.querySelector("td").querySelector("img").getAttribute('srcset')
if (sets.search(",") > -1) {
var srcSet = sets.split(",")
var newT = srcSet[srcSet.length - 1]
if (newT != null && newT.length > 5) {
thumb = newT
}
}
} catch (e) {
}
} catch (e) {
}
var magnet = ""
try {
e.querySelectorAll("td").forEach(function (e) {
e.querySelectorAll("a").forEach(function (e) {
if (e.getAttribute("href").startsWith("magnet")) {
magnet = e.getAttribute("href").replaceAll("&amp;", "&");
}
})
})
} catch (e) {
}
var title = "";
try {
e.querySelector(".name").querySelector("a").querySelectorAll("span").forEach(function (e) {
if (e.hasAttribute("class") == false) {
title = e.textContent.trim();
}
})
} catch (e) {
}
var originPage = ""
try {
originPage = location.protocol + "//" + location.hostname + e.querySelector(".name").querySelector("a").getAttribute("href");
} catch (e) {
}
var screenshots = ""
try {
e.querySelectorAll("a").forEach(function (e) {
if (e.getAttribute("href").search("screenshots") > -1) {
screenshots = e.getAttribute("href");
}
})
} catch (e) {
}
if (thumb.length > 0 || screenshots.length > 0) {
}
datas.push({
"title": title,
"description": desc,
"originPage": originPage,
"magnet_link": magnet,
"thumbnail": thumb,
"pubDate": date,
"screenshotsUrl": screenshots,
"chosung": "",
"category": "PRIVATE"
});
})
sendMessage(
{
type: "PRIVATES",
privates: datas,
currentPage: location.href
}
);
gotoNext()
}
window.scrollTo({ top: 2, behavior: 'smooth' });
}
function handleToreentZota() {
if (location.href.search("torrentzota") > -1 && document.querySelectorAll('a')) {
document.querySelectorAll('a').forEach(function (e) {
if (e.getAttribute('href') != null && e.getAttribute('href').startsWith("/adver-")) {
@ -812,125 +660,122 @@ function autoScrollAndSave(senContents) {
}
}
if (document.querySelectorAll('[class^="col-md-4 mb-4 video-item"]').length > 1) {
var datas = []
document.querySelectorAll('[class^="col-md-4 mb-4 video-item"]').forEach(function (e) {
var date = 0
try {
const dateString = e.querySelector('[class^="mb-2"]').querySelector("a").textContent.trim();
const [day, month, year] = dateString.split("/").map(Number);
date = new Date(year, month - 1, day).getTime();
} catch (e) {
}
var actor = ""
try {
actor = e.querySelector('[class^="mb-1"]').getAttribute("alt").trim()
} catch (e) {
}
var desc = ""
try {
e.querySelectorAll('[class^="badge badge-"]').forEach(function (e) {
try {
if (Number(e.textContent) > 0) {
} else {
if (desc.length > 0) {
desc += ","
}
desc += e.textContent.trim()
}
} catch (e) {
}
}
)
} catch (e) {
}
var thumb = ""
try {
thumb = e.querySelector("td").querySelector("a").getAttribute('data-link')
try {
var sets = e.querySelector("td").querySelector("img").getAttribute('srcset')
if (sets.search(",") > -1) {
var srcSet = sets.split(",")
var newT = srcSet[srcSet.length - 1]
if (newT != null && newT.length > 5) {
thumb = newT
}
}
} catch (e) {
}
} catch (e) {
}
var magnet = ""
try {
e.querySelectorAll("td").forEach(function (e) {
e.querySelectorAll("a").forEach(function (e) {
if (e.getAttribute("href").startsWith("magnet")) {
magnet = e.getAttribute("href").replaceAll("&amp;", "&");
}
})
})
} catch (e) {
}
var title = "";
try {
e.querySelector(".name").querySelector("a").querySelectorAll("span").forEach(function (e) {
if (e.hasAttribute("class") == false) {
title = e.textContent.trim();
}
})
} catch (e) {
}
var originPage = ""
try {
originPage = location.protocol + "//" + location.hostname + e.querySelector(".name").querySelector("a").getAttribute("href");
} catch (e) {
}
var screenshots = ""
try {
e.querySelectorAll("a").forEach(function (e) {
if (e.getAttribute("href").search("screenshots") > -1) {
screenshots = e.getAttribute("href");
}
})
} catch (e) {
}
if (thumb.length > 0 || screenshots.length > 0) {
}
datas.push({
"title": title,
"description": desc,
"originPage": originPage,
"magnet_link": magnet,
"thumbnail": thumb,
"pubDate": date,
"screenshotsUrl": screenshots,
"chosung": "",
"category": "PRIVATE"
});
})
sendMessage(
{
type: "PRIVATES",
privates: datas,
currentPage: location.href
}
);
gotoNext()
}
window.scrollTo({ top: 2, behavior: 'smooth' });
if (senContents) {
sendMessage({type: "MainContentsEl", contents: mainContentsEl.outerHTML, currentPage: location.href});
}
// 도메인별 처리 함수 모음
function handleNate() {
if (location.href.startsWith("https://news.nate.com")) {
location.href = location.href.replace("https://", "https://m.");
}
document.querySelectorAll('[class^="float-adv-wrap"]').forEach(e => e.remove());
document.querySelectorAll('[id^="ad_"]').forEach(e => e.remove());
document.querySelectorAll("#ad_sponsorBar, #newsSidebar, #header, #module_ssul, #mediaFooter").forEach(e => e.remove());
document.querySelectorAll('iframe[id^="ad"]').forEach(e => e.remove());
document.querySelectorAll('[class^="boxtype1 thisTimeNews"], [class^="news_cmt"], section[class^="rwd_right"]').forEach(e => e.remove());
}
function handleZdnet() {
document.querySelectorAll('div[id*="FloatCon"]').forEach(e => e.remove());
}
function handleInews24() {
document.querySelectorAll('ad[alt^="a"], div[class^="topnews"], form[class^="talkplus"], form[class^="timeline"], form[class^="photo"], ins[class^="adsbygoogle"], footer[class^="footer"], ad, footer').forEach(e => e.remove());
}
function handleStarnewsKorea() {
document.querySelectorAll('div[class^="floating-top-ad"], div[class^="fixed bottom"], section[class^="mt-4"], div[id^="dablewidget_"], footer[class^="footer"]').forEach(e => e.remove());
}
function handleKhan() {
document.querySelectorAll('[class^="banner-"], [class^="google-auto-placed"], [class^="relationList"], [class^="reporter_news"], [class^="box"], footer, iframe, [class^="bottom-wrap"], aside[class^="list-wrap"], div[style^="width:100%;height:250px"]').forEach(e => e.remove());
}
function handleHealthChosun() {
document.querySelectorAll('ins, iframe, footer, div[class^="google-"], div[id^="banner-"], section[class^="hranking"], section[class^="related-articles"], div[class^="separator-container"], div[class^="raw-html"]').forEach(e => e.remove());
mainContentsEl = document.querySelectorAll('article[class^="layout_"]');
}
function handleBetanews() {
document.querySelectorAll('[class^="banner"], aside[class^="list-wrap"], section[class^="bottom"], footer').forEach(e => e.remove());
}
function handleNewsis() {
document.querySelectorAll('div[style^="width: 300px; margin: 0 auto 25px;"], div[class^="listStyle"], div[class^="headtopBanner"], div[id^="ad_"], div[id^="adbay"], div[class^="article"], div[class^="Float"], div[class^="bxcn"], div[class^="A1"], div[id^="news_body_end"], div[class^="linkNews"], div[id^="footer"]').forEach(e => e.remove());
document.querySelectorAll('div[class^="article"]').forEach(e => e.querySelectorAll("iframe").forEach(i => i.remove()));
}
function handleSeoul() {
document.querySelectorAll('div[class^="rowAd"], div[class^="ad"], [class^="articleBottomNews"], [class^="sub-layoutBorder"], [class^="m-sectionLayout"], [id^="rollingAdDiv"], footer, [class^="banner"], [class^="joinPopup"]').forEach(e => e.remove());
}
function handleYtn() {
document.querySelectorAll(
'div[class^="footer"], div[style^="width: 100%; background-color: rgb(255, 255, 255); display: flex; justify-content: center;"], div[style^="width:100%"], ' +
'div[class^="ad_"], div[class^="bx_hot"], div[class^="bx_ad"], div[class^="bx_pro"], div[class^="bx_main"], div[class^="bx_sns"], div[id^="popularArea"]'
).forEach(e => e.remove());
}
function handleNocutnews() {
document.querySelectorAll('div[style^="width:300px; margin:0 auto 25px;"], div[class^="ct2"], div[class^="footer"], div[class^="con_b"], div[class^="v_like"], div[id^="divCommonRelated"], iframe').forEach(e => e.remove());
}
function handleSedaily() {
document.querySelectorAll('div[class^="article"]').forEach(e => e.querySelectorAll("iframe").forEach(i => i.remove()));
document.querySelectorAll('div[class^="banner"], div[class^=" banner"], div[class^="col-right"], footer[class^="footer"]').forEach(e => e.remove());
}
function handleDigitaltoday() {
document.querySelectorAll('[class^="aedi"], [id^="layer-popups"], [id^="enters"], [class^="view-toast"], footer[id^="user"], [class^="clearfix"], [id^="bottom-sticky"]').forEach(e => e.remove());
}
function handleKormedi() {
document.querySelectorAll('[class^="entry-content-after"], [class^="code-block"], [class^="post-featured"], footer[class^="cs"], ins[class^="adsbygoogle"]').forEach(e => e.remove());
}
function handleChosun() {
document.querySelectorAll('[class^="arcad-wrapper"], [class^="flex-chain-wrapper"], footer[class^="layout"], aside[class^="layout"], ins, div[id^="paywallCont"], div[class^="paywall--bg"]').forEach(e => e.remove());
}
function handleTraveltimes() {
document.querySelectorAll('ins, [class^="teads"], [class^="tdn"], aside[class^="grid"], footer[id^="user"], [class^="clearfix"]').forEach(e => e.remove());
}
function handleTheqoo() {
document.querySelectorAll('[class^="m-list m-element"], [class^="button_area"], [class^="board_content_google_ad"], [class^="clearfix list-header"], [class^="clearfix list-footer"], [class^="main-footer"], ins[class^="adsbygoogle"]').forEach(e => e.remove());
mainContentsEl = document.querySelector('div[id="grid-content"]');
}
function handleDoctorsnews() {
document.querySelectorAll('[class^="clearfix"], aside, [class^="banner_"]').forEach(e => e.remove());
}
function handleDcinside() {
document.querySelectorAll(
'[id^="view_btn_area"], [class^="trend-rank"], [class^="view-btm-con"], [class^="md-tit-box"], [class^="gall-detail-lst"], [class^="outside-search-box"], [class^="footer ftlong"], [class^="adv-group"], li[style^="cursor:default;"], [id^="div_adnmore_area"]'
).forEach(e => e.remove());
document.querySelectorAll('div[class="container"]').forEach(e => e.remove()); // just in case
mainContentsEl = document.querySelector('div[class="container"]');
}
function handleFmkorea() {
document.querySelectorAll('[class^="fmad_wrapper fmad_naver_power_link"], [class^="ad ad_wrapper"], [class^="bd_lst_wrp"], [class^="m_top_hotdeal"], [style^="margin-top:2px;line-height:0;"]').forEach(e => e.remove());
mainContentsEl = document.querySelector('div[class="bd bd_mobile"]');
}
function handleAcrofan() {
document.querySelectorAll('[class^="header"], [class^="footer"], [id^="tabmenu"]').forEach(e => e.remove());
mainContentsEl = document.querySelector('div[class="wide"]');
}
function handleYna() {
document.querySelectorAll('[class^="aside-box426 sticky"], aside[class^="aside-box"], [class^="section02"], [class^="content03 wide"], [id="footer"]').forEach(e => e.remove());
mainContentsEl = document.querySelector('div[id="container"]');
}
function handleClien() {
document.querySelectorAll('[class^="view_top"], [id^="naverAd"], [class^="content_view_list"], [class^="footer_wrap"]').forEach(e => e.remove());
mainContentsEl = document.querySelector('div[id="content_view"]');
}
function handleToki() {
document.querySelectorAll('[id^="id_mbv"], [class^="basic-banner"]').forEach(e => e.remove());
}

View File

@ -79,6 +79,8 @@ import bums.lunatic.launcher.home.RssViewBuilder
import bums.lunatic.launcher.model.RssData
import bums.lunatic.launcher.model.RssDataType
import bums.lunatic.launcher.tokiz.Comics
import bums.lunatic.launcher.tokiz.Magnet
import bums.lunatic.launcher.tokiz.Perplexity
import bums.lunatic.launcher.tokiz.Twitter
import bums.lunatic.launcher.tokiz.Webtoons
import bums.lunatic.launcher.tokiz.Zota
@ -518,15 +520,6 @@ internal class LauncherActivity : CommonActivity() {
binding.tabs.setOnCheckedChangeListener { g, id ->
showContents(id)
}
binding.hidden.setOnLongClickListener {
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, RssHome().apply {
arguments = Bundle().apply {
putBoolean("HIDDEN", true)
}}
).commit()
true
}
/* handle navigation back events */
handleBackPress()
@ -559,6 +552,11 @@ internal class LauncherActivity : CommonActivity() {
.replace(R.id.fragment_container, Comics())
.commit()
}
R.id.perplexity ->{
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, Perplexity())
.commit()
}
R.id.zota ->{
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, Zota())
@ -569,6 +567,11 @@ internal class LauncherActivity : CommonActivity() {
.replace(R.id.fragment_container, Twitter())
.commit()
}
R.id.magnet ->{
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, Magnet())
.commit()
}
else -> {}
}
}

View File

@ -75,7 +75,10 @@ class GeckoWeb : BWebview {
super.setVisibility(visibility)
decoViews.forEach { it.visibility = visibility }
}
interface OnSave {
fun saved()
}
var mOnSave : OnSave? = null
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
buildWeb()
}
@ -173,6 +176,28 @@ class GeckoWeb : BWebview {
Blog.LOGE(Gson().toJson(message))
mPort?.postMessage(message)
}
fun sendSearch(keyword: String) {
val message: JSONObject = JSONObject()
try {
message.put("type", "search")
message.put("keyword", keyword)
} catch (ex: JSONException) {
throw RuntimeException(ex)
}
Blog.LOGE(Gson().toJson(message))
mPort?.postMessage(message)
}
fun sendSearchDo() {
val message: JSONObject = JSONObject()
try {
message.put("type", "searchDo")
} catch (ex: JSONException) {
throw RuntimeException(ex)
}
Blog.LOGE(Gson().toJson(message))
mPort?.postMessage(message)
}
var mExtension: WebExtension? = null
var mSession: GeckoSession? = null
@ -342,6 +367,7 @@ class GeckoWeb : BWebview {
// 저장 성공 알림 등 후속 처리
println("PDF 저장 완료: ${outputFile.absolutePath}")
mOnSave?.saved()
}
fun downloadImage(context: Context, url: Uri, isGif : Boolean = false) {
@ -604,10 +630,11 @@ class GeckoWeb : BWebview {
}
}
fun saveMd() {
fun saveMd(fast : Boolean? = false) {
val message: JSONObject = JSONObject()
try {
message.put("type", "saveContent")
message.put("fast", fast)
} catch (ex: JSONException) {
throw RuntimeException(ex)
}

View File

@ -103,7 +103,7 @@ internal class RssHome : Fragment() {
var infosJob: Job? = null
// var rssId = ""
lateinit var mRssAdapter: RssItemAdapter
var mLastedQuery: RealmQuery<RssData>? = null
var mRssDataResult: RealmResults<RssData>? = null
val mSimpleFingerGestures =
@ -199,7 +199,7 @@ internal class RssHome : Fragment() {
return false
}
})
var useHiddenMenu = false
var useHiddenMenu = true
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
@ -319,39 +319,72 @@ internal class RssHome : Fragment() {
fun ask() {
val builder: AlertDialog.Builder = AlertDialog.Builder(requireContext())
builder.setTitle("Command Line")
val viewInflated: View = LayoutInflater.from(requireContext())
.inflate(R.layout.text_inpu_password, binding.root as ViewGroup?, false)
val input = viewInflated.findViewById<View>(R.id.input) as EditText
val privateMode = viewInflated.findViewById<CheckBox>(R.id.private_mode) as CheckBox
(viewInflated.findViewById<CheckBox>(R.id.add_vote) as CheckBox)?.let{ it.visibility = View.GONE}
(viewInflated.findViewById<CheckBox>(R.id.add_read) as CheckBox)?.let{ it.visibility = View.GONE}
privateMode.setOnCheckedChangeListener { v,c->
binding.geckoWeb.privateMode = c
}
privateMode.isChecked = true
binding.geckoWeb.privateMode = true
builder.setView(viewInflated)
builder.setPositiveButton(
android.R.string.ok,
DialogInterface.OnClickListener { dialog, which ->
dialog.dismiss()
var command = input.editableText?.toString()
if (command?.length ?: 0 > 0) {
binding.geckoWeb.loadUrl(
"aHR0cHM6Ly9pamF2dG9ycmVudC5jb20=",
"/?searchTerm=${command}"
)
} else {
binding.geckoWeb.loadUrl("aHR0cHM6Ly9pamF2dG9ycmVudC5jb20=")
}
})
builder.setNegativeButton(
android.R.string.cancel,
DialogInterface.OnClickListener { dialog, which -> dialog.cancel() })
val bottomSheet = WebBottomSheet()
bottomSheet.listener = object : WebBottomSheet.OnGoToWebListener{
override fun enterSearch() {
binding.geckoWeb.sendSearchDo()
}
override fun onGotoWeb(
keyword: String,
category: RssDataType,
privateMode: Boolean
) {
when (category) {
RssDataType.GOOGLE -> {
if (binding.geckoWeb.lastedUrl?.contains("www.google") == true) {
binding.geckoWeb.sendSearch(keyword)
} else {
binding.geckoWeb.loadUrl("https://www.google.com/", if (keyword.length ?: 0 > 0) { "/?q=${keyword}" } else { null })
}
binding.geckoWeb.privateMode = false
}
RssDataType.NAMU -> {
if (binding.geckoWeb.lastedUrl?.contains("namu.wiki") == true) {
binding.geckoWeb.sendSearch(keyword)
} else {
binding.geckoWeb.loadUrl("https://namu.wiki", if (keyword.length ?: 0 > 0) {"/Search?q=${keyword}"} else {null})
}
binding.geckoWeb.privateMode = false
}
RssDataType.NAVER -> {
if (binding.geckoWeb.lastedUrl?.contains("naver") == true) {
binding.geckoWeb.sendSearch(keyword)
} else {
binding.geckoWeb.loadUrl("https://search.naver.com/", if (keyword.length ?: 0 > 0) {"search.naver?where=nexearch&query==${keyword}"} else {null})
}
binding.geckoWeb.privateMode = false
}
RssDataType.NEWSFEED -> {
if (binding.geckoWeb.lastedUrl?.contains("news.google") == true) {
binding.geckoWeb.sendSearch(keyword)
} else {
binding.geckoWeb.loadUrl("https://news.google.com/", if (keyword.length ?: 0 > 0) {"search?q=${keyword}&hl=ko&gl=KR&ceid=KR%3Ako"} else {null})
}
binding.geckoWeb.privateMode = false
}
RssDataType.NEWS -> {
if (binding.geckoWeb.lastedUrl?.contains("bigkinds") == true) {
binding.geckoWeb.sendSearch(keyword)
} else {
binding.geckoWeb.loadUrl("https://www.bigkinds.or.kr/", if (keyword.length ?: 0 > 0) {"search?q=${keyword}&hl=ko&gl=KR&ceid=KR%3Ako"} else {null})
}
binding.geckoWeb.privateMode = false
}
RssDataType.PRIVATE -> {
// if (binding.geckoWeb.lastedUrl?.contains("naver") == true) {
//
// } else {
binding.geckoWeb.privateMode = privateMode
binding.geckoWeb.loadUrl("aHR0cHM6Ly9pamF2dG9ycmVudC5jb20=", if (keyword.length ?: 0 > 0) {"/?searchTerm=${keyword}"} else {null})
// }
}
else -> {
builder.show()
}
}
}
}
bottomSheet.show(childFragmentManager, "SearchBottomSheet")
}
var currentRss : RssData? = null
@ -500,7 +533,25 @@ internal class RssHome : Fragment() {
binding.privateBtn.visibility = View.GONE
binding.search.visibility = View.GONE
}
binding.geckoWeb?.mOnSave = object : GeckoWeb.OnSave{
override fun saved() {
currentRss?.originPage.let {
Blog.LOGE("Arrow Center Click")
WorkersDb.getRealm().apply {
writeBlocking {
val result = query<RssData>().query(
if (imageView) "thumbnail == $0" else "originPage == $0",
it
).find()
if (result.size > 0) {
result.forEach { it.vote = true }
}
}
}
doNextPage()
}
}
}
binding.hide.setOnClickListener {
if (binding.geckoWeb.isVisible) {
WorkersDb.getRealm().apply {
@ -583,22 +634,7 @@ internal class RssHome : Fragment() {
}
fun vote() {
binding.geckoWeb?.saveMd()
currentRss?.originPage.let {
Blog.LOGE("Arrow Center Click")
WorkersDb.getRealm().apply {
writeBlocking {
val result = query<RssData>().query(
if (imageView) "thumbnail == $0" else "originPage == $0",
it
).find()
if (result.size > 0) {
result.forEach { it.vote = true }
}
}
}
doNextPage()
}
binding.geckoWeb?.saveMd(true)
}
@ -654,14 +690,11 @@ internal class RssHome : Fragment() {
Blog.LOGE("updateQuery >>> ${q.description()}")
infosJob?.cancel()
commandHandler.removeCallbacks(infoUpdate)
mRssDataResult =
q.sort("pubDate ", Sort.DESCENDING).limit(300).distinct("originPage", "title").find()
mLastedQuery = q.sort("pubDate ", Sort.DESCENDING).limit(300).distinct("originPage", "title")
mRssDataResult = mLastedQuery?.find()
mRssDataResult?.asFlow()?.let { flow ->
infosJob = CoroutineScope(Dispatchers.IO).launch {
flow.collect { changes: ResultsChange<RssData> ->
// when (changes) {
// is InitialResults -> {
synchronized(lasted) {
commandHandler.removeCallbacks(infoUpdate)
WorkersDb.getRealm().apply {
@ -670,17 +703,6 @@ internal class RssHome : Fragment() {
}
commandHandler.post(infoUpdate)
}
// }
//
// is UpdatedResults -> {
// CoroutineScope(Dispatchers.Main).launch {
// changes.changeRanges.forEach {
// mRssAdapter.notifyItemRangeChanged(it.startIndex, it.length)
// }
// }
// }
//
// }
}
}
infosJob?.start()
@ -771,18 +793,7 @@ internal class RssHome : Fragment() {
Blog.LOGE("onViewCreated()")
// fragManager.addOnBackStackChangedListener {
// Blog.LOGE("addOnBackStackChangedListener()")
// shouldResume = if (fragManager.backStackEntryCount == 0) {
// binding.root.visibility = View.VISIBLE
// true
// } else {
// binding.root.visibility = View.GONE
// false
// }
// }
enableSwipeToDeleteAndUndo()
// enableSwipeToDeleteAndUndo()
}

View File

@ -64,17 +64,12 @@ class SearchBottomSheet : BottomSheetDialogFragment() {
text = category.name
isAllCaps = false
setBackgroundResource(android.R.drawable.btn_default)
// 초기 색상(활성 상태 색 표시)
updateButtonStyle(this, true)
setTextColor(Color.WHITE)
setBackgroundResource(R.color.tabs_black)
setOnClickListener {
// 상태 반전
val current = categoryStates[category.name] ?: true
categoryStates[category.name] = !current
// 스타일 업데이트
updateButtonStyle(this, !current)
this.isSelected = !this.isSelected
triggerSearchWithDebounce(inputKeyword.text.toString())
}
}
@ -113,15 +108,6 @@ class SearchBottomSheet : BottomSheetDialogFragment() {
}
}
private fun updateButtonStyle(button: Button, isActive: Boolean) {
if (isActive) {
button.setBackgroundColor(Color.parseColor("#FF9800")) // 활성화 색상
button.setTextColor(Color.WHITE)
} else {
button.setBackgroundColor(Color.LTGRAY) // 비활성화 색상
button.setTextColor(Color.DKGRAY)
}
}
private fun triggerSearchWithDebounce(keyword: String) {
searchRunnable?.let { debounceHandler.removeCallbacks(it) }

View File

@ -0,0 +1,136 @@
package bums.lunatic.launcher.home
import android.content.Context
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.text.Editable
import android.text.TextWatcher
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.Button
import android.widget.CheckBox
import android.widget.EditText
import android.widget.LinearLayout
import android.widget.RadioButton
import android.widget.RadioGroup
import android.widget.Toast
import androidx.core.view.children
import bums.lunatic.launcher.R
import bums.lunatic.launcher.model.RssDataType
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
class WebBottomSheet : BottomSheetDialogFragment() {
private val debounceHandler = Handler(Looper.getMainLooper())
private var searchRunnable: Runnable? = null
private val debounceDelay = 300L // 0.3초 지연
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.bottom_sheet_web, container, false)
}
interface OnGoToWebListener {
fun onGotoWeb(keyword: String, category: RssDataType, privateMode : Boolean)
fun enterSearch()
}
var listener: OnGoToWebListener? = null
var selectedRssType : RssDataType = RssDataType.GOOGLE
lateinit var privateMode : CheckBox
lateinit var inputKeyword : EditText
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
inputKeyword = view.findViewById<EditText>(R.id.inputKeyword)
val categoryContainer = view.findViewById<RadioGroup>(R.id.categoryContainer)
privateMode = view.findViewById<CheckBox>(R.id.check_private) as CheckBox
privateMode.setOnCheckedChangeListener {v,b->triggerSearchWithDebounce(inputKeyword.text.toString())}
// 카테고리 목록
val categories = listOf<RssDataType>(RssDataType.GOOGLE, RssDataType.NAVER,RssDataType.NEWS,RssDataType.NEWSFEED,RssDataType.NAMU,RssDataType.PRIVATE)
// 버튼 동적 생성
categoryContainer.removeAllViews()
categoryContainer.setOnCheckedChangeListener { g,checked ->
((g.findViewById<RadioButton>(checked))?.tag as? RssDataType)?.let {
selectedRssType = it
}
}
for (category in categories) {
val btn = RadioButton(requireContext()).apply {
layoutParams = RadioGroup.LayoutParams(RadioGroup.LayoutParams.WRAP_CONTENT,RadioGroup.LayoutParams.MATCH_PARENT)
setPadding(50,2,50,2)
tag = category
text = category.name
isAllCaps = false
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
setButtonIcon(null)
}
setTextColor(Color.WHITE)
setBackgroundResource(R.color.tabs_black)
setOnClickListener {
triggerSearchWithDebounce(inputKeyword.text.toString())
}
}
categoryContainer.addView(btn)
}
(categoryContainer.getChildAt(0) as? RadioButton)?.let { it.isChecked = true }
inputKeyword.requestFocus()
// 키보드 강제 오픈
inputKeyword.postDelayed( {
val imm = requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.showSoftInput(inputKeyword, InputMethodManager.SHOW_IMPLICIT)
},150L)
// EditText 입력 실시간 감지 + debounce
inputKeyword.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
triggerSearchWithDebounce(s.toString())
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
})
// 키보드 "검색" 액션 처리
inputKeyword.setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
val keyword = inputKeyword.text.toString()
if (keyword.isNotEmpty()) {
triggerSearchWithDebounce(keyword)
dismiss() // 필요 시 닫기
}
true
} else {
false
}
}
}
private fun triggerSearchWithDebounce(keyword: String) {
searchRunnable?.let { debounceHandler.removeCallbacks(it) }
searchRunnable = Runnable {
listener?.onGotoWeb(keyword, selectedRssType, privateMode.isChecked)
}
debounceHandler.postDelayed(searchRunnable!!, debounceDelay)
}
override fun onStart() {
super.onStart()
// 전체 창 높이를 키보드에 맞게 조정
dialog?.window?.setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE or
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
)
}
}

View File

@ -10,6 +10,7 @@ enum class RssDataType {
TORRENT,
YOUTUBE,
NEWSFEED,
NEWS,
TAGS,
REDDIT,
REDDIT_NSFW,
@ -19,6 +20,9 @@ enum class RssDataType {
RULIWEB,
CLIEN,
THEQOO,
NAVER,
GOOGLE,
NAMU,
ARCA;
fun getResId() = when (this) {

View File

@ -0,0 +1,185 @@
package bums.lunatic.launcher.tokiz
import android.content.DialogInterface
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.Color
import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.text.InputType
import android.text.SpannableStringBuilder
import android.text.style.RelativeSizeSpan
import android.util.Log
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.View.GONE
import android.view.View.OnTouchListener
import android.view.View.VISIBLE
import android.view.View.inflate
import android.view.ViewGroup
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.ArrayAdapter
import android.widget.EditText
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.net.toUri
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import bums.lunatic.launcher.LauncherActivity.Companion.getRuntime
import bums.lunatic.launcher.R
import bums.lunatic.launcher.tokiz.common.PairArray
import bums.lunatic.launcher.tokiz.common.TouchArea
import bums.lunatic.launcher.tokiz.common.colorz
import bums.lunatic.launcher.tokiz.common.getIndex
import bums.lunatic.launcher.tokiz.common.typesfacez
import bums.lunatic.launcher.tokiz.data.HistoryManager
import bums.lunatic.launcher.tokiz.data.model.ContentsPageInfo
import bums.lunatic.launcher.tokiz.data.model.ContentsCollection
import bums.lunatic.launcher.tokiz.data.model.PageInfosJ
import bums.lunatic.launcher.tokiz.data.model.HistoryItem
import bums.lunatic.launcher.tokiz.data.model.LastInfo
import bums.lunatic.launcher.tokiz.data.model.PortMessage
import bums.lunatic.launcher.tokiz.data.model.ReaderConfig
import bums.lunatic.launcher.tokiz.dialog.DefaultList
import bums.lunatic.launcher.tokiz.view.JxEvent
import bums.lunatic.launcher.tokiz.view.PagedTextLayout
import bums.lunatic.launcher.tokiz.view.PagedTextViewInterface
import bums.lunatic.launcher.databinding.BooktokiBinding
import bums.lunatic.launcher.utils.Blog
import com.google.gson.Gson
import io.realm.kotlin.Realm
import io.realm.kotlin.UpdatePolicy
import io.realm.kotlin.ext.copyFromRealm
import io.realm.kotlin.ext.query
import org.json.JSONException
import org.json.JSONObject
import org.mozilla.gecko.util.ThreadUtils
import org.mozilla.geckoview.GeckoResult
import org.mozilla.geckoview.GeckoSession
import org.mozilla.geckoview.MediaSession
import org.mozilla.geckoview.WebExtension
import org.mozilla.geckoview.WebExtension.MessageDelegate
import org.mozilla.geckoview.WebExtension.PortDelegate
import org.mozilla.geckoview.WebExtensionController.AddonManagerDelegate
import org.mozilla.geckoview.WebRequestError
import java.lang.System.currentTimeMillis
import java.text.SimpleDateFormat
import java.util.Date
import kotlin.collections.ArrayList
import kotlin.collections.List
import kotlin.collections.MutableList
import kotlin.collections.arrayListOf
import kotlin.collections.first
import kotlin.collections.isNotEmpty
import kotlin.collections.last
import kotlin.collections.sortBy
import kotlin.random.Random
import kotlin.text.contains
import kotlin.text.endsWith
import kotlin.text.equals
import kotlin.text.replace
import kotlin.text.split
import kotlin.text.startsWith
import kotlin.text.toInt
import kotlin.text.toRegex
import kotlin.text.trim
class Magnet : BaseToki(), PagedTextViewInterface {
// "https://btsearch.love/
override val contentsType = "btsearch"
override var lastNumber : Int = 143
override val webcontentsName : String = "btsearch"
override val afterDot = "love"
override fun getLastedDoamin(): String {
return String.format("https://%s.%s", webcontentsName, afterDot)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
super.onCreateView(inflater, container, savedInstanceState)
return binding.root
}
override fun onStart() {
super.onStart()
}
override fun onResume() {
super.onResume()
loadLastInfo()
}
override fun onTouch(touchArea: TouchArea) {
Blog.LOGD(log = "onTouch ${touchArea}")
when (touchArea) {
TouchArea.Center -> {
}
TouchArea.Right -> {
actionNextEvent()
}
TouchArea.Left -> {
actionPrevEvent()
}
TouchArea.DoubleRight -> {
actionNextEvent(true)
}
TouchArea.DoubleLeft -> {
actionPrevEvent(true)
}
else -> {
}
}
}
override fun onLongClick() {
Blog.LOGD(log = "onLongClick")
}
override fun onSwipeLeft(count: Int) {
Blog.LOGD(log = "onSwipeLeft ${count}")
actionNextEvent(count > 1)
}
override fun onSwipeRight(count: Int) {
Blog.LOGD(log = "onSwipeRight ${count}")
actionPrevEvent(count > 1)
}
override fun onSwipeUp(touchCount: Int) {
}
override fun onSwipeDown(touchCount: Int) {
if (touchCount == 2) {
if (binding.pagedLayer.isVisible) {
binding.pagedLayer.visibility = GONE
}
}
}
override fun onTimeoverTouch() {
}
}

View File

@ -0,0 +1,185 @@
package bums.lunatic.launcher.tokiz
import android.content.DialogInterface
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.Color
import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.text.InputType
import android.text.SpannableStringBuilder
import android.text.style.RelativeSizeSpan
import android.util.Log
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.View.GONE
import android.view.View.OnTouchListener
import android.view.View.VISIBLE
import android.view.View.inflate
import android.view.ViewGroup
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.ArrayAdapter
import android.widget.EditText
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.net.toUri
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import bums.lunatic.launcher.LauncherActivity.Companion.getRuntime
import bums.lunatic.launcher.R
import bums.lunatic.launcher.tokiz.common.PairArray
import bums.lunatic.launcher.tokiz.common.TouchArea
import bums.lunatic.launcher.tokiz.common.colorz
import bums.lunatic.launcher.tokiz.common.getIndex
import bums.lunatic.launcher.tokiz.common.typesfacez
import bums.lunatic.launcher.tokiz.data.HistoryManager
import bums.lunatic.launcher.tokiz.data.model.ContentsPageInfo
import bums.lunatic.launcher.tokiz.data.model.ContentsCollection
import bums.lunatic.launcher.tokiz.data.model.PageInfosJ
import bums.lunatic.launcher.tokiz.data.model.HistoryItem
import bums.lunatic.launcher.tokiz.data.model.LastInfo
import bums.lunatic.launcher.tokiz.data.model.PortMessage
import bums.lunatic.launcher.tokiz.data.model.ReaderConfig
import bums.lunatic.launcher.tokiz.dialog.DefaultList
import bums.lunatic.launcher.tokiz.view.JxEvent
import bums.lunatic.launcher.tokiz.view.PagedTextLayout
import bums.lunatic.launcher.tokiz.view.PagedTextViewInterface
import bums.lunatic.launcher.databinding.BooktokiBinding
import bums.lunatic.launcher.utils.Blog
import com.google.gson.Gson
import io.realm.kotlin.Realm
import io.realm.kotlin.UpdatePolicy
import io.realm.kotlin.ext.copyFromRealm
import io.realm.kotlin.ext.query
import org.json.JSONException
import org.json.JSONObject
import org.mozilla.gecko.util.ThreadUtils
import org.mozilla.geckoview.GeckoResult
import org.mozilla.geckoview.GeckoSession
import org.mozilla.geckoview.MediaSession
import org.mozilla.geckoview.WebExtension
import org.mozilla.geckoview.WebExtension.MessageDelegate
import org.mozilla.geckoview.WebExtension.PortDelegate
import org.mozilla.geckoview.WebExtensionController.AddonManagerDelegate
import org.mozilla.geckoview.WebRequestError
import java.lang.System.currentTimeMillis
import java.text.SimpleDateFormat
import java.util.Date
import kotlin.collections.ArrayList
import kotlin.collections.List
import kotlin.collections.MutableList
import kotlin.collections.arrayListOf
import kotlin.collections.first
import kotlin.collections.isNotEmpty
import kotlin.collections.last
import kotlin.collections.sortBy
import kotlin.random.Random
import kotlin.text.contains
import kotlin.text.endsWith
import kotlin.text.equals
import kotlin.text.replace
import kotlin.text.split
import kotlin.text.startsWith
import kotlin.text.toInt
import kotlin.text.toRegex
import kotlin.text.trim
class Perplexity : BaseToki(), PagedTextViewInterface {
// "https://btsearch.love/
override val contentsType = "perplexity"
override var lastNumber : Int = 143
override val webcontentsName : String = "www.perplexity"
override val afterDot = "ai"
override fun getLastedDoamin(): String {
return String.format("https://%s.%s", webcontentsName, afterDot)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
super.onCreateView(inflater, container, savedInstanceState)
return binding.root
}
override fun onStart() {
super.onStart()
}
override fun onResume() {
super.onResume()
loadLastInfo()
}
override fun onTouch(touchArea: TouchArea) {
Blog.LOGD(log = "onTouch ${touchArea}")
when (touchArea) {
TouchArea.Center -> {
}
TouchArea.Right -> {
actionNextEvent()
}
TouchArea.Left -> {
actionPrevEvent()
}
TouchArea.DoubleRight -> {
actionNextEvent(true)
}
TouchArea.DoubleLeft -> {
actionPrevEvent(true)
}
else -> {
}
}
}
override fun onLongClick() {
Blog.LOGD(log = "onLongClick")
}
override fun onSwipeLeft(count: Int) {
Blog.LOGD(log = "onSwipeLeft ${count}")
actionNextEvent(count > 1)
}
override fun onSwipeRight(count: Int) {
Blog.LOGD(log = "onSwipeRight ${count}")
actionPrevEvent(count > 1)
}
override fun onSwipeUp(touchCount: Int) {
}
override fun onSwipeDown(touchCount: Int) {
if (touchCount == 2) {
if (binding.pagedLayer.isVisible) {
binding.pagedLayer.visibility = GONE
}
}
}
override fun onTimeoverTouch() {
}
}

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/finestSilver" android:state_checked="true"/>
<item android:color="@color/finestSilver" android:state_selected="true"/>
<item android:color="@color/black" />
</selector>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size android:width="3px" />
<!-- 투명도 50%의 흰색 (#80FFFFFF) -->
<solid android:color="#50FFFFFF" />
<!-- 1px 패딩 -->
<padding
android:left="1px"
android:top="10dp"
android:right="1px"
android:bottom="10dp" />
</shape>

View File

@ -1,46 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/grey_hex_20"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<!-- 검색어 입력 -->
<EditText
app:layout_constraintBottom_toTopOf="@id/add_read"
android:padding="16dp"
android:id="@+id/inputKeyword"
android:layout_width="match_parent"
android:textColor="@color/white"
android:gravity="right|center_vertical"
android:layout_marginTop="8dp"
android:layout_height="wrap_content"
android:hint="검색어 입력"
android:imeOptions="actionSearch"
android:inputType="text"
android:singleLine="true" />
<LinearLayout
android:gravity="right|center_vertical"
android:padding="10dp"
android:layout_height="48dp"
android:orientation="horizontal"
android:layout_width="match_parent">
<CheckBox
android:padding="0dp"
android:text="Add Vote"
android:textColor="@color/black"
android:id="@+id/add_vote"
android:checked="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<CheckBox
android:padding="0dp"
android:text="Add Read"
android:textColor="@color/black"
android:id="@+id/add_read"
android:checked="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<CheckBox
app:layout_constraintRight_toLeftOf="@id/add_read"
app:layout_constraintBottom_toTopOf="@+id/accessoryToolbar"
android:layout_margin="0dp"
android:padding="0dp"
android:minHeight="0dp"
android:includeFontPadding="false"
android:text="Add Vote"
android:textColor="@color/white"
android:id="@+id/add_vote"
android:checked="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<CheckBox
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toTopOf="@+id/accessoryToolbar"
android:padding="0dp"
android:text="Add Read"
android:minHeight="0dp"
android:includeFontPadding="false"
android:textColor="@color/white"
android:id="@+id/add_read"
android:checked="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<!-- 카테고리 툴바 -->
<HorizontalScrollView
app:layout_constraintBottom_toBottomOf="parent"
android:id="@+id/accessoryToolbar"
android:layout_width="match_parent"
android:layout_height="40dp"
@ -49,6 +60,7 @@
android:scrollbars="none">
<LinearLayout
android:id="@+id/categoryContainer"
android:layout_width="wrap_content"
android:layout_height="match_parent"
@ -56,4 +68,4 @@
android:gravity="center_vertical" />
</HorizontalScrollView>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/grey_hex_20"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<!-- 검색어 입력 -->
<EditText
app:layout_constraintBottom_toTopOf="@id/check_private"
android:padding="16dp"
android:id="@+id/inputKeyword"
android:layout_width="match_parent"
android:layout_marginTop="8dp"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:hint="검색어 입력"
android:imeOptions="actionSearch"
android:inputType="text"
android:singleLine="true" />
<CheckBox
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toTopOf="@id/accessoryToolbar"
android:layout_margin="0dp"
android:padding="0dp"
android:includeFontPadding="false"
android:text="private mode"
android:gravity="right|center_vertical"
android:textColor="@color/white"
android:id="@+id/check_private"
android:checked="true"
android:minHeight="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<!-- 카테고리 툴바 -->
<HorizontalScrollView
app:layout_constraintBottom_toBottomOf="parent"
android:id="@+id/accessoryToolbar"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginTop="8dp"
android:background="#F0F0F0"
android:scrollbars="none">
<RadioGroup
android:id="@+id/categoryContainer"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="center_vertical" />
</HorizontalScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -59,6 +59,11 @@
android:id="@+id/comics"
style="@style/tabItem"
android:layout_height="match_parent"/>
<androidx.appcompat.widget.AppCompatRadioButton
android:text="perplexity"
android:id="@+id/perplexity"
style="@style/tabItem"
android:layout_height="match_parent"/>
<androidx.appcompat.widget.AppCompatRadioButton
android:text="zota"
android:id="@+id/zota"
@ -66,16 +71,13 @@
android:layout_height="match_parent"/>
<androidx.appcompat.widget.AppCompatRadioButton
android:text="twitter"
android:visibility="gone"
android:id="@+id/twitter"
style="@style/tabItem"
android:layout_height="match_parent"/>
<Button
android:text="hidden"
android:id="@+id/hidden"
android:gravity="center"
android:layout_width="60dp"
<androidx.appcompat.widget.AppCompatRadioButton
android:text="magnet"
android:id="@+id/magnet"
style="@style/tabItem"
android:layout_height="match_parent"/>
</RadioGroup>
</HorizontalScrollView>