From 4acc01b742e2bf55aa9dcb9e395a757813db5d19 Mon Sep 17 00:00:00 2001 From: lunaticbum Date: Fri, 22 Aug 2025 13:06:45 +0900 Subject: [PATCH] .... --- .../extensions/my_extension/messaging.js | 4 +- .../bums/lunatic/launcher/LauncherActivity.kt | 14 ++ .../launcher/helpers/ForeGroundService.kt | 90 +++++++- .../bums/lunatic/launcher/home/GeckoWeb.kt | 215 +----------------- .../bums/lunatic/launcher/home/RssHome.kt | 2 + .../launcher/home/SearchBottomSheet.kt | 3 +- .../lunatic/launcher/receiver/NLService.kt | 61 +++-- .../bums/lunatic/launcher/tokiz/BaseToki.kt | 30 ++- .../bums/lunatic/launcher/tokiz/YouTube.kt | 60 +++++ .../lunatic/launcher/tokiz/view/BWebview.kt | 58 ++++- .../bums/lunatic/launcher/utils/DataUtils.kt | 21 ++ app/src/main/res/layout/launcher_activity.xml | 27 ++- app/src/main/res/layout/launcher_home.xml | 16 +- 13 files changed, 349 insertions(+), 252 deletions(-) create mode 100644 app/src/main/kotlin/bums/lunatic/launcher/tokiz/YouTube.kt diff --git a/app/src/main/assets/extensions/my_extension/messaging.js b/app/src/main/assets/extensions/my_extension/messaging.js index 6cfbc1ae..7eb9cc56 100644 --- a/app/src/main/assets/extensions/my_extension/messaging.js +++ b/app/src/main/assets/extensions/my_extension/messaging.js @@ -901,5 +901,5 @@ function autoScrollAndHandleDotax() { // // // 시작 // scrollAndSend(); -// } - } \ No newline at end of file + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/bums/lunatic/launcher/LauncherActivity.kt b/app/src/main/kotlin/bums/lunatic/launcher/LauncherActivity.kt index 7e5a4f43..731a3829 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/LauncherActivity.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/LauncherActivity.kt @@ -76,16 +76,21 @@ import bums.lunatic.launcher.tokiz.Novels import bums.lunatic.launcher.tokiz.Perplexity import bums.lunatic.launcher.tokiz.Twitter import bums.lunatic.launcher.tokiz.Webtoons +import bums.lunatic.launcher.tokiz.YouTube import bums.lunatic.launcher.tokiz.Zota import bums.lunatic.launcher.utils.Blog +import bums.lunatic.launcher.utils.KakaoPublicTransfer import bums.lunatic.launcher.workers.WorkersDb +import com.google.android.gms.common.util.DataUtils import com.google.android.material.color.DynamicColors +import com.google.gson.Gson import com.yausername.ffmpeg.FFmpeg import com.yausername.youtubedl_android.YoutubeDL import com.yausername.youtubedl_android.YoutubeDLException import io.realm.kotlin.ext.query import kr.lunaticbum.utils.ui.DisplayUtil import org.json.JSONObject +import org.jsoup.helper.DataUtil import org.mozilla.geckoview.ExperimentDelegate import org.mozilla.geckoview.GeckoResult import org.mozilla.geckoview.GeckoRuntime @@ -465,6 +470,7 @@ open class LauncherActivity : CommonActivity() { } catch (e: YoutubeDLException) { Blog.LOGE("failed to initialize youtubedl-android", e) } + val intent = Intent(this, ForeGroundService::class.java) this.startForegroundService(intent) @@ -536,6 +542,11 @@ open class LauncherActivity : CommonActivity() { .replace(R.id.fragment_container, Comics()) .commit() } + R.id.youtube ->{ + supportFragmentManager.beginTransaction() + .replace(R.id.fragment_container, YouTube()) + .commit() + } R.id.perplexity ->{ supportFragmentManager.beginTransaction() .replace(R.id.fragment_container, Perplexity()) @@ -679,6 +690,9 @@ open class LauncherActivity : CommonActivity() { currentFragment.doNextPage() } } + is YouTube -> { + currentFragment.back() + } is Novels -> { currentFragment.actionNextEvent(false) } diff --git a/app/src/main/kotlin/bums/lunatic/launcher/helpers/ForeGroundService.kt b/app/src/main/kotlin/bums/lunatic/launcher/helpers/ForeGroundService.kt index 87e2a2fb..9a9645c7 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/helpers/ForeGroundService.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/helpers/ForeGroundService.kt @@ -13,7 +13,9 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import android.content.pm.PackageManager +import android.net.Uri import android.os.Build +import android.os.Environment import android.os.IBinder import androidx.core.app.ActivityCompat import androidx.core.app.NotificationCompat @@ -41,6 +43,8 @@ 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 com.yausername.youtubedl_android.YoutubeDL +import com.yausername.youtubedl_android.YoutubeDLRequest import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -49,6 +53,8 @@ import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response import okhttp3.ResponseBody +import java.io.File +import java.util.UUID import java.util.concurrent.TimeUnit @@ -56,7 +62,12 @@ class ForeGroundService : Service() { companion object { val ACTION_SENDMSG = "ACTION_SEND_TO_LOVE" val EXTRA_MSGKEY = "SEND_MSG" + + val ACTION_VIDEO_DOWNLOAD = "ACTION_YTURL_DOWNLOAD" + val EXTRA_TARGET_URL = "ACTION_SEND_TO_LOVE" + val targetUrls = arrayListOf() } + enum class BLUETOOTH_STATE(val statestr: String) { ENABLED("enabledBlutooth"), DISABLED("disableBlutooth"), @@ -78,20 +89,80 @@ class ForeGroundService : Service() { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { Blog.LOGE("onStartCommand >>> ${intent}") - if (ACTION_SENDMSG.equals(intent?.action)) { - intent?.getStringExtra(EXTRA_MSGKEY)?.let { - sendToI(it) + when(intent?.action) { + ACTION_SENDMSG -> { + intent?.getStringExtra(EXTRA_MSGKEY)?.let { + sendToI(it) + } + } + ACTION_VIDEO_DOWNLOAD -> { + intent?.getStringExtra(EXTRA_TARGET_URL)?.let { + Uri.parse(it)?.let { + addToTargetYtubeUrl(it.toString()) + } + } } } + startForeGround() return START_STICKY } - fun startForeGround() { + fun addToTargetYtubeUrl(url : String) { + targetUrls.add(url) + if((targetUrls?.size ?: 0) > 0) { + downloadVideo(targetUrls?.firstOrNull()) + } + } + + var currentProcessId : String? = null + set(value) { + field = value + if (value == null) { + startForeGround(0,0) + } + } + + fun downloadVideo(url: String?) { + url?.let { + CoroutineScope(Dispatchers.IO).launch { + + + try { + val youtubeDLDir = File( + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), + "youtubedl-android" + ) + val command = YoutubeDLRequest(url) + command.addOption("-o", youtubeDLDir.getAbsolutePath() + "/%(title)s.%(ext)s"); + currentProcessId = UUID.randomUUID().toString() + YoutubeDL.getInstance() + .execute(command, currentProcessId) { progress, est, str -> + startForeGround(100, progress.toInt(),str) + if (progress >= 100) { + targetUrls.remove(url) + currentProcessId = null + if((targetUrls?.size ?: 0) > 0) { + downloadVideo(targetUrls?.firstOrNull()) + } + } + } + } catch (e: Exception) { + e.printStackTrace() + currentProcessId = null + if((targetUrls?.size ?: 0) > 0) { + downloadVideo(targetUrls?.firstOrNull()) + } + } + } + } + } + + fun startForeGround(max : Int = 0 , progress : Int = 0, str : String = "실행중입니다.") { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( CHANNEL_ID, - "BLE 서비스 채널", + "BUM'S 서비스", NotificationManager.IMPORTANCE_HIGH ) val manager = getSystemService(NotificationManager::class.java) @@ -110,7 +181,7 @@ class ForeGroundService : Service() { startForeground(NOTIF_ID, NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle("BLE 서비스") - .setContentText("실행중입니다.") + .setContentText(str) .setPriority(NotificationCompat.PRIORITY_MAX) .setSmallIcon(R.drawable.ic_b) .setContentIntent(pendingIntent) @@ -118,6 +189,7 @@ class ForeGroundService : Service() { .addAction(android.R.drawable.ic_btn_speak_now,"버스 탐", makeSendMsgAction(1,"돼지 버스 탔다요~!")) .addAction(android.R.drawable.ic_btn_speak_now,"버스 내림", makeSendMsgAction(2,"돼지 버스 내린다요~!")) .setOngoing(true) // 사용자가 알림을 스와이프로 지울 수 없게 만듦 + .setProgress(max, progress, false) .build()) } @@ -241,6 +313,8 @@ class ForeGroundService : Service() { return mWorkManager } + + fun sendToI(msg: String) { if (PrefString.telegramSendTarget.get().length > 5) { CoroutineScope(Dispatchers.IO).launch { @@ -256,6 +330,8 @@ class ForeGroundService : Service() { } } } + + fun sendToI(boolean: Boolean) { if (PrefString.telegramSendTarget.get().length > 5) { CoroutineScope(Dispatchers.IO).launch { @@ -280,6 +356,8 @@ class ForeGroundService : Service() { } else Blog.LOGE("sendToI telegram Error Occurred") } } + + } @SuppressLint("MissingPermission") fun isConnected(device: BluetoothDevice): Boolean { diff --git a/app/src/main/kotlin/bums/lunatic/launcher/home/GeckoWeb.kt b/app/src/main/kotlin/bums/lunatic/launcher/home/GeckoWeb.kt index aeb47dc9..5f5a7857 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/home/GeckoWeb.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/home/GeckoWeb.kt @@ -11,6 +11,7 @@ import android.content.Intent import android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP import android.content.Intent.FLAG_ACTIVITY_NEW_TASK import android.net.Uri +import android.os.Build import android.os.Environment import android.os.Handler import android.os.Looper @@ -41,6 +42,11 @@ import androidx.core.view.isVisible import androidx.work.Worker import bums.lunatic.launcher.LauncherActivity.Companion.getRuntime import bums.lunatic.launcher.R +import bums.lunatic.launcher.helpers.ForeGroundService +import bums.lunatic.launcher.helpers.ForeGroundService.Companion.ACTION_SENDMSG +import bums.lunatic.launcher.helpers.ForeGroundService.Companion.ACTION_VIDEO_DOWNLOAD +import bums.lunatic.launcher.helpers.ForeGroundService.Companion.EXTRA_MSGKEY +import bums.lunatic.launcher.helpers.ForeGroundService.Companion.EXTRA_TARGET_URL import bums.lunatic.launcher.model.Dotax import bums.lunatic.launcher.model.DotaxArticles import bums.lunatic.launcher.model.getRssData @@ -93,7 +99,6 @@ class GeckoWeb : BWebview { constructor(context: Context?) : super(context) { buildWeb() } - var decoViews = arrayListOf() override fun setVisibility(visibility: Int) { super.setVisibility(visibility) decoViews.filter { it != null && it.id > -1 && it.id != R.id.dl_video }.forEach { it.visibility = visibility } @@ -124,38 +129,7 @@ class GeckoWeb : BWebview { session.mediaDelegate = mediaDelegate session.promptDelegate = promptDelegate session.mediaSessionDelegate = mediaSessionDelegate -// session.permissionDelegate = (object : PermissionDelegate { -// override fun onContentPermissionRequest( -// session: GeckoSession, -// perm: PermissionDelegate.ContentPermission -// ): GeckoResult? { -// -// return super.onContentPermissionRequest(session, perm) -// } -// -// override fun onAndroidPermissionsRequest( -// session: GeckoSession, -// permissions: Array?, -// callback: PermissionDelegate.Callback -// ) { -// super.onAndroidPermissionsRequest(session, permissions, callback) -// } -// -// override fun onMediaPermissionRequest( -// session: GeckoSession, -// uri: String, -// video: Array?, -// audio: Array?, -// callback: PermissionDelegate.MediaCallback -// ) { -// -// // 첫 번째 비디오·오디오 소스를 허용 -// -// callback.grant(video?.firstOrNull(), audio?.firstOrNull()) -// } -// -// -// }); + it.webExtensionController .ensureBuiltIn(extPath, extId) .accept( // Register message delegate for background script @@ -420,109 +394,6 @@ class GeckoWeb : BWebview { mOnSave?.saved() } - fun downloadImage(context: Context, url: Uri, isGif : Boolean = false) { - Blog.LOGE("url.lastPathSegment ${url.lastPathSegment}") - val fileName = url.host + "_${SimpleDateFormat("yyyyMMddHHmmsss").format(Date())}.${if(isGif){"gif"}else{"jpg"}}" - val request = DownloadManager.Request(url) - request.setTitle(fileName) - request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI or DownloadManager.Request.NETWORK_MOBILE) - request.setDescription("이미지 다운로드 중...") - request.setAllowedOverMetered(true) // 선택 사항 - 데이터 요금제 네트워크에서도 허용 - request.setVisibleInDownloadsUi(true) - request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName) - // 네트워크타입, 알림설정 등 옵션 추가 가능 - request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) - - val dm = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager - - Toast.makeText(context, "다운로드 시작: $fileName", Toast.LENGTH_SHORT).show() - val downloadId = dm.enqueue(request) - monitorDownloadStatus(dm, downloadId, context) - } - fun monitorDownloadStatus(dm: DownloadManager, downloadId: Long, context: Context) { - val handler = CoroutineExceptionHandler { _, exception -> - // 에러 처리 로직 (선택) - exception.printStackTrace() - } - - // 백그라운드에서 5초 간격으로 상태 체크 - CoroutineScope(Dispatchers.IO + handler).launch { - while (true) { - val query = DownloadManager.Query().setFilterById(downloadId) - val cursor = dm.query(query) - var downloadFinished = false - - if (cursor != null && cursor.moveToFirst()) { - val status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) - when (status) { - DownloadManager.STATUS_SUCCESSFUL -> { - // 다운로드 성공 처리 - withContext(Dispatchers.Main) { - // UI 갱신 등 메인 스레드 작업 (필요 시) - } - downloadFinished = true - } - DownloadManager.STATUS_FAILED -> { - val reason = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_REASON)) - // 여기에 reason값에 따라 적절한 처리 또는 로깅 수행 - Log.e("DownloadManager", "Download failed with reason code: $reason") - // 다운로드 실패 처리 - withContext(Dispatchers.Main) { - // UI 갱신 등 메인 스레드 작업 (필요 시) - } - downloadFinished = true - } - else -> { - Blog.LOGE("DownloadManager.STATUS >> ${status}") - } - // 진행 중, 대기 중 등 기타 상태는 계속 확인 - } - } - cursor?.close() - - if (downloadFinished) break - - delay(5000L) // 5초 대기 후 다시 반복 - } - } - } - - 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) { @@ -562,76 +433,6 @@ class GeckoWeb : BWebview { - lateinit var progressDialog: AlertDialog - fun showProgressDialog() { - val dialogView = layoutInflater.inflate(R.layout.progress_dialog, null) - val progressBar = dialogView.findViewById(R.id.progressBar) - val textProgress = dialogView.findViewById(R.id.textProgress) - val btn = dialogView.findViewById(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(R.id.progressBar) - val tv = - progressDialog.findViewById(R.id.textProgress) - pb?.progress = progress.toInt() - val btn = progressDialog.findViewById(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 fun getFilterF() = String(java.util.Base64.getMimeDecoder().decode("aHR0cHM6Ly9pamF2dG9ycmVudC5jb20=".toByteArray())) @@ -890,8 +691,6 @@ class GeckoWeb : BWebview { lastedUrl = url } checkIfDownloadable(url) - - } diff --git a/app/src/main/kotlin/bums/lunatic/launcher/home/RssHome.kt b/app/src/main/kotlin/bums/lunatic/launcher/home/RssHome.kt index 0f118deb..90740060 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/home/RssHome.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/home/RssHome.kt @@ -281,6 +281,7 @@ internal class RssHome : Fragment() { } } fun searchKeyword() { + binding.geckoWeb.visibility = View.GONE // val builder: AlertDialog.Builder = AlertDialog.Builder(requireContext()) // builder.setTitle("Keyword") // val viewInflated: View = LayoutInflater.from(requireContext()) @@ -327,6 +328,7 @@ internal class RssHome : Fragment() { fun ask() { + binding.geckoWeb.visibility = View.GONE val bottomSheet = WebBottomSheet() bottomSheet.listener = object : WebBottomSheet.OnGoToWebListener{ override fun enterSearch() { diff --git a/app/src/main/kotlin/bums/lunatic/launcher/home/SearchBottomSheet.kt b/app/src/main/kotlin/bums/lunatic/launcher/home/SearchBottomSheet.kt index 639ed12d..9839cf64 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/home/SearchBottomSheet.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/home/SearchBottomSheet.kt @@ -54,7 +54,7 @@ class SearchBottomSheet : BottomSheetDialogFragment() { val categoryContainer = view.findViewById(R.id.categoryContainer) addVote = view.findViewById(R.id.add_vote) as CheckBox addRead = view.findViewById(R.id.add_read) as CheckBox - addRead.setOnCheckedChangeListener {v,b->triggerSearchWithDebounce(inputKeyword.text.toString())} + addVote.setOnCheckedChangeListener {v,b->triggerSearchWithDebounce(inputKeyword.text.toString())} addRead.setOnCheckedChangeListener {v,b->triggerSearchWithDebounce(inputKeyword.text.toString())} // 카테고리 목록 val categories = RssDataType.getAll() @@ -66,6 +66,7 @@ class SearchBottomSheet : BottomSheetDialogFragment() { text = category.name tag = category isAllCaps = false + this.isSelected = true setBackgroundResource(android.R.drawable.btn_default) setTextColor(Color.WHITE) setBackgroundResource(R.color.tabs_black) diff --git a/app/src/main/kotlin/bums/lunatic/launcher/receiver/NLService.kt b/app/src/main/kotlin/bums/lunatic/launcher/receiver/NLService.kt index f83ae8bf..d5bc8a2f 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/receiver/NLService.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/receiver/NLService.kt @@ -1,6 +1,7 @@ package bums.lunatic.launcher.receiver import android.Manifest +import android.annotation.SuppressLint import android.app.Notification import android.content.BroadcastReceiver import android.content.ComponentName @@ -30,6 +31,7 @@ import bums.lunatic.launcher.model.LocationLog import bums.lunatic.launcher.model.NotificationItem import bums.lunatic.launcher.utils.BitmapConverter import bums.lunatic.launcher.utils.Blog +import bums.lunatic.launcher.utils.KakaoPublicTransfer import bums.lunatic.launcher.workers.LocationUpdateService.Companion.inRangeLocation import bums.lunatic.launcher.workers.WorkersDb import com.google.android.gms.location.LocationServices @@ -70,6 +72,7 @@ class NLService : NotificationListenerService() { } + @SuppressLint("MissingPermission") @RequiresApi(Build.VERSION_CODES.S) override fun onNotificationPosted(sbn: StatusBarNotification) { Blog.LOGE("onNotificationPosted ${sbn}") @@ -95,27 +98,53 @@ class NLService : NotificationListenerService() { Blog.LOGE("title >> ${title} text >> ${text} bigText >> ${bigText} extraInfo >> ${extraInfo} subText >> ${subText} conversationTitle >> ${conversationTitle} summaryText >> ${summaryText} verificationText >> ${verificationText}") mHourlyLogWriter?.writeLog("${sbn.packageName}\n${stringBuffer.toString()}") when (sbn.packageName){ - "com.kakao.talk" -> { - - } - "kakaopay.app" -> { - if (stringBuffer.contains("모바일") && stringBuffer.contains("교통카드")) { - var usePublicTransportation = PrefBoolean.usePublicTransportation.get(false) - PrefBoolean.usePublicTransportation.set(!usePublicTransportation) - val actionIntent = Intent(this, ForeGroundService::class.java).apply { - action = ACTION_SENDMSG - putExtra(EXTRA_MSGKEY, "돼지가 대중교통에${if (!usePublicTransportation){" 탑승 "} else {"서 하차"}} 했다요~!") // 전달할 데이터 - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - startForegroundService(actionIntent) - } else { - startService(actionIntent) - } + "com.kakao.taxi" -> { + var defaultMsg : StringBuffer? = StringBuffer("돼지 택시 ") + if (stringBuffer.contains("택시") && stringBuffer.contains("탑승") && stringBuffer.contains("완료")) { + defaultMsg?.append("탔다요~!") + }else if(stringBuffer.contains("택시") && stringBuffer.contains("자동결제") && stringBuffer.contains("물건")) { + defaultMsg?.append("거의 다 왔다요~!") + }else if(stringBuffer.contains("택시") && stringBuffer.contains("도착") && stringBuffer.contains("선택")) { + defaultMsg?.append("내린다요~!") + } else { + defaultMsg = null + } + defaultMsg?.let { + makeMsgByTransferInfomation(it) } } + "com.kakao.talk" -> { + if (stringBuffer.contains("카카오페이") && stringBuffer.contains("모바일") && stringBuffer.contains("교통카드") && stringBuffer.contains("사용 내역")) { + var usePublicTransportation = PrefBoolean.usePublicTransportation.get(false) + PrefBoolean.usePublicTransportation.set(!usePublicTransportation) + var defaultMsg = StringBuffer("돼지가 대중교통에${if (!usePublicTransportation){" 탑승 "} else {"서 하차"}} 했다요~!") + KakaoPublicTransfer(stringBuffer.toString()).let { + defaultMsg.append("\n${it.transportType}(${it.transportName})") + defaultMsg.append("\n${it.dateTime}") + } + makeMsgByTransferInfomation(defaultMsg) + } + } + "kakaopay.app" -> { + + } } } + @RequiresPermission(allOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION]) + fun makeMsgByTransferInfomation(stringBuffer : StringBuffer) { + val actionIntent = Intent(this, ForeGroundService::class.java).apply { + action = ACTION_SENDMSG + putExtra(EXTRA_MSGKEY, stringBuffer.toString()) // 전달할 데이터 + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + startForegroundService(actionIntent) + } else { + startService(actionIntent) + } + pushLocation(this) + } + @RequiresPermission(allOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION]) fun pushLocation(context: Context) { try { diff --git a/app/src/main/kotlin/bums/lunatic/launcher/tokiz/BaseToki.kt b/app/src/main/kotlin/bums/lunatic/launcher/tokiz/BaseToki.kt index 46ced71f..e914fb37 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/tokiz/BaseToki.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/tokiz/BaseToki.kt @@ -28,12 +28,14 @@ import android.webkit.WebView import android.webkit.WebViewClient import android.widget.ArrayAdapter import android.widget.EditText +import android.widget.ImageButton import android.widget.TextView import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.core.net.toUri import androidx.core.view.isVisible import androidx.fragment.app.Fragment +import bums.lunatic.launcher.LauncherActivity import bums.lunatic.launcher.LauncherActivity.Companion.getRuntime import bums.lunatic.launcher.R import bums.lunatic.launcher.tokiz.common.PairArray @@ -404,6 +406,21 @@ abstract class BaseToki : Fragment(), PagedTextViewInterface { } else { lastedUrl = url } + binding.menuWeb.checkIfDownloadable(url) + binding.menuWeb.decoViews.filter { it != null && it.id > -1 }.forEach { + if (it != null && it.id > -1) { + if (it.id == R.id.back) { + it.setOnClickListener { session.goBack() } + } else if (it.id == R.id.current_address) { + (it as? TextView)?.let { + it.tag = currentTitle + it.text = url + } + }else if (it.id == R.id.reload) { + it.setOnClickListener { session.reload() } + } + } + } } completePageLoad(LastInfo().apply { this.pageUrl = url?.toUri()?.path ?: getLastedDoamin() ?: "" @@ -635,6 +652,15 @@ abstract class BaseToki : Fragment(), PagedTextViewInterface { } val nullCursor = PointerIcon.getSystemIcon(context!!, PointerIcon.TYPE_NULL) binding.root.setPointerIcon(nullCursor) + + + binding.menuWeb + (activity as? LauncherActivity)?.let { activity -> + binding.menuWeb.decoViews.add(activity.findViewById(R.id.current_address)) + binding.menuWeb.decoViews.add(activity.findViewById(R.id.back)) + binding.menuWeb.decoViews.add(activity.findViewById(R.id.reload)) + binding.menuWeb.decoViews.add(activity.findViewById(R.id.dl_video)) + } return binding.root } @@ -1500,6 +1526,8 @@ abstract class BaseToki : Fragment(), PagedTextViewInterface { } - + open fun back() { +// binding.menuWeb.session?.goBack() + } } \ No newline at end of file diff --git a/app/src/main/kotlin/bums/lunatic/launcher/tokiz/YouTube.kt b/app/src/main/kotlin/bums/lunatic/launcher/tokiz/YouTube.kt new file mode 100644 index 00000000..b3f8866c --- /dev/null +++ b/app/src/main/kotlin/bums/lunatic/launcher/tokiz/YouTube.kt @@ -0,0 +1,60 @@ +package bums.lunatic.launcher.tokiz + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import bums.lunatic.launcher.tokiz.common.TouchArea +import bums.lunatic.launcher.tokiz.view.PagedTextViewInterface + +class YouTube : BaseToki(){ + override val contentsType = "youtube" + override var lastNumber : Int = 143 + override val webcontentsName : String = "youtube" + 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 back() { + binding.menuWeb.session?.goBack() + } + + override fun onTouch(touchArea: TouchArea) { + } + + override fun onTimeoverTouch() { + } + + override fun onSwipeLeft(touchCount: Int) { + } + + override fun onSwipeRight(touchCount: Int) { + } + + override fun onSwipeDown(touchCount: Int) { + } + + override fun onSwipeUp(touchCount: Int) { + } + + override fun onLongClick() { + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/bums/lunatic/launcher/tokiz/view/BWebview.kt b/app/src/main/kotlin/bums/lunatic/launcher/tokiz/view/BWebview.kt index a1ba752e..db65a572 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/tokiz/view/BWebview.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/tokiz/view/BWebview.kt @@ -2,15 +2,25 @@ package bums.lunatic.launcher.tokiz.view import android.annotation.SuppressLint import android.content.Context +import android.content.Intent import android.os.Build import android.util.AttributeSet import android.view.MotionEvent import android.view.PointerIcon import android.view.View import androidx.core.view.isVisible +import bums.lunatic.launcher.R +import bums.lunatic.launcher.helpers.ForeGroundService +import bums.lunatic.launcher.helpers.ForeGroundService.Companion.ACTION_VIDEO_DOWNLOAD +import bums.lunatic.launcher.helpers.ForeGroundService.Companion.EXTRA_TARGET_URL import bums.lunatic.launcher.tokiz.common.TouchArea import bums.lunatic.launcher.utils.Blog import bums.lunatic.launcher.utils.SimpleFingerGestures +import com.yausername.youtubedl_android.YoutubeDL +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import org.mozilla.gecko.util.ThreadUtils.runOnUiThread import org.mozilla.geckoview.GeckoView import java.util.Base64 @@ -23,6 +33,7 @@ enum class JxEvent { } typealias JxInteface = (JxEvent)->Unit open class BWebview : GeckoView { + var decoViews = arrayListOf() @SuppressLint("ClickableViewAccessibility") constructor(context: Context?) : super(context) { this.setOnTouchListener { v, event -> @@ -57,8 +68,53 @@ open class BWebview : GeckoView { this.setPointerIcon(nullCursor) } } + fun videoDlownLoad(videoUrl : String) { + val actionIntent = Intent(context, ForeGroundService::class.java).apply { + action = ACTION_VIDEO_DOWNLOAD + putExtra(EXTRA_TARGET_URL, videoUrl) // 전달할 데이터 + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + context.startForegroundService(actionIntent) + } else { + context.startService(actionIntent) + } + } + 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 + }}} + } + } + } val mSimpleFingerGestures = SimpleFingerGestures(omfgl = object : SimpleFingerGestures.OnFingerGestureListener{ override fun onSwipeUp( diff --git a/app/src/main/kotlin/bums/lunatic/launcher/utils/DataUtils.kt b/app/src/main/kotlin/bums/lunatic/launcher/utils/DataUtils.kt index 26002fed..307b868a 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/utils/DataUtils.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/utils/DataUtils.kt @@ -8,6 +8,27 @@ import android.provider.ContactsContract.PhoneLookup import java.util.Calendar import java.util.Date +data class KakaoPayTransitHistory( + val dateTime: String, + val transportType: String, + val transportName: String, + val balance: Int +) + +fun KakaoPublicTransfer(raw : String) : KakaoPayTransitHistory { + val dateTimeRegex = Regex("""사용일시\s*:\s*([^\n]+)""") + val transportTypeRegex = Regex("""이용수단\s*:\s*([^\n]+)""") + val transportNameRegex = Regex("""이용수단명\s*:\s*([^\n]+)""") + val balanceRegex = Regex("""잔액\s*:\s*([\d,]+)""") + + val dateTime = dateTimeRegex.find(raw)?.groups?.get(1)?.value?.trim() ?: "" + val transportType = transportTypeRegex.find(raw)?.groups?.get(1)?.value?.trim() ?: "" + val transportName = transportNameRegex.find(raw)?.groups?.get(1)?.value?.trim() ?: "" + val balance = balanceRegex.find(raw)?.groups?.get(1)?.value?.replace(",", "")?.toInt() ?: 0 + + return KakaoPayTransitHistory(dateTime, transportType, transportName, balance) +} + fun afterDay(date: Long): Long { val cal: Calendar = Calendar.getInstance() cal.setTime(Date(date)) diff --git a/app/src/main/res/layout/launcher_activity.xml b/app/src/main/res/layout/launcher_activity.xml index 01f48345..8c9a967d 100644 --- a/app/src/main/res/layout/launcher_activity.xml +++ b/app/src/main/res/layout/launcher_activity.xml @@ -33,9 +33,9 @@ android:src="@drawable/back_vector" android:tint="@color/white" android:foregroundTint="@color/white" - android:layout_width="40dp" + android:layout_width="@dimen/main_top_height" tools:ignore="ContentDescription" - android:layout_height="40dp" /> + android:layout_height="@dimen/main_top_height" /> + android:layout_height="@dimen/main_top_height" /> + android:layout_height="@dimen/main_top_height"/> + "/> + android:layout_height="@dimen/main_top_height" /> + android:layout_height="@dimen/main_top_height" /> + + android:layout_height="@dimen/main_top_height" /> @@ -62,9 +62,9 @@ android:tintMode="multiply" android:scaleType="fitCenter" android:background="@null" - android:layout_width="40dp" + android:layout_width="@dimen/main_top_height" android:adjustViewBounds="true" - android:layout_height="40dp" + android:layout_height="@dimen/main_top_height" app:tint="@color/white" tools:ignore="ContentDescription" /> @@ -75,10 +75,10 @@ android:src="@drawable/bookmark" android:scaleType="fitCenter" android:background="@null" - android:layout_width="40dp" + android:layout_width="@dimen/main_top_height" android:adjustViewBounds="true" tools:ignore="ContentDescription" - android:layout_height="40dp"/> + android:layout_height="@dimen/main_top_height"/>