|
|
@@ -20,8 +20,11 @@ import androidx.activity.result.contract.ActivityResultContracts
|
|
|
import androidx.core.app.ActivityCompat
|
|
|
import androidx.core.content.ContextCompat
|
|
|
import androidx.fragment.app.Fragment
|
|
|
+import androidx.lifecycle.Lifecycle
|
|
|
import androidx.lifecycle.lifecycleScope
|
|
|
+import androidx.lifecycle.repeatOnLifecycle
|
|
|
import com.example.modifier.R
|
|
|
+import com.example.modifier.TAG
|
|
|
import com.example.modifier.Utils
|
|
|
import com.example.modifier.constants.servers
|
|
|
import com.example.modifier.data.AppPreferences
|
|
|
@@ -99,164 +102,164 @@ class SettingsFragment : Fragment() {
|
|
|
// decision.
|
|
|
}
|
|
|
}
|
|
|
+ Log.i(TAG, "SettingsFragment.onCreate")
|
|
|
}
|
|
|
|
|
|
override fun onCreateView(
|
|
|
inflater: LayoutInflater, container: ViewGroup?,
|
|
|
savedInstanceState: Bundle?
|
|
|
): View {
|
|
|
- if (this::binding.isInitialized) {
|
|
|
- return binding.root
|
|
|
- }
|
|
|
- binding = FragmentSettingsBinding.inflate(inflater, container, false)
|
|
|
- binding.tlIccid.setEndIconOnClickListener {
|
|
|
- if (binding.etMcc.text.isNullOrEmpty() || binding.etMnc.text.isNullOrEmpty() || binding.etAreaCode.text.isNullOrEmpty()) {
|
|
|
- Toast.makeText(context, "MNC and Code required", Toast.LENGTH_SHORT).show()
|
|
|
- return@setEndIconOnClickListener
|
|
|
- }
|
|
|
- binding.etIccid.setText(
|
|
|
- genICCID(
|
|
|
- binding.etMnc.text.toString(),
|
|
|
- binding.etAreaCode.text.toString()
|
|
|
+ Log.i(TAG, "SettingsFragment.onCreateView")
|
|
|
+ if (!this::binding.isInitialized) {
|
|
|
+ binding = FragmentSettingsBinding.inflate(inflater, container, false)
|
|
|
+ binding.tlIccid.setEndIconOnClickListener {
|
|
|
+ if (binding.etMcc.text.isNullOrEmpty() || binding.etMnc.text.isNullOrEmpty() || binding.etAreaCode.text.isNullOrEmpty()) {
|
|
|
+ Toast.makeText(context, "MNC and Code required", Toast.LENGTH_SHORT).show()
|
|
|
+ return@setEndIconOnClickListener
|
|
|
+ }
|
|
|
+ binding.etIccid.setText(
|
|
|
+ genICCID(
|
|
|
+ binding.etMnc.text.toString(),
|
|
|
+ binding.etAreaCode.text.toString()
|
|
|
+ )
|
|
|
)
|
|
|
- )
|
|
|
- }
|
|
|
- binding.tlImsi.setEndIconOnClickListener {
|
|
|
- val mcc = Optional.ofNullable(
|
|
|
- binding.etMcc.text
|
|
|
- ).map { o: Editable? -> Objects.toString(o) }.orElse("")
|
|
|
- val mnc = Optional.ofNullable(binding.etMnc.text)
|
|
|
- .map { o: Editable? -> Objects.toString(o) }
|
|
|
- .orElse("")
|
|
|
- if (StringUtils.isEmpty(mcc) || StringUtils.isEmpty(mnc)) {
|
|
|
- Toast.makeText(context, "MCC and MNC are required", Toast.LENGTH_SHORT).show()
|
|
|
- return@setEndIconOnClickListener
|
|
|
}
|
|
|
- binding.etImsi.setText(mcc + mnc + RandomStringUtils.randomNumeric(15 - mcc.length - mnc.length))
|
|
|
- }
|
|
|
- binding.tlImei.setEndIconOnClickListener {
|
|
|
- binding.etImei.setText(genIMEI())
|
|
|
- }
|
|
|
- binding.btnSave.setOnClickListener {
|
|
|
- onSave()
|
|
|
- }
|
|
|
- binding.etServer.threshold = 1000
|
|
|
- binding.etServer.setSimpleItems(servers)
|
|
|
- binding.btnServer.setOnClickListener { v: View? ->
|
|
|
- val server = binding.etServer.text.toString()
|
|
|
- if (StringUtils.isEmpty(server)) {
|
|
|
- Toast.makeText(context, "Server is required", Toast.LENGTH_SHORT).show()
|
|
|
- return@setOnClickListener
|
|
|
+ binding.tlImsi.setEndIconOnClickListener {
|
|
|
+ val mcc = Optional.ofNullable(
|
|
|
+ binding.etMcc.text
|
|
|
+ ).map { o: Editable? -> Objects.toString(o) }.orElse("")
|
|
|
+ val mnc = Optional.ofNullable(binding.etMnc.text)
|
|
|
+ .map { o: Editable? -> Objects.toString(o) }
|
|
|
+ .orElse("")
|
|
|
+ if (StringUtils.isEmpty(mcc) || StringUtils.isEmpty(mnc)) {
|
|
|
+ Toast.makeText(context, "MCC and MNC are required", Toast.LENGTH_SHORT).show()
|
|
|
+ return@setEndIconOnClickListener
|
|
|
+ }
|
|
|
+ binding.etImsi.setText(mcc + mnc + RandomStringUtils.randomNumeric(15 - mcc.length - mnc.length))
|
|
|
}
|
|
|
- if (!UrlValidator(arrayOf("http", "https")).isValid(server)) {
|
|
|
- Toast.makeText(context, "Invalid server URL", Toast.LENGTH_SHORT).show()
|
|
|
- return@setOnClickListener
|
|
|
+ binding.tlImei.setEndIconOnClickListener {
|
|
|
+ binding.etImei.setText(genIMEI())
|
|
|
}
|
|
|
- lifecycleScope.launch {
|
|
|
- appPreferencesRepository.updateServer(server)
|
|
|
- appPreferencesRepository.updateName(binding.etDeviceLabel.text.toString())
|
|
|
- instance?.connect()
|
|
|
- Utils.makeLoadingButton(context, binding.btnServer)
|
|
|
- binding.btnServer.isEnabled = false
|
|
|
- delay(500)
|
|
|
- binding.btnServer.setIconResource(R.drawable.ic_done)
|
|
|
- binding.btnServer.text = "OK"
|
|
|
- delay(500)
|
|
|
- binding.btnServer.isEnabled = true
|
|
|
- binding.btnServer.icon = null
|
|
|
- binding.btnServer.text = "Save"
|
|
|
+ binding.btnSave.setOnClickListener {
|
|
|
+ onSave()
|
|
|
+ }
|
|
|
+ binding.etServer.threshold = 1000
|
|
|
+ binding.etServer.setSimpleItems(servers)
|
|
|
+ binding.btnServer.setOnClickListener { v: View? ->
|
|
|
+ val server = binding.etServer.text.toString()
|
|
|
+ if (StringUtils.isEmpty(server)) {
|
|
|
+ Toast.makeText(context, "Server is required", Toast.LENGTH_SHORT).show()
|
|
|
+ return@setOnClickListener
|
|
|
+ }
|
|
|
+ if (!UrlValidator(arrayOf("http", "https")).isValid(server)) {
|
|
|
+ Toast.makeText(context, "Invalid server URL", Toast.LENGTH_SHORT).show()
|
|
|
+ return@setOnClickListener
|
|
|
+ }
|
|
|
+ lifecycleScope.launch {
|
|
|
+ appPreferencesRepository.updateServer(server)
|
|
|
+ appPreferencesRepository.updateName(binding.etDeviceLabel.text.toString())
|
|
|
+ instance?.connect()
|
|
|
+ Utils.makeLoadingButton(context, binding.btnServer)
|
|
|
+ binding.btnServer.isEnabled = false
|
|
|
+ delay(500)
|
|
|
+ binding.btnServer.setIconResource(R.drawable.ic_done)
|
|
|
+ binding.btnServer.text = "OK"
|
|
|
+ delay(500)
|
|
|
+ binding.btnServer.isEnabled = true
|
|
|
+ binding.btnServer.icon = null
|
|
|
+ binding.btnServer.text = "Save"
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- binding.btnRequest.setOnClickListener { v: View? ->
|
|
|
- lifecycleScope.launch {
|
|
|
- Utils.makeLoadingButton(context, binding.btnRequest)
|
|
|
- binding.btnRequest.isEnabled = false
|
|
|
+ binding.btnRequest.setOnClickListener { v: View? ->
|
|
|
+ lifecycleScope.launch {
|
|
|
+ Utils.makeLoadingButton(context, binding.btnRequest)
|
|
|
+ binding.btnRequest.isEnabled = false
|
|
|
|
|
|
- withContext(Dispatchers.IO) req@{
|
|
|
- val response: HttpResponse
|
|
|
- try {
|
|
|
- response = ktorClient(appPreferences.value.server).put(
|
|
|
- RcsNumberApi()
|
|
|
- ) {
|
|
|
- contentType(ContentType.Application.Json)
|
|
|
- setBody(RcsNumberRequest(deviceId = uniqueId))
|
|
|
+ withContext(Dispatchers.IO) req@{
|
|
|
+ val response: HttpResponse
|
|
|
+ try {
|
|
|
+ response = ktorClient(appPreferences.value.server).put(
|
|
|
+ RcsNumberApi()
|
|
|
+ ) {
|
|
|
+ contentType(ContentType.Application.Json)
|
|
|
+ setBody(RcsNumberRequest(deviceId = uniqueId))
|
|
|
+ }
|
|
|
+ } catch (e: Exception) {
|
|
|
+ withContext(Dispatchers.Main) {
|
|
|
+ MaterialAlertDialogBuilder(requireContext())
|
|
|
+ .setTitle("Error")
|
|
|
+ .setMessage(e.message)
|
|
|
+ .setPositiveButton("OK") { dialog: DialogInterface, which: Int ->
|
|
|
+ dialog.dismiss()
|
|
|
+ }
|
|
|
+ .show()
|
|
|
+ binding.btnRequest.isEnabled = true
|
|
|
+ binding.btnRequest.text = "Request"
|
|
|
+ binding.btnRequest.icon = null
|
|
|
+ }
|
|
|
+ return@req
|
|
|
}
|
|
|
- } catch (e: Exception) {
|
|
|
- withContext(Dispatchers.Main) {
|
|
|
- MaterialAlertDialogBuilder(requireContext())
|
|
|
- .setTitle("Error")
|
|
|
- .setMessage(e.message)
|
|
|
- .setPositiveButton("OK") { dialog: DialogInterface, which: Int ->
|
|
|
- dialog.dismiss()
|
|
|
- }
|
|
|
- .show()
|
|
|
- binding.btnRequest.isEnabled = true
|
|
|
- binding.btnRequest.text = "Request"
|
|
|
- binding.btnRequest.icon = null
|
|
|
- }
|
|
|
- return@req
|
|
|
- }
|
|
|
- Log.i(com.example.modifier.TAG, "response: ${response.bodyAsText()}")
|
|
|
- val res = response.body<RcsNumberResponse>()
|
|
|
- val number = res.number
|
|
|
- val mcc = res.mcc
|
|
|
- val mnc = res.mnc
|
|
|
- val country = res.country
|
|
|
- val areaCode = res.areaCode
|
|
|
- val iccid = genICCID(mnc, areaCode)
|
|
|
- val imsi =
|
|
|
- mcc + mnc + RandomStringUtils.randomNumeric(15 - mcc.length - mnc.length)
|
|
|
- val imei = genIMEI()
|
|
|
- spoofedSimInfoRepository.updateSpoofedSimInfo(
|
|
|
- SpoofedSimInfo(
|
|
|
- number,
|
|
|
- mcc,
|
|
|
- mnc,
|
|
|
- iccid,
|
|
|
- imsi,
|
|
|
- imei,
|
|
|
- country,
|
|
|
- areaCode,
|
|
|
- false,
|
|
|
- res.carrierId,
|
|
|
- res.carrierName
|
|
|
+ Log.i(com.example.modifier.TAG, "response: ${response.bodyAsText()}")
|
|
|
+ val res = response.body<RcsNumberResponse>()
|
|
|
+ val number = res.number
|
|
|
+ val mcc = res.mcc
|
|
|
+ val mnc = res.mnc
|
|
|
+ val country = res.country
|
|
|
+ val areaCode = res.areaCode
|
|
|
+ val iccid = genICCID(mnc, areaCode)
|
|
|
+ val imsi =
|
|
|
+ mcc + mnc + RandomStringUtils.randomNumeric(15 - mcc.length - mnc.length)
|
|
|
+ val imei = genIMEI()
|
|
|
+ spoofedSimInfoRepository.updateSpoofedSimInfo(
|
|
|
+ SpoofedSimInfo(
|
|
|
+ number,
|
|
|
+ mcc,
|
|
|
+ mnc,
|
|
|
+ iccid,
|
|
|
+ imsi,
|
|
|
+ imei,
|
|
|
+ country,
|
|
|
+ areaCode,
|
|
|
+ false,
|
|
|
+ res.carrierId,
|
|
|
+ res.carrierName
|
|
|
+ )
|
|
|
)
|
|
|
- )
|
|
|
+ }
|
|
|
+ binding.btnRequest.icon = null
|
|
|
+ binding.btnRequest.isEnabled = true
|
|
|
+ binding.btnRequest.text = "Request"
|
|
|
}
|
|
|
- binding.btnRequest.icon = null
|
|
|
- binding.btnRequest.isEnabled = true
|
|
|
- binding.btnRequest.text = "Request"
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- binding.switchClean.setOnCheckedChangeListener { buttonView, isChecked ->
|
|
|
- CoroutineScope(Dispatchers.IO).launch {
|
|
|
- appPreferencesRepository.updatePreventClean(isChecked)
|
|
|
+ binding.switchClean.setOnCheckedChangeListener { buttonView, isChecked ->
|
|
|
+ CoroutineScope(Dispatchers.IO).launch {
|
|
|
+ appPreferencesRepository.updatePreventClean(isChecked)
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
- binding.switchRequest.setOnCheckedChangeListener { buttonView, isChecked ->
|
|
|
- CoroutineScope(Dispatchers.IO).launch {
|
|
|
- appPreferencesRepository.updatePreventRequest(isChecked)
|
|
|
+ binding.switchRequest.setOnCheckedChangeListener { buttonView, isChecked ->
|
|
|
+ CoroutineScope(Dispatchers.IO).launch {
|
|
|
+ appPreferencesRepository.updatePreventRequest(isChecked)
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
- binding.swReset.setOnCheckedChangeListener { buttonView, isChecked ->
|
|
|
- CoroutineScope(Dispatchers.IO).launch {
|
|
|
- appPreferencesRepository.updatePreventReset(isChecked)
|
|
|
+ binding.swReset.setOnCheckedChangeListener { buttonView, isChecked ->
|
|
|
+ CoroutineScope(Dispatchers.IO).launch {
|
|
|
+ appPreferencesRepository.updatePreventReset(isChecked)
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- binding.btnCheck.setOnClickListener {
|
|
|
+ binding.btnCheck.setOnClickListener {
|
|
|
|
|
|
- when {
|
|
|
- ContextCompat.checkSelfPermission(
|
|
|
- requireContext(),
|
|
|
- Manifest.permission.READ_PHONE_STATE
|
|
|
- ) == PackageManager.PERMISSION_GRANTED -> {
|
|
|
- val telephonyManager =
|
|
|
- requireContext().getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
|
|
|
+ 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}
|
|
|
+ var res = """IMEI: ${telephonyManager.imei}
|
|
|
|IMSI: ${telephonyManager.subscriberId}
|
|
|
|ICCID: ${telephonyManager.simSerialNumber}
|
|
|
|Phone Number: ${telephonyManager.line1Number}
|
|
|
@@ -269,15 +272,15 @@ class SettingsFragment : Fragment() {
|
|
|
|DefaultSmsSubscriptionId: ${SmsManager.getDefaultSmsSubscriptionId()}
|
|
|
|
|
|
|
""".trimMargin()
|
|
|
- val subscriptionManager: SubscriptionManager =
|
|
|
- requireContext().getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as SubscriptionManager
|
|
|
+ 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 += """
|
|
|
+ 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)}
|
|
|
@@ -287,39 +290,41 @@ class SettingsFragment : Fragment() {
|
|
|
|carrierId: ${info.carrierId}
|
|
|
|roaming: ${info.dataRoaming}
|
|
|
""".trimMargin()
|
|
|
- }
|
|
|
- MaterialAlertDialogBuilder(requireContext())
|
|
|
- .setTitle("Info")
|
|
|
- .setMessage(res)
|
|
|
- .setPositiveButton("OK") { dialog: DialogInterface, which: Int ->
|
|
|
- dialog.dismiss()
|
|
|
}
|
|
|
- .show()
|
|
|
- }
|
|
|
+ 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.
|
|
|
- }
|
|
|
+ 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
|
|
|
- )
|
|
|
+ else -> {
|
|
|
+ // You can directly ask for the permission.
|
|
|
+ // The registered ActivityResultCallback gets the result of this request.
|
|
|
+ requestPermissionLauncher.launch(
|
|
|
+ Manifest.permission.READ_PHONE_STATE
|
|
|
+ )
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
+ }
|
|
|
}
|
|
|
- lifecycleScope.launch {
|
|
|
+ viewLifecycleOwner.lifecycleScope.launch {
|
|
|
launch {
|
|
|
appPreferencesRepository.getAppPreferences().collect {
|
|
|
+ Log.i(TAG, "appPreferencesRepository.collect")
|
|
|
binding.etServer.setText(it.server)
|
|
|
binding.etDeviceLabel.setText(it.name)
|
|
|
binding.switchClean.isChecked = it.preventClean
|
|
|
@@ -329,6 +334,7 @@ class SettingsFragment : Fragment() {
|
|
|
}
|
|
|
launch {
|
|
|
spoofedSimInfo.collect {
|
|
|
+ Log.i(TAG, "spoofedSimInfo.collect")
|
|
|
binding.etNumber.setText(it.number)
|
|
|
binding.etMcc.setText(it.mcc)
|
|
|
binding.etMnc.setText(it.mnc)
|