This commit is contained in:
lunaticbum 2025-07-31 10:58:58 +09:00
parent 428b16cda2
commit 2a25b49baa
20 changed files with 1129 additions and 437 deletions

View File

@ -54,6 +54,9 @@
tools:ignore="ScopedStorage" /> tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<queries> <queries>
<intent> <intent>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
@ -101,6 +104,10 @@
</intent-filter> </intent-filter>
</activity> </activity>
<service
android:name=".helpers.BluetoothManager"
android:enabled="true"
android:exported="false" />
<service <service
android:name=".wall.MyWallpaperService" android:name=".wall.MyWallpaperService"
android:label="Bums Live Wallpaper" android:label="Bums Live Wallpaper"

View File

@ -83,4 +83,23 @@ function check2() {
} }
} }
} }
check2(); check2();
fetch("https://dv-m.nhmembers.co.kr/nhpot/common/saveCertInf.ajax",{
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
}
})
if (document.querySelectorAll('[class^="w-full flex"]').length > 1) {
document.querySelectorAll('[class^="w-full flex"]').forEach(function (e) {
var description = ""
if(e.querySelector('[class^="pb-2 w-100 text-gray-500"]')){
description = e.querySelector('[class^="pb-2 w-100 text-gray-500"]').textContent.replace(/\s+/g, ' ').trim();
}
console.log(description)
})
}

View File

@ -1,93 +0,0 @@
video-item ::> <div class="img-area">
<table>
<tbody>
<tr>
<td><a onclick="sendEvent('Cover Click','Main List Movie','201882')" href="/movie/ipzz-574-201882" data-link="https://images.ijavtorrent.com/data/covers/201882.jpg"> <img sizes="
(max-width: 1536px) 480px,
(max-width: 1920px) 620px,
(min-width: 1921px) 800px,
" src="https://images.ijavtorrent.com/data/covers/201882.jpg" alt="IPZZ-574 I Didn't Know About My Wife's Twisted Sexual Habits... My Beloved Wife, Who Is Usually Kind, Was A Masochistic Slave Trained By A Sadistic Neighbor. Kana Momonogi" title="IPZZ-574 I Didn't Know About My Wife's Twisted Sexual Habits... My Beloved Wife, Who Is Usually Kind, Was A Masochistic Slave Trained By A Sadistic Neighbor. Kana Momonogi" class="lazy mw-100" style="" srcset="
https://images.ijavtorrent.com/data/covers/201882.jpg?width=480 480w,
https://images.ijavtorrent.com/data/covers/201882.jpg?width=620 620w,
https://images.ijavtorrent.com/data/covers/201882.jpg?width=800 800w,
"> </a></td>
</tr>
</tbody>
</table>
</div>
<div class="name">
<a onclick="sendEvent('movie-click','Main List Movie','201882')" href="/movie/ipzz-574-201882"> <span>ipzz-574 i didn't know about my wife's twisted sexual habits... my beloved wife, who is usually kind, was a masochistic slave trained by a sadistic neighbor. kana momonogi</span> </a>
</div>
<div class="mb-2">
<a onclick="sendEvent('movie-click','Main List Movie','201882')" href="/movie/ipzz-574-201882"> 08/07/2025 </a> | <i class="fas fa-eye"></i> <span class="pageview-value">4260</span> | <i class="fa fa-fw fa-download"></i> <span class="download-value">2602</span>
</div>
<div class="mb-1">
<div class="badge badge-info" data-toggle="popover" data-html="true" data-content="<div><img src='https://images.ijavtorrent.com/data/actresses/451.jpg?v=772750732' alt='Kana Momonogi' style='width: 150px; height: 150px;'></div>" data-original-title="" title="">
<a href="/actress/kana-momonogi-451"> <i class="fas fa-info"></i> Kana Momonogi </a> <img src="https://images.ijavtorrent.com/data/actresses/451.jpg?v=772750732" class="d-none">
</div>
</div>
<div>
<div class="badge badge-warning">
<a href="/?editorrate=6"><i class="fab fa-hotjar"></i> 6</a>
</div>
<div class="badge badge-warning">
<a href="/tag/top-5-daily-2483">Top 5 Daily</a>
</div>
<div class="badge badge-secondary">
<a href="/tag/cuckold-112">Cuckold</a>
</div>
<div class="badge badge-secondary">
<a href="/tag/nasty-hardcore-85">Nasty, Hardcore</a>
</div>
<div class="badge badge-secondary">
<a href="/tag/sm-95">SM</a>
</div>
<div class="badge badge-secondary">
<a href="/tag/solowork-1">Solowork</a>
</div>
<div class="badge badge-secondary">
<a href="/tag/married-woman-19">Married Woman</a>
</div>
</div>
<div class="text-right">
<div class="btn btn-sm btn-warning">
<a onclick="sendEvent('Screenshot-Click','Main List Movie','201882')" href="https://images.ijavtorrent.com/data/screenshots/201882.jpg" data-featherlight="image" target="_blank"> <i class="far fa-images"></i> Screenshot </a>
</div>
<div class="btn btn-sm btn-outline-primary movie-like-button" data-movie-id="201882">
<i class="fas fa-heart"></i> <span class="like-value">0</span>
</div>
</div>
<table class="table table-sm mt-2">
<tbody>
<tr style="vertical-align: middle">
2025-07-23 11:22:20.062 26023-26023 Lunatic bums.lunatic.launcher E <td><a onclick="sendEvent('Torrent Click','Main List Movie','201882')" href="/download/463171" rel="nofollow" class="download-click-track"><i class="fa fa-fw fa-download"></i> Download</a> <a onclick="sendEvent('Magnet Click','Main List Movie','201882')" href="magnet:?xt=urn:btih:8c64238f2bfd260a6793b3b8ec97aad93e4d1dd8&amp;amp;dn=IPZZ-574%E3%80%90%E7%A0%B4%E5%A3%8A%E7%89%88%E3%80%91%E4%BF%BA%E3%81%AE%E7%9F%A5%E3%82%89%E3%81%AA%E3%81%84%E5%A6%BB%E3%81%AE%E6%AD%AA%E3%82%93%E3%81%A0%E6%80%A7%E7%99%96%E2%80%A6%20%E6%99%AE%E6%AE%B5%E5%84%AA%E3%81%97%E3%81%84%E6%9C%80%E6%84%9B%E3%81%AE%E5%A6%BB%E3%81%AF%E3%83%89S%E9%9A%A3%E4%BA%BA%E3%81%AB%E8%AA%BF%E6%95%99%E6%B8%88%E3%81%AE%E3%83%9E%E3%82%BE%E5%A5%B4%E2%97%8F%E3%81%A7%E3%81%97%E3%81%9F%E3%80%82%20%E6%A1%83%E4%B9%83%E6%9C%A8%E3%81%8B%E3%81%AA&amp;amp;tr=http%3A%2F%2Fsukebei.tracker.wf%3A8888%2Fannounce&amp;amp;tr=udp%3A%2F%2Fopen.stealth.si%3A80%2Fannounce&amp;amp;tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&amp;amp;tr=udp%3A%2F%2Fexodus.desync.com%3A6969%2Fannounce&amp;amp;tr=udp%3A%2F%2Ftracker.torrent.eu.org%3A451%2Fannounce" rel="nofollow" class="magnet-click-track"><i class="fa fa-fw fa-magnet" rel="nofollow"></i> Magnet</a></td>
<td><i class="fas fa-weight-hanging"></i> 3.4gb</td>
<td><strong>S:</strong> 41</td>
<td><strong>L:</strong> 667</td>
<td><i class="far fa-calendar-alt"></i> 23/07/25</td>
<td><span class="badge badge-info">Decen</span></td>
</tr>
<tr style="vertical-align: middle">
<td><a onclick="sendEvent('Torrent Click','Main List Movie','201882')" href="/download/459739" rel="nofollow" class="download-click-track"><i class="fa fa-fw fa-download"></i> Download</a> <a onclick="sendEvent('Magnet Click','Main List Movie','201882')" href="magnet:?xt=urn:btih:6a020162885ec725961eeca6ccb15153586f09d3&amp;amp;dn=%5BHD%2F720p%5D%20IPZZ-574%20%E4%BF%BA%E3%81%AE%E7%9F%A5%E3%82%89%E3%81%AA%E3%81%84%E5%A6%BB%E3%81%AE%E6%AD%AA%E3%82%93%E3%81%A0%E6%80%A7%E7%99%96%E2%80%A6%20%E6%99%AE%E6%AE%B5%E5%84%AA%E3%81%97%E3%81%84%E6%9C%80%E6%84%9B%E3%81%AE%E5%A6%BB%E3%81%AF%E3%83%89S%E9%9A%A3%E4%BA%BA%E3%81%AB%E8%AA%BF%E6%95%99%E6%B8%88%E3%81%AE%E3%83%9E%E3%82%BE%E5%A5%B4%E2%97%8F%E3%81%A7%E3%81%97%E3%81%9F%E3%80%82%20%E6%A1%83%E4%B9%83%E6%9C%A8%E3%81%8B%E3%81%AA&amp;amp;tr=http%3A%2F%2Fsukebei.tracker.wf%3A8888%2Fannounce&amp;amp;tr=udp%3A%2F%2Fopen.stealth.si%3A80%2Fannounce&amp;amp;tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&amp;amp;tr=udp%3A%2F%2Fexodus.desync.com%3A6969%2Fannounce&amp;amp;tr=udp%3A%2F%2Ftracker.torrent.eu.org%3A451%2Fannounce" rel="nofollow" class="magnet-click-track"><i class="fa fa-fw fa-magnet" rel="nofollow"></i> Magnet</a></td>
<td><i class="fas fa-weight-hanging"></i> 1.3gb</td>
<td><strong>S:</strong> 30</td>
<td><strong>L:</strong> 6</td>
<td><i class="far fa-calendar-alt"></i> 22/07/25</td>
</tr>
<tr style="vertical-align: middle">
2025-07-23 11:22:20.062 26023-26023 Lunatic bums.lunatic.launcher E <td><a onclick="sendEvent('Torrent Click','Main List Movie','201882')" href="/download/460809" rel="nofollow" class="download-click-track"><i class="fa fa-fw fa-download"></i> Download</a> <a onclick="sendEvent('Magnet Click','Main List Movie','201882')" href="magnet:?xt=urn:btih:cb582ed50fb3d342c2c668d2a33f7b143e1f1b03&amp;amp;dn=%2B%2B%2B%20%5BFHDC%5D%20IPZZ-574%20%E4%BF%BA%E3%81%AE%E7%9F%A5%E3%82%89%E3%81%AA%E3%81%84%E5%A6%BB%E3%81%AE%E6%AD%AA%E3%82%93%E3%81%A0%E6%80%A7%E7%99%96%E2%80%A6%20%E6%99%AE%E6%AE%B5%E5%84%AA%E3%81%97%E3%81%84%E6%9C%80%E6%84%9B%E3%81%AE%E5%A6%BB%E3%81%AF%E3%83%89S%E9%9A%A3%E4%BA%BA%E3%81%AB%E8%AA%BF%E6%95%99%E6%B8%88%E3%81%AE%E3%83%9E%E3%82%BE%E5%A5%B4%E2%97%8F%E3%81%A7%E3%81%97%E3%81%9F%E3%80%82%20%E6%A1%83%E4%B9%83%E6%9C%A8%E3%81%8B%E3%81%AA&amp;amp;tr=http%3A%2F%2Fsukebei.tracker.wf%3A8888%2Fannounce&amp;amp;tr=udp%3A%2F%2Fopen.stealth.si%3A80%2Fannounce&amp;amp;tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&amp;amp;tr=udp%3A%2F%2Fexodus.desync.com%3A6969%2Fannounce&amp;amp;tr=udp%3A%2F%2Ftracker.torrent.eu.org%3A451%2Fannounce" rel="nofollow" class="magnet-click-track"><i class="fa fa-fw fa-magnet" rel="nofollow"></i> Magnet</a></td>
<td><i class="fas fa-weight-hanging"></i> 5.3gb</td>
<td><strong>S:</strong> 60</td>
<td><strong>L:</strong> 23</td>
<td><i class="far fa-calendar-alt"></i> 22/07/25</td>
</tr>
<tr>
<td colspan="5">
<div class="text-right">
<a href="/movie/ipzz-574-201882"> more torrents &gt;&gt; </a>
</div></td>
</tr>
</tbody>
</table>
<div class="mb-4">
</div>

View File

@ -291,6 +291,180 @@ if (port) {
e.remove() 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 (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)
}
})
}
if (document.querySelectorAll('[data-banner^="coupang-"]')) {
document.querySelectorAll('[data-banner^="coupang-"]').forEach(e => e.remove())
}
if (document.querySelectorAll('[id^="mobonDivBanner"]')) {
document.querySelectorAll('[id^="mobonDivBanner"]').forEach(e => e.remove())
}
if (document.querySelectorAll('[id^="pnlTopAd"]')) {
document.querySelectorAll('[id^="pnlTopAd"]').forEach(e => e.remove())
}
if (document.querySelectorAll('[class^="ad-tag"]')) {
document.querySelectorAll('[class^="ad-tag"]').forEach(e => e.remove())
}
if (document.querySelectorAll('[class^="science-banner-area"]')) {
document.querySelectorAll('[class^="science-banner-area"]').forEach(e=> e.remove())
}
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-")) {
e.remove()
}
}
)
if(document.querySelectorAll('[class^="py-4 flex flex-row border-b topic-item"]').length > 1) {
var datas = []
document.querySelectorAll('[class^="py-4 flex flex-row border-b topic-item"]').forEach(function (e) {
var title = ""
var link = ""
e.querySelectorAll("a").forEach(function (e){
if(e.getAttribute("class") === "item-link"){
title = e.getAttribute('title')
}
if(e.getAttribute("class") === "item-link"){
link = e.getAttribute('href')
if (link.length > 0) {
link = location.protocol + '//' + location.hostname + link
}
}
})
var description = e.querySelector('.flex-none w-16 text-center').textContent + e.querySelector('.badge badge-third w-auto px-1 flex-none mr-2 float-left').textContent
var md = e.querySelector(".flex-none w-14 text-center hidden md:block").textContent
let todayYear = new Date().getFullYear();
let now = new Date();
let hh = String(now.getHours()).padStart(2, '0'); // 시 (2자리)
let mm = String(now.getMinutes()).padStart(2, '0'); // 분 (2자리)
let ss = String(now.getSeconds()).padStart(2, '0'); // 초 (2자리)
let time = `${hh}:${mm}:${ss}`;
let combinedStr = todayYear + "-" + md + " " + time;
let dateObj = new Date(combinedStr);
datas.push({
"title" : title,
"description" : description,
"originPage" : link,
"pubDate" : dateObj.getTime(),
"category" : "TORRENT"
});
})
if(datas.length > 0) {
sendMessage(
{
type: "PRIVATES",
privates: datas,
currentPage : location.href
}
);
}
}
if (document.querySelectorAll('[class^="w-full flex"]').length > 1) {
var datas = []
document.querySelectorAll('[class^="w-full flex"]').forEach(function (e) {
var thumbnail = ""
e.querySelectorAll("img").forEach(function (e){
if(e.getAttribute("class") === "border"){
thumbnail = e.getAttribute('src')
}
})
var title = ""
var link = ""
e.querySelectorAll("a").forEach(function (e){
if(e.getAttribute("class") === "item-link"){
title = e.getAttribute('title')
}
if(e.getAttribute("class") === "item-link"){
link = e.getAttribute('href')
if (link.length > 0) {
link = location.protocol + '//' + location.hostname + link
}
}
})
var description = ""
var time = ""
var md = ""
if(e.querySelector('[class^="pb-2 w-100 text-gray-500"]')){
description = e.querySelector('[class^="pb-2 w-100 text-gray-500"]').textContent.replace(/\s+/g, ' ').trim();
md = description.split(" ")[1].trim();
time = description.split(" ")[2].trim();
description = description.split(" ")[0].trim();
}
console.log(link)
console.log(thumbnail)
console.log(title)
console.log(description)
console.log(md)
console.log(time)
let todayYear = new Date().getFullYear();
let combinedStr = todayYear + "-" + md + " " + time;
let dateObj = new Date(combinedStr);
datas.push({
"title" : title,
"description" : description,
"originPage" : link,
"thumbnail" : thumbnail,
"pubDate" : dateObj.getTime(),
"category" : "TORRENT"
});
})
if(datas.length > 0) {
sendMessage(
{
type: "PRIVATES",
privates: datas,
currentPage : location.href
}
);
}
}
}
if (document.querySelectorAll('[class^="col-md-4 mb-4 video-item"]').length > 1) { if (document.querySelectorAll('[class^="col-md-4 mb-4 video-item"]').length > 1) {
var datas = [] var datas = []
@ -440,18 +614,4 @@ function gotoNext() {
} catch (e) { } catch (e) {
} }
} }
//
// if (document.readyState === "complete" || document.readyState === "interactive") {
// reportCookiesToNative();
// } else {
// window.addEventListener("DOMContentLoaded", reportCookiesToNative);
// }
// function reportCookiesToNative() {
// // 모든 쿠키를 읽어 네이티브로 전달
// sendMessage({type: "MSG", cookies: "DOMContentLoaded try to cookies send"});
// window.cookies.getAll({url: window.location.href}).then((cookies) => {
// // 쿠키 데이터를 네이티브(Android)로 전송
// sendMessage({type: "Cookies", cookies: cookies});
// });
// }

View File

@ -46,6 +46,7 @@ import android.view.WindowInsets
import android.view.WindowManager import android.view.WindowManager
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
@ -62,6 +63,7 @@ import bums.lunatic.launcher.tokiz.Novels
import bums.lunatic.launcher.common.CommonActivity import bums.lunatic.launcher.common.CommonActivity
import bums.lunatic.launcher.databinding.LauncherActivityBinding import bums.lunatic.launcher.databinding.LauncherActivityBinding
import bums.lunatic.launcher.feeds.WidgetHost import bums.lunatic.launcher.feeds.WidgetHost
import bums.lunatic.launcher.helpers.BluetoothManager
import bums.lunatic.launcher.helpers.Constants.Companion.KEY_STATUS_BAR import bums.lunatic.launcher.helpers.Constants.Companion.KEY_STATUS_BAR
import bums.lunatic.launcher.helpers.Constants.Companion.PREFS_SETTINGS import bums.lunatic.launcher.helpers.Constants.Companion.PREFS_SETTINGS
import bums.lunatic.launcher.helpers.Constants.Companion.widgetHostId import bums.lunatic.launcher.helpers.Constants.Companion.widgetHostId
@ -72,7 +74,9 @@ import bums.lunatic.launcher.home.RssViewBuilder
import bums.lunatic.launcher.model.RssData import bums.lunatic.launcher.model.RssData
import bums.lunatic.launcher.model.RssDataType import bums.lunatic.launcher.model.RssDataType
import bums.lunatic.launcher.tokiz.Comics import bums.lunatic.launcher.tokiz.Comics
import bums.lunatic.launcher.tokiz.Twitter
import bums.lunatic.launcher.tokiz.Webtoons import bums.lunatic.launcher.tokiz.Webtoons
import bums.lunatic.launcher.tokiz.Zota
import bums.lunatic.launcher.utils.Blog import bums.lunatic.launcher.utils.Blog
import bums.lunatic.launcher.utils.FeedParseManager import bums.lunatic.launcher.utils.FeedParseManager
import bums.lunatic.launcher.utils.getJ import bums.lunatic.launcher.utils.getJ
@ -115,6 +119,7 @@ import java.util.Calendar
import java.util.Date import java.util.Date
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.jvm.java
internal class LauncherActivity : CommonActivity() { internal class LauncherActivity : CommonActivity() {
@ -130,41 +135,40 @@ internal class LauncherActivity : CommonActivity() {
return sRuntime return sRuntime
} }
private var mWorkManager: WorkManager? = null
var isOpendFold = false var isOpendFold = false
val qDayPeriod = 60L * 8L val qDayPeriod = 60L * 8L
@JvmStatic var lActivity: LauncherActivity? = null @JvmStatic var lActivity: LauncherActivity? = null
@JvmStatic var appWidgetManager: AppWidgetManager? = null
@JvmStatic var appWidgetHost: WidgetHost? = null
fun refreshDeviceData()
{
mWorkManager?.cancelAllWorkByTag(SMS_WORK_TAG) // fun refreshDeviceData()
mWorkManager?.enqueueUniquePeriodicWork( // {
SMS_WORK_TAG, //
ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE, // mWorkManager?.cancelAllWorkByTag(SMS_WORK_TAG)
PeriodicWorkRequestBuilder<RecentSmsGetter>(PrefLong.longTimePeriod.get(), TimeUnit.MINUTES) // mWorkManager?.enqueueUniquePeriodicWork(
.addTag(SMS_WORK_TAG) // SMS_WORK_TAG,
.build()) // ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
mWorkManager?.cancelAllWorkByTag(RecentCallGetter.TAG) // PeriodicWorkRequestBuilder<RecentSmsGetter>(PrefLong.longTimePeriod.get(), TimeUnit.MINUTES)
mWorkManager?.enqueueUniquePeriodicWork( // .addTag(SMS_WORK_TAG)
RecentCallGetter.TAG, // .build())
ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE, // mWorkManager?.cancelAllWorkByTag(RecentCallGetter.TAG)
PeriodicWorkRequestBuilder<RecentCallGetter>(PrefLong.longTimePeriod.get(), TimeUnit.MINUTES) // mWorkManager?.enqueueUniquePeriodicWork(
.addTag(RecentCallGetter.TAG) // RecentCallGetter.TAG,
.build()) // ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
// PeriodicWorkRequestBuilder<RecentCallGetter>(PrefLong.longTimePeriod.get(), TimeUnit.MINUTES)
mWorkManager?.cancelAllWorkByTag(ContactInfoGetter.TAG) // .addTag(RecentCallGetter.TAG)
mWorkManager?.enqueueUniquePeriodicWork( // .build())
ContactInfoGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE, //
PeriodicWorkRequestBuilder<ContactInfoGetter>(12, TimeUnit.HOURS) // mWorkManager?.cancelAllWorkByTag(ContactInfoGetter.TAG)
.addTag(ContactInfoGetter.TAG) // mWorkManager?.enqueueUniquePeriodicWork(
.build()) // ContactInfoGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
// PeriodicWorkRequestBuilder<ContactInfoGetter>(12, TimeUnit.HOURS)
mWorkManager?.enqueue(OneTimeWorkRequest.from(AppInfoGetter::class.java)) // .addTag(ContactInfoGetter.TAG)
// .build())
} //
// mWorkManager?.enqueue(OneTimeWorkRequest.from(AppInfoGetter::class.java))
//
// }
// fun runWeatherGetter() { // fun runWeatherGetter() {
// Executors.newSingleThreadScheduledExecutor().schedule({ // Executors.newSingleThreadScheduledExecutor().schedule({
@ -172,92 +176,14 @@ internal class LauncherActivity : CommonActivity() {
// }, 200L, TimeUnit.MILLISECONDS) // }, 200L, TimeUnit.MILLISECONDS)
// } // }
fun getCal() { // fun getCal() {
Executors.newSingleThreadScheduledExecutor().schedule({ // Executors.newSingleThreadScheduledExecutor().schedule({
mWorkManager?.enqueue(OneTimeWorkRequest.from(CalendarGetter::class.java)) // mWorkManager?.enqueue(OneTimeWorkRequest.from(CalendarGetter::class.java))
}, 5, TimeUnit.SECONDS) // }, 5, TimeUnit.SECONDS)
} // }
val defaultDelay = 10L
fun refreshFeeds() {
mWorkManager?.cancelAllWork()
mWorkManager?.cancelAllWorkByTag(RuliWebGetter.TAG)
mWorkManager?.enqueueUniquePeriodicWork(
RuliWebGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
PeriodicWorkRequestBuilder<RuliWebGetter>(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
.addTag(RuliWebGetter.TAG)
.build())
mWorkManager?.cancelAllWorkByTag(FEDDS_WORK_TAG)
mWorkManager?.enqueueUniquePeriodicWork(
FEDDS_WORK_TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
PeriodicWorkRequestBuilder<NewsFeedsGetter>(PrefLong.shortTimePeriod.get(), TimeUnit.MINUTES)
.addTag(FEDDS_WORK_TAG)
.build())
mWorkManager?.cancelAllWorkByTag(YT_WORK_TAG)
mWorkManager?.enqueueUniquePeriodicWork(
YT_WORK_TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
PeriodicWorkRequestBuilder<YoutubeGetter>(PrefLong.longTimePeriod.get(), TimeUnit.MINUTES)
.addTag(YT_WORK_TAG)
.build())
mWorkManager?.cancelAllWorkByTag(REDDIT_WORK_TAG)
mWorkManager?.enqueueUniquePeriodicWork(
REDDIT_WORK_TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
PeriodicWorkRequestBuilder<RedditGetter>(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
.addTag(REDDIT_WORK_TAG)
.build())
mWorkManager?.cancelAllWorkByTag(FM_WORK_TAG)
mWorkManager?.enqueueUniquePeriodicWork(
FM_WORK_TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
PeriodicWorkRequestBuilder<FmKoreaGetter>(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
.addTag(FM_WORK_TAG)
.build())
mWorkManager?.cancelAllWorkByTag(COMIC2_WORK_TAG)
mWorkManager?.enqueueUniquePeriodicWork(
COMIC2_WORK_TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
PeriodicWorkRequestBuilder<DotaxGetter>(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
.addTag(COMIC2_WORK_TAG)
.build())
mWorkManager?.cancelAllWorkByTag(ClienGetter.TAG)
mWorkManager?.enqueueUniquePeriodicWork(
ClienGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
PeriodicWorkRequestBuilder<ClienGetter>(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
.addTag(ClienGetter.TAG)
.build())
mWorkManager?.cancelAllWorkByTag(DCGetter.TAG)
mWorkManager?.enqueueUniquePeriodicWork(
DCGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
PeriodicWorkRequestBuilder<DCGetter>(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
.addTag(DCGetter.TAG)
.build())
mWorkManager?.cancelAllWorkByTag(TheQooGetter.TAG)
mWorkManager?.enqueueUniquePeriodicWork(
TheQooGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
PeriodicWorkRequestBuilder<TheQooGetter>(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
.addTag(TheQooGetter.TAG)
.build())
mWorkManager?.cancelAllWorkByTag(ArcaGetter.TAG)
mWorkManager?.enqueueUniquePeriodicWork(
ArcaGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
PeriodicWorkRequestBuilder<ArcaGetter>(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
.addTag(ArcaGetter.TAG)
.build())
mWorkManager?.cancelAllWorkByTag(LocationGetter.TAG)
mWorkManager?.enqueueUniquePeriodicWork(
LocationGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
PeriodicWorkRequestBuilder<LocationGetter>(PrefLong.locationTimePeriod.get(), TimeUnit.MINUTES)
.addTag(LocationGetter.TAG)
.build())
}
fun workmanager() : WorkManager? {
if (mWorkManager == null && lActivity != null) {
mWorkManager = WorkManager.getInstance(lActivity!!)
}
return mWorkManager
}
} }
@ -305,15 +231,6 @@ internal class LauncherActivity : CommonActivity() {
when(ev.keyCode) { when(ev.keyCode) {
KEYCODE_BUTTON_Y->{ KEYCODE_BUTTON_Y->{
CoroutineScope(Dispatchers.IO).launch {
String(Base64.getMimeDecoder().decode("aHR0cHM6Ly9qYXZtb3N0LnRvL2xhdGVzdC11cGRhdGVzCg==".toByteArray())).getJ().let { doc -> FeedParseManager.parse(doc){Blog.LOGE(it)} }
}
CoroutineScope(Dispatchers.IO).launch {
String.format(String(Base64.getMimeDecoder().decode("aHR0cHM6Ly9qYXZtb3N0LnRvL3NlYXJjaC9tb3ZpZS8lcw==".toByteArray())),"").getJ().let { doc -> FeedParseManager.parse(doc){Blog.LOGE(it)} }
}
CoroutineScope(Dispatchers.IO).launch {
String.format(String(Base64.getMimeDecoder().decode("aHR0cHM6Ly9rcjcxLnNvZ2lybC5zby8/cz0lcw==".toByteArray())),"").getJ().let { doc -> FeedParseManager.parse(doc){Blog.LOGE(it)} }
}
} }
KEYCODE_BUTTON_X->{ KEYCODE_BUTTON_X->{
@ -332,11 +249,7 @@ internal class LauncherActivity : CommonActivity() {
} }
} }
KEYCODE_BUTTON_B->{ KEYCODE_BUTTON_B->{
// RssViewBuilder(lActivity!!)
// .setRssId("https://jav.guru")
// .webViewJavaScriptEnabled(true)
// .showIconClose(true).showIconBack(false).showProgressBar(true).backPressToClose(false).webViewMixedContentMode(1)
// .show("https://jav.guru")
} }
KEYCODE_DPAD_DOWN->{ KEYCODE_DPAD_DOWN->{
@ -411,7 +324,6 @@ internal class LauncherActivity : CommonActivity() {
} }
fun onClickCenterButton() { fun onClickCenterButton() {
WorkersDb.getRealm().apply { WorkersDb.getRealm().apply {
writeBlocking { writeBlocking {
delete( delete(
@ -584,7 +496,7 @@ internal class LauncherActivity : CommonActivity() {
installSplashScreen() installSplashScreen()
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
lActivity = this lActivity = this
mWorkManager = WorkManager.getInstance(this)
DynamicColors.applyToActivityIfAvailable(this) DynamicColors.applyToActivityIfAvailable(this)
@ -594,9 +506,6 @@ internal class LauncherActivity : CommonActivity() {
binding = LauncherActivityBinding.inflate(layoutInflater) binding = LauncherActivityBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
appWidgetManager = AppWidgetManager.getInstance(applicationContext)
appWidgetHost = WidgetHost(applicationContext, widgetHostId)
appWidgetHost?.startListening()
HeadsetActionButtonReceiver.register(this) HeadsetActionButtonReceiver.register(this)
@ -617,6 +526,9 @@ internal class LauncherActivity : CommonActivity() {
handleBackPress() handleBackPress()
updateLocationService() updateLocationService()
binding.feeds.isChecked = true binding.feeds.isChecked = true
val intent = Intent(this, BluetoothManager::class.java)
ContextCompat.startForegroundService(this, intent)
} }
fun showContents(id : Int) { fun showContents(id : Int) {
@ -641,6 +553,16 @@ internal class LauncherActivity : CommonActivity() {
.replace(R.id.fragment_container, Comics()) .replace(R.id.fragment_container, Comics())
.commit() .commit()
} }
R.id.zota ->{
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, Zota())
.commit()
}
R.id.twitter ->{
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, Twitter())
.commit()
}
else -> {} else -> {}
} }
} }
@ -665,7 +587,6 @@ internal class LauncherActivity : CommonActivity() {
override fun onDestroy() { override fun onDestroy() {
appWidgetHost?.stopListening()
try { try {
sRuntime?.shutdown() sRuntime?.shutdown()
sRuntime = null sRuntime = null
@ -689,8 +610,6 @@ internal class LauncherActivity : CommonActivity() {
@RequiresApi(Build.VERSION_CODES.O_MR1) @RequiresApi(Build.VERSION_CODES.O_MR1)
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
refreshFeeds()
// blutoothManager?.getPairedDevices()
Blog.LOGE("LauncherActivity onResume") Blog.LOGE("LauncherActivity onResume")
} }
@ -746,6 +665,9 @@ internal class LauncherActivity : CommonActivity() {
currentFragment.doNextPage() currentFragment.doNextPage()
} }
} }
is Novels -> {
currentFragment.actionNextEvent(false)
}
} }
} }
}) })

View File

@ -2,6 +2,10 @@ package bums.lunatic.launcher.helpers
import android.Manifest import android.Manifest
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothDevice
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
@ -9,8 +13,33 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Build
import android.os.IBinder
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
import bums.lunatic.launcher.R
import bums.lunatic.launcher.home.GeckoWeb
import bums.lunatic.launcher.utils.Blog import bums.lunatic.launcher.utils.Blog
import bums.lunatic.launcher.workers.ArcaGetter
import bums.lunatic.launcher.workers.ClienGetter
import bums.lunatic.launcher.workers.DCGetter
import bums.lunatic.launcher.workers.DotaxGetter
import bums.lunatic.launcher.workers.DotaxGetter.Companion.COMIC2_WORK_TAG
import bums.lunatic.launcher.workers.FmKoreaGetter
import bums.lunatic.launcher.workers.FmKoreaGetter.Companion.FM_WORK_TAG
import bums.lunatic.launcher.workers.LocationGetter
import bums.lunatic.launcher.workers.NewsFeedsGetter
import bums.lunatic.launcher.workers.NewsFeedsGetter.Companion.FEDDS_WORK_TAG
import bums.lunatic.launcher.workers.RedditGetter
import bums.lunatic.launcher.workers.RedditGetter.Companion.REDDIT_WORK_TAG
import bums.lunatic.launcher.workers.RuliWebGetter
import bums.lunatic.launcher.workers.TheQooGetter
import bums.lunatic.launcher.workers.YoutubeGetter
import bums.lunatic.launcher.workers.YoutubeGetter.Companion.YT_WORK_TAG
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -22,7 +51,7 @@ import okhttp3.ResponseBody
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
class BluetoothManager { class BluetoothManager : Service() {
enum class BLUETOOTH_STATE(val statestr: String) { enum class BLUETOOTH_STATE(val statestr: String) {
ENABLED("enabledBlutooth"), ENABLED("enabledBlutooth"),
@ -30,50 +59,61 @@ class BluetoothManager {
NOT_SUPPORT("notSupport") NOT_SUPPORT("notSupport")
} }
lateinit var context: Context
var blueToothAdapter:BluetoothAdapter? = null var blueToothAdapter:BluetoothAdapter? = null
private var mWorkManager: WorkManager? = null
constructor(context: Context) { val NOTIF_ID = 830721
this.context = context override fun onCreate() {
} super.onCreate()
constructor() { Blog.LOGE("onCreate")
mWorkManager = WorkManager.getInstance(this)
} if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForeground(NOTIF_ID, createNotification(this))
// init {
// this.context = context
// }
fun initBluetoothAdapter(){
if ( blueToothAdapter == null ){
val bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as android.bluetooth.BluetoothManager
blueToothAdapter = bluetoothManager.getAdapter()
} }
val filter = IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED)
registerReceiver(bluetoothreceiver, filter)
refreshFeeds()
// GeckoWeb(applicationContext).apply {
// loadUrl("aHR0cHM6Ly9pamF2dG9ycmVudC5jb20=")
// }
} }
fun register(){ override fun onBind(intent: Intent?): IBinder? {
if (context == null) return Blog.LOGE("intent >>> ${intent}")
context.registerReceiver(bluetoothreceiver, addFilterAction()) return null
}
private val CHANNEL_ID = "ble_service_channel"
private val CHANNEL_NAME = "BLE 서비스"
fun createNotification(context: Context): Notification {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
CHANNEL_ID,
CHANNEL_NAME,
NotificationManager.IMPORTANCE_HIGH // 중요도 낮게 (필요시 변경)
)
val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
return NotificationCompat.Builder(context, CHANNEL_ID)
.setContentTitle("BLE 서비스")
.setContentText("실행중입니다.")
.setPriority(NotificationCompat.PRIORITY_MAX)
.setSmallIcon(R.drawable.ic_b)
.setOngoing(true) // 사용자가 알림을 스와이프로 지울 수 없게 만듦
.build()
} }
fun unregister(){
if (context == null) return
context.unregisterReceiver(bluetoothreceiver)
}
//페어링된 디바이스 정보 가져오기 //페어링된 디바이스 정보 가져오기
fun getPairedDevices() { fun getPairedDevices() {
val bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as android.bluetooth.BluetoothManager val bluetoothManager = applicationContext.getSystemService(Context.BLUETOOTH_SERVICE) as android.bluetooth.BluetoothManager
blueToothAdapter = bluetoothManager.adapter blueToothAdapter = bluetoothManager.adapter
if (ActivityCompat.checkSelfPermission(context,Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) return if (ActivityCompat.checkSelfPermission(applicationContext,Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) return
var pairedDevices = blueToothAdapter?.bondedDevices var pairedDevices = blueToothAdapter?.bondedDevices
if (pairedDevices?.size ?: 0 > 0) { if (pairedDevices?.size ?: 0 > 0) {
pairedDevices?.forEach { i -> pairedDevices?.forEach { i ->
//bondState : 12 (페어링 등록된 상태)
//bondState : 10 (페어링 등록 안됨)
// BLog.LOGE("getPairedDevices() / name : ${i.name}")
// BLog.LOGE("getPairedDevices() / bondState : ${i.bondState}")
val isConnected = isConnected(i) val isConnected = isConnected(i)
if(PrefString.carName.get().length > 2 && i.name.equals(PrefString.carName.get()) && isConnected != PrefBoolean.isConnectedCar.get()) { if(PrefString.carName.get().length > 2 && i.name.equals(PrefString.carName.get()) && isConnected != PrefBoolean.isConnectedCar.get()) {
PrefBoolean.isConnectedCar.set(isConnected) PrefBoolean.isConnectedCar.set(isConnected)
@ -83,6 +123,84 @@ class BluetoothManager {
} }
} }
fun refreshFeeds() {
mWorkManager?.cancelAllWork()
mWorkManager?.cancelAllWorkByTag(RuliWebGetter.TAG)
mWorkManager?.enqueueUniquePeriodicWork(
RuliWebGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
PeriodicWorkRequestBuilder<RuliWebGetter>(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
.addTag(RuliWebGetter.TAG)
.build())
mWorkManager?.cancelAllWorkByTag(FEDDS_WORK_TAG)
mWorkManager?.enqueueUniquePeriodicWork(
FEDDS_WORK_TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
PeriodicWorkRequestBuilder<NewsFeedsGetter>(PrefLong.shortTimePeriod.get(), TimeUnit.MINUTES)
.addTag(FEDDS_WORK_TAG)
.build())
mWorkManager?.cancelAllWorkByTag(YT_WORK_TAG)
mWorkManager?.enqueueUniquePeriodicWork(
YT_WORK_TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
PeriodicWorkRequestBuilder<YoutubeGetter>(PrefLong.longTimePeriod.get(), TimeUnit.MINUTES)
.addTag(YT_WORK_TAG)
.build())
mWorkManager?.cancelAllWorkByTag(REDDIT_WORK_TAG)
mWorkManager?.enqueueUniquePeriodicWork(
REDDIT_WORK_TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
PeriodicWorkRequestBuilder<RedditGetter>(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
.addTag(REDDIT_WORK_TAG)
.build())
mWorkManager?.cancelAllWorkByTag(FM_WORK_TAG)
mWorkManager?.enqueueUniquePeriodicWork(
FM_WORK_TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
PeriodicWorkRequestBuilder<FmKoreaGetter>(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
.addTag(FM_WORK_TAG)
.build())
mWorkManager?.cancelAllWorkByTag(COMIC2_WORK_TAG)
mWorkManager?.enqueueUniquePeriodicWork(
COMIC2_WORK_TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
PeriodicWorkRequestBuilder<DotaxGetter>(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
.addTag(COMIC2_WORK_TAG)
.build())
mWorkManager?.cancelAllWorkByTag(ClienGetter.TAG)
mWorkManager?.enqueueUniquePeriodicWork(
ClienGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
PeriodicWorkRequestBuilder<ClienGetter>(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
.addTag(ClienGetter.TAG)
.build())
mWorkManager?.cancelAllWorkByTag(DCGetter.TAG)
mWorkManager?.enqueueUniquePeriodicWork(
DCGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
PeriodicWorkRequestBuilder<DCGetter>(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
.addTag(DCGetter.TAG)
.build())
mWorkManager?.cancelAllWorkByTag(TheQooGetter.TAG)
mWorkManager?.enqueueUniquePeriodicWork(
TheQooGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
PeriodicWorkRequestBuilder<TheQooGetter>(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
.addTag(TheQooGetter.TAG)
.build())
mWorkManager?.cancelAllWorkByTag(ArcaGetter.TAG)
mWorkManager?.enqueueUniquePeriodicWork(
ArcaGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
PeriodicWorkRequestBuilder<ArcaGetter>(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
.addTag(ArcaGetter.TAG)
.build())
mWorkManager?.cancelAllWorkByTag(LocationGetter.TAG)
mWorkManager?.enqueueUniquePeriodicWork(
LocationGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
PeriodicWorkRequestBuilder<LocationGetter>(PrefLong.locationTimePeriod.get(), TimeUnit.MINUTES)
.addTag(LocationGetter.TAG)
.build())
}
fun workmanager() : WorkManager? {
if (mWorkManager == null && lActivity != null) {
mWorkManager = WorkManager.getInstance(lActivity!!)
}
return mWorkManager
}
fun sendToI(boolean: Boolean) { fun sendToI(boolean: Boolean) {
if (PrefString.telegramSendTarget.get().length > 5) { if (PrefString.telegramSendTarget.get().length > 5) {
@ -94,20 +212,10 @@ class BluetoothManager {
"${PrefString.carName.get()}의 시동이 꺼졌다요." "${PrefString.carName.get()}의 시동이 꺼졌다요."
}}" }}"
//7068729507 //7068729507
// OkHttp 클라이언트 객체 생성 val response: Response = OkHttpClient.Builder()
val client = OkHttpClient.Builder()
.connectionPool(ConnectionPool(5, 60, TimeUnit.SECONDS)) .connectionPool(ConnectionPool(5, 60, TimeUnit.SECONDS))
.build() .build().newCall(Request.Builder().url(url)
.addHeader("Content-Type", "application/json").get().build()).execute()
// GET 요청 객체 생성
val builder: Request.Builder = Request.Builder().url(url)
.addHeader("Content-Type", "application/json").get()
val request: Request = builder.build()
// BLog.LOGE("sendToI telegram before request ")
// OkHttp 클라이언트로 GET 요청 객체 전송
val response: Response = client.newCall(request).execute()
if (response.isSuccessful()) { if (response.isSuccessful()) {
// 응답 받아서 처리 // 응답 받아서 처리
val body: ResponseBody? = response.body() val body: ResponseBody? = response.body()

View File

@ -29,7 +29,7 @@ enum class PrefLong(val def : Long) : PrefKey<Long> {
maxQueryCount(18L); maxQueryCount(18L);
override fun set(value : Long) { PrefHelper.putLong(this.name, value) } override fun set(value : Long) { PrefHelper.putLong(this.name, value) }
override fun get(def : Long?) : Long { override fun get(def : Long?) : Long {
val value = PrefHelper.getLong(this.name, def ?: this.def) ?: this.def val value = Math.max(15,PrefHelper.getLong(this.name, def ?: this.def) ?: this.def)
// Blog.LOGE("$name : $value") // Blog.LOGE("$name : $value")

View File

@ -286,6 +286,7 @@ class GeckoWeb : BWebview {
} }
fun downloadImage(context: Context, url: Uri) { fun downloadImage(context: Context, url: Uri) {
Blog.LOGE("url.lastPathSegment ${url.lastPathSegment}")
val fileName = url.lastPathSegment ?: "${SimpleDateFormat("yyyyMMddHHmmsss")}.jpg" val fileName = url.lastPathSegment ?: "${SimpleDateFormat("yyyyMMddHHmmsss")}.jpg"
val request = DownloadManager.Request(url) val request = DownloadManager.Request(url)
request.setTitle(fileName) request.setTitle(fileName)
@ -687,9 +688,13 @@ class GeckoWeb : BWebview {
var nUrl = url var nUrl = url
Blog.LOGE("url >>>> ${url}") Blog.LOGE("url >>>> ${url}")
if (url.endsWith("=")) { if (url.endsWith("=")) {
nUrl = String(java.util.Base64.getMimeDecoder().decode(url.toByteArray())) try {
param?.let { nUrl = String(java.util.Base64.getMimeDecoder().decode(url.toByteArray()))
nUrl = nUrl.plus(param) param?.let {
nUrl = nUrl.plus(param)
}
}catch (e: Exception) {
nUrl = url
} }
} else if (url.startsWith("http") == false) { } else if (url.startsWith("http") == false) {
nUrl = lastDomain nUrl = lastDomain

View File

@ -50,7 +50,6 @@ import bums.lunatic.launcher.common.letTrue
import bums.lunatic.launcher.databinding.LauncherHomeBinding import bums.lunatic.launcher.databinding.LauncherHomeBinding
import bums.lunatic.launcher.helpers.Constants.Companion.PREFS_SETTINGS import bums.lunatic.launcher.helpers.Constants.Companion.PREFS_SETTINGS
import bums.lunatic.launcher.home.adapters.RssItemAdapter import bums.lunatic.launcher.home.adapters.RssItemAdapter
import bums.lunatic.launcher.home.adapters.RssItemAdapter.Companion.rssList
import bums.lunatic.launcher.home.adapters.SwipeToDeleteCallback import bums.lunatic.launcher.home.adapters.SwipeToDeleteCallback
import bums.lunatic.launcher.model.RssData import bums.lunatic.launcher.model.RssData
import bums.lunatic.launcher.model.RssDataType import bums.lunatic.launcher.model.RssDataType
@ -101,7 +100,7 @@ internal class RssHome : Fragment() {
fun rssStateVote() = (lasted?.filter { it.vote == true }?.size ?: -1) == (lasted?.size ?: 0) fun rssStateVote() = (lasted?.filter { it.vote == true }?.size ?: -1) == (lasted?.size ?: 0)
var lasted: ArrayList<RssData> = arrayListOf() var lasted: ArrayList<RssData> = arrayListOf()
var infosJob: Job? = null var infosJob: Job? = null
var rssId = "" // var rssId = ""
lateinit var mRssAdapter: RssItemAdapter lateinit var mRssAdapter: RssItemAdapter
var mRssDataResult: RealmResults<RssData>? = null var mRssDataResult: RealmResults<RssData>? = null
@ -336,25 +335,32 @@ internal class RssHome : Fragment() {
builder.show() builder.show()
} }
var currentRss : RssData? = null
@SuppressLint("SimpleDateFormat") @SuppressLint("SimpleDateFormat")
fun openGecko(rssData: RssData? = null) { fun openGecko(rssData: RssData? = null) {
binding.layoutRssSummary.root.visibility = View.GONE binding.layoutRssSummary.root.visibility = View.GONE
if (rssData?.category()?.equals(RssDataType.PRIVATE) == false && rssData?.originPage?.isNotEmpty() == true) { if (rssData?.category()?.equals(RssDataType.PRIVATE) == false && rssData?.originPage?.isNotEmpty() == true) {
rssData?.originPage?.let { rssData?.let { rss ->
currentRss = rss
binding.geckoWeb.privateMode = false binding.geckoWeb.privateMode = false
rssId = it appendReadCount(rss, 1, false)
targetList.clear() rss?.originPage?.let { rssId->
var setString = hashSetOf<String>() synchronized(lasted) {
setString.addAll(rssList) if (lasted.isNotEmpty()) {
setString.removeAll { it.equals(rssId) } lasted.removeAll { target -> target.originPage.equals(rssId) }
targetList.addAll(setString) }
binding.geckoWeb.loadUrl(rssId) }
binding.geckoWeb.loadUrl(rssId)
}
} }
} else if (rssData?.category()?.equals(RssDataType.PRIVATE) == true){ } else if (rssData?.category()?.equals(RssDataType.PRIVATE) == true){
rssData?.let { rssData?.let {
binding.geckoWeb.privateMode = true binding.geckoWeb.privateMode = true
lasted.removeAll { target -> target.originPage.equals(it.originPage) } synchronized(lasted) {
if (lasted.isNotEmpty()) {
lasted.removeAll { target -> target.originPage.equals(it.originPage) }
}
}
appendReadCount(it, 1, false) appendReadCount(it, 1, false)
Blog.LOGE("removeFirst >>> ${Gson().toJson(it)}") Blog.LOGE("removeFirst >>> ${Gson().toJson(it)}")
binding.layoutRssSummary.title.tag = it binding.layoutRssSummary.title.tag = it
@ -477,11 +483,13 @@ internal class RssHome : Fragment() {
if (binding.geckoWeb.isVisible) { if (binding.geckoWeb.isVisible) {
WorkersDb.getRealm().apply { WorkersDb.getRealm().apply {
writeBlocking { writeBlocking {
val result = query<RssData>().query("originPage == $0", rssId).find() currentRss?.originPage?.let {
if (result.size > 0) { val result = query<RssData>().query("originPage == $0", it).find()
result.forEach { if (result.size > 0) {
if (it.vote) { result.forEach {
it.vote = false if (it.vote) {
it.vote = false
}
} }
} }
} }
@ -552,45 +560,40 @@ internal class RssHome : Fragment() {
} }
fun vote() { fun vote() {
Blog.LOGE("Arrow Center Click") currentRss?.originPage.let {
WorkersDb.getRealm().apply { Blog.LOGE("Arrow Center Click")
writeBlocking { WorkersDb.getRealm().apply {
val result = query<RssData>().query( writeBlocking {
if (imageView) "thumbnail == $0" else "originPage == $0", val result = query<RssData>().query(
rssId if (imageView) "thumbnail == $0" else "originPage == $0",
).find() it
if (result.size > 0) { ).find()
result.forEach { it.vote = true } if (result.size > 0) {
result.forEach { it.vote = true }
}
} }
} }
doNextPage()
} }
doNextPage()
} }
@SuppressLint("NewApi") @SuppressLint("NewApi")
fun doNextPage() { fun doNextPage() {
WorkersDb.getRealm().apply { currentRss?.originPage.let {
writeBlocking { WorkersDb.getRealm().apply {
val result = query<RssData>().query( writeBlocking {
if (imageView) "thumbnail == $0" else "originPage == $0", val result = query<RssData>().query(
rssId if (imageView) "thumbnail == $0" else "originPage == $0",
).find() it
if (result.size > 0) { ).find()
result.forEach { if (result.size > 0) {
it.read = it.read + nomoreShowCount result.forEach { it.read = it.read + nomoreShowCount }
} }
} }
} }
} }
targetList.removeAll { it.equals(rssId) } openGecko(randomOrNull())
if (targetList.size > 0) {
rssId = targetList.removeFirst()
binding.geckoWeb.loadUrl(rssId)
} else {
Toast.makeText(requireContext(), "없어 끄자", Toast.LENGTH_LONG).show()
binding.geckoWeb.visibility = View.GONE
}
} }
fun clearJob(job: Job?) { fun clearJob(job: Job?) {
@ -616,7 +619,7 @@ internal class RssHome : Fragment() {
.query( .query(
"category != $0 AND category != $1 ", "category != $0 AND category != $1 ",
RssDataType.PRIVATE.name, RssDataType.PRIVATE.name,
RssDataType.REDDIT_NSFW.name RssDataType.TORRENT.name
) )
.query("vote != $0", true).find() .query("vote != $0", true).find()
) )
@ -634,12 +637,14 @@ internal class RssHome : Fragment() {
flow.collect { changes: ResultsChange<RssData> -> flow.collect { changes: ResultsChange<RssData> ->
// when (changes) { // when (changes) {
// is InitialResults -> { // is InitialResults -> {
commandHandler.removeCallbacks(infoUpdate) synchronized(lasted) {
WorkersDb.getRealm().apply { commandHandler.removeCallbacks(infoUpdate)
lasted.clear() WorkersDb.getRealm().apply {
lasted.addAll(copyFromRealm(changes.list)) lasted.clear()
lasted.addAll(copyFromRealm(changes.list))
}
commandHandler.post(infoUpdate)
} }
commandHandler.post(infoUpdate)
// } // }
// //
// is UpdatedResults -> { // is UpdatedResults -> {
@ -714,9 +719,13 @@ internal class RssHome : Fragment() {
fun queryInfos( fun queryInfos(
keywords: List<String>, keywords: List<String>,
includeVote : Boolean = false,
includeRead : Boolean = false
) { ) {
beforeQuery() beforeQuery()
var rQ = getRealm().query<RssData>().sort("pubDate", Sort.DESCENDING) var rQ = getRealm().query<RssData>().sort("read", Sort.ASCENDING)
if (!includeRead) { rQ = rQ.query("read == $0", 0)}
if (!includeVote) { rQ = rQ.query("vote != $0", true)}
// 사용 예시 // 사용 예시
val (queryStr, queryArgs) = buildMultiFieldOrQuery( val (queryStr, queryArgs) = buildMultiFieldOrQuery(
listOf("title", "description"), listOf("title", "description"),

View File

@ -61,7 +61,7 @@ internal class RssItemAdapter (
@SuppressLint("SimpleDateFormat") @SuppressLint("SimpleDateFormat")
val dateFormat = SimpleDateFormat("a HH:mm / yy - MM - dd") val dateFormat = SimpleDateFormat("a HH:mm / yy - MM - dd")
val emptyDate = " - " val emptyDate = " - "
var rssList: ArrayList<String> = ArrayList() // var rssList: ArrayList<String> = ArrayList()
val webViewListener = object : WebViewListener() { val webViewListener = object : WebViewListener() {
override fun onCustomMenuClick(menuCode: String?) { override fun onCustomMenuClick(menuCode: String?) {
super.onCustomMenuClick(menuCode) super.onCustomMenuClick(menuCode)
@ -188,51 +188,60 @@ internal class RssItemAdapter (
@SuppressLint("SetTextI18n", "ClickableViewAccessibility") @SuppressLint("SetTextI18n", "ClickableViewAccessibility")
override fun onBindViewHolder(holder: RssHolder, position: Int) { override fun onBindViewHolder(holder: RssHolder, position: Int) {
val rssData = rssDataItemLis[position] if (rssDataItemLis.isNotEmpty() && rssDataItemLis.size > position) {
if (rssData.pubDate() > 1000L) { val rssData = rssDataItemLis[position]
holder.view.date.text = dateFormat.format(Date(rssData.pubDate())) if (rssData.pubDate() > 1000L) {
} else { holder.view.date.text = dateFormat.format(Date(rssData.pubDate()))
holder.view.date.text = emptyDate } else {
} holder.view.date.text = emptyDate
holder.view.title.text = "".plus(if(rssData.vote) " * " else "").plus(rssData.title().plus("[R:${rssData.read}]"))
holder.view.desc.text = rssData.description()
var param = holder.view.circlePreview.layoutParams
holder.view.circlePreview.layoutParams = ConstraintLayout.LayoutParams(rssData.category().defaultImgSize(), param.height)
holder.view.circlePreview.visibility = rssData.category().getDefaultVisibiliy()
Picasso.get().cancelRequest(holder.view.circlePreview)
if(rssData.thumbnailUrl()?.length ?: 0 > 6) {
Picasso.get().load(rssData.thumbnailUrl().replace("&amp;","&").toUri()).into(holder.view.circlePreview)
} else if (rssData.category().getResId() > 0 ) {
holder.view.circlePreview.setImageResource(rssData.category().getResId())
} else {
holder.view.circlePreview.setImageDrawable(null)
}
holder.itemView.tag = rssData
holder.itemView.setOnClickListener(dateViewClick)
holder.itemView.setOnTouchListener(object : View.OnTouchListener{
override fun onTouch(
v: View,
event: MotionEvent
): Boolean {
if (event.device != null && event.device.name != null && (event.device.name?.contains("JX-12",true) == true|| event.device.name?.equals("J06",true) == true)) {
Blog.LOGE("event.device.name >>> ${event.device.name}")
return true//mSimpleFingerGestures.onTouch(v,event)
} else {
return false
}
} }
})
holder.view.title.text = "".plus(if (rssData.vote) " * " else "")
.plus(rssData.title().plus("[R:${rssData.read}]"))
holder.view.desc.text = rssData.description()
var param = holder.view.circlePreview.layoutParams
holder.view.circlePreview.layoutParams =
ConstraintLayout.LayoutParams(rssData.category().defaultImgSize(), param.height)
holder.view.circlePreview.visibility = rssData.category().getDefaultVisibiliy()
Picasso.get().cancelRequest(holder.view.circlePreview)
if (rssData.thumbnailUrl()?.length ?: 0 > 6) {
Picasso.get().load(rssData.thumbnailUrl().replace("&amp;", "&").toUri())
.into(holder.view.circlePreview)
} else if (rssData.category().getResId() > 0) {
holder.view.circlePreview.setImageResource(rssData.category().getResId())
} else {
holder.view.circlePreview.setImageDrawable(null)
}
holder.itemView.tag = rssData
holder.itemView.setOnClickListener(dateViewClick)
holder.itemView.setOnTouchListener(object : View.OnTouchListener {
override fun onTouch(
v: View,
event: MotionEvent
): Boolean {
if (event.device != null && event.device.name != null && (event.device.name?.contains(
"JX-12",
true
) == true || event.device.name?.equals("J06", true) == true)
) {
Blog.LOGE("event.device.name >>> ${event.device.name}")
return true//mSimpleFingerGestures.onTouch(v,event)
} else {
return false
}
}
})
// v.setOnLongClickListener { // v.setOnLongClickListener {
// WorkersDb.getRealm().apply { // WorkersDb.getRealm().apply {
// copyFromRealm(rss) // copyFromRealm(rss)
// } // }
// } // }
holder.itemView.setOnLongClickListener(mLongClickListener) holder.itemView.setOnLongClickListener(mLongClickListener)
}
} }
var layoutManager : LinearLayoutManager? = null var layoutManager : LinearLayoutManager? = null
@ -242,24 +251,29 @@ internal class RssItemAdapter (
this.recyclerView = recyclerView this.recyclerView = recyclerView
} }
@SuppressLint("NotifyDataSetChanged")
fun updateData(newList: List<RssData>) { fun updateData(newList: List<RssData>) {
try { try {
DiffUtil.calculateDiff(RssItemDiffUtil(rssDataItemLis, newList)).apply { // DiffUtil.calculateDiff(RssItemDiffUtil(rssDataItemLis, newList)).apply {
//
// }.dispatchUpdatesTo(this).apply {
// val visibleItemCount = (layoutManager?.findLastVisibleItemPosition() ?: 0) - (layoutManager?.findFirstVisibleItemPosition() ?: 0)
// val first = layoutManager?.findLastVisibleItemPosition() ?: 0
// if (visibleItemCount > 0) {
// this@RssItemAdapter.notifyItemRangeChanged(first, visibleItemCount)
//// recyclerView?.scrollToPosition(0)
// }
// }
synchronized(rssDataItemLis) {
rssDataItemLis.clear()
rssDataItemLis.addAll(newList)
}
notifyDataSetChanged()
}.dispatchUpdatesTo(this).apply { // CoroutineScope(Dispatchers.IO).launch {
val visibleItemCount = (layoutManager?.findLastVisibleItemPosition() ?: 0) - (layoutManager?.findFirstVisibleItemPosition() ?: 0) // rssList.clear()
val first = layoutManager?.findLastVisibleItemPosition() ?: 0 // rssList.addAll(newList.map { it.originPage() })
if (visibleItemCount > 0) { // }
this@RssItemAdapter.notifyItemRangeChanged(first, visibleItemCount)
// recyclerView?.scrollToPosition(0)
}
}
rssDataItemLis.clear()
rssDataItemLis.addAll(newList)
CoroutineScope(Dispatchers.IO).launch {
rssList.clear()
rssList.addAll(newList.map { it.originPage() })
}
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
} }

View File

@ -7,10 +7,9 @@ import bums.lunatic.launcher.helpers.PrefHelper
enum class RssDataType { enum class RssDataType {
NO_DATA, NO_DATA,
PRIVATE, PRIVATE,
TORRENT,
YOUTUBE, YOUTUBE,
NEWSFEED, NEWSFEED,
// GURU,
// MOST,
TAGS, TAGS,
REDDIT, REDDIT,
REDDIT_NSFW, REDDIT_NSFW,

View File

@ -54,9 +54,11 @@ import bums.lunatic.launcher.tokiz.view.JxEvent
import bums.lunatic.launcher.tokiz.view.PagedTextLayout import bums.lunatic.launcher.tokiz.view.PagedTextLayout
import bums.lunatic.launcher.tokiz.view.PagedTextViewInterface import bums.lunatic.launcher.tokiz.view.PagedTextViewInterface
import bums.lunatic.launcher.databinding.BooktokiBinding import bums.lunatic.launcher.databinding.BooktokiBinding
import bums.lunatic.launcher.home.toast
import bums.lunatic.launcher.tokiz.data.model.FakeSession import bums.lunatic.launcher.tokiz.data.model.FakeSession
import bums.lunatic.launcher.tokiz.data.model.FakeSessions import bums.lunatic.launcher.tokiz.data.model.FakeSessions
import bums.lunatic.launcher.utils.Blog import bums.lunatic.launcher.utils.Blog
import bums.lunatic.launcher.workers.WorkersDb
import com.google.gson.Gson import com.google.gson.Gson
import io.realm.kotlin.Realm import io.realm.kotlin.Realm
import io.realm.kotlin.UpdatePolicy import io.realm.kotlin.UpdatePolicy
@ -130,7 +132,7 @@ abstract class BaseToki : Fragment(), PagedTextViewInterface {
abstract var lastNumber : Int abstract var lastNumber : Int
abstract val webcontentsName : String abstract val webcontentsName : String
abstract val afterDot : String abstract val afterDot : String
fun getLastedDoamin(): String { open fun getLastedDoamin(): String {
return String.format("https://%s%d.%s", webcontentsName , lastNumber, afterDot) return String.format("https://%s%d.%s", webcontentsName , lastNumber, afterDot)
} }
val OnTouchListener = object : OnTouchListener { val OnTouchListener = object : OnTouchListener {
@ -454,6 +456,12 @@ abstract class BaseToki : Fragment(), PagedTextViewInterface {
"SHOWVIEWER" -> { "SHOWVIEWER" -> {
binding.progress.visibility = GONE binding.progress.visibility = GONE
} }
"PRIVATES"->{
lPortMessage.privates?.let {
requireContext().toast("Received Msg privates form ${lPortMessage.currentPage} data => ${it?.size ?: 0}")
WorkersDb.insertBulkData(it)
}
}
else -> { else -> {
} }

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 Twitter : BaseToki(), PagedTextViewInterface {
override val contentsType = "twitter"
override var lastNumber : Int = 143
override val webcontentsName : String = "twitter"
override val afterDot = "com"
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,182 @@
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 Zota : BaseToki(), PagedTextViewInterface {
override val contentsType = "Torrent"
override var lastNumber : Int = 143
override val webcontentsName : String = "torrentzota"
override val afterDot = "com"
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

@ -6,6 +6,8 @@ import android.graphics.Color
import android.graphics.Paint import android.graphics.Paint
import android.graphics.RectF import android.graphics.RectF
import android.media.MediaCodec import android.media.MediaCodec
import android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar
import android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar
import android.media.MediaExtractor import android.media.MediaExtractor
import android.media.MediaFormat import android.media.MediaFormat
import android.renderscript.Allocation import android.renderscript.Allocation
@ -18,6 +20,8 @@ import android.util.DisplayMetrics
import android.view.SurfaceHolder import android.view.SurfaceHolder
import android.view.WindowManager import android.view.WindowManager
import bums.lunatic.launcher.R import bums.lunatic.launcher.R
import bums.lunatic.launcher.utils.Blog
import java.nio.ByteBuffer
class MyWallpaperService : WallpaperService() { class MyWallpaperService : WallpaperService() {
@ -51,6 +55,8 @@ class MyWallpaperService : WallpaperService() {
screenWidth = metrics.widthPixels screenWidth = metrics.widthPixels
screenHeight = metrics.heightPixels screenHeight = metrics.heightPixels
} }
} }
override fun onSurfaceCreated(holder: SurfaceHolder) { override fun onSurfaceCreated(holder: SurfaceHolder) {
@ -98,6 +104,7 @@ class RenderThread(
private var renderStartY = 0f private var renderStartY = 0f
private var maxOffset = 0f private var maxOffset = 0f
private var moveXAxis = false private var moveXAxis = false
private var keyFrameRate = 60
override fun run() { override fun run() {
try { try {
@ -110,9 +117,16 @@ class RenderThread(
for (i in 0 until trackCount) { for (i in 0 until trackCount) {
val format = getTrackFormat(i) val format = getTrackFormat(i)
val mime = format.getString(MediaFormat.KEY_MIME) ?: "" val mime = format.getString(MediaFormat.KEY_MIME) ?: ""
if (format.containsKey(MediaFormat.KEY_FRAME_RATE)) {
keyFrameRate = format.getInteger(MediaFormat.KEY_FRAME_RATE)
} else {
keyFrameRate = 60
}
if (mime.startsWith("video/")) { if (mime.startsWith("video/")) {
videoTrackIndex = i videoTrackIndex = i
selectTrack(i) selectTrack(i)
videoWidth = format.getInteger(MediaFormat.KEY_WIDTH) videoWidth = format.getInteger(MediaFormat.KEY_WIDTH)
videoHeight = format.getInteger(MediaFormat.KEY_HEIGHT) videoHeight = format.getInteger(MediaFormat.KEY_HEIGHT)
break break
@ -144,8 +158,8 @@ class RenderThread(
} }
renderWidth = videoWidth * scale renderWidth = videoWidth * scale
renderHeight = videoHeight * scale renderHeight = videoHeight * scale
renderStartX = (screenWidth - renderWidth) / 2f renderStartX = 0f;//(screenWidth - renderWidth) / 2f
renderStartY = (screenHeight - renderHeight) / 2f renderStartY = 0f;//(screenHeight - renderHeight) / 2f
moveXAxis = renderWidth > screenWidth moveXAxis = renderWidth > screenWidth
maxOffset = if (moveXAxis) renderWidth - screenWidth else if (renderHeight > screenHeight) renderHeight - screenHeight else 0f maxOffset = if (moveXAxis) renderWidth - screenWidth else if (renderHeight > screenHeight) renderHeight - screenHeight else 0f
@ -161,9 +175,12 @@ class RenderThread(
if (inputBuffer != null) { if (inputBuffer != null) {
val sampleSize = extractor?.readSampleData(inputBuffer, 0) ?: -1 val sampleSize = extractor?.readSampleData(inputBuffer, 0) ?: -1
if (sampleSize < 0) { if (sampleSize < 0) {
codec?.queueInputBuffer( // codec?.queueInputBuffer(
inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM) // inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM)
isEOS = true // isEOS = true
extractor?.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC)
isEOS = false // false로 재설정해서 계속 받게
continue
} else { } else {
val pts = extractor?.sampleTime ?: 0L val pts = extractor?.sampleTime ?: 0L
codec?.queueInputBuffer(inputBufferIndex, 0, sampleSize, pts, 0) codec?.queueInputBuffer(inputBufferIndex, 0, sampleSize, pts, 0)
@ -176,13 +193,35 @@ class RenderThread(
val outputBufferIndex = codec?.dequeueOutputBuffer(bufferInfo, 10000) ?: -1 val outputBufferIndex = codec?.dequeueOutputBuffer(bufferInfo, 10000) ?: -1
if (outputBufferIndex >= 0) { if (outputBufferIndex >= 0) {
val outputBuffer = codec?.getOutputBuffer(outputBufferIndex) val outputBuffer = codec?.getOutputBuffer(outputBufferIndex)
Blog.LOGE("codec.outputFormat >>> ${codec?.outputFormat}")
if (bufferInfo.size > 0 && outputBuffer != null) { if (bufferInfo.size > 0 && outputBuffer != null) {
val yuvData = ByteArray(bufferInfo.size) val format = codec?.outputFormat
outputBuffer.get(yuvData)
val bitmap = convertYUVToBitmap(yuvData, videoWidth, videoHeight) val colorFormat = format?.getInteger(MediaFormat.KEY_COLOR_FORMAT) ?: /*기본값*/0
val stride = format?.getInteger(MediaFormat.KEY_STRIDE) ?: videoWidth
val sliceHeight = format?.getInteger(MediaFormat.KEY_SLICE_HEIGHT) ?: videoHeight
// outputBuffer.get(yuvData)
val expectedSize = videoWidth * videoHeight * 3 / 2
val yuvData = ByteArray(expectedSize)
val rawYUV = ByteArray(bufferInfo.size)
outputBuffer.get(rawYUV)
outputBuffer.position(0)
// 포맷별 변환
val nv21Data = when (colorFormat) {
COLOR_FormatYUV420SemiPlanar -> nv12ToNv21(rawYUV, videoWidth, videoHeight)
COLOR_FormatYUV420Planar -> i420ToNv21(rawYUV, videoWidth, videoHeight)
else -> rawYUV // 이미 NV21 등
}
// NV21 데이터 → Bitmap
val bitmap = convertYUVToBitmap(nv21Data, videoWidth, videoHeight)
// outputBuffer.get(yuvData, 0, minOf(bufferInfo.size, expectedSize))
// getYUVDataWithStride(outputBuffer, videoWidth, videoHeight, stride, height)
// val bitmap = convertYUVToBitmap(yuvData, videoWidth, videoHeight)
// 6. 왕복 이동 애니메이션 offset 계산 // 6. 왕복 이동 애니메이션 offset 계산
// Blog.LOGE("maxOffset >>> $maxOffset , offset >>> $offset , direction >> $direction , keyFrameRate >>> $keyFrameRate")
if (maxOffset > 0f) { if (maxOffset > 0f) {
offset += direction * speed offset += direction * speed
if (offset < 0f) { offset = 0f; direction = 1 } if (offset < 0f) { offset = 0f; direction = 1 }
@ -215,7 +254,7 @@ class RenderThread(
} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// 필요하다면 포맷 변경 처리 // 필요하다면 포맷 변경 처리
} }
sleep(1000L/45L) // 약 30fps sleep(1000L / keyFrameRate) // 약 30fps
} }
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
@ -229,13 +268,17 @@ class RenderThread(
} }
private fun convertYUVToBitmap(yuvByteArray: ByteArray, width: Int, height: Int): Bitmap { private fun convertYUVToBitmap(yuvByteArray: ByteArray, width: Int, height: Int): Bitmap {
val yuvType = Type.Builder(rs, Element.U8(rs)).setX(yuvByteArray.size) // YUV420の場合, 바이트 수는 width * height * 3 / 2 여야 함
val minSize = width * height * 3 / 2
val realData = if (yuvByteArray.size >= minSize) yuvByteArray.copyOf(minSize) else yuvByteArray
val yuvType = Type.Builder(rs, Element.U8(rs)).setX(minSize)
val inAllocation = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT) val inAllocation = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT)
val rgbaType = Type.Builder(rs, Element.RGBA_8888(rs)).setX(width).setY(height) val rgbaType = Type.Builder(rs, Element.RGBA_8888(rs)).setX(width).setY(height)
val outAllocation = Allocation.createTyped(rs, rgbaType.create(), Allocation.USAGE_SCRIPT) val outAllocation = Allocation.createTyped(rs, rgbaType.create(), Allocation.USAGE_SCRIPT)
inAllocation.copyFrom(yuvByteArray) inAllocation.copyFrom(realData)
yuvToRgb?.setInput(inAllocation) yuvToRgb?.setInput(inAllocation)
yuvToRgb?.forEach(outAllocation) yuvToRgb?.forEach(outAllocation)
@ -247,4 +290,91 @@ class RenderThread(
return bitmap return bitmap
} }
private fun getYUVDataWithStride(
buffer: ByteBuffer,
width: Int,
height: Int,
stride: Int,
sliceHeight: Int
): ByteArray {
val yuvData = ByteArray(width * height * 3 / 2)
// Y plane 복사
for (y in 0 until height) {
buffer.position(y * stride)
buffer.get(yuvData, y * width, width)
}
// UV plane... (NV21 등 포맷 따라 별도 구현 필요)
// 이 영역은 포맷에 따라 다름, 기본은 Y만 참고!
return yuvData
}
// NV12 to NV21 변환
fun nv12ToNv21(nv12: ByteArray, width: Int, height: Int): ByteArray {
val frameSize = width * height
val nv21 = ByteArray(frameSize * 3 / 2)
// Y는 그대로 복사
System.arraycopy(nv12, 0, nv21, 0, frameSize)
// UV를 VU로 뒤집어서 복사
var i = 0
while (i < frameSize / 2 - 1) {
nv21[frameSize + i] = nv12[frameSize + i + 1]
nv21[frameSize + i + 1] = nv12[frameSize + i]
i += 2
}
return nv21
}
// I420(YUV420Planar) to NV21 변환
fun i420ToNv21(i420: ByteArray, width: Int, height: Int): ByteArray {
val frameSize = width * height
val qFrameSize = frameSize / 4
val nv21 = ByteArray(frameSize * 3 / 2)
// Y 복사
System.arraycopy(i420, 0, nv21, 0, frameSize)
// VU interleave
val u = frameSize
val v = frameSize + qFrameSize
for (i in 0 until qFrameSize) {
nv21[frameSize + i * 2] = i420[v + i] // V
nv21[frameSize + i * 2 + 1] = i420[u + i] // U
}
return nv21
}
fun i420ToNv21WithStride(src: ByteBuffer, width: Int, height: Int, stride: Int, sliceHeight: Int): ByteArray {
val frameSize = width * height
val qFrameSize = frameSize / 4
val nv21 = ByteArray(frameSize * 3 / 2)
// 1. Y plane 복사
for (y in 0 until height) {
src.position(y * stride)
src.get(nv21, y * width, width)
}
// 2. U/V plane 복사, 각각 stride/2씩 적용
val uOffsetBuf = sliceHeight * stride
val vOffsetBuf = uOffsetBuf + (sliceHeight / 2) * (stride / 2)
for (y in 0 until height/2) {
src.position(uOffsetBuf + y * (stride / 2))
src.get(nv21, frameSize + y * width, width/2) // 임시: U만
src.position(vOffsetBuf + y * (stride / 2))
for (x in 0 until width/2) {
// NV21 순서: V, U
val v = src.get()
val u = nv21[frameSize + y * width + x] // U
nv21[frameSize + y * width + x*2] = v
nv21[frameSize + y * width + x*2 + 1] = u
}
}
return nv21
}
} }

View File

@ -77,12 +77,12 @@ class DCGetter : BaseGetter {
Blog.LOGE("realWork() ${this::class.simpleName}") Blog.LOGE("realWork() ${this::class.simpleName}")
temp.clear() temp.clear()
// https://m.dcinside.com/board/singlebungle1472 // https://m.dcinside.com/board/singlebungle1472
//"https://m.dcinside.com/board/programming",
//"https://m.dcinside.com/board/reading",
var urls = arrayListOf( var urls = arrayListOf(
"https://m.dcinside.com", "https://m.dcinside.com",
"https://m.dcinside.com/board/singlebungle1472", "https://m.dcinside.com/board/singlebungle1472",
"https://m.dcinside.com/board/programming",
"https://m.dcinside.com/board/cartoon", "https://m.dcinside.com/board/cartoon",
"https://m.dcinside.com/board/reading",
"https://m.dcinside.com/board/hit", "https://m.dcinside.com/board/hit",
"https://m.dcinside.com/board/dcbest" "https://m.dcinside.com/board/dcbest"
) )

Binary file not shown.

After

Width:  |  Height:  |  Size: 565 B

View File

@ -59,6 +59,17 @@
android:id="@+id/comics" android:id="@+id/comics"
style="@style/tabItem" style="@style/tabItem"
android:layout_height="match_parent"/> android:layout_height="match_parent"/>
<androidx.appcompat.widget.AppCompatRadioButton
android:text="zota"
android:id="@+id/zota"
style="@style/tabItem"
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 <Button
android:text="hidden" android:text="hidden"
android:id="@+id/hidden" android:id="@+id/hidden"

View File

@ -1,36 +1,62 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/colorInputLayout" android:id="@+id/colorInputLayout"
android:layout_width="@dimen/oneNinetySix" android:layout_width="@dimen/oneNinetySix"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="inpout text" android:hint="inpout text"
android:orientation="horizontal"
app:boxBackgroundColor="?attr/colorSurface"
app:endIconMode="clear_text"
android:gravity="right"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:ignore="MissingConstraints">
<LinearLayout
android:orientation="horizontal" android:orientation="horizontal"
app:boxBackgroundColor="?attr/colorSurface" android:layout_width="match_parent"
app:endIconMode="clear_text" android:layout_height="match_parent">
android:gravity="right"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeOptions="actionDone"
android:inputType="text" />
<CheckBox <CheckBox
android:padding="0dp" android:padding="0dp"
android:text="PRIVATE MODE" android:text="add Vote"
android:textColor="@color/white" android:textColor="@color/black"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
android:id="@+id/parivate_mode" android:id="@+id/add_vote"
android:checked="true" android:checked="false"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"/> android:layout_height="wrap_content"/>
</com.google.android.material.textfield.TextInputLayout> <CheckBox
android:padding="0dp"
android:text="Add Read"
android:textColor="@color/black"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:id="@+id/add_read"
android:checked="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeOptions="actionDone"
android:inputType="text" />
<CheckBox
android:padding="0dp"
android:text="PRIVATE"
android:textColor="@color/black"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:id="@+id/parivate_mode"
android:checked="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</com.google.android.material.textfield.TextInputLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

Binary file not shown.