| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397 |
- 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.os.Handler
- import android.os.Looper
- 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.Global
- import com.example.modifier.Global.save
- import com.example.modifier.Global.saveServer
- import com.example.modifier.Global.servers
- import com.example.modifier.R
- import com.example.modifier.Utils
- 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.TelephonyConfig
- import com.example.modifier.service.ModifierService
- import com.example.modifier.service.ModifierService.Companion
- 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.put
- import io.ktor.client.plugins.retry
- 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 io.ktor.http.isSuccess
- import kotlinx.coroutines.Dispatchers
- import kotlinx.coroutines.delay
- import kotlinx.coroutines.launch
- import kotlinx.coroutines.withContext
- import kotlinx.coroutines.withTimeout
- import kotlinx.coroutines.withTimeoutOrNull
- 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 = "SettingsFragment"
- private lateinit var binding: FragmentSettingsBinding
- var handler: Handler = Handler(Looper.getMainLooper())
- lateinit var requestPermissionLauncher: ActivityResultLauncher<String>
- 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.
- }
- }
- }
- override fun onResume() {
- super.onResume()
- lifecycleScope.launch {
- loadConfigs()
- }
- }
- 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(
- Global.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(Utils.generateIMEI())
- }
- binding.btnSave.setOnClickListener {
- onSave()
- }
- binding.etServer.threshold = 1000
- 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
- }
- saveServer(server, binding.etDeviceLabel.text.toString())
- binding.etServer.setSimpleItems(servers.toTypedArray<String>())
- val modifierService = instance
- modifierService?.connect()
- lifecycleScope.launch {
- 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 = Utils.getUniqueID()
- )
- )
- }
- } 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 = Global.genICCID(mnc, areaCode)
- val imsi =
- mcc + mnc + RandomStringUtils.randomNumeric(15 - mcc.length - mnc.length)
- val imei = Utils.generateIMEI()
- save(
- TelephonyConfig(
- number,
- mcc,
- mnc,
- iccid,
- imsi,
- imei,
- country,
- areaCode,
- false,
- res.carrierId,
- res.carrierName
- )
- )
- loadConfigs()
- }
- binding.btnRequest.icon = null
- binding.btnRequest.isEnabled = true
- binding.btnRequest.text = "Request"
- }
- }
- val prefs = requireContext().getSharedPreferences("settings", Context.MODE_PRIVATE)
- binding.switchClean.isChecked = prefs.getBoolean("do_not_clean", false)
- binding.switchClean.setOnCheckedChangeListener { buttonView, isChecked ->
- prefs.edit().putBoolean("do_not_clean", isChecked).apply()
- }
- binding.switchRequest.isChecked = prefs.getBoolean("do_not_request", false)
- binding.switchRequest.setOnCheckedChangeListener { buttonView, isChecked ->
- prefs.edit().putBoolean("do_not_request", isChecked).apply()
- }
- binding.swReset.isChecked = prefs.getBoolean("do_not_reset", false)
- binding.swReset.setOnCheckedChangeListener { buttonView, isChecked ->
- prefs.edit().putBoolean("do_not_reset", 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}
- |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
- )
- }
- }
- }
- lifecycleScope.launch {
- loadConfigs()
- try {
- delay(5000)
- val req = RcsNumberRequest(
- deviceId = Utils.getUniqueID()
- )
- val response = KtorClient.put(
- RcsNumberApi()
- ) {
- contentType(ContentType.Application.Json)
- setBody(req)
- }
- var rcsNumber = response.body<RcsNumberResponse>()
- Log.i("xxx", "requestNumber response: $rcsNumber")
- } catch (e: Exception) {
- Log.e("xxx", "requestNumber error: $e")
- }
- }
- return binding.root
- }
- private suspend fun loadConfigs() {
- withContext(Dispatchers.IO) {
- Global.load()
- withContext(Dispatchers.Main) {
- binding.etServer.setText(Global.serverUrl)
- binding.etServer.setSimpleItems(servers.toTypedArray<String>())
- binding.etDeviceLabel.setText(Global.name)
- val telephonyConfig = Global.telephonyConfig
- binding.etNumber.setText(telephonyConfig.number)
- binding.etMcc.setText(telephonyConfig.mcc)
- binding.etMnc.setText(telephonyConfig.mnc)
- binding.etIccid.setText(telephonyConfig.iccid)
- binding.etImsi.setText(telephonyConfig.imsi)
- binding.etImei.setText(telephonyConfig.imei)
- binding.etCountry.setText(telephonyConfig.country)
- binding.etAreaCode.setText(telephonyConfig.areaCode)
- binding.etCarrierId.setText(telephonyConfig.carrierId)
- binding.etCarrierName.setText(telephonyConfig.carrierName)
- }
- }
- }
- private fun onSave() {
- Utils.makeLoadingButton(context, binding.btnSave)
- binding.btnSave.isEnabled = false
- lifecycleScope.launch {
- withContext(Dispatchers.IO) {
- save(
- TelephonyConfig(
- 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"
- }
- }
- }
|