diff --git a/app/build.gradle b/app/build.gradle
index f0957b0..54cddc6 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -9,11 +9,11 @@ android {
defaultConfig {
applicationId "com.sealdice.dice"
- minSdk 26
+ minSdk 23
//noinspection ExpiredTargetSdkVersion
targetSdk 28
- versionCode 27
- versionName "v0.4.7-rc2"
+ versionCode 28
+ versionName "v0.4.8-rc"
buildConfigField "String", "DOCUMENTS_AUTHORITY", "\"com.sealdice.dice.authorities\""
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
}
@@ -48,10 +48,10 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.8.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
- implementation 'androidx.navigation:navigation-fragment-ktx:2.6.0-alpha08'
- implementation 'androidx.navigation:navigation-ui-ktx:2.6.0-alpha08'
+ implementation 'androidx.navigation:navigation-fragment-ktx:2.6.0-alpha09'
+ implementation 'androidx.navigation:navigation-ui-ktx:2.6.0-alpha09'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
- implementation 'androidx.core:core:1.9.0'
+ implementation 'androidx.core:core:1.10.0'
implementation 'androidx.preference:preference:1.2.0'
implementation 'androidx.lifecycle:lifecycle-service:2.6.1'
implementation 'com.squareup.okhttp3:okhttp:4.9.3'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 72761c1..28064e6 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -49,7 +49,9 @@
-
+
diff --git a/app/src/main/java/com/sealdice/dice/DimWakeLockService.kt b/app/src/main/java/com/sealdice/dice/DimWakeLockService.kt
new file mode 100644
index 0000000..4789c17
--- /dev/null
+++ b/app/src/main/java/com/sealdice/dice/DimWakeLockService.kt
@@ -0,0 +1,19 @@
+package com.sealdice.dice
+
+import android.app.Service
+import android.content.Context
+import android.content.Intent
+import android.os.IBinder
+import android.os.PowerManager
+
+class DimWakeLockService : Service(){
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
+ val wakeLock = powerManager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "DIM:LocationManagerService")
+ wakeLock.acquire()
+ return START_STICKY
+ }
+ override fun onBind(intent: Intent?): IBinder? {
+ return null
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/sealdice/dice/FirstFragment.kt b/app/src/main/java/com/sealdice/dice/FirstFragment.kt
index 9942a51..c8d898c 100644
--- a/app/src/main/java/com/sealdice/dice/FirstFragment.kt
+++ b/app/src/main/java/com/sealdice/dice/FirstFragment.kt
@@ -1,14 +1,14 @@
package com.sealdice.dice
import android.Manifest
-import android.content.Context
-import android.content.DialogInterface
-import android.content.Intent
+import android.content.*
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.net.Uri
import android.os.Build
import android.os.Bundle
+import android.os.IBinder
+import android.provider.Settings
import android.util.Log
import android.view.*
import android.widget.Toast
@@ -25,7 +25,9 @@ import com.sealdice.dice.databinding.FragmentFirstBinding
import com.sealdice.dice.utils.Utils
import com.sealdice.dice.utils.ViewModelMain
import kotlinx.coroutines.*
+import java.io.BufferedReader
import java.io.File
+import java.io.InputStreamReader
import kotlin.system.exitProcess
@@ -40,6 +42,20 @@ class FirstFragment : Fragment() {
// onDestroyView.
private val binding get() = _binding
private var shellLogs = ""
+ private var isBound = false
+ private var processService: ProcessService? = null
+
+ private val connection = object : ServiceConnection {
+ override fun onServiceConnected(name: ComponentName, service: IBinder) {
+ val binder = service as ProcessService.MyBinder
+ processService = binder.getService()
+ isBound = true
+ }
+
+ override fun onServiceDisconnected(name: ComponentName) {
+ isBound = false
+ }
+ }
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
@@ -53,9 +69,6 @@ class FirstFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
var isrun = false
-// val packageManager = this.activity?.packageManager
-// val packageName = this.activity?.packageName
-// val packageInfo = packageName?.let { packageManager?.getPackageInfo(it, 0) }
val versionName = BuildConfig.VERSION_NAME
val packageName = BuildConfig.APPLICATION_ID
val sharedPreferences = context?.let { PreferenceManager.getDefaultSharedPreferences(it) }
@@ -94,7 +107,7 @@ class FirstFragment : Fragment() {
)
}
alertDialogBuilder?.setTitle("控制台")
- alertDialogBuilder?.setMessage(shellLogs)
+ alertDialogBuilder?.setMessage(processService?.getShellLogs())
alertDialogBuilder?.setPositiveButton("确定") { _: DialogInterface, _: Int ->
}
alertDialogBuilder?.create()?.show()
@@ -228,8 +241,22 @@ class FirstFragment : Fragment() {
alertDialogBuilder?.create()?.show()
}
binding.buttonFirst.setOnClickListener {
- if (isrun) {
- shellLogs += "sealdice is running"
+ val intentBtr = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
+ intentBtr.data = Uri.parse("package:$packageName")
+ startActivity(intentBtr)
+ if (BuildConfig.DEBUG) {
+ val alertDialogBuilder = context?.let { it1 ->
+ AlertDialog.Builder(
+ it1, R.style.Theme_Mshell_DialogOverlay
+ )
+ }
+ alertDialogBuilder?.setTitle("DEBUG")
+ alertDialogBuilder?.setMessage("Support 64 abis:"+Build.SUPPORTED_64_BIT_ABIS.contentToString()+"\nSupport 32 abis:"+Build.SUPPORTED_32_BIT_ABIS.contentToString()+"\nSupport abis:"+Build.SUPPORTED_ABIS.contentToString())
+ alertDialogBuilder?.setPositiveButton("确定") { _: DialogInterface, _: Int ->
+ }
+ alertDialogBuilder?.create()?.show()
+ }
+ if (processService?.isRunning() == true) {
val alertDialogBuilder = context?.let { it1 ->
AlertDialog.Builder(
it1, R.style.Theme_Mshell_DialogOverlay
@@ -245,8 +272,8 @@ class FirstFragment : Fragment() {
if (sharedPreferences?.getBoolean("extract_on_start", true) == true) {
ExtractAssets(context).extractResources("sealdice")
}
- val args = sharedPreferences?.getString("launch_args", "")
- execShell("cd sealdice&&./sealdice-core $args",true)
+// val args = sharedPreferences?.getString("launch_args", "")
+// execShell("cd sealdice&&./sealdice-core $args\n",true)
binding.buttonTut.visibility = View.GONE
binding.buttonInput.visibility = View.GONE
binding.buttonOutput.visibility = View.GONE
@@ -255,17 +282,34 @@ class FirstFragment : Fragment() {
binding.buttonThird.visibility = View.VISIBLE
binding.buttonConsole.visibility = View.VISIBLE
binding.buttonFirst.visibility = View.GONE
- if (!launchAliveService(context)) {
- val alertDialogBuilder = context?.let { it1 ->
- AlertDialog.Builder(
- it1, R.style.Theme_Mshell_DialogOverlay
- )
+ if (Build.VERSION.SDK_INT >= 28) {
+ val permissionState =
+ context?.let { it1 -> ContextCompat.checkSelfPermission(it1, Manifest.permission.FOREGROUND_SERVICE) }
+ if (permissionState != PackageManager.PERMISSION_GRANTED) {
+ this.activity?.let { it1 -> ActivityCompat.requestPermissions(it1, arrayOf(Manifest.permission.FOREGROUND_SERVICE), 1) }
}
- alertDialogBuilder?.setTitle("提示")
- alertDialogBuilder?.setMessage("似乎并没有开启任何保活策略,这可能导致后台被清理")
- alertDialogBuilder?.setPositiveButton("确定") { _: DialogInterface, _: Int ->}
- alertDialogBuilder?.create()?.show()
}
+ val intentNoti = Intent(context, ProcessService::class.java)
+ if (Build.VERSION.SDK_INT >= 26) {
+ context?.startForegroundService(intentNoti).also { _ ->
+ activity?.bindService(intentNoti, connection, Context.BIND_AUTO_CREATE)
+ }
+ } else {
+ context?.startService(intentNoti).also { _ ->
+ activity?.bindService(intentNoti, connection, Context.BIND_AUTO_CREATE)
+ }
+ }
+ launchAliveService(context)
+// val alertDialogBuilder = context?.let { it1 ->
+// AlertDialog.Builder(
+// it1, R.style.Theme_Mshell_DialogOverlay
+// )
+// }
+// alertDialogBuilder?.setTitle("提示")
+// alertDialogBuilder?.setMessage("似乎并没有开启任何保活策略,这可能导致后台被清理")
+// alertDialogBuilder?.setPositiveButton("确定") { _: DialogInterface, _: Int ->}
+// alertDialogBuilder?.create()?.show()
+
GlobalScope.launch(context = Dispatchers.IO) {
for (i in 0..10) {
withContext(Dispatchers.Main) {
@@ -292,12 +336,36 @@ class FirstFragment : Fragment() {
}
binding.buttonSecond.setOnClickListener {
binding.buttonSecond.visibility = View.GONE
- this.activity?.stopService(Intent(context, NotificationService::class.java))
+ activity?.unbindService(connection)
+ this.activity?.stopService(Intent(context, ProcessService::class.java))
this.activity?.stopService(Intent(context, MediaService::class.java))
this.activity?.stopService(Intent(context, WakeLockService::class.java))
this.activity?.stopService(Intent(context, FloatWindowService::class.java))
this.activity?.stopService(Intent(context, HeartbeatService::class.java))
- execShell("pkill -SIGINT sealdice-core",false)
+ this.activity?.stopService(Intent(context, UpdateService::class.java))
+ val builder: AlertDialog.Builder? = context?.let { it1 -> AlertDialog.Builder(it1) }
+ builder?.setCancelable(false) // if you want user to wait for some process to finish,
+ builder?.setView(R.layout.layout_loading_dialog)
+ val dialog = builder?.create()
+ dialog?.show()
+ GlobalScope.launch(context = Dispatchers.IO){
+ for (i in 0..5) {
+ Thread.sleep(1000)
+ }
+ withContext(Dispatchers.Main){
+ dialog?.dismiss()
+ }
+ }
+ val alertDialogBuilder = context?.let { it1 ->
+ AlertDialog.Builder(
+ it1, R.style.Theme_Mshell_DialogOverlay
+ )
+ }
+ alertDialogBuilder?.setTitle("提示")
+ alertDialogBuilder?.setMessage("请等到ui彻底无法打开后再点退出!")
+ alertDialogBuilder?.setPositiveButton("确定") { _: DialogInterface, _: Int ->
+ }
+ alertDialogBuilder?.create()?.show()
binding.buttonExit.visibility = View.VISIBLE
}
}
@@ -307,17 +375,21 @@ class FirstFragment : Fragment() {
val sharedPreferences = context?.let { PreferenceManager.getDefaultSharedPreferences(it) }
var executed = false
if (sharedPreferences != null) {
- if (sharedPreferences.getBoolean("alive_notification", true)) {
- if (Build.VERSION.SDK_INT >= 28) {
- val permissionState = context.let { it1 -> ContextCompat.checkSelfPermission(it1, Manifest.permission.FOREGROUND_SERVICE) }
- if (permissionState != PackageManager.PERMISSION_GRANTED) {
- this.activity?.let { it1 -> ActivityCompat.requestPermissions(it1, arrayOf(Manifest.permission.FOREGROUND_SERVICE), 1) }
- }
- }
- val intentNoti = Intent(context, NotificationService::class.java)
- context.startForegroundService(intentNoti)
- executed = true
- }
+// if (sharedPreferences.getBoolean("alive_notification", true)) {
+// if (Build.VERSION.SDK_INT >= 28) {
+// val permissionState = context.let { it1 -> ContextCompat.checkSelfPermission(it1, Manifest.permission.FOREGROUND_SERVICE) }
+// if (permissionState != PackageManager.PERMISSION_GRANTED) {
+// this.activity?.let { it1 -> ActivityCompat.requestPermissions(it1, arrayOf(Manifest.permission.FOREGROUND_SERVICE), 1) }
+// }
+// }
+// val intentNoti = Intent(context, NotificationService::class.java)
+// if (Build.VERSION.SDK_INT >= 26) {
+// context.startForegroundService(intentNoti)
+// } else {
+// context.startService(intentNoti)
+// }
+// executed = true
+// }
if (sharedPreferences.getBoolean("alive_media", false)) {
val intentMedia = Intent(context, MediaService::class.java)
context.startService(intentMedia)
@@ -349,37 +421,25 @@ class FirstFragment : Fragment() {
@OptIn(DelicateCoroutinesApi::class)
private fun execShell(cmd: String, recordLog: Boolean) {
GlobalScope.launch(context = Dispatchers.IO) {
- val process = Runtime.getRuntime().exec("sh")
+ val process = ProcessBuilder("sh").redirectErrorStream(true).directory(context?.filesDir?.absolutePath?.let {
+ File(
+ it
+ )
+ }).start()
val os = process.outputStream
os.write("cd ${context?.filesDir?.absolutePath}&&".toByteArray())
os.write(cmd.toByteArray())
os.flush()
os.close()
-// Thread.sleep(3000)
-// val data = process.inputStream.readBytes()
-// val error = process.errorStream.readBytes()
-// if (data.isNotEmpty()) {
-// shellLogs += String(data)
-// shellLogs += "\n"
-// } else {
-// shellLogs += String(error)
-// shellLogs += "\n"
-// }
-// Log.i("ExecShell", shellLogs)
-// withContext(Dispatchers.Main) {
-// binding.textviewFirst.text = shellLogs
-// }
+ val data = process.inputStream
+ val ir = BufferedReader(InputStreamReader(data))
while (recordLog) {
- val data = process.inputStream.readBytes()
- val error = process.errorStream.readBytes()
- if (data.isNotEmpty()) {
- shellLogs += String(data)
- shellLogs += "\n"
- } else {
- shellLogs += String(error)
+ var line = ir.readLine()
+ while (line != null) {
+ shellLogs += line
shellLogs += "\n"
+ line = ir.readLine()
}
- Log.i("ExecShell", shellLogs)
Thread.sleep(1000)
}
}
@@ -392,11 +452,6 @@ class FirstFragment : Fragment() {
private fun delete(delFile: String): Boolean {
val file = File(delFile)
return if (!file.exists()) {
-// Toast.makeText(
-// ApplicationProvider.getApplicationContext(),
-// "删除文件失败:" + delFile + "不存在!",
-// Toast.LENGTH_SHORT
-// ).show()
false
} else {
if (file.isFile) deleteSingleFile(delFile) else deleteDirectory(delFile)
@@ -418,19 +473,9 @@ class FirstFragment : Fragment() {
)
true
} else {
-// Toast.makeText(
-// ApplicationProvider.getApplicationContext(),
-// "删除单个文件" + `filePath$Name` + "失败!",
-// Toast.LENGTH_SHORT
-// ).show()
false
}
} else {
-// Toast.makeText(
-// ApplicationProvider.getApplicationContext(),
-// "删除单个文件失败:" + `filePath$Name` + "不存在!",
-// Toast.LENGTH_SHORT
-// ).show()
false
}
}
@@ -446,11 +491,6 @@ class FirstFragment : Fragment() {
val dirFile = File(filePath)
// 如果dir对应的文件不存在,或者不是一个目录,则退出
if (!dirFile.exists() || !dirFile.isDirectory) {
-// Toast.makeText(
-// ApplicationProvider.getApplicationContext(),
-// "删除目录失败:" + filePath + "不存在!",
-// Toast.LENGTH_SHORT
-// ).show()
return false
}
var flag = true
@@ -469,11 +509,6 @@ class FirstFragment : Fragment() {
}
}
if (!flag) {
-// Toast.makeText(
-// ApplicationProvider.getApplicationContext(),
-// "删除目录失败!",
-// Toast.LENGTH_SHORT
-// ).show()
return false
}
// 删除当前目录
@@ -481,11 +516,6 @@ class FirstFragment : Fragment() {
Log.e("--Method--", "Copy_Delete.deleteDirectory: 删除目录" + filePath + "成功!")
true
} else {
-// Toast.makeText(
-// ApplicationProvider.getApplicationContext(),
-// "删除目录:" + filePath + "失败!",
-// Toast.LENGTH_SHORT
-// ).show()
false
}
}
diff --git a/app/src/main/java/com/sealdice/dice/MediaService.kt b/app/src/main/java/com/sealdice/dice/MediaService.kt
index ec30f4d..d7764df 100644
--- a/app/src/main/java/com/sealdice/dice/MediaService.kt
+++ b/app/src/main/java/com/sealdice/dice/MediaService.kt
@@ -13,10 +13,10 @@ class MediaService : Service() {
// declaring object of MediaPlayer
private lateinit var player: MediaPlayer
-
// execution of service will start
// on calling this method
- override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ override fun onCreate() {
+ super.onCreate()
val base64 =
"AAAAGGZ0eXBtcDQyAAAAAG1wNDFpc29tAAAAKHV1aWRcpwj7Mo5CBahhZQ7KCpWWAAAADDEwLjAuMTgzNjMuMAAAAG5tZGF0AAAAAAAAABAnDEMgBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBAIBDSX5AAAAAAAAB9Pp9Pp9Pp9Pp9Pp9Pp9Pp9Pp9Pp9Pp9Pp9AAAC/m1vb3YAAABsbXZoZAAAAADeilCc3opQnAAAu4AAAAIRAAEAAAEAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAHBdHJhawAAAFx0a2hkAAAAAd6KUJzeilCcAAAAAgAAAAAAAAIRAAAAAAAAAAAAAAAAAQAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAABXW1kaWEAAAAgbWRoZAAAAADeilCc3opQnAAAu4AAAAIRVcQAAAAAAC1oZGxyAAAAAAAAAABzb3VuAAAAAAAAAAAAAAAAU291bmRIYW5kbGVyAAAAAQhtaW5mAAAAEHNtaGQAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAAAMxzdGJsAAAAZHN0c2QAAAAAAAAAAQAAAFRtcDRhAAAAAAAAAAEAAAAAAAAAAAACABAAAAAAu4AAAAAAADBlc2RzAAAAAAOAgIAfAAAABICAgBRAFQAGAAACM2gAAjNoBYCAgAIRkAYBAgAAABhzdHRzAAAAAAAAAAEAAAABAAACEQAAABxzdHNjAAAAAAAAAAEAAAABAAAAAQAAAAEAAAAYc3RzegAAAAAAAAAAAAAAAQAAAF4AAAAUc3RjbwAAAAAAAAABAAAAUAAAAMl1ZHRhAAAAkG1ldGEAAAAAAAAAIWhkbHIAAAAAAAAAAG1kaXIAAAAAAAAAAAAAAAAAAAAAY2lsc3QAAAAeqW5hbQAAABZkYXRhAAAAAQAAAADlvZXpn7MAAAAcqWRheQAAABRkYXRhAAAAAQAAAAAyMDIyAAAAIWFBUlQAAAAZZGF0YQAAAAEAAAAA5b2V6Z+z5py6AAAAMVh0cmEAAAApAAAAD1dNL0VuY29kaW5nVGltZQAAAAEAAAAOABUA2rD/dVfYAQ=="
@@ -37,6 +37,9 @@ class MediaService : Service() {
// value as true to play
// the audio on loop
player.isLooping = true
+ }
+
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// starting the process
player.start()
diff --git a/app/src/main/java/com/sealdice/dice/NotificationActivity.kt b/app/src/main/java/com/sealdice/dice/NotificationActivity.kt
index 99a5b7d..3146ef7 100644
--- a/app/src/main/java/com/sealdice/dice/NotificationActivity.kt
+++ b/app/src/main/java/com/sealdice/dice/NotificationActivity.kt
@@ -17,6 +17,7 @@ class NotificationActivity : AppCompatActivity() {
return when (item.itemId) {
android.R.id.home -> {
onBackPressedDispatcher.onBackPressed()
+ finish()
true
}
else -> super.onOptionsItemSelected(item)
diff --git a/app/src/main/java/com/sealdice/dice/NotificationService.kt b/app/src/main/java/com/sealdice/dice/NotificationService.kt
deleted file mode 100644
index e6d3cd3..0000000
--- a/app/src/main/java/com/sealdice/dice/NotificationService.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.sealdice.dice
-
-import android.app.*
-import android.app.PendingIntent
-import android.content.Context
-import android.content.Intent
-import android.os.IBinder
-
-
-class NotificationService : Service(){
- override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
- val ns: String = Context.NOTIFICATION_SERVICE
- val mNotificationManager = getSystemService(ns) as NotificationManager
- val notificationChannel = NotificationChannel("sealdice","SealDice", NotificationManager.IMPORTANCE_HIGH)
- mNotificationManager.createNotificationChannel(notificationChannel)
- val pendingIntent = PendingIntent.getActivity(applicationContext, 0, Intent(applicationContext, NotificationActivity::class.java), PendingIntent.FLAG_MUTABLE)
- val notification: Notification = Notification.Builder(this,"sealdice")
- .setContentTitle("SealDice is running")
- .setSmallIcon(R.drawable.ic_launcher_foreground)
- .setContentIntent(pendingIntent)
- .build()
- startForeground(1, notification)
- return START_STICKY
- }
-
- override fun onBind(p0: Intent?): IBinder? {
- return null
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/sealdice/dice/ProcessService.kt b/app/src/main/java/com/sealdice/dice/ProcessService.kt
new file mode 100644
index 0000000..72e0715
--- /dev/null
+++ b/app/src/main/java/com/sealdice/dice/ProcessService.kt
@@ -0,0 +1,104 @@
+package com.sealdice.dice
+
+import android.app.*
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.os.Binder
+import android.os.Build
+import android.os.IBinder
+import androidx.preference.PreferenceManager
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
+import java.io.BufferedReader
+import java.io.File
+import java.io.InputStreamReader
+
+
+class ProcessService : Service(){
+ private val binder = MyBinder()
+ private var processBuilder: ProcessBuilder = ProcessBuilder("sh").redirectErrorStream(true)
+ private lateinit var process: Process
+ private var isRunning = false
+ private var shellLogs = ""
+ inner class MyBinder : Binder() {
+ fun getService(): ProcessService = this@ProcessService
+ }
+ fun getShellLogs(): String {
+ return shellLogs
+ }
+ fun stopProcess() {
+ isRunning = false
+ Thread.sleep(5)
+ process.destroy()
+ }
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ if (Build.VERSION.SDK_INT >= 26) {
+ val ns: String = Context.NOTIFICATION_SERVICE
+ val mNotificationManager = getSystemService(ns) as NotificationManager
+ val notificationChannel = NotificationChannel("sealdice","SealDice", NotificationManager.IMPORTANCE_HIGH)
+ mNotificationManager.createNotificationChannel(notificationChannel)
+ val pendingIntent = PendingIntent.getActivity(applicationContext, 0, Intent(applicationContext, NotificationActivity::class.java), PendingIntent.FLAG_MUTABLE)
+ val notification: Notification = Notification.Builder(this,"sealdice")
+ .setContentTitle("SealDice is running")
+ .setSmallIcon(R.drawable.ic_launcher_foreground)
+ .setContentIntent(pendingIntent)
+ .build()
+ startForeground(1, notification)
+ }
+ else {
+ val pendingIntent = PendingIntent.getActivity(applicationContext, 0, Intent(applicationContext, NotificationActivity::class.java), PendingIntent.FLAG_MUTABLE)
+ val notification: Notification = Notification.Builder(this)
+ .setContentTitle("SealDice is running")
+ .setSmallIcon(R.drawable.ic_launcher_foreground)
+ .setContentIntent(pendingIntent)
+ .build()
+ startForeground(1, notification)
+ }
+ if (!isRunning) {
+ isRunning = true
+ process = processBuilder.directory(File(this.filesDir.absolutePath)).start()
+ val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
+ val args = sharedPreferences.getString("launch_args", "")
+ val cmd = "cd sealdice&&./sealdice-core $args"
+ GlobalScope.launch(context = Dispatchers.IO) {
+ val os = process.outputStream
+ os.write(cmd.toByteArray())
+ os.flush()
+ os.close()
+ val data = process.inputStream
+ val ir = BufferedReader(InputStreamReader(data))
+ while (isRunning) {
+ var line: String?
+ try {
+ line = ir.readLine()
+ } catch (e: Exception) {
+ break
+ }
+ while (line != null && isRunning) {
+ shellLogs += line
+ shellLogs += "\n"
+ try {
+ line = ir.readLine()
+ } catch (e: Exception) {
+ break
+ }
+ }
+ Thread.sleep(1000)
+ }
+ }
+ }
+ return START_STICKY
+ }
+ fun isRunning(): Boolean {
+ return isRunning
+ }
+ override fun onBind(p0: Intent?): IBinder {
+ return binder
+ }
+ override fun onDestroy() {
+ super.onDestroy()
+ stopProcess()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/sealdice/dice/SettingsActivity.kt b/app/src/main/java/com/sealdice/dice/SettingsActivity.kt
index da1c14d..a482abb 100644
--- a/app/src/main/java/com/sealdice/dice/SettingsActivity.kt
+++ b/app/src/main/java/com/sealdice/dice/SettingsActivity.kt
@@ -4,6 +4,7 @@ import android.os.Bundle
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
+
class SettingsActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
@@ -19,6 +20,7 @@ class SettingsActivity : AppCompatActivity() {
return when (item.itemId) {
android.R.id.home -> {
onBackPressedDispatcher.onBackPressed()
+ finish()
true
}
else -> super.onOptionsItemSelected(item)
diff --git a/app/src/main/java/com/sealdice/dice/SettingsFragment.kt b/app/src/main/java/com/sealdice/dice/SettingsFragment.kt
index 9d79d43..80c74d8 100644
--- a/app/src/main/java/com/sealdice/dice/SettingsFragment.kt
+++ b/app/src/main/java/com/sealdice/dice/SettingsFragment.kt
@@ -7,6 +7,7 @@ import android.widget.SeekBar
import android.widget.SeekBar.OnSeekBarChangeListener
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SeekBarPreference
+import androidx.preference.SwitchPreferenceCompat
class SettingsFragment : PreferenceFragmentCompat() {
@@ -15,5 +16,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
setPreferencesFromResource(R.xml.prefrences, rootKey)
sharedPreferences = preferenceScreen.sharedPreferences!!
val mySeekBarPreference : SeekBarPreference? = findPreference("launch_waiting_time")
+// val switchPreference: SwitchPreferenceCompat = findPreference("my_switch_preference")
+// switchPreference.setTitleTextColor(resources.getColor(android.R.color.my_color))
}
}
diff --git a/app/src/main/java/com/sealdice/dice/UpdateService.kt b/app/src/main/java/com/sealdice/dice/UpdateService.kt
index dd3b737..8992e15 100644
--- a/app/src/main/java/com/sealdice/dice/UpdateService.kt
+++ b/app/src/main/java/com/sealdice/dice/UpdateService.kt
@@ -6,9 +6,11 @@ import android.app.PendingIntent
import android.app.Service
import android.content.Intent
import android.net.Uri
+import android.os.Build
import android.os.IBinder
import android.util.Log
import androidx.core.app.NotificationCompat
+import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
@@ -21,6 +23,7 @@ class UpdateService : Service() {
private val UPDATE_URL = "https://get.sealdice.com/seal/version/android"
private val NOTIFICATION_ID = 2
+ @OptIn(DelicateCoroutinesApi::class)
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
GlobalScope.launch(context = Dispatchers.IO) {
checkForUpdates()
@@ -61,14 +64,33 @@ class UpdateService : Service() {
private fun showUpdateNotification(version: String) {
// Create a notification channel for Android Oreo and higher
val notificationManager = getSystemService(NotificationManager::class.java)
- val channel = NotificationChannel(
- "update_channel",
- "Update Channel",
- NotificationManager.IMPORTANCE_DEFAULT
- )
- notificationManager.createNotificationChannel(channel)
+ if (Build.VERSION.SDK_INT >= 26) {
+ val channel = NotificationChannel(
+ "update_channel",
+ "Update Channel",
+ NotificationManager.IMPORTANCE_DEFAULT
+ )
+ notificationManager.createNotificationChannel(channel)
+ } else {
+ val builder = NotificationCompat.Builder(this)
+ .setSmallIcon(R.drawable.ic_launcher_foreground)
+ .setContentTitle("SealDice 检测到更新")
+ .setContentText("点击此处下载新版本 $version")
+ .setAutoCancel(true)
+ .setPriority(NotificationCompat.PRIORITY_DEFAULT)
+ // Create a pending intent for the update dialog
+ val uri = Uri.parse("https://d.catlevel.com/seal/android/latest")
+ val intent = Intent()
+ intent.action = "android.intent.action.VIEW"
+ intent.data = uri
+ val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
+ builder.setContentIntent(pendingIntent)
+ // Show the notification
+ notificationManager.notify(NOTIFICATION_ID, builder.build())
+ return
+ }
// Create a notification builder
- val builder = NotificationCompat.Builder(this, "update_channel")
+ val builder = NotificationCompat.Builder(this,"update_channel")
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle("SealDice 检测到更新")
.setContentText("点击此处下载新版本 $version")
@@ -85,6 +107,4 @@ class UpdateService : Service() {
notificationManager.notify(NOTIFICATION_ID, builder.build())
}
- // ...
-
}
\ No newline at end of file
diff --git a/app/src/main/java/com/sealdice/dice/WakeLockService.kt b/app/src/main/java/com/sealdice/dice/WakeLockService.kt
index e8258f5..e529df7 100644
--- a/app/src/main/java/com/sealdice/dice/WakeLockService.kt
+++ b/app/src/main/java/com/sealdice/dice/WakeLockService.kt
@@ -1,5 +1,6 @@
package com.sealdice.dice
+import android.annotation.SuppressLint
import android.app.Service
import android.content.Context
import android.content.Intent
@@ -7,30 +8,17 @@ import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
-import android.os.Handler
import android.os.IBinder
-import android.os.Looper
import android.os.PowerManager
class WakeLockService : Service(){
- private lateinit var wakeLockHelper: WakeLockHelper
- private val handler = Handler(Looper.getMainLooper())
-
- private val task = object : Runnable {
- override fun run() {
- wakeLockHelper.restartWakeLock(0, 0)
- handler.postDelayed(this, 300000) // 每隔5分钟执行一次
- }
- }
-
- override fun onCreate() {
- super.onCreate()
- wakeLockHelper = WakeLockHelper(this)
- wakeLockHelper.acquireWakeLock()
- }
-
+ @SuppressLint("InvalidWakeLockTag", "WakelockTimeout")
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
- handler.postDelayed(task, 300000) // 开始执行任务
+ val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
+ val wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "LocationManagerService")
+ wakeLock.acquire()
+
+// Keep the network connection active using the ConnectivityManager class
val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val networkRequest = NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
@@ -45,40 +33,7 @@ class WakeLockService : Service(){
connectivityManager.registerNetworkCallback(networkRequest, networkCallback)
return START_STICKY
}
-
- override fun onDestroy() {
- super.onDestroy()
- handler.removeCallbacks(task)
- wakeLockHelper.releaseWakeLock()
- }
-
override fun onBind(intent: Intent?): IBinder? {
return null
}
-
- class WakeLockHelper(private val context: Context) {
- private var wakeLock: PowerManager.WakeLock? = null
-
- fun acquireWakeLock() {
- val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
- wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SealDice::SealDiceWakelockTag")
- wakeLock?.acquire(10*60*1000L /*10 minutes*/)
- }
-
- fun releaseWakeLock() {
- wakeLock?.let {
- if (it.isHeld) {
- it.release()
- }
- }
- wakeLock = null
- }
-
- fun restartWakeLock(releaseDelayMillis: Long, acquireDelayMillis: Long) {
- releaseWakeLock()
- Thread.sleep(releaseDelayMillis)
- acquireWakeLock()
- Thread.sleep(acquireDelayMillis)
- }
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/sealdice/dice/WebviewActivity.kt b/app/src/main/java/com/sealdice/dice/WebviewActivity.kt
index b53c2d1..6c27f85 100644
--- a/app/src/main/java/com/sealdice/dice/WebviewActivity.kt
+++ b/app/src/main/java/com/sealdice/dice/WebviewActivity.kt
@@ -1,12 +1,14 @@
package com.sealdice.dice
import android.content.Context
+import android.content.DialogInterface
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.KeyEvent
import android.view.KeyEvent.KEYCODE_BACK
import android.view.MenuItem
+import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat.startActivity
import com.tencent.smtt.export.external.interfaces.SslError
@@ -129,6 +131,16 @@ class WebViewActivity : AppCompatActivity() {
webSettings.domStorageEnabled = true
val url = intent.getStringExtra("url")
webView.webViewClient = object : WebViewClient() {
+ override fun onReceivedError(view: WebView?, errorCode: Int, description: String?, failingUrl: String?) {
+ super.onReceivedError(view, errorCode, description, failingUrl)
+ val alertDialogBuilder = AlertDialog.Builder(
+ this@WebViewActivity, R.style.Theme_Mshell_DialogOverlay
+ )
+ alertDialogBuilder.setTitle("提示")
+ alertDialogBuilder.setMessage("加载ui失败,请点返回键根据日志内信息进行处理")
+ alertDialogBuilder.setPositiveButton("确定") { _: DialogInterface, _: Int ->}
+ alertDialogBuilder.create().show()
+ }
override fun shouldOverrideUrlLoading(
view: WebView?,
request: WebResourceRequest?
@@ -154,6 +166,7 @@ class WebViewActivity : AppCompatActivity() {
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
if (keyCode == KEYCODE_BACK && mWebView.canGoBack()) {
mWebView.goBack()
+ finish()
return true
}
return super.onKeyDown(keyCode, event)
diff --git a/app/src/main/res/layout/danger_preference_layout.xml b/app/src/main/res/layout/danger_preference_layout.xml
new file mode 100644
index 0000000..ce3d737
--- /dev/null
+++ b/app/src/main/res/layout/danger_preference_layout.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 8800fc7..bf1ca0c 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -27,6 +27,14 @@
+