This commit is contained in:
lunaticbum 2026-01-30 16:52:36 +09:00
parent 16640559b3
commit 9f6c424e04
10 changed files with 1917 additions and 2477 deletions

View File

@ -155,6 +155,7 @@ dependencies {
// implementation 'com.vladsch.flexmark:flexmark-all:0.64.8' // implementation 'com.vladsch.flexmark:flexmark-all:0.64.8'
// implementation("org.opencv:opencv-android:4.11.0") // implementation("org.opencv:opencv-android:4.11.0")
// build.gradle에 추가 // build.gradle에 추가
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
// 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:library:0.17.4")
implementation("io.github.junkfood02.youtubedl-android:ffmpeg:0.17.4") implementation("io.github.junkfood02.youtubedl-android:ffmpeg:0.17.4")

View File

@ -91,8 +91,6 @@
android:screenOrientation="userPortrait" android:screenOrientation="userPortrait"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|screenLayout|layoutDirection|navigation" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|screenLayout|layoutDirection|navigation"
android:windowSoftInputMode="adjustResize" android:windowSoftInputMode="adjustResize"
android:noHistory="true"
android:excludeFromRecents="true"
android:exported="false"> android:exported="false">
<intent-filter> <intent-filter>

View File

@ -291,7 +291,9 @@ class AppDrawerBottomSheet : BottomSheetDialogFragment() {
if (item.type == "APP") { if (item.type == "APP") {
val app = realm.query<AppInfo>("pkgName == $0", item.key).first().find() val app = realm.query<AppInfo>("pkgName == $0", item.key).first().find()
if (app != null && !app.blockRecommend) { if (app != null && !app.blockRecommend) {
unifiedList.add(RecommendationItem.AppItem(realm.copyFromRealm(app))) try { if(pm.getLaunchIntentForPackage(app.pkgName ?: "") != null) {
unifiedList.add(RecommendationItem.AppItem(realm.copyFromRealm(app)))
}} catch (e: Exception) { }
} }
} else if (item.type == "CONTACT") { } else if (item.type == "CONTACT") {
// 연락처 ID나 전화번호로 조회 (Log 저장 시 key가 무엇인지에 따라 다름) // 연락처 ID나 전화번호로 조회 (Log 저장 시 key가 무엇인지에 따라 다름)

View File

@ -82,6 +82,40 @@ open class GeckoWeb @JvmOverloads constructor(
// --- 1. Properties & Initialization --- // --- 1. Properties & Initialization ---
// 1. 세션 상태를 저장할 SharedPreferences 키
private val PREF_SESSION_STATE = "gecko_session_state"
// 2. 현재 세션 상태 저장 메서드
fun saveCurrentSessionState() {
lastSessionState?.let { state ->
// SessionState.toString()은 내부적으로 JSON 문자열을 반환합니다.
val stateJson = state.toString()
context.getSharedPreferences("GeckoPrefs", Context.MODE_PRIVATE)
.edit()
.putString("gecko_session_state", stateJson)
.apply()
Log.d("GeckoWeb", "Session State Saved: $stateJson")
}
}
// 3. 저장된 세션 상태 복구 메서드
fun restoreSessionState(session: GeckoSession) {
val stateJson = context.getSharedPreferences("GeckoPrefs", Context.MODE_PRIVATE)
.getString("gecko_session_state", null)
if (!stateJson.isNullOrEmpty()) {
// 문자열에서 SessionState 객체 생성
val state = GeckoSession.SessionState.fromString(stateJson)
if (state != null) {
session.restoreState(state)
Log.d("GeckoWeb", "Session State Restored")
}
}
}
// UI & State // UI & State
var decoViews = arrayListOf<View>() var decoViews = arrayListOf<View>()
var progress: ProgressBar? = null var progress: ProgressBar? = null
@ -138,8 +172,8 @@ open class GeckoWeb @JvmOverloads constructor(
override fun onLongPress(targetView: View, fingers: Int) = true override fun onLongPress(targetView: View, fingers: Int) = true
}) })
class GKCookie { var COOKIES: String? = null } // class GKCookie { var COOKIES: String? = null }
var mGKCookie: GKCookie? = null // var mGKCookie: GKCookie? = null
// Markdown/Scraping // Markdown/Scraping
var markdownContents: String? = null var markdownContents: String? = null
@ -192,23 +226,23 @@ open class GeckoWeb @JvmOverloads constructor(
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
try { try {
val request = YoutubeDLRequest(url) val request = YoutubeDLRequest(url)
mGKCookie?.COOKIES?.let { cookieStr -> // mGKCookie?.COOKIES?.let { cookieStr ->
val cookieFile = File(context.filesDir, "cookies.txt") // val cookieFile = File(context.filesDir, "cookies.txt")
val cookies = cookieStr.split(";").mapNotNull { // val cookies = cookieStr.split(";").mapNotNull {
val p = it.trim().split("=", limit = 2) // val p = it.trim().split("=", limit = 2)
if (p.size == 2) p[0] to p[1] else null // if (p.size == 2) p[0] to p[1] else null
}.toMap() // }.toMap()
val expires = (System.currentTimeMillis() / 1000) + 3600 * 24 * 7 // val expires = (System.currentTimeMillis() / 1000) + 3600 * 24 * 7
//
val content = buildString { // val content = buildString {
appendLine("# Netscape HTTP Cookie File") // appendLine("# Netscape HTTP Cookie File")
cookies.forEach { (k, v) -> // cookies.forEach { (k, v) ->
appendLine(".${url.toUri().host}\tTRUE\t/\tTRUE\t$expires\t$k\t$v") // appendLine(".${url.toUri().host}\tTRUE\t/\tTRUE\t$expires\t$k\t$v")
} // }
} // }
cookieFile.writeText(content) // cookieFile.writeText(content)
request.addOption("--cookies", cookieFile.absolutePath) // request.addOption("--cookies", cookieFile.absolutePath)
} // }
val videoInfo = YoutubeDL.getInstance().getInfo(request) val videoInfo = YoutubeDL.getInstance().getInfo(request)
if (videoInfo != null && !videoInfo.title.isNullOrEmpty()) { if (videoInfo != null && !videoInfo.title.isNullOrEmpty()) {
@ -321,12 +355,14 @@ open class GeckoWeb @JvmOverloads constructor(
} }
override fun onPageStop(session: GeckoSession, success: Boolean) { override fun onPageStop(session: GeckoSession, success: Boolean) {
onPageStopCallback?.invoke(success) onPageStopCallback?.invoke(success)
saveCurrentSessionState()
} }
override fun onSessionStateChange(session: GeckoSession, sessionState: GeckoSession.SessionState) { override fun onSessionStateChange(session: GeckoSession, sessionState: GeckoSession.SessionState) {
onSessionStateChangeCallback?.invoke(sessionState) onSessionStateChangeCallback?.invoke(sessionState)
} }
} }
private var lastSessionState: GeckoSession.SessionState? = null
// [Extension Delegates] // [Extension Delegates]
private val addonManagerDelegate = object : AddonManagerDelegate { private val addonManagerDelegate = object : AddonManagerDelegate {
override fun onReady(extension: WebExtension) { mExtension = extension } override fun onReady(extension: WebExtension) { mExtension = extension }
@ -386,6 +422,7 @@ open class GeckoWeb @JvmOverloads constructor(
private fun buildWeb() { private fun buildWeb() {
getRuntime()?.let { runtime -> getRuntime()?.let { runtime ->
val session = GeckoSession() val session = GeckoSession()
restoreSessionState(session)
session.open(runtime) session.open(runtime)
this.setSession(session) this.setSession(session)

View File

@ -41,12 +41,7 @@ import bums.lunatic.launcher.home.adapters.BookmarkPagerFragment
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.settings.SettingsActivity import bums.lunatic.launcher.settings.SettingsActivity
import bums.lunatic.launcher.home.tokiz.Comics
import bums.lunatic.launcher.home.tokiz.Novels
import bums.lunatic.launcher.home.tokiz.Perplexity
import bums.lunatic.launcher.home.tokiz.TokiFragment import bums.lunatic.launcher.home.tokiz.TokiFragment
import bums.lunatic.launcher.home.tokiz.Webtoons
import bums.lunatic.launcher.home.tokiz.YouTube
import bums.lunatic.launcher.utils.Blog import bums.lunatic.launcher.utils.Blog
import bums.lunatic.launcher.utils.beforeDay import bums.lunatic.launcher.utils.beforeDay
import bums.lunatic.launcher.workers.WorkersDb import bums.lunatic.launcher.workers.WorkersDb
@ -63,6 +58,7 @@ 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.io.File
open class NeoRssActivity : CommonActivity() { open class NeoRssActivity : CommonActivity() {
@ -116,7 +112,7 @@ open class NeoRssActivity : CommonActivity() {
var onExit = false var onExit = false
var lastAction = MotionEvent.ACTION_HOVER_EXIT var lastAction = MotionEvent.ACTION_HOVER_EXIT
override fun dispatchKeyEvent(ev: KeyEvent): Boolean { override fun dispatchKeyEvent(ev: KeyEvent): Boolean {
val currentFragment = supportFragmentManager.findFragmentById(R.id.fragment_container) Blog.LOGE("dispatchKeyEvent >>> ${ev}")
if (ev?.device?.name?.contains("SM-031N Mouse") == true) { if (ev?.device?.name?.contains("SM-031N Mouse") == true) {
when(ev.action) { when(ev.action) {
ACTION_UP -> { ACTION_UP -> {
@ -162,36 +158,35 @@ open class NeoRssActivity : CommonActivity() {
} }
else { else {
val currentFragment = supportFragmentManager.findFragmentById(R.id.fragment_container) val currentFragment = supportFragmentManager.findFragmentById(R.id.fragment_container)
when(currentFragment) { Blog.LOGE("currentFragment >>> ${currentFragment} ${currentFragment is TokiFragment && currentFragment.isbooktoki()}")
is Novels -> { if (currentFragment is TokiFragment && currentFragment.isbooktoki()) {
if(MotionEvent.ACTION_UP.equals(ev?.action ?: MotionEvent.ACTION_CANCEL) == true) { if(MotionEvent.ACTION_UP.equals(ev?.action ?: MotionEvent.ACTION_CANCEL) == true) {
return when (ev.keyCode) { return when (ev.keyCode) {
KeyEvent.KEYCODE_VOLUME_DOWN -> { KeyEvent.KEYCODE_VOLUME_DOWN -> {
if(currentFragment is Novels){ currentFragment.actionNextEvent() } if(currentFragment is TokiFragment){ currentFragment.actionNextEvent() }
true true
}
KeyEvent.KEYCODE_VOLUME_UP -> {
if(currentFragment is Novels){ currentFragment.actionPrevEvent() }
true
}
else -> false
} }
} else { KeyEvent.KEYCODE_VOLUME_UP -> {
return when (ev.keyCode) { if(currentFragment is TokiFragment){ currentFragment.actionPrevEvent() }
KeyEvent.KEYCODE_VOLUME_DOWN -> { true
true
}
KeyEvent.KEYCODE_VOLUME_UP -> {
true
}
else -> false
} }
else -> false
}
} else {
return when (ev.keyCode) {
KeyEvent.KEYCODE_VOLUME_DOWN -> {
true
}
KeyEvent.KEYCODE_VOLUME_UP -> {
true
}
else -> false
} }
} }
else -> return super.dispatchKeyEvent(ev)
} }
else { return super.dispatchKeyEvent(ev) }
} }
return super.dispatchKeyEvent(ev) return super.dispatchKeyEvent(ev)
} }
@ -293,67 +288,6 @@ open class NeoRssActivity : CommonActivity() {
showContents(v.id) showContents(v.id)
} }
// override fun onNewIntent(intent: Intent) {
// Blog.LOGE("onNewIntent intent >> ${intent}")
// if(intent?.action?.equals(Intent.ACTION_SEND) == true &&
// intent?.hasExtra(Intent.EXTRA_TEXT) == true) {
// intent?.getStringExtra(Intent.EXTRA_TEXT)?.let {
// if(it.startsWith("http") == false) {
// it.split("http").forEach { string ->
// if(string.startsWith("http")) {
// try {
// string.toUri()?.let { uri ->
// Blog.LOGE("onNewIntent string uri.lastPathSegment >>>>> ${uri.host}")
// Blog.LOGE("onNewIntent string uri.lastPathSegment >>>>> ${uri.lastPathSegment}")
// }
// } catch (e: Exception) {
//
// }
// }
// }
// } else {
// try {
// it.toUri()?.let { uri ->
// Blog.LOGE("onNewIntent it uri.lastPathSegment >>>>> ${uri.host}")
// Blog.LOGE("onNewIntent it uri.lastPathSegment >>>>> ${uri.lastPathSegment}")
// }
// } catch (e: Exception) {
//
// }
// }
// }
// } else if (intent?.action == Intent.ACTION_WEB_SEARCH) {
// openWithIntent(intent)
// } else {
// Blog.LOGE("onNewIntent intent?.hasExtra >> ${intent?.hasExtra(Intent.EXTRA_STREAM)}")
//
//
// if (intent?.action?.equals(Intent.ACTION_MAIN) == true && intent.categories.contains(
// Intent.CATEGORY_HOME
// ) && intent.hasExtra("android.intent.extra.EXTRA_START_REASON") && intent.getStringExtra(
// "android.intent.extra.EXTRA_START_REASON"
// ).equals("startDockOrHome")
// ) {
//
// } else {
// intent?.extras?.keySet()?.forEach {
// try {
// Blog.LOGE(
// "onNewIntent :: key >> ${it} :: value >> ${
// intent?.extras?.getString(
// it
// )
// }"
// )
// } catch (e: Exception) {
// e.printStackTrace()
// }
//
// }
// }
// }
// super.onNewIntent(intent)
// }
private fun openWithIntent(intent: Intent){ private fun openWithIntent(intent: Intent){
Blog.LOGE("intent >> ${intent}") Blog.LOGE("intent >> ${intent}")
@ -528,7 +462,13 @@ open class NeoRssActivity : CommonActivity() {
private fun initGeckoRuntime() { private fun initGeckoRuntime() {
if (sRuntime == null) { if (sRuntime == null) {
try { try {
val profileDir = File(this.filesDir, "geckoview_profile")
if (!profileDir.exists()) profileDir.mkdirs()
sRuntime = GeckoRuntime.create(this, GeckoRuntimeSettings.Builder() sRuntime = GeckoRuntime.create(this, GeckoRuntimeSettings.Builder()
.configFilePath(profileDir.absolutePath)
.aboutConfigEnabled(true)
.javaScriptEnabled(true)
.extensionsProcessEnabled(true) .extensionsProcessEnabled(true)
.extensionsWebAPIEnabled(true) .extensionsWebAPIEnabled(true)
.experimentDelegate(experimentDelegate) .experimentDelegate(experimentDelegate)
@ -559,16 +499,9 @@ open class NeoRssActivity : CommonActivity() {
sRuntime = null sRuntime = null
} catch (e: Exception) { e.printStackTrace() } } catch (e: Exception) { e.printStackTrace() }
// appWidgetHost?.stopListening() // 이 줄은 제거하고 onStop으로 이동
super.onDestroy() super.onDestroy()
} }
override fun onUserLeaveHint() {
showContents(R.id.close)
super.onUserLeaveHint()
}
@RequiresApi(Build.VERSION_CODES.O_MR1) @RequiresApi(Build.VERSION_CODES.O_MR1)
override fun onResume() { override fun onResume() {
@ -605,12 +538,12 @@ open class NeoRssActivity : CommonActivity() {
currentFragment.doNextPage() currentFragment.doNextPage()
} }
} }
is YouTube -> { // is YouTube -> {
currentFragment.back() // currentFragment.back()
} // }
is Novels -> { // is Novels -> {
currentFragment.actionNextEvent(false) // currentFragment.actionNextEvent(false)
} // }
else -> { else -> {
showContents(R.id.close) showContents(R.id.close)
} }

View File

@ -33,7 +33,6 @@ import androidx.fragment.app.Fragment
import bums.lunatic.launcher.R import bums.lunatic.launcher.R
import bums.lunatic.launcher.databinding.BooktokiBinding import bums.lunatic.launcher.databinding.BooktokiBinding
import bums.lunatic.launcher.home.GeckoWeb import bums.lunatic.launcher.home.GeckoWeb
import bums.lunatic.launcher.home.GeckoWeb.GKCookie
import bums.lunatic.launcher.home.GeckoWeb.JxEvent import bums.lunatic.launcher.home.GeckoWeb.JxEvent
import bums.lunatic.launcher.home.NeoRssActivity import bums.lunatic.launcher.home.NeoRssActivity
import bums.lunatic.launcher.home.NeoRssActivity.Companion.getRuntime import bums.lunatic.launcher.home.NeoRssActivity.Companion.getRuntime
@ -224,6 +223,9 @@ class TokiFragment : Fragment(), PagedTextViewInterface {
} }
} }
fun isbooktoki() : Boolean {
return webcontentsName.contains("booktoki")
}
override fun onSwipeLeft(count: Int) { override fun onSwipeLeft(count: Int) {
if (!enableGestures) return if (!enableGestures) return
Blog.LOGD(log = "onSwipeLeft ${count}") Blog.LOGD(log = "onSwipeLeft ${count}")
@ -1060,7 +1062,7 @@ class TokiFragment : Fragment(), PagedTextViewInterface {
view.visibility = VISIBLE view.visibility = VISIBLE
binding.menuWeb.visibility = GONE binding.menuWeb.visibility = GONE
} }
view.forceUpdateUI() // view.forceUpdateUI()
lastedUrl?.let { lastedUrl?.let {
Uri.parse(it)?.let { uri -> Uri.parse(it)?.let { uri ->
uri.path?.let { uri.path?.let {

View File

@ -1,336 +0,0 @@
//package bums.lunatic.launcher.home.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.net.toUri
//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.utils.Blog
//import bums.lunatic.launcher.utils.SimpleFingerGestures
//import com.yausername.youtubedl_android.YoutubeDL
//import com.yausername.youtubedl_android.YoutubeDLRequest
//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.io.File
//import java.util.Base64
//import kotlin.collections.iterator
//
//enum class JxEvent {
// SCROLL_UP,
// SCROLL_DOWN,
// SWIPE_LEFT,
// SWIPE_RIGHT,
// ON_CLICK,
//}
//typealias JxInteface = (JxEvent)->Unit
//open class BWebview : GeckoView {
// var decoViews = arrayListOf<View>()
// @SuppressLint("ClickableViewAccessibility")
// constructor(context: Context?) : super(context) {
// this.setOnTouchListener { v, event ->
// if (event.device.name?.contains(
// "JX-12",
// true
// ) == true || event.device.name?.equals("J06", true) == true
// ) {
// return@setOnTouchListener mSimpleFingerGestures.onTouch(v, event)
// } else {
// return@setOnTouchListener super.onTouchEvent(event)
// }
// }
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// val nullCursor = PointerIcon.getSystemIcon(context!!, PointerIcon.TYPE_NULL)
// this.setPointerIcon(nullCursor)
// }
// }
//
//
// @SuppressLint("ClickableViewAccessibility")
// constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
// this.setOnTouchListener { v,event ->
// if (event.device.name?.contains("JX-12",true) == true|| event.device.name?.equals("J06",true) == true) {
// return@setOnTouchListener mSimpleFingerGestures.onTouch(v,event)
// } else {
// return@setOnTouchListener super.onTouchEvent(event)
// }
// }
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// val nullCursor = PointerIcon.getSystemIcon(context!!, PointerIcon.TYPE_NULL)
// 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)
// }
// }
//
// class GKCookie {
// var COOKIES : String? = null
// }
//
// var mGKCookie : GKCookie? = null
//
// fun checkIfDownloadable(url: String) {
// CoroutineScope(Dispatchers.Main).launch {
// runOnUiThread {
// decoViews.filter { it.id == R.id.dl_video }.firstOrNull()?.let {
// it.setOnClickListener {}
// it.visibility = GONE
// }}}
// Blog.LOGE("checkIfDownloadable ${url}")
// CoroutineScope(Dispatchers.IO).launch {
// try {
// var request = YoutubeDLRequest(url)
// (mGKCookie?.COOKIES)?.let{
// Blog.LOGE(it)
// val cookies = it.split(";")
// .map { it.trim() }
// .mapNotNull {
// val parts = it.split("=", limit = 2)
// if (parts.size == 2) parts[0] to parts[1] else null
// }
// .toMap()
// val expires = (System.currentTimeMillis() / 1000) + 3600 * 24 * 7 // 일주일 후 만료 예시
//
// val cookieFileContent = buildString {
// appendLine("# Netscape HTTP Cookie File")
// for ((name, value) in cookies) {
// appendLine(".${url.toUri().host}\tTRUE\t/\tTRUE\t$expires\t$name\t$value")
// }
// }
// val cookieFile = File(context.filesDir, "cookies.txt")
// cookieFile.writeText(cookieFileContent)
// request.addOption("--cookies", cookieFile.absolutePath)
// }
//
// val videoInfo = YoutubeDL.getInstance().getInfo(request)
// // 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){
// VISIBLE
// } else{
// GONE
// }
// }
// }
// }
//
// } catch (e: Exception) {
// e.printStackTrace()
// Blog.LOGE("checkIfDownloadable ${url} ${e}")
// CoroutineScope(Dispatchers.Main).launch {
// runOnUiThread {
// decoViews.filter { it.id == R.id.dl_video }.firstOrNull()?.let {
// it.setOnClickListener {}
// it.visibility = GONE
// }}}
// }
// }
// }
// val mSimpleFingerGestures = SimpleFingerGestures(omfgl = object : SimpleFingerGestures.OnFingerGestureListener{
//
// override fun onSwipeUp(
// targetView: View,
// fingers: Int,
// gestureDuration: Long,
// gestureDistance: Double
// ): Boolean {
// Blog.LOGE("")
// jxInteface?.invoke(JxEvent.SCROLL_UP )
// return true
// }
//
// override fun onSwipeDown(
// targetView: View,
// fingers: Int,
// gestureDuration: Long,
// gestureDistance: Double
// ): Boolean {
// Blog.LOGE("")
// jxInteface?.invoke(JxEvent.SCROLL_DOWN)
// return true
// }
//
// override fun onSwipeLeft(
// targetView: View,
// fingers: Int,
// gestureDuration: Long,
// gestureDistance: Double
// ): Boolean {
// Blog.LOGE("")
// jxInteface?.invoke(JxEvent.SWIPE_LEFT)
// return true
// }
//
// override fun onSwipeRight(
// targetView: View,
// fingers: Int,
// gestureDuration: Long,
// gestureDistance: Double
// ): Boolean {
// Blog.LOGE("")
// jxInteface?.invoke(JxEvent.SWIPE_RIGHT)
// return true
// }
//
// override fun onPinch(
// targetView: View,
// fingers: Int,
// gestureDuration: Long,
// gestureDistance: Double
// ): Boolean {
// Blog.LOGE("onPinch")
// return true
// }
//
// override fun onUnpinch(
// targetView: View,
// fingers: Int,
// gestureDuration: Long,
// gestureDistance: Double
// ): Boolean {
// Blog.LOGE("")
// return true
// }
//
// override fun onDoubleTap(
// targetView: View,
// fingers: Int
// ): Boolean {
// Blog.LOGE("")
// return true
// }
//
// override fun onLongPress(
// targetView: View,
// fingers: Int
// ): Boolean {
// Blog.LOGE("onLongPress")
// return true
// }
//
// override fun onClick(
// targetView: View,
// fingers: Int
// ): Boolean {
// Blog.LOGE("onClick")
// jxInteface?.invoke(JxEvent.ON_CLICK)
// return true
// }
//
//
// })
// companion object {
// var currentRetryCount = 0
// }
//
// var jxInteface : JxInteface? = null
//
// var lastDomain : String = ""
//
// open fun loadUrl(url: String, param : String? = null) {
// var nUrl = url
// Blog.LOGE("url >>>> ${url}")
// if (url.endsWith("=")) {
// nUrl = String(Base64.getMimeDecoder().decode(url.toByteArray()))
// param?.let {
// nUrl = nUrl.plus(param)
// }
// } else if (url.startsWith("http") == false) {
// nUrl = lastDomain
// }
// if (this.isVisible == false) {
// this.visibility = VISIBLE
// }
// Blog.LOGE("nUrl >>>> ${nUrl}")
//
//
// nUrl?.let { url ->
// if (url.split("//").size > 1) {
// url.replace("//","/").replace("https:/","https://").let {
// Blog.LOGE("url >> ${url} , it >>> ${it}")
// this.session?.loadUri(it)
// }
// } else {
// this.session?.loadUri(url)
// }
// }
// currentRetryCount = 0;
// }
// private var lastX = 0f
// private var lastY = 0f
//
//
// override fun onTouchEvent(event: MotionEvent): Boolean {
// Blog.LOGE("event.device.name >>> ${event.device.name}")
// if (event.device.name?.contains("JX-12",true) == true || event.device.name?.equals("J06",true) == true) {
// Blog.LOGE("BWebview onTouchEvent $event")
// when (event.action) {
// MotionEvent.ACTION_DOWN -> {
// lastX = event.x
// lastY = event.y
// return true
// }
//
// MotionEvent.ACTION_MOVE -> {
// val deltaX = event.x - lastX
// val deltaY = event.y - lastY
// // 상하 이동이 더 크면(즉, 거의 수직 이동이면)만 처리
// if (Math.abs(deltaY) > Math.abs(deltaX)) {
// // 원하는 감도 적용
// val scrollFactor = 0.1f
//// scrollBy(0, (-deltaY * scrollFactor).toInt())
//
// jxInteface?.invoke(if ((-deltaY * scrollFactor).toInt() > 0){
// JxEvent.SCROLL_DOWN
// } else {
// JxEvent.SCROLL_UP
// })
// lastY = event.y
// lastX = event.x
// Blog.LOGE("return true for scroll")
// return true
// }
// // 좌우 이동은 무시
// lastY = event.y
// lastX = event.x
// Blog.LOGE("return false")
// return false
// }
//
// else -> {
// Blog.LOGE("call super")
// return super.onTouchEvent(event)
// }
// }
// } else {
// return super.onTouchEvent(event)
// }
// }
//}

View File

@ -3,11 +3,13 @@ package bums.lunatic.launcher.home.tokiz.view
import android.annotation.TargetApi import android.annotation.TargetApi
import android.content.Context import android.content.Context
import android.graphics.Color import android.graphics.Color
import android.graphics.Paint
import android.graphics.Typeface import android.graphics.Typeface
import android.os.Build import android.os.Build
import android.os.Handler import android.os.Handler
import android.text.Layout import android.text.Layout
import android.text.StaticLayout import android.text.StaticLayout
import android.text.TextPaint
import android.util.AttributeSet import android.util.AttributeSet
import android.util.TypedValue import android.util.TypedValue
import android.view.View import android.view.View
@ -19,8 +21,10 @@ import bums.lunatic.launcher.R
import bums.lunatic.launcher.home.tokiz.TouchArea import bums.lunatic.launcher.home.tokiz.TouchArea
import bums.lunatic.launcher.utils.Blog import bums.lunatic.launcher.utils.Blog
import bums.lunatic.launcher.utils.SimpleFingerGestures import bums.lunatic.launcher.utils.SimpleFingerGestures
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.math.min import kotlin.math.min
@ -33,248 +37,6 @@ interface PagedTextViewInterface {
fun onSwipeUp(touchCount : Int) fun onSwipeUp(touchCount : Int)
fun onLongClick() fun onLongClick()
} }
class PagedTextView : AppCompatTextView {
private var needPaginate = false
private var isPaginating = false
private val pageList = arrayListOf<CharSequence>()
private var pageIndex: Int = 0
private var pageHeight: Int = 0
private var originalText: CharSequence = ""
var mPagedTextGenerateInterface : PagedTextGenerateInterface? = null
constructor(context: Context?) : super(context!!){initView(context)}
constructor(context: Context?, attrs: AttributeSet?) : super(context!!, attrs){initView(context)}
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context!!, attrs, defStyleAttr){initView(context)}
fun initView(context: Context?){
}
fun size(): Int = pageList.size
fun current() : Int = pageIndex
fun doPrev() {
if (pageIndex > 0 )
pageIndex = pageIndex - 1
setPageText()
}
fun doNext() {
if (pageIndex < pageList.size)
pageIndex = pageIndex + 1
setPageText()
}
fun next(index: Int) {
pageIndex = index
setPageText()
}
private fun setPageText() {
if(pageList.size > 0) {
isPaginating = true
text = pageList[pageIndex]
isPaginating = false
}
}
fun setTxtF(text: CharSequence?) {
needPaginate = true
this.setText(text , null)
}
override fun setText(text: CharSequence?, type: BufferType?) {
if (!isPaginating) {
needPaginate = true
originalText = text ?: ""
}
super.setText(text, type)
}
override fun setTextSize(size: Float) {
setTextSize(TypedValue.COMPLEX_UNIT_SP, size)
}
override fun setTextSize(unit: Int, size: Float) {
super.setTextSize(unit, size)
paint.textSize = TypedValue.applyDimension(unit, size, context.resources.getDisplayMetrics())
needPaginate = true
}
override fun setPadding(left: Int, top: Int, right: Int, bottom: Int) {
super.setPadding(left, top, right, bottom)
needPaginate = true
}
override fun setPaddingRelative(start: Int, top: Int, end: Int, bottom: Int) {
super.setPaddingRelative(start, top, end, bottom)
needPaginate = true
}
override fun setTextScaleX(size: Float) {
if (size != textScaleX) {
needPaginate = true
}
super.setTextScaleX(size)
}
override fun setTypeface(tf: Typeface?) {
if (typeface != null && tf != typeface) {
needPaginate = true
paint.typeface = tf
}
super.setTypeface(tf)
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
override fun setLetterSpacing(letterSpacing: Float) {
if (letterSpacing != this.letterSpacing) {
needPaginate = true
}
super.setLetterSpacing(letterSpacing)
}
override fun setHorizontallyScrolling(whether: Boolean) {
super.setHorizontallyScrolling(false)
}
override fun setLineSpacing(add: Float, mult: Float) {
if (add != lineSpacingExtra || mult != lineSpacingMultiplier) {
needPaginate = true
}
super.setLineSpacing(add, mult)
}
override fun setMaxLines(maxLines: Int) {
if (maxLines != this.maxLines) {
needPaginate = true
}
super.setMaxLines(maxLines)
}
override fun setLines(lines: Int) {
super.setLines(lines)
if (lines != this.lineCount) {
needPaginate = true
}
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
pageHeight = h - (paddingTop + paddingBottom) // 마진 제외
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
Blog.LOGD(log = "onLayout>> ${this::class.java.name} changed >> ${changed}")
if (changed || needPaginate) {
paginate()
setPageText()
needPaginate = false
}
}
fun doUpdate() {
if (needPaginate && layout != null) {
paginate()
setPageText()
needPaginate = false
}
}
private fun paginate() {
if (layout != null) {
MainScope().launch {
pageList.clear()
// Blog.LOGD(log = "paginate>> ${this::class.java.name} && ${layout.text}")
val layout = from(layout)
val lines = if(min(maxLines, layout.lineCount) > 10) {min(maxLines, layout.lineCount) - 1} else {min(maxLines, layout.lineCount)}
var startOffset = 0
val heightWithoutPaddings = pageHeight //- (marginTop + marginBottom + paddingTop + paddingBottom)
var height = heightWithoutPaddings
for (i in 0 until lines) {
if (height < layout.getLineBottom(i)) {
pageList.add(
layout.text.subSequence(startOffset, layout.getLineStart(i))
)
startOffset = layout.getLineStart(i)
height = layout.getLineTop(i) + heightWithoutPaddings
}
if (i == lines - 1) {
pageList.add(
if(layout.lineCount > i) {
layout.text.subSequence(startOffset, layout.getLineEnd(i + 1))
} else {
layout.text.subSequence(startOffset, layout.getLineEnd(i))
}
)
}
}
mPagedTextGenerateInterface?.completePagination(pageList)
}
}
}
private fun from(layout: Layout): Layout =
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
@Suppress("DEPRECATION")
StaticLayout(
originalText,
paint,
layout.width - paddingLeft - paddingRight, // margin 제외
layout.alignment,
lineSpacingMultiplier,
lineSpacingExtra,
includeFontPadding
)
} else {
StaticLayout.Builder
.obtain(
originalText, 0, originalText.length, paint,
(layout.width - paddingLeft - paddingRight)
)
.setAlignment(layout.alignment)
.setLineSpacing(lineSpacingExtra, lineSpacingMultiplier)
.setIncludePad(includeFontPadding)
.setUseLineSpacingFromFallbacks()
.setBreakStrategy(breakStrategy)
.setHyphenationFrequency(hyphenationFrequency)
.setMaxLines(maxLines)
.build()
}
private fun StaticLayout.Builder.setUseLineSpacingFromFallbacks(): StaticLayout.Builder {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
this.setUseLineSpacingFromFallbacks(isFallbackLineSpacing)
}
return this
}
private fun StaticLayout.Builder.setJustificationMode(): StaticLayout.Builder {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
this.setJustificationMode(justificationMode)
}
return this
}
}
interface PagedTextGenerateInterface { interface PagedTextGenerateInterface {
fun completePagination(pageList: ArrayList<CharSequence>) fun completePagination(pageList: ArrayList<CharSequence>)
@ -297,26 +59,95 @@ class PagedTextLayout : ConstraintLayout , PagedTextGenerateInterface {
var mainTextView : TextView? = null var mainTextView : TextView? = null
var sencondTextView : TextView? = null var sencondTextView : TextView? = null
var demp : View? = null var demp : View? = null
var hiddenTextView : PagedTextView? = null // var hiddenTextView : PagedTextView? = null
var guideLine : Guideline? = null var guideLine : Guideline? = null
var pageList: ArrayList<CharSequence>? = null var pageList = mutableListOf<CharSequence>()
var summaryText : String = "" var summaryText : String = ""
var text : String = "" var text : String = ""
set(new) { set(new) {
// Blog.LOGE("field >> ${field}")
// Blog.LOGE("new >> ${new}")
field = new field = new
val summary = new.replace(" " ,"").replace("\n" ,"").substring(0,Math.min(30,new.length)) val summary = new.replace(" " ,"").replace("\n" ,"").substring(0,Math.min(30,new.length))
if (summary.equals(summaryText)) { if (summary.equals(summaryText) && summaryText.length > 100) {
} else { } else {
// Blog.LOGE("field >> ${field}") if (field.length > 0) {
hiddenTextView?.setTxtF(field) post {
hiddenTextView?.visibility = VISIBLE if (width > 0 && height > 0) {
MainScope().launch {
pageList.clear()
val contentWidth =
mainTextView!!.width - (mainTextView!!.paddingLeft + mainTextView!!.paddingRight)
val contentHeight =
mainTextView!!.height - (mainTextView!!.paddingTop + mainTextView!!.paddingBottom)
val pages = paginateAsync(text, contentWidth, contentHeight)
pageList.addAll(pages)
Blog.LOGE("pages >>> ${pages.size}")
setPageBy(0)
}
}
}
}
} }
summaryText = summary summaryText = summary
} }
suspend fun paginateAsync(text: CharSequence, width: Int, height: Int): List<CharSequence> = withContext(Dispatchers.Default) {
val resultPages = ArrayList<CharSequence>()
val paint = mainTextView?.paint ?: TextPaint()
val lineSpacingMult = mainTextView?.lineSpacingMultiplier ?: 1.0f
val lineSpacingAdd = mainTextView?.lineSpacingExtra ?: 1.0f
val letterSpacing = mainTextView?.letterSpacing ?: 1.0f
val includePad = false
Blog.LOGE("text ${text.length}, width $width, height $height")
// 1. 전체 텍스트에 대한 StaticLayout 생성 (API 버전에 따른 분기 처리 권장)
val layout = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
StaticLayout.Builder.obtain(text, 0, text.length, paint, width)
.setAlignment(Layout.Alignment.ALIGN_NORMAL)
.setLineSpacing(lineSpacingAdd, lineSpacingMult)
.setIncludePad(includePad)
.build()
} else {
@Suppress("DEPRECATION")
StaticLayout(text, paint, width, Layout.Alignment.ALIGN_NORMAL, lineSpacingMult, lineSpacingAdd, includePad)
}
val totalLines = layout.lineCount
var startLine = 0
var startOffset = 0
// 2. 라인 정보를 순회하며 높이 계산
while (startLine < totalLines) {
// 현재 페이지의 시작 y 좌표
val pageTopY = layout.getLineTop(startLine)
// 현재 페이지가 끝날 수 있는 최대 y 좌표
val targetBottomY = pageTopY + height
// 이 페이지에 들어갈 수 있는 마지막 라인 찾기
var endLine = layout.getLineForVertical(targetBottomY)
// getLineForVertical은 정확한 좌표가 없으면 마지막 라인을 반환하므로 검증 필요
if (layout.getLineBottom(endLine) > targetBottomY) {
endLine--
}
// 한 줄도 못 들어가는 경우(폰트가 뷰보다 큰 경우 등) 방어 코드
if (endLine < startLine) endLine = startLine
// 페이지 범위의 텍스트 추출
// endLine의 끝 오프셋을 가져옴
val endOffset = layout.getLineEnd(endLine)
// 텍스트 자르기 (subSequence는 메모리 복사를 최소화함)
if (startOffset < endOffset) {
resultPages.add(text.subSequence(startOffset, endOffset))
}
// 다음 페이지 준비
startOffset = endOffset
startLine = endLine + 1
}
return@withContext resultPages
}
private val hanler = Handler() private val hanler = Handler()
var mPagedTextViewInterface : PagedTextViewInterface? = null var mPagedTextViewInterface : PagedTextViewInterface? = null
val touchTimeover = Runnable { val touchTimeover = Runnable {
@ -328,11 +159,8 @@ class PagedTextLayout : ConstraintLayout , PagedTextGenerateInterface {
inflate(context, R.layout.layout_textviewer, this) inflate(context, R.layout.layout_textviewer, this)
mainTextView = findViewById(R.id.first_view) mainTextView = findViewById(R.id.first_view)
sencondTextView = findViewById(R.id.sencond_view) sencondTextView = findViewById(R.id.sencond_view)
demp = findViewById(R.id.demp)
currentPageTextView = findViewById(R.id.current_page) currentPageTextView = findViewById(R.id.current_page)
hiddenTextView = findViewById(R.id.hidden_view)
hiddenTextView?.mPagedTextGenerateInterface = this
currentPageTextView?.text = "" currentPageTextView?.text = ""
hanler.removeCallbacks(touchTimeover) hanler.removeCallbacks(touchTimeover)
setOnLongClickListener { v -> setOnLongClickListener { v ->
@ -450,8 +278,7 @@ class PagedTextLayout : ConstraintLayout , PagedTextGenerateInterface {
super.onLayout(changed, left, top, right, bottom) super.onLayout(changed, left, top, right, bottom)
Blog.LOGD(log = "onLayout>> ${this::class.java.name} changed >> ${changed}") Blog.LOGD(log = "onLayout>> ${this::class.java.name} changed >> ${changed}")
if(changed) { if(changed) {
hiddenTextView?.text = text // forceUpdateUI()
forceUpdateUI()
} }
} }
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
@ -470,14 +297,12 @@ class PagedTextLayout : ConstraintLayout , PagedTextGenerateInterface {
var currentPage = 0 var currentPage = 0
override fun completePagination(pageList: ArrayList<CharSequence>) { override fun completePagination(pageList: ArrayList<CharSequence>) {
// Blog.LOGD(log = "completePagination>> ${this::class.java.name} >> pageList ${pageList}")
if(text.length > 0 && pageList!= null && pageList.size == 0) { if(text.length > 0 && pageList!= null && pageList.size == 0) {
} else { } else {
this.pageList = pageList this.pageList = pageList
setPageBy(0) setPageBy(0)
} }
hiddenTextView?.visibility = GONE
} }
var defaultAlpha = 171 var defaultAlpha = 171
fun setColorStyle(colors : Array<String>) { fun setColorStyle(colors : Array<String>) {
@ -490,11 +315,7 @@ class PagedTextLayout : ConstraintLayout , PagedTextGenerateInterface {
sencondTextView?.setTextColor(Color.parseColor("#FFFFFF")) sencondTextView?.setTextColor(Color.parseColor("#FFFFFF"))
} }
// fun setPagedTextViewInterface(pagedTextViewInterface: PagedTextViewInterface) = hiddenTextView?.setPagedTextViewInterface(pagedTextViewInterface)
// fun setText(replace: String) = hiddenTextView?.setText(replace)
fun setTextSize(fl: Float) { fun setTextSize(fl: Float) {
hiddenTextView?.setTextSize(fl)
mainTextView?.setTextSize(fl) mainTextView?.setTextSize(fl)
sencondTextView?.setTextSize(fl) sencondTextView?.setTextSize(fl)
} }
@ -508,22 +329,38 @@ class PagedTextLayout : ConstraintLayout , PagedTextGenerateInterface {
} }
fun setPageBy(num : Int) { fun setPageBy(num : Int) {
this@PagedTextLayout.currentPage = num if (pageList.size > 0) {
var realPage = if(isDualPage()) this@PagedTextLayout.currentPage * 2 else this@PagedTextLayout.currentPage this@PagedTextLayout.currentPage = num
Blog.LOGE("realPage = if(${pageList?.size} ?: 0 > ${realPage}) { realPage} else { ${(pageList?.size ?: 0) - 1 }}") var realPage = if (isDualPage()) this@PagedTextLayout.currentPage * 2 else this@PagedTextLayout.currentPage
realPage = if(pageList?.size ?: 0 > realPage) { realPage} else { (pageList?.size ?: 0) - 1 } Blog.LOGE("realPage = if(${pageList?.size} ?: 0 > ${realPage}) { realPage} else { ${(pageList?.size ?: 0) - 1}}")
currentPageTextView?.text = "${realPage + 1 }/${ pageList?.size ?: 0 + 1}" realPage = if (pageList?.size ?: 0 > realPage) {
realPage
} else {
(pageList?.size ?: 0) - 1
}
currentPageTextView?.text = "${realPage + 1}/${pageList?.size ?: 0 + 1}"
mainTextView?.text = pageList?.get(realPage) ?: "NONE" mainTextView?.text = pageList?.get(realPage) ?: "NONE"
if(isDualPage()) { Blog.LOGE("pageList.get($realPage) ${pageList?.get(realPage)}")
realPage = realPage.inc() if (isDualPage()) {
sencondTextView?.text = if(pageList?.size ?: 0 > realPage) { pageList?.get(realPage)} else { ""} realPage = realPage.inc()
} else { sencondTextView?.text = if (pageList?.size ?: 0 > realPage) {
sencondTextView?.text = "" pageList?.get(realPage)
} else {
""
}
} else {
sencondTextView?.text = ""
}
} }
} }
fun size(): Int = if(isDualPage()) Math.round((hiddenTextView?.size() ?:0) * 0.5f) else hiddenTextView?.size() ?: 0 fun size(): Int = (if(isDualPage()) {
Math.round(pageList.size * 0.5f)
}else {
pageList.size
})
fun getFastPageCount() = if(isDualPage()) 3 else 6 fun getFastPageCount() = if(isDualPage()) 3 else 6
fun current(): Int = this@PagedTextLayout.currentPage fun current(): Int = this@PagedTextLayout.currentPage
fun doNext(fast : Boolean = false) { fun doNext(fast : Boolean = false) {
@ -547,33 +384,26 @@ class PagedTextLayout : ConstraintLayout , PagedTextGenerateInterface {
} }
} }
fun forceUpdateUI() {
hiddenTextView?.doUpdate()
}
override fun setPadding(left: Int, top: Int, right: Int, bottom: Int) { override fun setPadding(left: Int, top: Int, right: Int, bottom: Int) {
hiddenTextView?.setPadding(left,top,right, bottom)
mainTextView?.setPadding(left,top,right, bottom) mainTextView?.setPadding(left,top,right, bottom)
sencondTextView?.setPadding(left,top,right, bottom) sencondTextView?.setPadding(left,top,right, bottom)
} }
fun setTypeface(tf: Typeface?) { fun setTypeface(tf: Typeface?) {
hiddenTextView?.setTypeface(tf)
mainTextView?.setTypeface(tf) mainTextView?.setTypeface(tf)
sencondTextView?.setTypeface(tf) sencondTextView?.setTypeface(tf)
} }
@TargetApi(Build.VERSION_CODES.LOLLIPOP) @TargetApi(Build.VERSION_CODES.LOLLIPOP)
fun setLetterSpacing(letterSpacing: Float) { fun setLetterSpacing(letterSpacing: Float) {
hiddenTextView?.letterSpacing = letterSpacing.times(0.01f)
mainTextView?.letterSpacing = letterSpacing.times(0.01f) mainTextView?.letterSpacing = letterSpacing.times(0.01f)
sencondTextView?.letterSpacing = letterSpacing.times(0.01f) sencondTextView?.letterSpacing = letterSpacing.times(0.01f)
} }
fun setLineSpacing(mult: Float) { fun setLineSpacing(mult: Float) {
hiddenTextView?.setLineSpacing(1f, 1f.plus(mult.times(0.01f)))
mainTextView?.setLineSpacing(1f, 1f.plus(mult.times(0.01f))) mainTextView?.setLineSpacing(1f, 1f.plus(mult.times(0.01f)))
sencondTextView?.setLineSpacing(1f, 1f.plus(mult.times(0.01f))) sencondTextView?.setLineSpacing(1f, 1f.plus(mult.times(0.01f)))
} }

View File

@ -6,32 +6,6 @@
android:layout_margin="5dp" android:layout_margin="5dp"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:layout_margin="5dp"
android:layout_alignParentStart="true"
android:layout_alignParentEnd="true"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<bums.lunatic.launcher.home.tokiz.view.PagedTextView
android:layout_margin="5dp"
android:id="@+id/hidden_view"
android:visibility="visible"
android:layout_width="0dp"
android:layout_weight="1"
android:includeFontPadding="false"
android:lineSpacingExtra="0dp"
style="@style/sss"
android:layout_height="match_parent"/>
<View
android:id="@+id/demp"
android:visibility="invisible"
android:layout_margin="5dp"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent"/>
</LinearLayout>
<LinearLayout <LinearLayout
android:layout_margin="5dp" android:layout_margin="5dp"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
@ -55,7 +29,7 @@
android:gravity="start" android:gravity="start"
android:includeFontPadding="false" android:includeFontPadding="false"
android:lineSpacingExtra="0dp" android:lineSpacingExtra="0dp"
android:visibility="invisible" android:visibility="gone"
android:id="@+id/sencond_view" android:id="@+id/sencond_view"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_weight="1" android:layout_weight="1"