package com.example.modifier.fragments; import android.annotation.SuppressLint; import android.content.Intent; 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.annotation.NonNull; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import com.android.volley.Request; import com.android.volley.RequestQueue; import com.android.volley.toolbox.JsonObjectRequest; import com.android.volley.toolbox.RequestFuture; import com.android.volley.toolbox.Volley; import com.example.modifier.model.TelephonyConfig; import com.example.modifier.Global; import com.example.modifier.MainActivity; import com.example.modifier.ModifierService; import com.example.modifier.R; import com.example.modifier.Utils; import com.example.modifier.databinding.FragmentSettingsBinding; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.gson.Gson; import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; import org.json.JSONObject; import java.io.File; import java.io.FileWriter; import java.time.Instant; import java.util.Objects; import java.util.Optional; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; public class SettingsFragment extends Fragment { private FragmentSettingsBinding binding; Handler handler = new Handler(Looper.getMainLooper()); ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(8); public SettingsFragment() { Log.i("SettingsFragment", "SettingsFragment"); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @SuppressLint("SetTextI18n") @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (binding != null) { return binding.getRoot(); } binding = FragmentSettingsBinding.inflate(inflater, container, false); binding.tlIccid.setEndIconOnClickListener(v -> { binding.etIccid.setText(RandomStringUtils.randomNumeric(20)); }); binding.tlImsi.setEndIconOnClickListener(v -> { String mcc = Optional.ofNullable(binding.etMcc.getText()).map(Objects::toString).orElse(""); String mnc = Optional.ofNullable(binding.etMnc.getText()).map(Objects::toString).orElse(""); if (StringUtils.isEmpty(mcc) || StringUtils.isEmpty(mnc)) { Toast.makeText(getContext(), "MCC and MNC are required", Toast.LENGTH_SHORT).show(); return; } binding.etImsi.setText(mcc + mnc + RandomStringUtils.randomNumeric(15 - mcc.length() - mnc.length())); }); binding.tlImei.setEndIconOnClickListener(v -> { binding.etImei.setText(Utils.generateIMEI()); }); binding.btnSave.setOnClickListener(v -> { onSave(); }); binding.etServer.setThreshold(1000); binding.btnServer.setOnClickListener(v -> { String server = binding.etServer.getText().toString(); if (StringUtils.isEmpty(server)) { Toast.makeText(getContext(), "Server is required", Toast.LENGTH_SHORT).show(); return; } Global.saveServer(server, binding.etDeviceLabel.getText().toString()); binding.etServer.setSimpleItems(Global.getServers().toArray(new String[0])); ModifierService modifierService = ModifierService.getInstance(); if (modifierService != null) { modifierService.connect(); } Utils.makeLoadingButton(getContext(), binding.btnServer); binding.btnServer.setEnabled(false); handler.postDelayed(() -> { binding.btnServer.setIconResource(R.drawable.ic_done); binding.btnServer.setText("OK"); handler.postDelayed(() -> { binding.btnServer.setEnabled(true); binding.btnServer.setIcon(null); binding.btnServer.setText("Save"); }, 1500); }, 500); }); binding.btnRequest.setOnClickListener(v -> { Utils.makeLoadingButton(getContext(), binding.btnRequest); binding.btnRequest.setEnabled(false); executor.submit(() -> { try { RequestQueue queue = Volley.newRequestQueue(getContext()); RequestFuture future = RequestFuture.newFuture(); JsonObjectRequest request = new JsonObjectRequest(Request.Method.PUT, Global.serverUrl + "/api/rcs-number", null, future, future); queue.add(request); JSONObject jsonObject = future.get(60, TimeUnit.SECONDS); Integer id = jsonObject.getInt("id"); String expiryTimeStr = jsonObject.getString("expiryTime"); Instant expiryTime = Instant.parse(expiryTimeStr); String number = jsonObject.getString("number"); String mcc = jsonObject.getString("mcc"); String mnc = jsonObject.getString("mnc"); String country = jsonObject.getString("country"); String iccid = RandomStringUtils.randomNumeric(20); String imsi = mcc + mnc + RandomStringUtils.randomNumeric(15 - mcc.length() - mnc.length()); String imei = Utils.generateIMEI(); Global.save(new TelephonyConfig(number, mcc, mnc, iccid, imsi, imei, country)); Global.stop(false, true, true); handler.post(() -> { TelephonyConfig telephonyConfig = Global.telephonyConfig; binding.etNumber.setText(telephonyConfig.getNumber()); binding.etMcc.setText(telephonyConfig.getMcc()); binding.etMnc.setText(telephonyConfig.getMnc()); binding.etIccid.setText(telephonyConfig.getIccid()); binding.etImsi.setText(telephonyConfig.getImsi()); binding.etImei.setText(telephonyConfig.getImei()); binding.etCountry.setText(telephonyConfig.getCountry()); binding.btnRequest.setText("Waiting for OTP..."); }); try { Utils.runAsRoot("am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER com.google.android.apps.messaging"); Thread.sleep(1000); Utils.runAsRoot("am start com.google.android.apps.messaging/com.google.android.apps.messaging.ui.appsettings.SettingsActivity;"); Utils.runAsRoot("am start com.google.android.apps.messaging/com.google.android.apps.messaging.ui.appsettings.RcsSettingsActivity;"); Thread.sleep(1000); Intent intent = new Intent(getContext(), MainActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); startActivity(intent); } catch (Exception e) { e.printStackTrace(); } boolean success = false; while (Instant.now().isBefore(expiryTime)) { try { Log.i("SettingsFragment", "Waiting for OTP..."); RequestFuture future1 = RequestFuture.newFuture(); JsonObjectRequest request1 = new JsonObjectRequest(Request.Method.GET, Global.serverUrl + "/api/rcs-number/" + id, null, future1, future1); queue.add(request1); JSONObject jsonObject1 = null; try { jsonObject1 = future1.get(60, TimeUnit.SECONDS); } catch (Exception e) { e.printStackTrace(); } if (jsonObject1 == null) { continue; } String status = jsonObject1.optString("status"); if ("success".equals(status)) { String message = jsonObject1.optString("message"); Matcher matcher = Pattern.compile("Your Messenger verification code is G-(\\d{6})") .matcher(message); if (matcher.find()) { String otp = matcher.group(1); Intent intent = new Intent(); intent.setAction("com.example.modifier.sms"); intent.putExtra("sender", "3538"); intent.putExtra("message", "Your Messenger verification code is G-" + otp); getContext().sendBroadcast(intent); success = true; break; } } Thread.sleep(2000); } catch (Exception e) { e.printStackTrace(); } } if (!success) { handler.post(() -> { new MaterialAlertDialogBuilder(getContext()) .setTitle("Error") .setMessage("Failed to get OTP") .setPositiveButton("OK", (dialog, which) -> { dialog.dismiss(); }) .show(); }); } else { handler.post(() -> { new MaterialAlertDialogBuilder(getContext()) .setTitle("Success") .setMessage("OTP sent successfully") .setPositiveButton("OK", (dialog, which) -> { dialog.dismiss(); }) .show(); }); } } catch (Exception e) { e.printStackTrace(); } handler.post(() -> { binding.btnRequest.setEnabled(true); binding.btnRequest.setIcon(null); binding.btnRequest.setText("Request"); }); }); }); executor.execute(() -> { Global.load(); handler.post(() -> { binding.etServer.setText(Global.serverUrl); binding.etServer.setSimpleItems(Global.getServers().toArray(new String[0])); binding.etDeviceLabel.setText(Global.name); TelephonyConfig telephonyConfig = Global.telephonyConfig; binding.etNumber.setText(telephonyConfig.getNumber()); binding.etMcc.setText(telephonyConfig.getMcc()); binding.etMnc.setText(telephonyConfig.getMnc()); binding.etIccid.setText(telephonyConfig.getIccid()); binding.etImsi.setText(telephonyConfig.getImsi()); binding.etImei.setText(telephonyConfig.getImei()); binding.etCountry.setText(telephonyConfig.getCountry()); }); }); return binding.getRoot(); } private void onSave() { Utils.makeLoadingButton(getContext(), binding.btnSave); binding.btnSave.setEnabled(false); executor.execute(() -> { save(); handler.post(() -> { binding.btnSave.setIconResource(R.drawable.ic_done); binding.btnSave.setText("OK"); handler.postDelayed(() -> { binding.btnSave.setEnabled(true); binding.btnSave.setIcon(null); binding.btnSave.setText("Save"); }, 1500); }); }); } private void save() { try { TelephonyConfig telephonyConfig = new TelephonyConfig(binding.etNumber.getText().toString(), binding.etMcc.getText().toString(), binding.etMnc.getText().toString(), binding.etIccid.getText().toString(), binding.etImsi.getText().toString(), binding.etImei.getText().toString(), binding.etCountry.getText().toString()); File file = new File(ContextCompat.getDataDir(getContext()), "config.json"); Gson gson = new Gson(); String json = gson.toJson(telephonyConfig); try { FileWriter writer = new FileWriter(file); writer.write(json); writer.close(); } catch (Exception e) { e.printStackTrace(); } Utils.runAsRoot( "cp " + file.getPath() + " /data/data/com.android.phone/rcsConfig.json", "echo 'copied to phone'", "chmod 777 /data/data/com.android.phone/rcsConfig.json"); } catch (Exception e) { e.printStackTrace(); } } }