| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- package com.example.modifier.ui.settings
- import android.Manifest
- import android.annotation.SuppressLint
- import android.content.Context
- import android.content.DialogInterface
- import android.content.pm.PackageManager
- import android.os.Bundle
- import android.telephony.SmsManager
- 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.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.R
- import com.example.modifier.baseTag
- import com.example.modifier.Utils
- import com.example.modifier.constants.servers
- import com.example.modifier.repo.AppPrefsRepo
- import com.example.modifier.databinding.FragmentSettingsBinding
- import com.example.modifier.http.ktorClient
- import com.example.modifier.http.api.RcsNumberApi
- import com.example.modifier.http.request.RcsNumberRequest
- import com.example.modifier.http.response.RcsNumberResponse
- import com.example.modifier.model.SpoofedSimInfo
- import com.example.modifier.repo.SpoofedSimInfoRepo
- import com.example.modifier.utils.genICCID
- import com.example.modifier.utils.genIMEI
- import com.google.android.material.dialog.MaterialAlertDialogBuilder
- import io.ktor.client.call.body
- 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
- 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.flow.StateFlow
- import kotlinx.coroutines.launch
- import kotlinx.coroutines.withContext
- import org.apache.commons.lang3.RandomStringUtils
- import org.apache.commons.lang3.StringUtils
- import org.apache.commons.validator.routines.UrlValidator
- import java.util.Objects
- import java.util.Optional
- @SuppressLint("SetTextI18n", "MissingPermission", "HardwareIds", "NewApi")
- class SettingsFragment : Fragment() {
- private val tag = "$baseTag/SettingsFragment"
- private lateinit var binding: FragmentSettingsBinding
- private lateinit var requestPermissionLauncher: ActivityResultLauncher<String>
- private val appPrefsRepo = AppPrefsRepo.instance
- private val spoofedSimInfoRepo = SpoofedSimInfoRepo.instance
- init {
- Log.i("SettingsFragment", "SettingsFragment")
- }
- 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.
- }
- }
- Log.i(tag, "SettingsFragment.onCreate")
- }
- override fun onCreateView(
- inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- 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
- }
- if (!UrlValidator(arrayOf("http", "https")).isValid(server)) {
- Toast.makeText(context, "Invalid server URL", Toast.LENGTH_SHORT).show()
- return@setOnClickListener
- }
- lifecycleScope.launch {
- appPrefsRepo.updateServer(server)
- appPrefsRepo.updateName(binding.etDeviceLabel.text.toString())
- 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
- withContext(Dispatchers.IO) req@{
- val response: HttpResponse
- try {
- response = ktorClient.put(
- RcsNumberApi()
- ) {
- contentType(ContentType.Application.Json)
- setBody(RcsNumberRequest(deviceId = appPrefsRepo.appPrefs.value.id))
- }
- } 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(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()
- spoofedSimInfoRepo.updateSpoofedSimInfo(
- SpoofedSimInfo(
- numberId = res.id,
- number = number,
- mcc = mcc,
- mnc = mnc,
- iccid = iccid,
- imsi = imsi,
- imei = imei,
- country = country,
- areaCode = areaCode,
- available = false,
- carrierId = res.carrierId,
- carrierName = res.carrierName
- )
- )
- }
- binding.btnRequest.icon = null
- binding.btnRequest.isEnabled = true
- binding.btnRequest.text = "Request"
- }
- }
- binding.switchClean.setOnCheckedChangeListener { buttonView, isChecked ->
- CoroutineScope(Dispatchers.IO).launch {
- appPrefsRepo.updatePreventClean(isChecked)
- }
- }
- binding.switchRequest.setOnCheckedChangeListener { buttonView, isChecked ->
- CoroutineScope(Dispatchers.IO).launch {
- appPrefsRepo.updatePreventRequest(isChecked)
- }
- }
- binding.swReset.setOnCheckedChangeListener { buttonView, isChecked ->
- CoroutineScope(Dispatchers.IO).launch {
- appPrefsRepo.updatePreventReset(isChecked)
- }
- }
- 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}
- |simCarrierId: ${telephonyManager.simCarrierId}
- |carrierIdFromSimMccMnc: ${telephonyManager.carrierIdFromSimMccMnc}
- |simSpecificCarrierId: ${telephonyManager.simSpecificCarrierId}
- |DefaultSmsSubscriptionId: ${SmsManager.getDefaultSmsSubscriptionId()}
- |
- """.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}
- |carrierId: ${info.carrierId}
- |roaming: ${info.dataRoaming}
- """.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
- )
- }
- }
- }
- }
- viewLifecycleOwner.lifecycleScope.launch {
- launch {
- appPrefsRepo.appPrefs.collect {
- Log.i(tag, "appPreferencesRepository.collect")
- binding.etServer.setText(it.server)
- binding.etDeviceLabel.setText(it.name)
- binding.switchClean.isChecked = it.preventClean
- binding.switchRequest.isChecked = it.preventRequest
- binding.swReset.isChecked = it.preventReset
- }
- }
- launch {
- spoofedSimInfoRepo.spoofedSimInfo.collect {
- Log.i(tag, "spoofedSimInfo.collect")
- binding.etNumber.setText(it.number)
- binding.etMcc.setText(it.mcc)
- binding.etMnc.setText(it.mnc)
- binding.etIccid.setText(it.iccid)
- binding.etImsi.setText(it.imsi)
- binding.etImei.setText(it.imei)
- binding.etCountry.setText(it.country)
- binding.etAreaCode.setText(it.areaCode)
- binding.etCarrierId.setText(it.carrierId)
- binding.etCarrierName.setText(it.carrierName)
- }
- }
- }
- return binding.root
- }
- private fun onSave() {
- Utils.makeLoadingButton(context, binding.btnSave)
- binding.btnSave.isEnabled = false
- lifecycleScope.launch {
- withContext(Dispatchers.IO) {
- spoofedSimInfoRepo.updateSpoofedSimInfo(
- SpoofedSimInfo(
- 0,
- binding.etNumber.text.toString(),
- binding.etMcc.text.toString(),
- binding.etMnc.text.toString(),
- binding.etIccid.text.toString(),
- binding.etImsi.text.toString(),
- binding.etImei.text.toString(),
- binding.etCountry.text.toString(),
- binding.etAreaCode.text.toString(),
- false,
- binding.etCarrierId.text.toString(),
- binding.etCarrierName.text.toString()
- )
- )
- }
- binding.btnSave.setIconResource(R.drawable.ic_done)
- binding.btnSave.text = "OK"
- delay(1500)
- binding.btnSave.isEnabled = true
- binding.btnSave.icon = null
- binding.btnSave.text = "Save"
- }
- }
- }
|