UtilsFragment.kt 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. package com.example.modifier.ui.utils
  2. import android.os.Bundle
  3. import android.os.Handler
  4. import android.os.Looper
  5. import android.util.Log
  6. import android.view.LayoutInflater
  7. import android.view.View
  8. import android.view.ViewGroup
  9. import android.widget.Toast
  10. import androidx.appcompat.app.AlertDialog
  11. import androidx.fragment.app.Fragment
  12. import com.example.modifier.CMD_BACK_APP
  13. import com.example.modifier.Frida
  14. import com.example.modifier.Global
  15. import com.example.modifier.Global.clear
  16. import com.example.modifier.Global.clearConv
  17. import com.example.modifier.Global.resetAll
  18. import com.example.modifier.Global.suspend
  19. import com.example.modifier.Global.unsuspend
  20. import com.example.modifier.R
  21. import com.example.modifier.Utils
  22. import com.example.modifier.databinding.DialogUpdateBinding
  23. import com.example.modifier.databinding.FragmentUtilsBinding
  24. import com.example.modifier.http.KtorClient
  25. import com.example.modifier.http.api.SysConfigApi
  26. import com.example.modifier.http.response.SysConfigResponse
  27. import com.example.modifier.service.ModifierService
  28. import com.google.android.material.dialog.MaterialAlertDialogBuilder
  29. import com.google.android.material.dialog.MaterialDialogs
  30. import com.google.android.material.progressindicator.CircularProgressIndicatorSpec
  31. import com.google.android.material.progressindicator.DeterminateDrawable
  32. import com.google.android.material.progressindicator.IndeterminateDrawable
  33. import io.ktor.client.call.body
  34. import io.ktor.client.plugins.onDownload
  35. import io.ktor.client.plugins.resources.get
  36. import io.ktor.client.request.get
  37. import io.ktor.client.request.prepareGet
  38. import io.ktor.http.contentLength
  39. import io.ktor.utils.io.ByteReadChannel
  40. import io.ktor.utils.io.core.isEmpty
  41. import io.ktor.utils.io.core.readBytes
  42. import kotlinx.coroutines.CoroutineScope
  43. import kotlinx.coroutines.Dispatchers
  44. import kotlinx.coroutines.Job
  45. import kotlinx.coroutines.delay
  46. import kotlinx.coroutines.launch
  47. import kotlinx.coroutines.withContext
  48. import java.io.File
  49. import java.util.concurrent.ExecutorService
  50. import java.util.concurrent.Executors
  51. class UtilsFragment : Fragment() {
  52. private lateinit var binding: FragmentUtilsBinding
  53. var handler: Handler = Handler(Looper.getMainLooper())
  54. var executor: ExecutorService = Executors.newFixedThreadPool(4)
  55. override fun onCreate(savedInstanceState: Bundle?) {
  56. super.onCreate(savedInstanceState)
  57. }
  58. override fun onCreateView(
  59. inflater: LayoutInflater, container: ViewGroup?,
  60. savedInstanceState: Bundle?
  61. ): View? {
  62. if (this::binding.isInitialized) {
  63. return binding.root
  64. }
  65. binding = FragmentUtilsBinding.inflate(inflater, container, false)
  66. binding.btnClear.setOnClickListener { v: View? ->
  67. onClear()
  68. }
  69. binding.btnStop.setOnClickListener { v: View? ->
  70. onStopClick()
  71. }
  72. binding.btnSend.setOnClickListener { v: View? ->
  73. Utils.makeLoadingButton(context, binding.btnSend)
  74. CoroutineScope(Dispatchers.Main).launch {
  75. delay(1000L)
  76. val otp = binding.etOtp.text.toString()
  77. withContext(Dispatchers.IO) {
  78. Global.sendSmsIntent("3538", "Your Messenger verification code is G-$otp")
  79. }
  80. binding.btnSend.setIconResource(R.drawable.ic_done)
  81. binding.btnSend.text = "OK"
  82. delay(1500L)
  83. binding.btnSend.isEnabled = true
  84. binding.btnSend.icon = null
  85. binding.btnSend.text = "Send"
  86. }
  87. }
  88. binding.btnClearConv.setOnClickListener { v: View? ->
  89. binding.btnClearConv.isEnabled = false
  90. Utils.makeLoadingButton(context, binding.btnClearConv)
  91. handler.post {
  92. executor.execute {
  93. clearConv()
  94. handler.post {
  95. binding.btnClearConv.setIconResource(R.drawable.ic_done)
  96. binding.btnClearConv.text = "OK"
  97. handler.postDelayed({
  98. binding.btnClearConv.isEnabled = true
  99. binding.btnClearConv.icon = null
  100. binding.btnClearConv.text = "Clear Msg"
  101. }, 1500)
  102. }
  103. }
  104. }
  105. }
  106. binding.btnCheckA10y.setOnClickListener {
  107. Utils.makeLoadingButton(context, binding.btnCheckA10y)
  108. CoroutineScope(Dispatchers.Main).launch {
  109. var a10y: Boolean? = null
  110. withContext(Dispatchers.IO) {
  111. a10y = ModifierService.instance?.checkRcsAvailability()
  112. Utils.runAsRoot(CMD_BACK_APP)
  113. }
  114. if (isAdded) {
  115. MaterialAlertDialogBuilder(requireContext())
  116. .setMessage("RCS is ${if (a10y == true) "available" else "not available"}")
  117. .setPositiveButton("OK") { _, _ -> }
  118. .show()
  119. }
  120. binding.btnCheckA10y.setIconResource(R.drawable.ic_done)
  121. binding.btnCheckA10y.text = "OK"
  122. delay(1500L)
  123. binding.btnCheckA10y.isEnabled = true
  124. binding.btnCheckA10y.icon = null
  125. binding.btnCheckA10y.text = "Check A10y"
  126. }
  127. }
  128. binding.btnSuspend.setOnClickListener {
  129. binding.btnSuspend.isEnabled = false
  130. Utils.makeLoadingButton(context, binding.btnSuspend)
  131. executor.execute {
  132. val gsf = binding.cbGsf.isChecked
  133. val gms = binding.cbGms.isChecked
  134. val sms = binding.cbSms.isChecked
  135. suspend(gsf, gms, sms)
  136. handler.post {
  137. binding.btnSuspend.setIconResource(R.drawable.ic_done)
  138. binding.btnSuspend.text = "OK"
  139. handler.postDelayed({
  140. binding.btnSuspend.isEnabled = true
  141. binding.btnSuspend.icon = null
  142. binding.btnSuspend.text = "Suspend"
  143. }, 1500)
  144. }
  145. }
  146. }
  147. binding.btnUnsuspend.setOnClickListener {
  148. binding.btnUnsuspend.isEnabled = false
  149. Utils.makeLoadingButton(context, binding.btnUnsuspend)
  150. executor.execute {
  151. val gsf = binding.cbGsf.isChecked
  152. val gms = binding.cbGms.isChecked
  153. val sms = binding.cbSms.isChecked
  154. unsuspend(gsf, gms, sms)
  155. handler.post {
  156. binding.btnUnsuspend.setIconResource(R.drawable.ic_done)
  157. binding.btnUnsuspend.text = "OK"
  158. handler.postDelayed({
  159. binding.btnUnsuspend.isEnabled = true
  160. binding.btnUnsuspend.icon = null
  161. binding.btnUnsuspend.text = "Unsuspend"
  162. }, 1500)
  163. }
  164. }
  165. }
  166. binding.btnStartFrida.setOnClickListener {
  167. CoroutineScope(Dispatchers.Main).launch {
  168. withContext(Dispatchers.IO) {
  169. Frida.start()
  170. }
  171. }
  172. Toast.makeText(context, "Frida started", Toast.LENGTH_SHORT).show()
  173. }
  174. binding.btnStopFrida.setOnClickListener {
  175. CoroutineScope(Dispatchers.Main).launch {
  176. withContext(Dispatchers.IO) {
  177. Frida.stop()
  178. }
  179. }
  180. Toast.makeText(context, "Frida stopped", Toast.LENGTH_SHORT).show()
  181. }
  182. binding.btnKillPhone.setOnClickListener {
  183. binding.btnKillPhone.isEnabled = false
  184. Utils.makeLoadingButton(context, binding.btnKillPhone)
  185. CoroutineScope(Dispatchers.IO).launch {
  186. val success = Global.killPhoneProcess(force = true)
  187. withContext(Dispatchers.Main) {
  188. if (success) {
  189. binding.btnKillPhone.setIconResource(R.drawable.ic_done)
  190. binding.btnKillPhone.text = "OK"
  191. } else {
  192. binding.btnKillPhone.setIconResource(R.drawable.ic_error)
  193. binding.btnKillPhone.text = "Fail"
  194. }
  195. delay(1500L)
  196. binding.btnKillPhone.isEnabled = true
  197. binding.btnKillPhone.icon = null
  198. binding.btnKillPhone.text = "Kill Phone"
  199. }
  200. }
  201. }
  202. binding.btnSyncTime.setOnClickListener {
  203. binding.btnSyncTime.isEnabled = false
  204. Utils.makeLoadingButton(context, binding.btnSyncTime)
  205. CoroutineScope(Dispatchers.IO).launch {
  206. Global.syncTime()
  207. withContext(Dispatchers.Main) {
  208. binding.btnSyncTime.setIconResource(R.drawable.ic_done)
  209. binding.btnSyncTime.text = "OK"
  210. delay(1500L)
  211. binding.btnSyncTime.isEnabled = true
  212. binding.btnSyncTime.icon = null
  213. binding.btnSyncTime.text = "Sync Time"
  214. }
  215. }
  216. }
  217. binding.btnUpdateModifier.setOnClickListener {
  218. CoroutineScope(Dispatchers.IO).launch {
  219. try {
  220. val config = KtorClient.get(SysConfigApi.Id(SysConfigApi(), "modifier_apk"))
  221. .body<SysConfigResponse>()
  222. installApk(config.value)
  223. } catch (e: Exception) {
  224. Log.e("Modifier", "Failed to get message apk", e)
  225. }
  226. }
  227. }
  228. binding.btnUpdateMessage.setOnClickListener {
  229. CoroutineScope(Dispatchers.IO).launch {
  230. try {
  231. val config = KtorClient.get(SysConfigApi.Id(SysConfigApi(), "message_apk"))
  232. .body<SysConfigResponse>()
  233. installApk(config.value)
  234. } catch (e: Exception) {
  235. Log.e("Modifier", "Failed to get message apk", e)
  236. }
  237. }
  238. }
  239. return binding.root
  240. }
  241. private fun onClear() {
  242. binding.btnClear.isEnabled = false
  243. Utils.makeLoadingButton(context, binding.btnClear)
  244. executor.execute {
  245. val all = binding.cbAll.isChecked
  246. val gsf = binding.cbGsf.isChecked
  247. val gms = binding.cbGms.isChecked
  248. val sms = binding.cbSms.isChecked
  249. if (all) {
  250. resetAll()
  251. } else {
  252. clear(gsf, gms, sms)
  253. }
  254. handler.post {
  255. binding.btnClear.setIconResource(R.drawable.ic_done)
  256. binding.btnClear.text = "OK"
  257. handler.postDelayed({
  258. binding.btnClear.isEnabled = true
  259. binding.btnClear.icon = null
  260. binding.btnClear.text = "Clear"
  261. }, 1500)
  262. }
  263. }
  264. }
  265. private fun onStopClick() {
  266. binding.btnStop.isEnabled = false
  267. Utils.makeLoadingButton(context, binding.btnStop)
  268. executor.execute {
  269. val gsf = binding.cbGsf.isChecked
  270. val gms = binding.cbGms.isChecked
  271. val sms = binding.cbSms.isChecked
  272. suspend(gsf, gms, sms)
  273. unsuspend(gsf, gms, sms)
  274. handler.post {
  275. binding.btnStop.setIconResource(R.drawable.ic_done)
  276. binding.btnStop.text = "OK"
  277. handler.postDelayed({
  278. binding.btnStop.isEnabled = true
  279. binding.btnStop.icon = null
  280. binding.btnStop.text = "Stop"
  281. }, 1500)
  282. }
  283. }
  284. }
  285. private suspend fun installApk(url: String) {
  286. if (url.isEmpty()) {
  287. Toast.makeText(context, "URL is empty", Toast.LENGTH_SHORT).show()
  288. return
  289. }
  290. if (!url.endsWith(".apk")) {
  291. Toast.makeText(context, "URL is not an APK", Toast.LENGTH_SHORT).show()
  292. return
  293. }
  294. if (!url.startsWith("http")) {
  295. Toast.makeText(context, "URL is not valid", Toast.LENGTH_SHORT).show()
  296. return
  297. }
  298. var job: Job? = null
  299. lateinit var dialogBinding: DialogUpdateBinding
  300. lateinit var dialog: AlertDialog
  301. withContext(Dispatchers.Main) {
  302. dialogBinding = DialogUpdateBinding.inflate(layoutInflater)
  303. dialog = MaterialAlertDialogBuilder(requireContext())
  304. .setTitle("Installing...")
  305. .setView(dialogBinding.root)
  306. .setCancelable(false)
  307. .setNegativeButton("Cancel") { _, _ ->
  308. job?.cancel()
  309. }
  310. .create()
  311. dialog.show()
  312. }
  313. job = CoroutineScope(Dispatchers.IO).launch {
  314. try {
  315. val file = File.createTempFile("files", ".apk")
  316. KtorClient.prepareGet("https://nebuai.oss-cn-hangzhou.aliyuncs.com/application/20240701/bgpj4axa.apk")
  317. .execute { httpResponse ->
  318. val channel: ByteReadChannel = httpResponse.body()
  319. while (!channel.isClosedForRead) {
  320. val packet = channel.readRemaining(DEFAULT_BUFFER_SIZE.toLong())
  321. while (!packet.isEmpty) {
  322. val bytes = packet.readBytes()
  323. file.appendBytes(bytes)
  324. withContext(
  325. Dispatchers.Main
  326. ) {
  327. dialogBinding.progressBar.isIndeterminate = false
  328. dialogBinding.progressBar.progress =
  329. (file.length() * 100 / httpResponse.contentLength()!!).toInt()
  330. }
  331. }
  332. }
  333. }
  334. Log.i("Modifier", "A file saved to ${file.path}")
  335. Utils.runAsRoot("pm install -d -r ${file.path}")
  336. } catch (e: Exception) {
  337. Log.e("Modifier", "Failed to download apk", e)
  338. }
  339. withContext(Dispatchers.Main) {
  340. dialog.dismiss()
  341. }
  342. }
  343. }
  344. }