x1ongzhu 1 ano atrás
pai
commit
1c4ca4d452

+ 1 - 0
app/build.gradle

@@ -71,4 +71,5 @@ dependencies {
     }
     implementation 'com.android.volley:volley:1.2.1'
     implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01"
+    implementation "androidx.work:work-runtime:2.9.0"
 }

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

@@ -37,11 +37,13 @@
         <activity
             android:name=".MainActivity"
             android:exported="true"
+            android:launchMode="singleInstance"
             android:windowSoftInputMode="adjustResize">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
 
                 <category android:name="android.intent.category.LAUNCHER" />
+                <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
     </application>

+ 10 - 2
app/src/main/java/com/example/modifier/BackupAdapter.java

@@ -2,6 +2,7 @@ package com.example.modifier;
 
 import android.annotation.SuppressLint;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
@@ -54,9 +55,16 @@ public class BackupAdapter extends RecyclerView.Adapter<BackupAdapter.BackupView
                     .setPositiveButton("Yes", (dialog, which) -> {
                         new Thread(() -> {
                             try {
+                                PackageManager packageManager = context.getPackageManager();
+                                int uid = packageManager.getApplicationInfo("com.google.android.apps.messaging", 0).uid;
+                                int gmsUid = packageManager.getApplicationInfo("com.google.android.gms", 0).uid;
+
                                 Global.save(backup.getConfig());
-                                Global.stop(false, false, true);
-                                Utils.runAsRoot("pm clear com.google.android.apps.messaging", "cp -rf " + backup.getPath() + "/data/* /data/data/com.google.android.apps.messaging/");
+                                Utils.runAsRoot("pm clear com.google.android.apps.messaging",
+                                        "cp -rf " + backup.getPath() + "/data/* /data/data/com.google.android.apps.messaging/",
+                                        "chown -R " + uid + ":" + uid + " /data/data/com.google.android.apps.messaging/*",
+                                        "cp -rf " + backup.getPath() + "/gmsData/* /data/data/com.google.android.gms/",
+                                        "chown -R " + gmsUid + ":" + gmsUid + " /data/data/com.google.android.gms/*");
                                 Global.stop(false, false, true);
                             } catch (Exception e) {
                                 e.printStackTrace();

+ 144 - 0
app/src/main/java/com/example/modifier/FixFragmentNavigator.java

@@ -0,0 +1,144 @@
+package com.example.modifier;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+
+import androidx.annotation.IdRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+import androidx.navigation.NavDestination;
+import androidx.navigation.NavOptions;
+import androidx.navigation.Navigator;
+import androidx.navigation.fragment.FragmentNavigator;
+
+import java.lang.reflect.Field;
+import java.util.ArrayDeque;
+import java.util.List;
+import java.util.Map;
+
+@Navigator.Name("fixFragment")
+public class FixFragmentNavigator extends FragmentNavigator {
+    private final String TAG = "ReLoadFragmentNavictor";
+    private final Context mContext;
+    private final FragmentManager mFragmentManager;
+    private final int mContainerId;
+    String oldClassName = null;
+
+    public FixFragmentNavigator(@NonNull Context context, @NonNull FragmentManager manager, int containerId) {
+        super(context, manager, containerId);
+        mContext = context;
+        mFragmentManager = manager;
+        mContainerId = containerId;
+    }
+
+    @Nullable
+    @Override
+    public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args, @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
+
+        if (mFragmentManager.isStateSaved()) {
+            Log.i(TAG, "Ignoring navigate() call: FragmentManager has already"
+                    + " saved its state");
+            return null;
+        }
+        String className = destination.getClassName();
+        if (className.equals(oldClassName)) {
+            return null;
+        }
+        oldClassName = className;
+        if (className.charAt(0) == '.') {
+            className = mContext.getPackageName() + className;
+        }
+        Fragment frag = mFragmentManager.findFragmentByTag(className);
+        if (null == frag) {
+            //不存在,则创建
+            frag = instantiateFragment(mContext, mFragmentManager, className, args);
+
+        }
+
+        frag.setArguments(args);
+        final FragmentTransaction ft = mFragmentManager.beginTransaction();
+
+        int enterAnim = navOptions != null ? navOptions.getEnterAnim() : -1;
+        int exitAnim = navOptions != null ? navOptions.getExitAnim() : -1;
+        int popEnterAnim = navOptions != null ? navOptions.getPopEnterAnim() : -1;
+        int popExitAnim = navOptions != null ? navOptions.getPopExitAnim() : -1;
+        if (enterAnim != -1 || exitAnim != -1 || popEnterAnim != -1 || popExitAnim != -1) {
+            enterAnim = enterAnim != -1 ? enterAnim : 0;
+            exitAnim = exitAnim != -1 ? exitAnim : 0;
+            popEnterAnim = popEnterAnim != -1 ? popEnterAnim : 0;
+            popExitAnim = popExitAnim != -1 ? popExitAnim : 0;
+            ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim);
+        }
+
+//        ft.replace(mContainerId, frag);
+        List<Fragment> fragments = mFragmentManager.getFragments();
+        for (Fragment fragment : fragments) {
+            ft.hide(fragment);
+        }
+        if (!frag.isAdded()) {
+            ft.add(mContainerId, frag, className);
+        }
+
+        ft.show(frag);
+        ft.setPrimaryNavigationFragment(frag);
+
+        final @IdRes int destId = destination.getId();
+
+        //通过反射获取mBackStack
+        ArrayDeque<Integer> mBackStack;
+        try {
+            Field field = FragmentNavigator.class.getDeclaredField("mBackStack");
+            field.setAccessible(true);
+            mBackStack = (ArrayDeque<Integer>) field.get(this);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+
+        final boolean initialNavigation = mBackStack.isEmpty();
+        final boolean isSingleTopReplacement = navOptions != null && !initialNavigation
+                && navOptions.shouldLaunchSingleTop()
+                && mBackStack.peekLast() == destId;
+
+        boolean isAdded;
+        if (initialNavigation) {
+            isAdded = true;
+        } else if (isSingleTopReplacement) {
+            if (mBackStack.size() > 1) {
+                mFragmentManager.popBackStack(
+                        generateBackStackName(mBackStack.size(), mBackStack.peekLast()),
+                        FragmentManager.POP_BACK_STACK_INCLUSIVE);
+                ft.addToBackStack(generateBackStackName(mBackStack.size(), destId));
+            }
+            isAdded = false;
+        } else {
+            ft.addToBackStack(generateBackStackName(mBackStack.size() + 1, destId));
+            isAdded = true;
+        }
+        if (navigatorExtras instanceof Extras) {
+            Extras extras = (Extras) navigatorExtras;
+            for (Map.Entry<View, String> sharedElement : extras.getSharedElements().entrySet()) {
+                ft.addSharedElement(sharedElement.getKey(), sharedElement.getValue());
+            }
+        }
+        ft.setReorderingAllowed(true);
+        ft.commit();
+        if (isAdded) {
+            mBackStack.add(destId);
+            return destination;
+        } else {
+            return null;
+        }
+    }
+
+    //navigate需要打方法重复类直接复制过来就可以
+    @NonNull
+    private String generateBackStackName(int backStackIndex, int destId) {
+        return backStackIndex + "-" + destId;
+    }
+}

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

@@ -84,6 +84,14 @@ public class Global {
 
     public static void save(Config config) {
         Context context = Utils.getContext();
+        Global.config.setMcc(config.getMcc());
+        Global.config.setMnc(config.getMnc());
+        Global.config.setNumber(config.getNumber());
+        Global.config.setCountry(config.getCountry());
+        Global.config.setIccid(config.getIccid());
+        Global.config.setImei(config.getImei());
+        Global.config.setImsi(config.getImsi());
+
         try {
             File file = new File(ContextCompat.getDataDir(context), "config.json");
             Gson gson = new Gson();

+ 8 - 0
app/src/main/java/com/example/modifier/ModifierService.java

@@ -43,6 +43,12 @@ import io.socket.emitter.Emitter;
 
 public class ModifierService extends android.accessibilityservice.AccessibilityService implements Emitter.Listener {
 
+    public interface JobListener {
+        void onSuccess();
+
+        void onFail();
+    }
+
     private static final String TAG = "ModifierService";
 
     public static final String NAME = BuildConfig.APPLICATION_ID + ".ModifierService";
@@ -129,6 +135,8 @@ public class ModifierService extends android.accessibilityservice.AccessibilityS
                     if (data != null && StringUtils.isNoneBlank(id)) {
                         runTask(id, data);
                     }
+                } else if ("changeNumber".equals(action)) {
+
                 }
             }
         }

+ 9 - 1
app/src/main/java/com/example/modifier/fragments/BackupFragment.java

@@ -1,5 +1,6 @@
 package com.example.modifier.fragments;
 
+import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -58,6 +59,9 @@ public class BackupFragment extends Fragment {
     @Override
     public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
                              Bundle savedInstanceState) {
+        if (binding != null) {
+            return binding.getRoot();
+        }
         binding = FragmentBackupBinding.inflate(inflater, container, false);
         binding.fabBackup.setOnClickListener(v -> {
 
@@ -83,7 +87,6 @@ public class BackupFragment extends Fragment {
         binding.rvBackup.setAdapter(adapter);
         binding.refresh.setOnRefreshListener(this::loadBackups);
         loadBackups();
-
         return binding.getRoot();
     }
 
@@ -102,6 +105,11 @@ public class BackupFragment extends Fragment {
 
             Utils.runAsRoot("cp -r /data/data/com.google.android.apps.messaging/* " + dataDir.getPath());
 
+            File gmsDataDir = new File(backupDir, "gmsData");
+            gmsDataDir.mkdirs();
+
+            Utils.runAsRoot("cp -r /data/data/com.google.android.gms/* " + gmsDataDir.getPath());
+
             Log.d("BackupFragment", "Backup created at " + backupDir.getPath());
 
             handler.post(() -> {

+ 147 - 2
app/src/main/java/com/example/modifier/fragments/SettingsFragment.java

@@ -1,6 +1,7 @@
 package com.example.modifier.fragments;
 
 import android.annotation.SuppressLint;
+import android.content.Intent;
 import android.os.Bundle;
 
 import androidx.annotation.NonNull;
@@ -9,39 +10,57 @@ import androidx.fragment.app.Fragment;
 
 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 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.Global;
 import com.example.modifier.Config;
+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.FileReader;
 import java.io.FileWriter;
+import java.io.IOException;
+import java.time.Instant;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 public class SettingsFragment extends Fragment {
 
     private FragmentSettingsBinding binding;
 
     Handler handler = new Handler(Looper.getMainLooper());
-    ExecutorService executor = Executors.newFixedThreadPool(4);
+    ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(8);
 
     public SettingsFragment() {
-
+        Log.i("SettingsFragment", "SettingsFragment");
     }
 
 
@@ -55,6 +74,9 @@ public class SettingsFragment extends Fragment {
     @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));
@@ -102,6 +124,129 @@ public class SettingsFragment extends Fragment {
             }, 500);
         });
 
+        binding.btnRequest.setOnClickListener(v -> {
+
+            Utils.addProgressIndicator(getContext(), binding.btnRequest);
+            binding.btnRequest.setEnabled(false);
+            executor.submit(() -> {
+                try {
+
+                    RequestQueue queue = Volley.newRequestQueue(getContext());
+                    RequestFuture<JSONObject> 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 Config(number, mcc, mnc, iccid, imsi, imei, country));
+
+                    Global.stop(false, true, true);
+
+                    handler.post(() -> {
+                        Config config = Global.config;
+                        binding.etNumber.setText(config.getNumber());
+                        binding.etMcc.setText(config.getMcc());
+                        binding.etMnc.setText(config.getMnc());
+                        binding.etIccid.setText(config.getIccid());
+                        binding.etImsi.setText(config.getImsi());
+                        binding.etImei.setText(config.getImei());
+                        binding.etCountry.setText(config.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<JSONObject> 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(() -> {

+ 3 - 0
app/src/main/java/com/example/modifier/fragments/UtilsFragment.java

@@ -44,6 +44,9 @@ public class UtilsFragment extends Fragment {
     @Override
     public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
                              Bundle savedInstanceState) {
+        if (binding != null) {
+            return binding.getRoot();
+        }
         binding = FragmentUtilsBinding.inflate(inflater, container, false);
         binding.btnClear.setOnClickListener(v -> {
             onClear();

+ 24 - 8
app/src/main/res/layout/fragment_settings.xml

@@ -200,18 +200,34 @@
                             android:lines="1" />
                     </com.google.android.material.textfield.TextInputLayout>
 
-
-                    <com.google.android.material.button.MaterialButton
-                        android:id="@+id/btn_save"
-                        android:layout_width="120dp"
+                    <LinearLayout
+                        android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
-                        android:layout_gravity="center"
+                        android:layout_gravity="center_horizontal"
                         android:layout_marginTop="16dp"
-                        android:text="Save"
-                        android:visibility="visible" />
+                        android:orientation="horizontal">
+
+                        <com.google.android.material.button.MaterialButton
+                            android:id="@+id/btn_request"
+                            style="?attr/materialIconButtonFilledTonalStyle"
+                            android:layout_width="120dp"
+                            android:layout_height="wrap_content"
+                            android:layout_gravity="center"
+                            android:text="Request"
+                            android:visibility="visible" />
+
+                        <com.google.android.material.button.MaterialButton
+                            android:id="@+id/btn_save"
+                            android:layout_width="120dp"
+                            android:layout_height="wrap_content"
+                            android:layout_gravity="center"
+                            android:layout_marginLeft="16dp"
+                            android:text="Save"
+                            android:visibility="visible" />
+                    </LinearLayout>
+
                 </LinearLayout>
             </com.google.android.material.card.MaterialCardView>
         </LinearLayout>
     </ScrollView>
-
 </androidx.constraintlayout.widget.ConstraintLayout>