This commit is contained in:
lunaticbum 2025-08-20 17:08:17 +09:00
parent f7f71ca195
commit 2f63f8550a
14 changed files with 686 additions and 393 deletions

View File

@ -120,7 +120,9 @@ dependencies {
// implementation("org.opencv:opencv-android:4.11.0") // implementation("org.opencv:opencv-android:4.11.0")
// build.gradle에 추가 // build.gradle에 추가
// implementation ("com.github.aeonSolutions:FloatingActionButtonMenuDrag:1.1") // implementation ("com.github.aeonSolutions:FloatingActionButtonMenuDrag:1.1")
implementation("io.github.junkfood02.youtubedl-android:library:0.17.4")
implementation("io.github.junkfood02.youtubedl-android:ffmpeg:0.17.4")
implementation("io.github.junkfood02.youtubedl-android:aria2c:0.17.4")
implementation ("androidx.media:media:1.7.0") implementation ("androidx.media:media:1.7.0")

View File

@ -79,6 +79,7 @@
android:stateNotNeeded="true" android:stateNotNeeded="true"
android:enableOnBackInvokedCallback="true" android:enableOnBackInvokedCallback="true"
android:largeHeap="true" android:largeHeap="true"
android:extractNativeLibs="true"
android:networkSecurityConfig="@xml/network_security_config" android:networkSecurityConfig="@xml/network_security_config"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
@ -136,7 +137,7 @@
<!-- </activity>--> <!-- </activity>-->
<service <service
android:name=".helpers.BluetoothManager" android:name=".helpers.ForeGroundService"
android:enabled="true" android:enabled="true"
android:exported="false" /> android:exported="false" />
<service <service
@ -169,16 +170,16 @@
android:exported="true"> android:exported="true">
</activity> </activity>
<!-- <activity--> <activity
<!-- android:name=".settings.SettingsActivity"--> android:name=".settings.SettingsActivity"
<!-- android:label="@string/lunar_settings"--> android:label="@string/lunar_settings"
<!-- android:launchMode="singleTask"--> android:launchMode="singleTask"
<!-- android:excludeFromRecents="true"--> android:excludeFromRecents="true"
<!-- android:exported="true">--> android:exported="true">
<!-- <intent-filter>--> <intent-filter>
<!-- <action android:name="android.intent.action.APPLICATION_PREFERENCES" />--> <action android:name="android.intent.action.APPLICATION_PREFERENCES" />
<!-- </intent-filter>--> </intent-filter>
<!-- </activity>--> </activity>
<!-- <activity--> <!-- <activity-->
<!-- android:name=".behavior.Behavior"--> <!-- android:name=".behavior.Behavior"-->
<!-- android:label="@string/lunar_settings"--> <!-- android:label="@string/lunar_settings"-->
@ -269,15 +270,17 @@
android:name="android.support.FILE_PROVIDER_PATHS" android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" /> android:resource="@xml/file_paths" />
</provider> </provider>
<!-- <service android:name=".receiver.NLService"-->
<!-- android:label="@string/app_name"--> <service android:name=".receiver.NLService"
<!-- android:enabled="true"--> android:label="@string/app_name"
<!-- android:exported="true"--> android:enabled="true"
<!-- android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">--> android:exported="true"
<!-- <intent-filter>--> android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<!-- <action android:name="android.service.notification.NotificationListenerService" />--> <intent-filter>
<!-- </intent-filter>--> <action android:name="android.service.notification.NotificationListenerService" />
<!-- </service>--> </intent-filter>
</service>
<!-- <service android:name="bums.lunatic.launcher.workers.LocationUpdateService" />--> <!-- <service android:name="bums.lunatic.launcher.workers.LocationUpdateService" />-->
<!-- <receiver android:name=".LauncherActivity$EndCallReceiver"--> <!-- <receiver android:name=".LauncherActivity$EndCallReceiver"-->

View File

@ -253,9 +253,16 @@ document.addEventListener('DOMContentLoaded', function () {
if (port) { if (port) {
sendMessage({type: "MSG", msg: "connect prot"}); sendMessage({type: "MSG", msg: "connect prot"});
time1 = setTimeout(autoScrollAndSave(false), 3500) time1 = setTimeout(autoScrollAndSave(false), 3500)
} }
}) })
document.addEventListener('touchstart', function(e) {
console.log('터치 시작');
});
document.addEventListener('touchend', function(e) {
autoScrollAndSave()
});
function scrollToLazyImg(fastMode) { function scrollToLazyImg(fastMode) {
(function(autoScrollAndSave){ (function(autoScrollAndSave){
@ -346,12 +353,16 @@ function isNewerThanOneDay(dateStr) {
function autoScrollAndSave(senContents) { function autoScrollAndSave(senContents) {
// 도메인에 맞는 handler 실행 // 도메인에 맞는 handler 실행
const matchedRule = domainRules.find(rule => rule.test(location.href)); const matchedRule = domainRules.find(rule => rule.test(location.href));
try {
if (matchedRule) { if (matchedRule) {
matchedRule.handler(); matchedRule.handler();
} }
}catch (e) { }
try {
// 공통 광고 요소 제거는 항상 실행 // 공통 광고 요소 제거는 항상 실행
handleCommon(); handleCommon();
window.scrollTo({ top: 2, behavior: 'smooth' }); }catch (e) { }
if (mainContentsEl == null) { if (mainContentsEl == null) {
mainContentsEl = document.body.outerHTML mainContentsEl = document.body.outerHTML
} }
@ -545,7 +556,11 @@ function handleCommon() {
gotoNext() gotoNext()
} }
window.scrollTo({ top: 2, behavior: 'smooth' }); if (window.scrollY < 5) {
console.log("window.scrollY >>> " + window.scrollY)
window.scrollTo({ top: 5, behavior: 'smooth' });
}
} }
function handleToreentZota() { function handleToreentZota() {
if (location.href.search("torrentzota") > -1 && document.querySelectorAll('a')) { if (location.href.search("torrentzota") > -1 && document.querySelectorAll('a')) {
@ -759,13 +774,26 @@ function handleDoctorsnews() {
} }
function handleDcinside() { function handleDcinside() {
try {
document.querySelectorAll( 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"]' '[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()); ).forEach(e => e.remove());
}catch (e) {
}
try {
document.querySelectorAll('div[class^="imgwrap"]').forEach(function (e) { document.querySelectorAll('div[class^="imgwrap"]').forEach(function (e) {
try {e.style.backgroundColor = 'red';} catch (e) {} try {e.style.backgroundColor = 'red';} catch (e) {}
}) })
} catch (e) {
}
try {
document.querySelectorAll('div[class^="imgwrap"]')[0].click()
}catch (e) {
}
mainContentsEl = document.querySelector('div[class="container"]'); mainContentsEl = document.querySelector('div[class="container"]');
} }

View File

@ -22,13 +22,11 @@ package bums.lunatic.launcher
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.SearchManager import android.app.SearchManager
import android.appwidget.AppWidgetManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.Color import android.graphics.Color
import android.graphics.Rect
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
@ -49,89 +47,50 @@ import android.view.PointerIcon
import android.view.View import android.view.View
import android.view.WindowInsets import android.view.WindowInsets
import android.view.WindowManager import android.view.WindowManager
import android.widget.Button
import android.widget.HorizontalScrollView
import android.widget.LinearLayout
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
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import bums.lunatic.launcher.LauncherActivity.Companion.lActivity import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
import bums.lunatic.launcher.apps.AppDrawer import bums.lunatic.launcher.apps.AppDrawer
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.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.ForeGroundService
import bums.lunatic.launcher.helpers.HeadsetActionButtonReceiver import bums.lunatic.launcher.helpers.HeadsetActionButtonReceiver
import bums.lunatic.launcher.helpers.PrefHelper.putString
import bums.lunatic.launcher.helpers.PrefLong
import bums.lunatic.launcher.home.GeckoWeb
import bums.lunatic.launcher.home.RssHome import bums.lunatic.launcher.home.RssHome
import bums.lunatic.launcher.home.RssViewBuilder 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.receiver.NLService
import bums.lunatic.launcher.settings.SettingsActivity
import bums.lunatic.launcher.tokiz.Comics import bums.lunatic.launcher.tokiz.Comics
import bums.lunatic.launcher.tokiz.Magnet import bums.lunatic.launcher.tokiz.Magnet
import bums.lunatic.launcher.tokiz.Novels
import bums.lunatic.launcher.tokiz.Perplexity import bums.lunatic.launcher.tokiz.Perplexity
import bums.lunatic.launcher.tokiz.Twitter 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.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.getJ
import bums.lunatic.launcher.workers.AppInfoGetter
import bums.lunatic.launcher.workers.ArcaGetter
import bums.lunatic.launcher.workers.CalendarGetter
import bums.lunatic.launcher.workers.ClienGetter
import bums.lunatic.launcher.workers.ContactInfoGetter
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.RecentCallGetter
import bums.lunatic.launcher.workers.RecentSmsGetter
import bums.lunatic.launcher.workers.RecentSmsGetter.Companion.SMS_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.WorkersDb import bums.lunatic.launcher.workers.WorkersDb
import bums.lunatic.launcher.workers.YoutubeGetter
import bums.lunatic.launcher.workers.YoutubeGetter.Companion.YT_WORK_TAG
import com.google.android.material.color.DynamicColors import com.google.android.material.color.DynamicColors
import com.yausername.ffmpeg.FFmpeg
import com.yausername.youtubedl_android.YoutubeDL
import com.yausername.youtubedl_android.YoutubeDLException
import io.realm.kotlin.ext.query import io.realm.kotlin.ext.query
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kr.lunaticbum.utils.ui.DisplayUtil import kr.lunaticbum.utils.ui.DisplayUtil
import org.json.JSONObject import org.json.JSONObject
import org.mozilla.geckoview.ExperimentDelegate import org.mozilla.geckoview.ExperimentDelegate
import org.mozilla.geckoview.GeckoResult import org.mozilla.geckoview.GeckoResult
import org.mozilla.geckoview.GeckoRuntime import org.mozilla.geckoview.GeckoRuntime
import org.mozilla.geckoview.GeckoRuntimeSettings import org.mozilla.geckoview.GeckoRuntimeSettings
import java.util.Base64
import java.util.Calendar import java.util.Calendar
import java.util.Date import java.util.Date
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import kotlin.jvm.java
open class LauncherActivity : CommonActivity() { open class LauncherActivity : CommonActivity() {
@ -209,7 +168,8 @@ open class LauncherActivity : CommonActivity() {
Blog.LOGE("onConfigurationChanged newConfig?.screenWidthDp >> ${newConfig?.screenWidthDp}") Blog.LOGE("onConfigurationChanged newConfig?.screenWidthDp >> ${newConfig?.screenWidthDp}")
Blog.LOGE("onConfigurationChanged newConfig?.screenHeightDp >> ${newConfig?.screenHeightDp}") Blog.LOGE("onConfigurationChanged newConfig?.screenHeightDp >> ${newConfig?.screenHeightDp}")
isOpendFold = (newConfig.screenWidthDp * 1.1f) > newConfig.screenHeightDp isOpendFold = (newConfig.screenWidthDp * 1.1f) > newConfig.screenHeightDp
val nullCursor = PointerIcon.getSystemIcon(this, PointerIcon.TYPE_NULL)
binding.root.setPointerIcon(nullCursor)
} }
// override fun onKeyClick(keyCode: Int): Boolean { // override fun onKeyClick(keyCode: Int): Boolean {
// when (keyCode) { // when (keyCode) {
@ -533,15 +493,24 @@ open class LauncherActivity : CommonActivity() {
@SuppressLint("NewApi", "MissingPermission") @SuppressLint("NewApi", "MissingPermission")
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen() installSplashScreen()
super.onCreate(savedInstanceState)
try {
YoutubeDL.getInstance().init(this)
FFmpeg.getInstance().init(this);
} catch (e: YoutubeDLException) {
Blog.LOGE("failed to initialize youtubedl-android", e)
}
val intent = Intent(this, ForeGroundService::class.java)
this.startForegroundService(intent)
val nlService = Intent(this, NLService::class.java)
this.startService(nlService)
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
lActivity = this lActivity = this
DynamicColors.applyToActivityIfAvailable(this) DynamicColors.applyToActivityIfAvailable(this)
settingsPrefs = getSharedPreferences(PREFS_SETTINGS, 0) settingsPrefs = getSharedPreferences(PREFS_SETTINGS, 0)
// AppCompatDelegate.setDefaultNightMode(settingsPrefs.getInt(KEY_APPLICATION_THEME, MODE_NIGHT_FOLLOW_SYSTEM))
super.onCreate(savedInstanceState)
binding = LauncherActivityBinding.inflate(layoutInflater) binding = LauncherActivityBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
@ -553,8 +522,8 @@ open class LauncherActivity : CommonActivity() {
updateLocationService() updateLocationService()
val intent = Intent(this, BluetoothManager::class.java)
ContextCompat.startForegroundService(this, intent)
showContents(binding.feeds.id) showContents(binding.feeds.id)
binding.floatingActionMenu.setOnMenuButtonClickListener { v-> binding.floatingActionMenu.setOnMenuButtonClickListener { v->
Blog.LOGE("v >> ${v}") Blog.LOGE("v >> ${v}")
@ -622,6 +591,10 @@ open class LauncherActivity : CommonActivity() {
.replace(R.id.fragment_container, Magnet()) .replace(R.id.fragment_container, Magnet())
.commit() .commit()
} }
R.id.setting ->{
startActivity(Intent(this, SettingsActivity::class.java))
}
else -> {} else -> {}
} }
binding.floatingActionMenu.close(false) binding.floatingActionMenu.close(false)

View File

@ -2,9 +2,9 @@ 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.NotificationChannel
import android.app.NotificationManager import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service import android.app.Service
import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothDevice
@ -17,12 +17,13 @@ import android.os.Build
import android.os.IBinder import android.os.IBinder
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.PeriodicWorkRequestBuilder import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager import androidx.work.WorkManager
import bums.lunatic.launcher.LauncherActivity
import bums.lunatic.launcher.LauncherActivity.Companion.lActivity import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
import bums.lunatic.launcher.R 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.ArcaGetter
import bums.lunatic.launcher.workers.ClienGetter import bums.lunatic.launcher.workers.ClienGetter
@ -51,8 +52,11 @@ import okhttp3.ResponseBody
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
class BluetoothManager : Service() { class ForeGroundService : Service() {
companion object {
val ACTION_SENDMSG = "ACTION_SEND_TO_LOVE"
val EXTRA_MSGKEY = "SEND_MSG"
}
enum class BLUETOOTH_STATE(val statestr: String) { enum class BLUETOOTH_STATE(val statestr: String) {
ENABLED("enabledBlutooth"), ENABLED("enabledBlutooth"),
DISABLED("disableBlutooth"), DISABLED("disableBlutooth"),
@ -67,44 +71,71 @@ class BluetoothManager : Service() {
super.onCreate() super.onCreate()
Blog.LOGE("onCreate") Blog.LOGE("onCreate")
mWorkManager = WorkManager.getInstance(this) mWorkManager = WorkManager.getInstance(this)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForeground(NOTIF_ID, createNotification(this))
}
val filter = IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED) val filter = IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED)
registerReceiver(bluetoothreceiver, filter) registerReceiver(bluetoothreceiver, filter)
refreshFeeds() refreshFeeds()
// GeckoWeb(applicationContext).apply {
// loadUrl("https://arca.live/b/live")
// }
} }
override fun onBind(intent: Intent?): IBinder? { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Blog.LOGE("intent >>> ${intent}") Blog.LOGE("onStartCommand >>> ${intent}")
return null if (ACTION_SENDMSG.equals(intent?.action)) {
intent?.getStringExtra(EXTRA_MSGKEY)?.let {
sendToI(it)
}
}
startForeGround()
return START_STICKY
} }
private val CHANNEL_ID = "ble_service_channel"
private val CHANNEL_NAME = "BLE 서비스"
fun createNotification(context: Context): Notification { fun startForeGround() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel( val channel = NotificationChannel(
CHANNEL_ID, CHANNEL_ID,
CHANNEL_NAME, "BLE 서비스 채널",
NotificationManager.IMPORTANCE_HIGH // 중요도 낮게 (필요시 변경) NotificationManager.IMPORTANCE_HIGH
) )
val notificationManager = val manager = getSystemService(NotificationManager::class.java)
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager manager.createNotificationChannel(channel)
notificationManager.createNotificationChannel(channel)
} }
return NotificationCompat.Builder(context, CHANNEL_ID) val intent = Intent(this, LauncherActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP
}
val pendingIntent = PendingIntent.getActivity(
this,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
startForeground(NOTIF_ID, NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("BLE 서비스") .setContentTitle("BLE 서비스")
.setContentText("실행중입니다.") .setContentText("실행중입니다.")
.setPriority(NotificationCompat.PRIORITY_MAX) .setPriority(NotificationCompat.PRIORITY_MAX)
.setSmallIcon(R.drawable.ic_b) .setSmallIcon(R.drawable.ic_b)
.setContentIntent(pendingIntent)
.addAction(android.R.drawable.ic_btn_speak_now,"퇴근", makeSendMsgAction(0,"돼지 퇴근했다요~!"))
.addAction(android.R.drawable.ic_btn_speak_now,"버스 탐", makeSendMsgAction(1,"돼지 버스 탔다요~!"))
.addAction(android.R.drawable.ic_btn_speak_now,"버스 내림", makeSendMsgAction(2,"돼지 버스 내린다요~!"))
.setOngoing(true) // 사용자가 알림을 스와이프로 지울 수 없게 만듦 .setOngoing(true) // 사용자가 알림을 스와이프로 지울 수 없게 만듦
.build() .build())
} }
fun makeSendMsgAction(code : Int, msg : String) : PendingIntent {
val actionIntent = Intent(this, ForeGroundService::class.java).apply {
action = ACTION_SENDMSG
putExtra(EXTRA_MSGKEY, msg) // 전달할 데이터
}
return PendingIntent.getService(this, code, actionIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE )
}
override fun onBind(intent: Intent?): IBinder? {
Blog.LOGE("onBind intent >>> ${intent}")
return null
}
private val CHANNEL_ID = "ble_service_channel"
private val CHANNEL_NAME = "BLE 서비스"
//페어링된 디바이스 정보 가져오기 //페어링된 디바이스 정보 가져오기
fun getPairedDevices() { fun getPairedDevices() {
@ -192,6 +223,14 @@ class BluetoothManager : Service() {
PeriodicWorkRequestBuilder<LocationGetter>(PrefLong.locationTimePeriod.get(), TimeUnit.MINUTES) PeriodicWorkRequestBuilder<LocationGetter>(PrefLong.locationTimePeriod.get(), TimeUnit.MINUTES)
.addTag(LocationGetter.TAG) .addTag(LocationGetter.TAG)
.build()) .build())
mWorkManager?.cancelAllWorkByTag(ServiceWatchdogWorker.TAG)
mWorkManager?.enqueueUniquePeriodicWork(
ServiceWatchdogWorker.TAG,
ExistingPeriodicWorkPolicy.REPLACE,
PeriodicWorkRequestBuilder<ServiceWatchdogWorker>(15, TimeUnit.MINUTES)
.addTag(ServiceWatchdogWorker.TAG)
.build())
} }
@ -202,6 +241,21 @@ class BluetoothManager : Service() {
return mWorkManager return mWorkManager
} }
fun sendToI(msg: String) {
if (PrefString.telegramSendTarget.get().length > 5) {
CoroutineScope(Dispatchers.IO).launch {
OkHttpClient.Builder()
.connectionPool(ConnectionPool(5, 60, TimeUnit.SECONDS))
.build().newCall(Request.Builder().url("https://api.telegram.org/bot7934509464:AAE_xUbICxMdywLGnxo7BkeIqA1nVza4P9w/sendMessage?chat_id=${PrefString.telegramSendTarget.get()}&text=${msg}")
.addHeader("Content-Type", "application/json").get().build()).execute()?.let { response ->
if (response.isSuccessful()) {
val body: ResponseBody? = response.body()
if (body != null) { }
} else Blog.LOGE("sendToI telegram Error Occurred")
}
}
}
}
fun sendToI(boolean: Boolean) { fun sendToI(boolean: Boolean) {
if (PrefString.telegramSendTarget.get().length > 5) { if (PrefString.telegramSendTarget.get().length > 5) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
@ -251,6 +305,13 @@ class BluetoothManager : Service() {
return BLUETOOTH_STATE.NOT_SUPPORT.statestr return BLUETOOTH_STATE.NOT_SUPPORT.statestr
} }
override fun stopService(name: Intent?): Boolean {
Blog.LOGE("stopService ${name}")
val intent = Intent(this, ForeGroundService::class.java)
ContextCompat.startForegroundService(this, intent)
return super.stopService(name)
}
//add Receive action //add Receive action
private fun addFilterAction(): IntentFilter { private fun addFilterAction(): IntentFilter {
val stateFilter = IntentFilter() val stateFilter = IntentFilter()

View File

@ -0,0 +1,36 @@
package bums.lunatic.launcher.helpers
import android.app.ActivityManager
import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.work.Worker
import androidx.work.WorkerParameters
class ServiceWatchdogWorker(
private val context: Context,
workerParams: WorkerParameters
) : Worker(context, workerParams) {
companion object{
val TAG = "ServiceWatchdogWorker"
}
override fun doWork(): Result {
val isServiceRunning = isServiceRunning(ForeGroundService::class.java)
if (!isServiceRunning) {
val intent = Intent(context, ForeGroundService::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent)
} else {
context.startService(intent)
}
}
return Result.success()
}
fun isServiceRunning(serviceClass: Class<*>): Boolean {
val manager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
return manager.getRunningServices(Int.MAX_VALUE)
.any { it.service.className == serviceClass.name }
}
}

View File

@ -40,6 +40,7 @@ import androidx.core.net.toUri
import androidx.core.view.isVisible import androidx.core.view.isVisible
import bums.lunatic.launcher.LauncherActivity.Companion.getRuntime import bums.lunatic.launcher.LauncherActivity.Companion.getRuntime
import bums.lunatic.launcher.R import bums.lunatic.launcher.R
import bums.lunatic.launcher.model.others.Button
import bums.lunatic.launcher.tokiz.data.model.PortMessage import bums.lunatic.launcher.tokiz.data.model.PortMessage
import bums.lunatic.launcher.tokiz.view.BWebview import bums.lunatic.launcher.tokiz.view.BWebview
import bums.lunatic.launcher.utils.Blog import bums.lunatic.launcher.utils.Blog
@ -47,6 +48,9 @@ import bums.lunatic.launcher.utils.CommonUtils
import bums.lunatic.launcher.workers.WorkersDb import bums.lunatic.launcher.workers.WorkersDb
import com.google.gson.Gson import com.google.gson.Gson
import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter
import com.yausername.youtubedl_android.YoutubeDL
import com.yausername.youtubedl_android.YoutubeDLRequest
import com.yausername.youtubedl_android.YoutubeDLResponse
import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -54,6 +58,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kr.lunaticbum.utils.service.ServiceUtil.getSystemService import kr.lunaticbum.utils.service.ServiceUtil.getSystemService
import kr.lunaticbum.utils.service.ServiceUtil.layoutInflater
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import org.json.JSONException import org.json.JSONException
@ -61,6 +66,7 @@ import org.json.JSONObject
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.mozilla.gecko.util.ThreadUtils import org.mozilla.gecko.util.ThreadUtils
import org.mozilla.gecko.util.ThreadUtils.runOnUiThread
import org.mozilla.geckoview.ExperimentDelegate import org.mozilla.geckoview.ExperimentDelegate
import org.mozilla.geckoview.GeckoResult import org.mozilla.geckoview.GeckoResult
import org.mozilla.geckoview.GeckoSession import org.mozilla.geckoview.GeckoSession
@ -76,6 +82,7 @@ import java.io.FileOutputStream
import java.io.InputStream import java.io.InputStream
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
import java.util.UUID
class GeckoWeb : BWebview { class GeckoWeb : BWebview {
constructor(context: Context?) : super(context) { constructor(context: Context?) : super(context) {
@ -84,7 +91,7 @@ class GeckoWeb : BWebview {
var decoViews = arrayListOf<View>() var decoViews = arrayListOf<View>()
override fun setVisibility(visibility: Int) { override fun setVisibility(visibility: Int) {
super.setVisibility(visibility) super.setVisibility(visibility)
decoViews.filter { it != null && it.id > -1 }.forEach { it.visibility = visibility } decoViews.filter { it != null && it.id > -1 && it.id != R.id.dl_video }.forEach { it.visibility = visibility }
} }
interface OnSave { interface OnSave {
fun saved() fun saved()
@ -448,6 +455,153 @@ class GeckoWeb : BWebview {
} }
} }
} }
fun checkIfDownloadable(url: String) {
CoroutineScope(Dispatchers.Main).launch {
runOnUiThread {
decoViews.filter { it.id == R.id.dl_video }.firstOrNull()?.let {
it.setOnClickListener {}
it.visibility = View.GONE
}}}
Blog.LOGE("checkIfDownloadable ${url}")
CoroutineScope(Dispatchers.IO).launch {
try {
val videoInfo = YoutubeDL.getInstance().getInfo(url)
// videoInfo 가 null 아니고, 필요한 키(예: title, url 등)가 있으면 다운로드 가능
Blog.LOGE("checkIfDownloadable ${url}\n videoInfo : ${videoInfo}")
var canVideoDown = videoInfo != null && !videoInfo.title.isNullOrEmpty()
CoroutineScope(Dispatchers.Main).launch {
runOnUiThread {
decoViews.filter { it.id == R.id.dl_video }.firstOrNull()?.let {
it.setOnClickListener {
videoDlownLoad(url)
}
it.visibility = if (canVideoDown){View.VISIBLE} else{View.GONE}
}
}
}
} catch (e: Exception) {
Blog.LOGE("checkIfDownloadable ${url} ${e}")
CoroutineScope(Dispatchers.Main).launch {
runOnUiThread {
decoViews.filter { it.id == R.id.dl_video }.firstOrNull()?.let {
it.setOnClickListener {}
it.visibility = View.GONE
}}}
}
}
}
fun replaceDcUrl(origin: String): String {
var result = origin
for (i in 0..19) {
result = result.replace(String.format("dcimg%d.", i), "dcimg2.")
}
return result
}
fun copyToClipboard(text: String?) {
if (text == null) return
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("Media URL", text)
clipboard.setPrimaryClip(clip)
Toast.makeText(context, "주소가 복사되었습니다.", Toast.LENGTH_SHORT).show()
}
suspend fun getFormatList(url: String): List<String> = withContext(Dispatchers.IO) {
val command = YoutubeDLRequest(lastedUrl!!)
command.addOption("--list-formats", url)
val output = YoutubeDL.getInstance().execute(command)
// output.stdout에 포맷 리스트가 문자열로 들어있음
// 줄 단위로 분리 후 리턴
Blog.LOGE("output.out >>> ${output.out}")
return@withContext output.out.split("\n").filter { it.isNotBlank() }
}
fun showFormatSelectionDialog(formats: List<String>, onFormatSelected: (String) -> Unit) {
val builder = AlertDialog.Builder(context)
builder.setTitle("포맷 선택")
builder.setItems(formats.toTypedArray()) { _, which ->
onFormatSelected(formats[which])
}
builder.setNegativeButton("취소", null)
builder.show()
}
lateinit var progressDialog: AlertDialog
fun showProgressDialog() {
val dialogView = layoutInflater.inflate(R.layout.progress_dialog, null)
val progressBar = dialogView.findViewById<ProgressBar>(R.id.progressBar)
val textProgress = dialogView.findViewById<TextView>(R.id.textProgress)
val btn = dialogView.findViewById<android.widget.Button>(R.id.dl_cancel)
progressDialog = AlertDialog.Builder(context)
.setTitle("다운로드 중...")
.setView(dialogView)
.setCancelable(false)
.create()
progressDialog.show()
// UI 업데이트 함수 예 (나중에 실행)
fun updateProgress(progress: Int, est : Long, str : String) {
runOnUiThread {
progressBar.progress = progress
textProgress.text = "$progress%"
}
}
}
fun dismissProgressDialog() {
progressDialog.dismiss()
}
suspend fun downloadVideo(processId : String,url: String, updateProgress: (Float, Long, String) -> Unit) = withContext(Dispatchers.IO) {
val youtubeDLDir = File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
"youtubedl-android"
)
val command = YoutubeDLRequest(url)
command.addOption("-o", youtubeDLDir.getAbsolutePath() + "/%(title)s.%(ext)s");
var process = YoutubeDL.getInstance().execute(command,processId) { progress, est , str ->
updateProgress(progress, est, str)
}
return@withContext process
}
fun videoDlownLoad(videoUrl : String) {
CoroutineScope(Dispatchers.Main).launch {
try {
showProgressDialog()
var res: YoutubeDLResponse? = null
val processId = UUID.randomUUID().toString()
res = downloadVideo(processId, videoUrl) { progress , time , str->
runOnUiThread {
val pb =
progressDialog.findViewById<ProgressBar>(R.id.progressBar)
val tv =
progressDialog.findViewById<TextView>(R.id.textProgress)
pb?.progress = progress.toInt()
val btn = progressDialog.findViewById<android.widget.Button>(R.id.dl_cancel)
tv?.text = "$progress%\n$str"
btn?.setOnClickListener {
progressDialog?.dismiss()
YoutubeDL.getInstance().destroyProcessById(processId);
}
}
}
dismissProgressDialog()
Toast.makeText(context, "다운로드 완료", Toast.LENGTH_SHORT)
.show()
} catch (e: Exception) {
e.printStackTrace()
progressDialog?.dismiss()
Toast.makeText(context, "오류: ${e.message}", Toast.LENGTH_LONG)
.show()
}
}
}
var dialog : Dialog? = null var dialog : Dialog? = null
fun getFilterF() = String(java.util.Base64.getMimeDecoder().decode("aHR0cHM6Ly9pamF2dG9ycmVudC5jb20=".toByteArray())) fun getFilterF() = String(java.util.Base64.getMimeDecoder().decode("aHR0cHM6Ly9pamF2dG9ycmVudC5jb20=".toByteArray()))
var currentTitle = "" var currentTitle = ""
@ -474,21 +628,7 @@ class GeckoWeb : BWebview {
super.onFirstContentfulPaint(session) super.onFirstContentfulPaint(session)
} }
fun replaceDcUrl(origin: String): String {
var result = origin
for (i in 0..19) {
result = result.replace(String.format("dcimg%d.", i), "dcimg2.")
}
return result
}
fun copyToClipboard(text: String?) {
if (text == null) return
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("Media URL", text)
clipboard.setPrimaryClip(clip)
Toast.makeText(context, "주소가 복사되었습니다.", Toast.LENGTH_SHORT).show()
}
override fun onExternalResponse(session: GeckoSession, response: WebResponse) { override fun onExternalResponse(session: GeckoSession, response: WebResponse) {
Blog.LOGE("response >>> ${response.uri} ") Blog.LOGE("response >>> ${response.uri} ")
@ -504,6 +644,7 @@ class GeckoWeb : BWebview {
val client = OkHttpClient() val client = OkHttpClient()
val request = Request.Builder() val request = Request.Builder()
.url(url) .url(url)
.addHeader("Referer", lastedUrl)
.addHeader("User-Agent", "Mozilla/5.0") .addHeader("User-Agent", "Mozilla/5.0")
// 필요시 Referer, 쿠키 등 헤더 추가 // 필요시 Referer, 쿠키 등 헤더 추가
.build() .build()
@ -531,6 +672,7 @@ class GeckoWeb : BWebview {
super.onExternalResponse(session, response) super.onExternalResponse(session, response)
} }
override fun onContextMenu( override fun onContextMenu(
session: GeckoSession, session: GeckoSession,
screenX: Int, screenX: Int,
@ -539,18 +681,11 @@ class GeckoWeb : BWebview {
) { ) {
if (element.baseUri?.contains("youtube") == true) { if (element.baseUri?.contains("youtube") == true) {
copyToClipboard(lastedUrl) lastedUrl?.let { videoUrl ->
loadUrl("https://ko.savefrom.net/227lt/#url=${lastedUrl}") lastedUrl?.let {
// copyToClipboard(lastedUrl) videoDlownLoad(it)
// Dialog(context)?.let { dialog -> }
// val popupWebView = GeckoWeb(context).apply { }
// loadUrl(lastedUrl!!.replace("https://","https://ss"))
// this.dialog = dialog
// }
// dialog.setCanceledOnTouchOutside(true)
// dialog.setContentView(popupWebView)
// dialog.show()
// }
} else { } else {
Blog.LOGE("onContextMenu:: x = ${x}, y = ${y} , element = ${Gson().toJson(element)}") Blog.LOGE("onContextMenu:: x = ${x}, y = ${y} , element = ${Gson().toJson(element)}")
if (element.type == GeckoSession.ContentDelegate.ContextElement.TYPE_IMAGE) { if (element.type == GeckoSession.ContentDelegate.ContextElement.TYPE_IMAGE) {
@ -723,6 +858,7 @@ class GeckoWeb : BWebview {
} else { } else {
lastedUrl = url lastedUrl = url
} }
checkIfDownloadable(url)
} }
@ -735,6 +871,8 @@ class GeckoWeb : BWebview {
it.tag = currentTitle it.tag = currentTitle
it.text = url it.text = url
} }
}else if (it.id == R.id.reload) {
it.setOnClickListener { session.reload() }
} }
} }
} }

View File

@ -625,6 +625,8 @@ internal class RssHome : Fragment() {
(activity as? LauncherActivity)?.let { activity -> (activity as? LauncherActivity)?.let { activity ->
binding.geckoWeb.decoViews.add(activity.findViewById<TextView>(R.id.current_address)) binding.geckoWeb.decoViews.add(activity.findViewById<TextView>(R.id.current_address))
binding.geckoWeb.decoViews.add(activity.findViewById<ImageButton>(R.id.back)) binding.geckoWeb.decoViews.add(activity.findViewById<ImageButton>(R.id.back))
binding.geckoWeb.decoViews.add(activity.findViewById<ImageButton>(R.id.reload))
binding.geckoWeb.decoViews.add(activity.findViewById<ImageButton>(R.id.dl_video))
} }

View File

@ -1,5 +1,6 @@
package bums.lunatic.launcher.receiver package bums.lunatic.launcher.receiver
import android.app.Notification
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
@ -13,9 +14,11 @@ import android.service.notification.NotificationListenerService
import android.service.notification.StatusBarNotification import android.service.notification.StatusBarNotification
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import bums.lunatic.launcher.helpers.ForeGroundService
import bums.lunatic.launcher.model.CurrentPlayItem import bums.lunatic.launcher.model.CurrentPlayItem
import bums.lunatic.launcher.model.NotificationItem import bums.lunatic.launcher.model.NotificationItem
import bums.lunatic.launcher.utils.BitmapConverter import bums.lunatic.launcher.utils.BitmapConverter
import bums.lunatic.launcher.utils.Blog
import bums.lunatic.launcher.workers.WorkersDb import bums.lunatic.launcher.workers.WorkersDb
import io.realm.kotlin.UpdatePolicy import io.realm.kotlin.UpdatePolicy
import io.realm.kotlin.ext.query import io.realm.kotlin.ext.query
@ -29,7 +32,6 @@ class NLService : NotificationListenerService() {
super.onCreate() super.onCreate()
nlservicereciver = NLServiceReceiver() nlservicereciver = NLServiceReceiver()
val filter = IntentFilter() val filter = IntentFilter()
// filter.addAction("com.kpbird.nlsexample.NOTIFICATION_LISTENER_SERVICE_EXAMPLE")
registerReceiver(nlservicereciver, filter) registerReceiver(nlservicereciver, filter)
} }
@ -38,130 +40,54 @@ class NLService : NotificationListenerService() {
unregisterReceiver(nlservicereciver) unregisterReceiver(nlservicereciver)
} }
val skips = arrayListOf("com.wssyncmldm")
@RequiresApi(Build.VERSION_CODES.S) @RequiresApi(Build.VERSION_CODES.S)
override fun onNotificationPosted(sbn: StatusBarNotification) { override fun onNotificationPosted(sbn: StatusBarNotification) {
// BLog.LOGE("NLService********** onNotificationPosted") Blog.LOGE("onNotificationPosted ${sbn}")
// BLog.LOGE("NLServiceID :" + sbn.id + "\t${sbn.notification.tickerText}\t" + sbn.packageName) val notification = sbn.notification
// sbn.notification.extras.keySet().forEach { val extras = notification.extras
// BLog.LOGE("NLService********** keySet >> ${it} ${sbn.notification.extras.get(it)}") when (sbn.packageName){
// } "com.kakao.talk" -> {
try {
if (sbn.id != 0 && (sbn.packageName.contains(".") || sbn.packageName.contains("android")) && sbn.packageName.length > 0) {
NotificationItem().apply {
notiId = sbn.id
pkgName = sbn.packageName
title = sbn.notification?.extras?.getString("android.title") ?: ""
subtext = sbn.notification?.extras?.getString("android.subText") ?: ""
selfDisplayName = sbn.notification?.extras?.getString("android.selfDisplayName") ?: ""
tikerMsg = sbn.notification?.tickerText?.toString() ?: ""
postTime = sbn.postTime
var uniq = title ?: subtext ?: selfDisplayName ?: tikerMsg ?: ""
uniq_id = "${sbn.id}_${sbn.packageName}_${if (uniq.length > 3) uniq.substring(0,3) else uniq}"
// BLog.LOGE("NLService********** enqueue TelegramBotGetter ${true == "bumssavor".equals(title)}")
// BLog.LOGE("NLService********** enqueue TelegramBotGetter ${(true == "org.telegram.messenger".equals(pkgName))}")
// BLog.LOGE("NLService********** enqueue TelegramBotGetter ${sbn.notification?.extras?.getString("android.text")?.startsWith("/") == true}")
}.apply {
if (skips.contains(pkgName)) {
} else {
// WorkersDb.insertNoti(this)
// BLog.LOGE("NLService********** onNotificationPosted ${Gson().toJson(this)}")
}
}
}
} catch (e : Exception) {
e.printStackTrace()
}
try {
if (sbn.packageName.contains("youtube")) {
val m = getSystemService<MediaSessionManager>()!!
val component = ComponentName(this, NLService::class.java)
val sessions = m.getActiveSessions(component)
sessions.forEach { session ->
WorkersDb.getRealm().writeBlocking {
// Blog.LOGE("session.playbackState >>> ${session.playbackState}")
if (session.playbackState != null) {
if (session.playbackState?.isActive == true && session.playbackState?.state?.equals(
STATE_PLAYING
) == true
) {
session.playbackState?.state
val result = query<CurrentPlayItem>().find()
var current: CurrentPlayItem? = null
if (result.size > 0) {
current = result.first()
} else {
current = CurrentPlayItem()
copyToRealm(current, UpdatePolicy.ALL)
}
if (session?.metadata?.containsKey(MediaMetadata.METADATA_KEY_ALBUM_ART) == true) {
current.albumArt = BitmapConverter.BitmapToString(
session.metadata?.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART)
)
} else {
current.albumArt = ""
}
current.title =
session?.metadata?.getString(MediaMetadata.METADATA_KEY_TITLE)
current.artists =
session?.metadata?.getString(MediaMetadata.METADATA_KEY_ARTIST)
} else {
delete(query<CurrentPlayItem>().find())
}
} }
} }
val title = extras.getString(Notification.EXTRA_TITLE) ?: ""
val text = extras.getCharSequence(Notification.EXTRA_TEXT)?.toString() ?: ""
val bigText = extras.getCharSequence(Notification.EXTRA_BIG_TEXT)?.toString() ?: ""
val extraInfo = extras.getCharSequence(Notification.EXTRA_INFO_TEXT)?.toString() ?: ""
val subText = extras.getCharSequence(Notification.EXTRA_SUB_TEXT)?.toString() ?: ""
val conversationTitle = extras.getCharSequence(Notification.EXTRA_CONVERSATION_TITLE)?.toString() ?: ""
val summaryText = extras.getCharSequence(Notification.EXTRA_SUMMARY_TEXT)?.toString() ?: ""
val verificationText = extras.getCharSequence(Notification.EXTRA_VERIFICATION_TEXT)?.toString() ?: ""
Blog.LOGE("title >> ${title} text >> ${text} bigText >> ${bigText} extraInfo >> ${extraInfo} subText >> ${subText} conversationTitle >> ${conversationTitle} summaryText >> ${summaryText} verificationText >> ${verificationText}")
}
}}catch (e : Exception) {
e.printStackTrace()
}
// val i = Intent("com.kpbird.nlsexample.NOTIFICATION_LISTENER_EXAMPLE")
// i.putExtra("notification_event", "onNotificationPosted :" + sbn.packageName + "\n")
// sendBroadcast(i)
} }
override fun onNotificationRemoved(sbn: StatusBarNotification) { override fun onNotificationRemoved(sbn: StatusBarNotification) {
//// BLog.LOGE("NLService********** onNOtificationRemoved") Blog.LOGE("onNotificationPosted ${sbn}")
//// BLog.LOGE("NLService ID :" + sbn.id + "\t" + sbn.notification.tickerText + "\t" + sbn.packageName) if (sbn.packageName == "bums.lunatic.launcher" && sbn.id == 830721) {
// var uniq_id = "${sbn.id}_${sbn.packageName}" // 포그라운드 알림이 사라짐 감지
// try { Blog.LOGE("NotificationListener", "포그라운드 알림 제거 감지")
// WorkersDb.getRealm()?.apply {
// this.writeBlocking { // 서비스 재시작 시도
//// delete(query<NotificationItem>().query("pkgName == $0", sbn.packageName).find()) val intent = Intent(this, ForeGroundService::class.java)
// } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// } startForegroundService(intent)
// }catch (e : Exception){e.printStackTrace()} } else {
//// val i = Intent("com.kpbird.nlsexample.NOTIFICATION_LISTENER_EXAMPLE") startService(intent)
//// i.putExtra("notification_event", "onNotificationRemoved :" + sbn.packageName + "\n") }
//// sendBroadcast(i) }
} }
internal inner class NLServiceReceiver : BroadcastReceiver() { internal inner class NLServiceReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent) { override fun onReceive(context: Context?, intent: Intent) {
// BLog.LOGE("NLService intent >>> ${intent.action}")
if (intent.getStringExtra("command") == "clearall") { if (intent.getStringExtra("command") == "clearall") {
this@NLService.cancelAllNotifications() this@NLService.cancelAllNotifications()
} else if (intent.getStringExtra("command") == "list") { } else if (intent.getStringExtra("command") == "list") {
// val i1 = Intent("com.kpbird.nlsexample.NOTIFICATION_LISTENER_EXAMPLE")
// i1.putExtra("notification_event", "=====================")
// sendBroadcast(i1)
var i = 1
for (sbn in this@NLService.activeNotifications) {
// BLog.LOGE("NLService sbn >>> ${sbn.packageName} , ${Gson().toJson(sbn.notification.extras.keySet())}")
// val i2 = Intent("com.kpbird.nlsexample.NOTIFICATION_LISTENER_EXAMPLE")
// i2.putExtra("notification_event", i.toString() + " " + sbn.packageName + "\n")
// sendBroadcast(i2)
// i++
}
// val i3 = Intent("com.kpbird.nlsexample.NOTIFICATION_LISTENER_EXAMPLE")
// i3.putExtra("notification_event", "===== Notification List ====")
// sendBroadcast(i3)
}
}
}
} }
}
}
}

View File

@ -0,0 +1,57 @@
package bums.lunatic.launcher.view
import android.content.Context
import android.util.AttributeSet
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.ScaleGestureDetector
import androidx.appcompat.widget.AppCompatImageView
class ZoomImageView(context: Context, attrs: AttributeSet) : AppCompatImageView(context, attrs),
ScaleGestureDetector.OnScaleGestureListener, GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener {
private var scaleFactor = 1.0f
private val scaleGestureDetector = ScaleGestureDetector(context, this)
private val gestureDetector = GestureDetector(context, this)
override fun onTouchEvent(event: MotionEvent): Boolean {
scaleGestureDetector.onTouchEvent(event)
gestureDetector.onTouchEvent(event)
return true
}
// ScaleGestureDetector callbacks
override fun onScale(detector: ScaleGestureDetector): Boolean {
scaleFactor *= detector.scaleFactor
scaleFactor = scaleFactor.coerceIn(1.0f, 4.0f)
scaleX = scaleFactor
scaleY = scaleFactor
return true
}
override fun onScaleBegin(detector: ScaleGestureDetector) = true
override fun onScaleEnd(detector: ScaleGestureDetector) {}
// GestureDetector callbacks (클릭, 터치 등)
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
performClick() // 클릭 이벤트 발생
return true
}
override fun performClick(): Boolean {
super.performClick()
// 추가로 클릭 시 동작할 코드 넣기
if (hasOnClickListeners()) {
callOnClick()
}
return true
}
override fun onDown(e: MotionEvent) = true
override fun onShowPress(e: MotionEvent) {}
override fun onSingleTapUp(e: MotionEvent) = true
override fun onScroll(e1: MotionEvent?, e2: MotionEvent, distanceX: Float, distanceY: Float) = false
override fun onLongPress(e: MotionEvent) {}
override fun onFling(e1: MotionEvent?, e2: MotionEvent, velocityX: Float, velocityY: Float) = false
override fun onDoubleTap(e: MotionEvent) = true
override fun onDoubleTapEvent(e: MotionEvent) = true
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 530 B

View File

@ -36,12 +36,27 @@
android:layout_width="40dp" android:layout_width="40dp"
tools:ignore="ContentDescription" tools:ignore="ContentDescription"
android:layout_height="40dp" /> android:layout_height="40dp" />
<ImageButton
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/fragment_container"
app:layout_constraintLeft_toRightOf="@id/back"
android:id="@+id/reload"
android:scaleType="fitCenter"
android:adjustViewBounds="true"
android:visibility="visible"
android:background="@null"
android:src="@drawable/ic_refresh"
android:tint="@color/white"
android:foregroundTint="@color/white"
android:layout_width="40dp"
tools:ignore="ContentDescription"
android:layout_height="40dp" />
<TextView <TextView
android:text="asdasdsadasd" android:text="asdasdsadasd"
android:id="@+id/current_address" android:id="@+id/current_address"
app:layout_constraintTop_toTopOf="@id/back" app:layout_constraintTop_toTopOf="@id/back"
app:layout_constraintRight_toLeftOf="@id/share" app:layout_constraintRight_toLeftOf="@id/dl_video"
app:layout_constraintLeft_toRightOf="@id/back" app:layout_constraintLeft_toRightOf="@id/reload"
android:textColor="@color/white" android:textColor="@color/white"
android:gravity="center" android:gravity="center"
android:textSize="@dimen/_12sp" android:textSize="@dimen/_12sp"
@ -49,6 +64,21 @@
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="40dp"/> android:layout_height="40dp"/>
<ImageButton
app:layout_constraintTop_toTopOf="@id/back"
app:layout_constraintRight_toLeftOf="@id/share"
app:layout_constraintBottom_toBottomOf="parent"
android:id="@+id/dl_video"
android:scaleType="fitCenter"
android:adjustViewBounds="true"
android:visibility="gone"
android:background="@null"
android:tint="@color/white"
android:foregroundTint="@color/white"
android:src="@drawable/dl_vid"
android:layout_width="40dp"
tools:ignore="ContentDescription"
android:layout_height="40dp" />
<ImageButton <ImageButton
app:layout_constraintTop_toTopOf="@id/back" app:layout_constraintTop_toTopOf="@id/back"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
@ -140,5 +170,13 @@
android:onClick="floatClick" android:onClick="floatClick"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="20dp"/> android:layout_height="20dp"/>
<bums.lunatic.launcher.view.FloatingActionButton
app:fab_label="setting"
android:id="@+id/setting"
app:fab_showShadow="true"
app:fab_size="mini"
android:onClick="floatClick"
android:layout_width="wrap_content"
android:layout_height="20dp"/>
</bums.lunatic.launcher.view.FloatingActionMenu> </bums.lunatic.launcher.view.FloatingActionMenu>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -49,7 +49,7 @@
android:textColor="@color/white" android:textColor="@color/white"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"/> android:layout_height="wrap_content"/>
<ImageView <bums.lunatic.launcher.view.ZoomImageView
app:layout_constraintTop_toBottomOf="@id/date" app:layout_constraintTop_toBottomOf="@id/date"
android:alpha="0.05" android:alpha="0.05"
android:adjustViewBounds="true" android:adjustViewBounds="true"
@ -78,7 +78,7 @@
android:textColor="@color/white" android:textColor="@color/white"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"/> android:layout_height="wrap_content"/>
<ImageView <bums.lunatic.launcher.view.ZoomImageView
app:layout_constraintTop_toBottomOf="@id/desc" app:layout_constraintTop_toBottomOf="@id/desc"
android:id="@+id/screen" android:id="@+id/screen"
android:alpha="0.05" android:alpha="0.05"

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:padding="24dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ProgressBar
android:id="@+id/progressBar"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="250dp"
android:layout_height="wrap_content"
android:max="100"
android:progress="0" />
<TextView
android:id="@+id/textProgress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="0%" />
<Button
android:text="다운로드 취소"
android:gravity="center"
android:id="@+id/dl_cancel"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>