Skip to content

Commit b4d213f

Browse files
committed
Update
1 parent 23143d9 commit b4d213f

File tree

6 files changed

+156
-161
lines changed

6 files changed

+156
-161
lines changed
Lines changed: 130 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,31 @@
11
package com.tool.tree
22

3-
import android.content.Context
3+
import android.Manifest
44
import android.content.Intent
55
import android.content.pm.PackageManager
66
import android.content.res.Configuration
7-
import android.graphics.Color
8-
import android.net.Uri
97
import android.os.Build
108
import android.os.Bundle
11-
import android.os.Environment
12-
import android.os.Handler
13-
import android.provider.Settings
9+
import android.util.TypedValue
1410
import android.view.animation.AnimationUtils
15-
import android.widget.TextView
1611
import androidx.appcompat.app.AppCompatActivity
12+
import androidx.appcompat.app.AppCompatDelegate
1713
import androidx.core.app.ActivityCompat
1814
import androidx.core.content.ContextCompat
15+
import androidx.core.os.LocaleListCompat
1916
import androidx.core.view.WindowCompat
20-
import androidx.core.view.WindowInsetsControllerCompat
2117
import androidx.lifecycle.lifecycleScope
2218
import com.omarea.common.shell.KeepShellPublic
2319
import com.omarea.common.shell.ShellExecutor
2420
import com.omarea.common.ui.DialogHelper
2521
import com.omarea.krscript.executor.ScriptEnvironmen
2622
import com.tool.tree.databinding.ActivitySplashBinding
27-
import com.tool.tree.R
2823
import kotlinx.coroutines.Dispatchers
2924
import kotlinx.coroutines.launch
3025
import kotlinx.coroutines.withContext
3126
import java.io.BufferedReader
3227
import java.io.DataOutputStream
3328
import java.io.File
34-
import java.util.Locale
35-
import android.util.TypedValue
3629

3730
class SplashActivity : AppCompatActivity() {
3831

@@ -42,25 +35,36 @@ class SplashActivity : AppCompatActivity() {
4235
private var hasRoot = false
4336
private var started = false
4437
private var starting = false
38+
39+
private val rows = mutableListOf<String>()
40+
private var ignored = false
41+
private val maxLines = 5
4542

4643
override fun onCreate(savedInstanceState: Bundle?) {
44+
// 1. Áp dụng ngôn ngữ sớm nhất có thể
4745
applyAppLanguage()
4846
super.onCreate(savedInstanceState)
47+
4948
ThemeModeState.switchTheme(this)
5049
binding = ActivitySplashBinding.inflate(layoutInflater)
5150
setContentView(binding.root)
5251

52+
// 2. Hỗ trợ hiển thị tràn viền (Edge-to-Edge) cho các máy đời mới
53+
WindowCompat.setDecorFitsSystemWindows(window, false)
54+
5355
if (ScriptEnvironmen.isInited() && isTaskRoot &&
5456
!intent.getBooleanExtra("force_reset", false)) {
5557
gotoHome()
5658
return
5759
}
5860

59-
if (!hasAgreed()) showAgreementDialog()
61+
if (!hasAgreed()) {
62+
showAgreementDialog()
63+
}
6064

61-
binding.startLogoXml.postDelayed({
62-
binding.startLogoXml.startAnimation(AnimationUtils.loadAnimation(this, R.anim.blink))
63-
}, 2000)
65+
val imageView = findViewById<ImageView>(R.id.start_logo_xml)
66+
val rotateAnim = AnimationUtils.loadAnimation(this, R.anim.ic_settings_rotate)
67+
imageView.startAnimation(rotateAnim)
6468

6569
applyTheme()
6670
}
@@ -69,154 +73,101 @@ class SplashActivity : AppCompatActivity() {
6973
runCatching {
7074
val langFile = File(filesDir, "home/log/language")
7175
val lang = langFile.takeIf { it.exists() }?.readText()?.trim()?.takeIf { it.isNotEmpty() } ?: return
72-
val locale = Locale.forLanguageTag(lang.replace("_", "-"))
73-
if (Locale.getDefault() != locale) {
74-
Locale.setDefault(locale)
75-
val config = Configuration(resources.configuration).apply { setLocale(locale) }
76-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
77-
resources.updateConfiguration(config, resources.displayMetrics)
78-
} else {
79-
@Suppress("DEPRECATION")
80-
resources.updateConfiguration(config, resources.displayMetrics)
81-
}
82-
}
76+
77+
// Cách chuẩn hỗ trợ từ SDK thấp đến cao
78+
val appLocale: LocaleListCompat = LocaleListCompat.forLanguageTags(lang.replace("_", "-"))
79+
AppCompatDelegate.setApplicationLocales(appLocale)
8380
}
8481
}
8582

86-
private fun getThemeColor(attr: Int): Int {
87-
val typedValue = TypedValue()
88-
theme.resolveAttribute(attr, typedValue, true)
89-
90-
return if (typedValue.resourceId != 0) {
91-
getColor(typedValue.resourceId)
92-
} else {
93-
typedValue.data
94-
}
95-
}
96-
9783
private fun applyTheme() {
98-
val bgColor = getThemeColor(android.R.attr.windowBackground)
99-
84+
val typedValue = TypedValue()
85+
theme.resolveAttribute(android.R.attr.windowBackground, typedValue, true)
86+
val bgColor = if (typedValue.resourceId != 0) ContextCompat.getColor(this, typedValue.resourceId) else typedValue.data
87+
10088
window.statusBarColor = bgColor
10189
window.navigationBarColor = bgColor
10290

10391
val controller = WindowCompat.getInsetsController(window, window.decorView)
104-
10592
val isDark = ThemeModeState.isDarkMode()
10693

107-
controller?.isAppearanceLightStatusBars = !isDark
108-
controller?.isAppearanceLightNavigationBars = !isDark
94+
// WindowInsetsControllerCompat tự động xử lý các bản Android cũ hơn (dưới SDK 30)
95+
controller.isAppearanceLightStatusBars = !isDark
96+
controller.isAppearanceLightNavigationBars = !isDark
10997
}
11098

111-
// =================== AGREEMENT ===================
112-
private fun showAgreementDialog() {
113-
DialogHelper.warning(
114-
this,
115-
getString(R.string.permission_dialog_title),
116-
getString(R.string.permission_dialog_message),
117-
Runnable { requestAppPermissions() },
118-
Runnable { finish() }
119-
).setCancelable(false)
99+
// =================== PERMISSIONS (Hỗ trợ SDK 23+) ===================
100+
private fun getRequiredPermissions(): Array<String> {
101+
return when {
102+
// Android 13 (API 33) trở lên
103+
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> {
104+
arrayOf(
105+
Manifest.permission.READ_MEDIA_IMAGES,
106+
Manifest.permission.READ_MEDIA_VIDEO
107+
)
108+
}
109+
// Android 6.0 (API 23) đến Android 12
110+
else -> {
111+
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE)
112+
}
113+
}
120114
}
121115

122-
private fun hasAgreed(): Boolean =
123-
getSharedPreferences("kr-script-config", MODE_PRIVATE)
124-
.getBoolean("agreed_permissions", false)
125-
126-
private fun saveAgreement() {
127-
getSharedPreferences("kr-script-config", MODE_PRIVATE)
128-
.edit()
129-
.putBoolean("agreed_permissions", true)
130-
.apply()
116+
private fun hasPermissions(): Boolean {
117+
return getRequiredPermissions().all {
118+
ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED
119+
}
131120
}
132121

133-
// =================== PERMISSION ===================
134-
private fun hasReadPermission() = ContextCompat.checkSelfPermission(this, android.Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
135-
136-
private fun requestReadPermission() = ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.READ_EXTERNAL_STORAGE), REQUEST_CODE_PERMISSIONS)
137-
138122
private fun requestAppPermissions() {
139123
saveAgreement()
140-
if (hasReadPermission()) {
141-
started = true
142-
checkRootAndStart()
143-
} else requestReadPermission()
144-
}
145-
146-
override fun onResume() {
147-
super.onResume()
148-
if (hasAgreed() && !started) {
124+
if (hasPermissions()) {
149125
started = true
150126
checkRootAndStart()
127+
} else {
128+
ActivityCompat.requestPermissions(this, getRequiredPermissions(), REQUEST_CODE_PERMISSIONS)
151129
}
152130
}
153131

154132
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
133+
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
155134
if (requestCode == REQUEST_CODE_PERMISSIONS) {
156-
if (grantResults.all { it == PackageManager.PERMISSION_GRANTED }) {
135+
// Kiểm tra xem tất cả quyền được cấp chưa
136+
if (grantResults.isNotEmpty() && grantResults.all { it == PackageManager.PERMISSION_GRANTED }) {
157137
started = true
158138
checkRootAndStart()
159-
} else finish()
160-
}
161-
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
162-
}
163-
164-
// =================== ROOT ===================
165-
@Synchronized
166-
private fun checkRootAndStart() {
167-
if (!started || starting) return
168-
starting = true
169-
170-
lifecycleScope.launch(Dispatchers.IO) {
171-
hasRoot = KeepShellPublic.checkRoot()
172-
withContext(Dispatchers.Main) {
173-
starting = false
174-
startToFinish()
139+
} else {
140+
finish() // Hoặc thông báo yêu cầu quyền
175141
}
176142
}
177143
}
178144

179-
private fun startToFinish() {
180-
binding.startStateText.text = getString(R.string.pop_started)
181-
val config = KrScriptConfig().init(this)
182-
183-
if (config.beforeStartSh.isNotEmpty()) {
184-
runBeforeStartSh(config, hasRoot)
185-
} else gotoHome()
186-
}
187-
188-
private fun gotoHome() {
189-
startActivity(
190-
if (intent?.getBooleanExtra("JumpActionPage", false) == true)
191-
Intent(this, ActionPage::class.java).apply { putExtras(intent!!) }
192-
else Intent(this, MainActivity::class.java)
193-
)
194-
finish()
195-
}
196-
145+
// =================== SHELL & LOGS (Tối ưu Coroutines) ===================
197146
private fun runBeforeStartSh(config: KrScriptConfig, hasRoot: Boolean) {
198147
lifecycleScope.launch(Dispatchers.IO) {
199148
try {
200-
val process = if (hasRoot)
201-
ShellExecutor.getSuperUserRuntime()
202-
else
203-
ShellExecutor.getRuntime()
204-
process?.let {
205-
DataOutputStream(it.outputStream).use { os ->
206-
ScriptEnvironmen.executeShell(
207-
this@SplashActivity,
208-
os,
209-
config.beforeStartSh,
210-
config.variables,
211-
null,
212-
"pio-splash"
213-
)
149+
val process = if (hasRoot) ShellExecutor.getSuperUserRuntime() else ShellExecutor.getRuntime()
150+
process?.let { p ->
151+
// Thực thi lệnh trong một job riêng
152+
val shellJob = launch(Dispatchers.IO) {
153+
DataOutputStream(p.outputStream).use { os ->
154+
ScriptEnvironmen.executeShell(
155+
this@SplashActivity, os, config.beforeStartSh, config.variables, null, "pio-splash"
156+
)
157+
}
214158
}
215-
launch { readStreamAsync(it.inputStream.bufferedReader()) }
216-
launch { readStreamAsync(it.errorStream.bufferedReader()) }
217-
218-
it.waitFor()
159+
160+
// Đọc log đồng thời (hỗ trợ SDK 23+)
161+
val outJob = launch { readStreamAsync(p.inputStream.bufferedReader()) }
162+
val errJob = launch { readStreamAsync(p.errorStream.bufferedReader()) }
163+
164+
p.waitFor()
165+
shellJob.join()
166+
outJob.join()
167+
errJob.join()
219168
}
169+
} catch (e: Exception) {
170+
e.printStackTrace()
220171
} finally {
221172
withContext(Dispatchers.Main) {
222173
gotoHome()
@@ -225,30 +176,62 @@ class SplashActivity : AppCompatActivity() {
225176
}
226177
}
227178

228-
// Buffer lưu 4 dòng cuối
229-
private val rows = mutableListOf<String>()
230-
private var ignored = false
231-
private val maxLines = 5
232-
private val handler = android.os.Handler(android.os.Looper.getMainLooper())
233-
234-
private fun readStreamAsync(reader: BufferedReader) {
235-
Thread {
236-
reader.forEachLine { line ->
237-
onLogOutput(line)
179+
private suspend fun readStreamAsync(reader: BufferedReader) {
180+
withContext(Dispatchers.IO) {
181+
reader.useLines { lines ->
182+
lines.forEach { line ->
183+
withContext(Dispatchers.Main) {
184+
onLogOutput(line)
185+
}
186+
}
238187
}
239-
}.start()
188+
}
240189
}
241190

242191
private fun onLogOutput(log: String) {
243-
handler.post {
244-
synchronized(rows) {
245-
if (rows.size >= maxLines) {
246-
rows.removeAt(0)
247-
ignored = true
248-
}
249-
rows.add(log)
250-
binding.startStateText.text = rows.joinToString("\n", if (ignored) "……\n" else "")
192+
synchronized(rows) {
193+
if (rows.size >= maxLines) {
194+
rows.removeAt(0)
195+
ignored = true
251196
}
197+
rows.add(log)
198+
binding.startStateText.text = rows.joinToString("\n", if (ignored) "……\n" else "")
252199
}
253200
}
254-
}
201+
202+
// Các hàm phụ trợ khác giữ nguyên logic của bạn...
203+
private fun showAgreementDialog() {
204+
DialogHelper.warning(this, getString(R.string.permission_dialog_title), getString(R.string.permission_dialog_message), { requestAppPermissions() }, { finish() }).setCancelable(false)
205+
}
206+
207+
private fun hasAgreed() = getSharedPreferences("kr-script-config", MODE_PRIVATE).getBoolean("agreed_permissions", false)
208+
209+
private fun saveAgreement() = getSharedPreferences("kr-script-config", MODE_PRIVATE).edit().putBoolean("agreed_permissions", true).apply()
210+
211+
@Synchronized
212+
private fun checkRootAndStart() {
213+
if (!started || starting) return
214+
starting = true
215+
lifecycleScope.launch(Dispatchers.IO) {
216+
hasRoot = KeepShellPublic.checkRoot()
217+
withContext(Dispatchers.Main) {
218+
starting = false
219+
startToFinish()
220+
}
221+
}
222+
}
223+
224+
private fun startToFinish() {
225+
binding.startStateText.text = getString(R.string.pop_started)
226+
val config = KrScriptConfig().init(this)
227+
if (config.beforeStartSh.isNotEmpty()) runBeforeStartSh(config, hasRoot) else gotoHome()
228+
}
229+
230+
private fun gotoHome() {
231+
val nextIntent = if (intent?.getBooleanExtra("JumpActionPage", false) == true) {
232+
Intent(this, ActionPage::class.java).apply { intent?.extras?.let { putExtras(it) } }
233+
} else Intent(this, MainActivity::class.java)
234+
startActivity(nextIntent)
235+
finish()
236+
}
237+
}

app/src/main/res/anim/blink.xml

Lines changed: 0 additions & 7 deletions
This file was deleted.

0 commit comments

Comments
 (0)