705 lines
26 KiB
Java
705 lines
26 KiB
Java
package com.halo.assistant;
|
||
|
||
import android.annotation.SuppressLint;
|
||
import android.app.Application;
|
||
import android.content.Context;
|
||
import android.content.IntentFilter;
|
||
import android.content.pm.PackageInfo;
|
||
import android.net.ConnectivityManager;
|
||
import android.os.Build;
|
||
import android.os.StrictMode;
|
||
import android.preference.PreferenceManager;
|
||
import android.text.TextUtils;
|
||
import android.webkit.WebView;
|
||
|
||
import androidx.annotation.NonNull;
|
||
import androidx.collection.ArrayMap;
|
||
import androidx.lifecycle.ProcessLifecycleOwner;
|
||
import androidx.multidex.MultiDexApplication;
|
||
import androidx.webkit.WebViewCompat;
|
||
|
||
import com.alibaba.android.arouter.launcher.ARouter;
|
||
import com.facebook.animated.giflite.GifDecoder;
|
||
import com.facebook.imageformat.DefaultImageFormats;
|
||
import com.facebook.imagepipeline.core.ImagePipelineConfig;
|
||
import com.facebook.imagepipeline.core.ImagePipelineFactory;
|
||
import com.facebook.imagepipeline.core.ImageTranscoderType;
|
||
import com.facebook.imagepipeline.core.MemoryChunkType;
|
||
import com.facebook.imagepipeline.decoder.ImageDecoderConfig;
|
||
import com.gh.ad.AdDelegateHelper;
|
||
import com.gh.base.GlobalActivityLifecycleObserver;
|
||
import com.gh.common.FixedRateJobHelper;
|
||
import com.gh.common.filter.RegionSettingHelper;
|
||
import com.gh.common.prioritychain.GlobalPriorityChainHelper;
|
||
import com.gh.common.util.ActivationHelper;
|
||
import com.gh.common.util.DataUtils;
|
||
import com.gh.common.util.DeviceTokenUtils;
|
||
import com.gh.common.util.DownloadNotificationHelper;
|
||
import com.gh.common.util.DownloadObserver;
|
||
import com.gh.common.util.LogUtils;
|
||
import com.gh.common.util.LunchType;
|
||
import com.gh.common.util.PackageChangeHelper;
|
||
import com.gh.common.util.PackageHelper;
|
||
import com.gh.common.util.PackageUtils;
|
||
import com.gh.common.util.SignatureRepository;
|
||
import com.gh.common.videolog.VideoRecordUtils;
|
||
import com.gh.download.DownloadManager;
|
||
import com.gh.download.simple.DownloadMessageHandler;
|
||
import com.gh.download.simple.SimpleDownloadDatabase;
|
||
import com.gh.gamecenter.BuildConfig;
|
||
import com.gh.gamecenter.common.constant.Config;
|
||
import com.gh.gamecenter.common.constant.Constants;
|
||
import com.gh.gamecenter.common.constant.RouteConsts;
|
||
import com.gh.gamecenter.common.exposure.meta.MetaUtil;
|
||
import com.gh.gamecenter.common.image.EmptyDecoder;
|
||
import com.gh.gamecenter.common.tracker.Tracker;
|
||
import com.gh.gamecenter.common.utils.DarkModeUtils;
|
||
import com.gh.gamecenter.common.utils.DeviceUtils;
|
||
import com.gh.gamecenter.common.utils.EnvHelper;
|
||
import com.gh.gamecenter.common.utils.ExtensionsKt;
|
||
import com.gh.gamecenter.common.utils.ImageUtils;
|
||
import com.gh.gamecenter.common.utils.SensorsBridge;
|
||
import com.gh.gamecenter.core.AppExecutor;
|
||
import com.gh.gamecenter.core.iinterface.IApplication;
|
||
import com.gh.gamecenter.core.provider.IFlavorProvider;
|
||
import com.gh.gamecenter.core.provider.IPushProvider;
|
||
import com.gh.gamecenter.core.utils.DisplayUtils;
|
||
import com.gh.gamecenter.core.utils.GsonUtils;
|
||
import com.gh.gamecenter.core.utils.SPUtils;
|
||
import com.gh.gamecenter.login.user.UserManager;
|
||
import com.gh.gamecenter.login.user.UserRepository;
|
||
import com.gh.gamecenter.oaid.OAIDHelper;
|
||
import com.gh.gamecenter.packagehelper.PackageRepository;
|
||
import com.gh.gamecenter.provider.FlavorProviderImp;
|
||
import com.gh.gamecenter.receiver.ActivitySkipReceiver;
|
||
import com.gh.gamecenter.receiver.DownloadReceiver;
|
||
import com.gh.gamecenter.receiver.InstallReceiver;
|
||
import com.gh.gamecenter.receiver.NetworkStateReceiver;
|
||
import com.gh.gamecenter.wrapper.MainWrapperRepository;
|
||
import com.gh.vspace.VHelper;
|
||
import com.github.piasy.biv.BigImageViewer;
|
||
import com.github.piasy.biv.loader.fresco.FrescoImageLoader;
|
||
import com.lg.ndownload.DownloadCore;
|
||
import com.lightgame.download.DownloadEntity;
|
||
import com.lightgame.download.DownloadStatus;
|
||
import com.lg.ndownload.DownloadDbManager;
|
||
import com.lg.vspace.VaApp;
|
||
import com.lightgame.utils.Utils;
|
||
import com.llew.huawei.verifier.LoadedApkHuaWei;
|
||
import com.lody.virtual.client.core.VirtualCore;
|
||
import com.shuyu.gsyvideoplayer.cache.CacheFactory;
|
||
import com.shuyu.gsyvideoplayer.player.PlayerFactory;
|
||
|
||
import java.util.List;
|
||
import java.util.ServiceLoader;
|
||
|
||
import io.reactivex.Single;
|
||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||
import io.reactivex.plugins.RxJavaPlugins;
|
||
import io.reactivex.schedulers.Schedulers;
|
||
import tv.danmaku.ijk.media.exo2.Exo2PlayerManager;
|
||
import tv.danmaku.ijk.media.exo2.ExoPlayerCacheManager;
|
||
|
||
public class HaloApp extends MultiDexApplication {
|
||
|
||
private static HaloApp mInstance;
|
||
private static final ArrayMap<String, Object> sObjectMap = new ArrayMap<>();
|
||
|
||
private String mChannel;
|
||
private String mGid;
|
||
private String mTemporaryLocalDeviceId; // 临时设备ID (本地生成,与GID不相关)
|
||
private String mUA;
|
||
private String mOAID = "";
|
||
private String mServerUserMark = ""; // 服务端用来标记用户是新/老的标记,有 new 和 old 两个,获取不到时为空
|
||
|
||
private boolean mIsPostInitialized = false; // 是否已经延迟初始化过相关组件,避免重复初始化
|
||
private boolean mIsReinstallTheSameVersion = false; // 当前用户是否重新安装了一次当前版本后的第一次打开
|
||
|
||
public long deviceRamSize = 0;
|
||
public boolean isBrandNewInstall = false; // 当前用户是否是安装光环后第一次打开
|
||
public boolean isNewForThisVersion = false; // 当前用户是否是安装当前版本后第一次打开 (包括全新和更新)
|
||
public boolean isRunningForeground = false; // 标记当前 APP 是否处于前台运行中
|
||
|
||
public boolean isAlreadyUpAndRunning = false; // 应用是否处于运行中状态 (进入到 MainActivity 就当运行中)
|
||
|
||
public boolean isSkippingThirdParty = false; // 应用是否处于跳转第三方Activity状态
|
||
private List<String> webViewAbiList;
|
||
private final IFlavorProvider mFlavorProvider = new FlavorProviderImp();
|
||
private final ServiceLoader<IApplication> mApplicationList = ServiceLoader.load(IApplication.class, this.getClass().getClassLoader());
|
||
private LunchType mLaunchType = null;
|
||
|
||
public static void put(String key, Object object) {
|
||
sObjectMap.put(key, object);
|
||
}
|
||
|
||
public static Object get(String key, boolean isRemove) {
|
||
if (isRemove) {
|
||
return sObjectMap.remove(key);
|
||
} else {
|
||
return sObjectMap.get(key);
|
||
}
|
||
}
|
||
|
||
public static void remove(String key) {
|
||
sObjectMap.remove(key);
|
||
}
|
||
|
||
public void setGid(String gid) {
|
||
mGid = gid;
|
||
}
|
||
|
||
public String getGid() {
|
||
return mGid;
|
||
}
|
||
|
||
public void refreshGid() {
|
||
DataUtils.getGid();
|
||
}
|
||
|
||
public void setLocalTemporaryDeviceId(String key) {
|
||
mTemporaryLocalDeviceId = key;
|
||
}
|
||
|
||
public String getTemporaryLocalDeviceId() {
|
||
return mTemporaryLocalDeviceId;
|
||
}
|
||
|
||
public void setServerUserMark(String mark) {
|
||
mServerUserMark = mark;
|
||
}
|
||
|
||
public String getServerUserMark() {
|
||
return mServerUserMark;
|
||
}
|
||
|
||
public static synchronized HaloApp getInstance() {
|
||
return mInstance;
|
||
}
|
||
|
||
|
||
public String getChannel() {
|
||
// 存在 IO 初始化线程阻塞(万物皆可阻塞)导致 mChannel 为空的情况,这里特殊处理下
|
||
if (TextUtils.isEmpty(mChannel)) {
|
||
return "";
|
||
} else {
|
||
return mChannel;
|
||
}
|
||
}
|
||
|
||
public void setChannel(String channel) {
|
||
mChannel = channel;
|
||
}
|
||
|
||
public void setOAID(String oaid) {
|
||
mOAID = oaid;
|
||
}
|
||
|
||
public String getOAID() {
|
||
return mOAID;
|
||
}
|
||
|
||
public String getUserAgent() {
|
||
if (TextUtils.isEmpty(mUA)) {
|
||
mUA = DeviceUtils.getUserAgent();
|
||
}
|
||
return mUA;
|
||
}
|
||
|
||
public List<String> getWebViewAbiList() {
|
||
return webViewAbiList;
|
||
}
|
||
|
||
@Override
|
||
public void onCreate() {
|
||
super.onCreate();
|
||
VaApp.get().onCreate(this);
|
||
initArouter();
|
||
|
||
mInstance = this;
|
||
|
||
// 每个进程都用自己的进程名作为后缀的文件夹来存 WebView cache
|
||
// https://sentry.shanqu.cc/organizations/lightgame/issues/285063/?project=22&query=is%3Aunresolved
|
||
// https://stackoverflow.com/a/61748345/4812571 这个无效
|
||
// WebView.disableWebView() 也无效
|
||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||
try {
|
||
String process = getProcessName();
|
||
WebView.setDataDirectorySuffix(process);
|
||
} catch (IllegalStateException e) {
|
||
e.printStackTrace();
|
||
}
|
||
}
|
||
|
||
ExtensionsKt.doOnMainProcessOnly(() -> {
|
||
for (IApplication application : mApplicationList) {
|
||
application.onCreate(mInstance);
|
||
}
|
||
|
||
// 似乎只是 load SO 不涉及方法调用,所以可以在隐私政策前调用吧?
|
||
OAIDHelper.INSTANCE.doSystemLoad();
|
||
|
||
PlayerFactory.setPlayManager(Exo2PlayerManager.class);
|
||
CacheFactory.setCacheManager(ExoPlayerCacheManager.class);
|
||
|
||
initFresco();
|
||
|
||
isNewForThisVersion =
|
||
PreferenceManager.getDefaultSharedPreferences(this).getBoolean(Constants.SP_NEW_FIRST_LAUNCH_VERSION + PackageUtils.getGhVersionName(), true);
|
||
|
||
AppExecutor.getIoExecutor().execute(() -> {
|
||
initDataHelper();
|
||
Tracker.init(this);
|
||
DownloadCore.init(this);
|
||
|
||
deviceRamSize = DeviceUtils.getTotalRamSizeOfDevice(this);
|
||
mChannel = mFlavorProvider.getChannelStr(this);
|
||
|
||
// 初始化推送
|
||
initPushEngine();
|
||
|
||
// 异步初始化 SP
|
||
SPUtils.getString("");
|
||
});
|
||
|
||
RxJavaPlugins.setIoSchedulerHandler(scheduler -> AppExecutor.INSTANCE.getCachedScheduler());
|
||
|
||
if (isUserAcceptPrivacyPolicy(this)) {
|
||
postInit(false);
|
||
}
|
||
|
||
//设置严格模式
|
||
if (BuildConfig.DEBUG) {
|
||
StrictMode.VmPolicy policy = new StrictMode.VmPolicy.Builder()
|
||
.detectAll()
|
||
.penaltyLog()
|
||
.build();
|
||
StrictMode.setVmPolicy(policy);
|
||
}
|
||
|
||
registerActivityLifecycleCallbacks(new GlobalActivityLifecycleObserver());
|
||
|
||
DarkModeUtils.INSTANCE.initDarkMode();
|
||
|
||
return null;
|
||
});
|
||
|
||
}
|
||
|
||
/**
|
||
* 需要延迟初始化的东西,以下代码调用都放置到用户同意了隐私政策之后
|
||
*/
|
||
public void postInit(boolean initImmediately) {
|
||
if (mIsPostInitialized) return;
|
||
|
||
long delay = 500;
|
||
if (initImmediately) {
|
||
delay = 0;
|
||
}
|
||
postInit(delay);
|
||
|
||
mIsPostInitialized = true;
|
||
}
|
||
|
||
private void postInit(long delay) {
|
||
// 初始化 sentry
|
||
DataUtils.init(this, mChannel);
|
||
|
||
// 初始化广告 SDK
|
||
AdDelegateHelper.INSTANCE.initAdSdk(this);
|
||
|
||
SignatureRepository.INSTANCE.init();
|
||
|
||
// 获取/更新 GID 和 读 SP 的操作不需要 delay
|
||
DataUtils.getGid();
|
||
|
||
// https://jira.shanqu.cc/browse/GHZS-3765
|
||
// 港澳APP去掉oaid的获取
|
||
if (!EnvHelper.isGATApp()) {
|
||
OAIDHelper.INSTANCE.getOAID(HaloApp.this, (s, isSuccess) -> {
|
||
setOAID(s);
|
||
MetaUtil.INSTANCE.refreshMeta();
|
||
SensorsBridge.INSTANCE.setOAID(s);
|
||
|
||
registerPushEngine();
|
||
|
||
// 上报设备安装事件
|
||
if (isNewForThisVersion) {
|
||
final LunchType launchType = getLaunchType();
|
||
LogUtils.uploadDevice(launchType);
|
||
|
||
ActivationHelper.sendActivationInfo();
|
||
}
|
||
return null;
|
||
});
|
||
}
|
||
|
||
// 获取 settings 配置
|
||
com.gh.common.constant.Config.getGhzsSettings();
|
||
|
||
// 5 秒获取后台配置的获取应用列表限制接口结果超时后,回落到关闭状态
|
||
// PackageHelper.INSTANCE.fallbackInstalledPackageApiSwitchAfterTimeout(5000L);
|
||
|
||
String localTemporaryDeviceId = SPUtils.getString(Constants.SP_TEMPORARY_DEVICE_ID);
|
||
if (!TextUtils.isEmpty(localTemporaryDeviceId)) {
|
||
HaloApp.getInstance().setLocalTemporaryDeviceId(localTemporaryDeviceId);
|
||
}
|
||
|
||
// 刷新内存中的用户信息,避免应用进程重建时因没有用户信息数据而显示为掉登录状态
|
||
// 必须放在外面,否则不能及时刷新用户数据
|
||
UserRepository.getInstance().getLoginUserInfo();
|
||
|
||
GlobalPriorityChainHelper.INSTANCE.preStart(isNewForThisVersion);
|
||
|
||
MainWrapperRepository.Companion.getInstance().getDataUnion();
|
||
|
||
AppExecutor.getUiExecutor().executeWithDelay(() -> {
|
||
FixedRateJobHelper.begin();
|
||
|
||
RegionSettingHelper.getRegionSetting();
|
||
|
||
PackageRepository.initData();
|
||
PackageHelper.refreshLocalPackageList();
|
||
PackageHelper.refreshList();
|
||
|
||
initReceiver();
|
||
initPackageChangesReceiver();
|
||
initConnectivityChangesReceiver();
|
||
|
||
initTimeConsumingAction();
|
||
|
||
getWebviewAbiList();
|
||
|
||
// 注册回调以用于做各种统计
|
||
ProcessLifecycleOwner.get().getLifecycle().addObserver(new ProcessorLifeCycleOwner());
|
||
|
||
// 初始化畅玩相关数据
|
||
retrieveVGameInfoIfNeeded();
|
||
|
||
// 移除已安装但还在本地数据库中的包(避免因为没有监听到安装结果导致安装包没有删除的问题)
|
||
removeInstalledButRemainedPackages();
|
||
|
||
// 开发环境不要强制捕获相关异常,这些异常通常是需要处理的
|
||
if (!BuildConfig.DEBUG) {
|
||
RxJavaPlugins.setErrorHandler(throwable -> {
|
||
if (Config.DEFAULT_CHANNEL.equals(mChannel)) {
|
||
Utils.toast(this, "当前页面未捕获的异常:" + throwable.toString());
|
||
}
|
||
});
|
||
}
|
||
}, delay);
|
||
}
|
||
|
||
private void initArouter() {
|
||
if (BuildConfig.DEBUG) { // 这两行必须写在init之前,否则这些配置在init过程中将无效
|
||
ARouter.openLog(); // 打印日志
|
||
ARouter.openDebug(); // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
|
||
}
|
||
ARouter.init(this); // 尽可能早,推荐在Application中初始化
|
||
}
|
||
|
||
public void getWebviewAbiList() {
|
||
int versionCode = SPUtils.getInt(Constants.SP_WEBVIEW_VERSION_CODE);
|
||
final PackageInfo webviewPackageInfo = WebViewCompat.getCurrentWebViewPackage(this);
|
||
if (webviewPackageInfo == null) return;
|
||
int webviewVersionCode = webviewPackageInfo.versionCode;
|
||
String json = SPUtils.getString(Constants.SP_WEBVIEW_ABI_LIST);
|
||
if (versionCode == webviewVersionCode && !json.isEmpty()) {
|
||
try {
|
||
webViewAbiList = GsonUtils.fromJsonList(json);
|
||
return;
|
||
} catch (AssertionError e) {
|
||
e.printStackTrace();
|
||
}
|
||
}
|
||
AppExecutor.getIoExecutor().execute(() -> {
|
||
final String webviewPath = PackageUtils.getWebviewPath(this);
|
||
if (webviewPath == null) return;
|
||
final List<String> abiList = PackageUtils.getApkAbiList(webviewPath);
|
||
webViewAbiList = abiList;
|
||
SPUtils.setString(Constants.SP_WEBVIEW_ABI_LIST, GsonUtils.toJson(abiList));
|
||
SPUtils.setInt(Constants.SP_WEBVIEW_VERSION_CODE, webviewVersionCode);
|
||
});
|
||
}
|
||
|
||
private void initDataHelper() {
|
||
VideoRecordUtils.init(this, AppExecutor.getLogExecutor());
|
||
}
|
||
|
||
// todo 动态注册和静态注册并行的话会触发多次回调
|
||
// 3.5 开始将 targetSdk 升级至 26,原来写在 Manifest 的部分 receiver 由于系统限制需要换成在运行时注册
|
||
private void initReceiver() {
|
||
DownloadReceiver downloadReceiver = new DownloadReceiver();
|
||
IntentFilter downloadFilter = new IntentFilter();
|
||
downloadFilter.addAction(DownloadNotificationHelper.ACTION_DOWNLOAD);
|
||
downloadFilter.addAction(DownloadNotificationHelper.ACTION_VDOWNLOAD);
|
||
this.registerReceiver(downloadReceiver, downloadFilter);
|
||
|
||
InstallReceiver installReceiver = new InstallReceiver();
|
||
IntentFilter installFilter = new IntentFilter();
|
||
installFilter.addAction(DownloadNotificationHelper.ACTION_INSTALL);
|
||
this.registerReceiver(installReceiver, installFilter);
|
||
|
||
ActivitySkipReceiver skipReceiver = new ActivitySkipReceiver();
|
||
IntentFilter skipFilter = new IntentFilter();
|
||
skipFilter.addAction(ActivitySkipReceiver.ACTION_ACTIVITY_SKIP);
|
||
this.registerReceiver(skipReceiver, skipFilter);
|
||
}
|
||
|
||
/**
|
||
* 初始化推送
|
||
*/
|
||
private void initPushEngine() {
|
||
IPushProvider pushProvider = (IPushProvider) ARouter
|
||
.getInstance()
|
||
.build(RouteConsts.provider.push)
|
||
.navigation();
|
||
if (pushProvider != null) pushProvider.initialize(this);
|
||
}
|
||
|
||
/**
|
||
* 同意隐私政策后,注册推送功能
|
||
*/
|
||
@SuppressLint("CheckResult")
|
||
private void registerPushEngine() {
|
||
IPushProvider pushProvider = (IPushProvider) ARouter
|
||
.getInstance()
|
||
.build(RouteConsts.provider.push)
|
||
.navigation();
|
||
|
||
if (pushProvider == null) return;
|
||
|
||
// 绑定账号
|
||
Single<String> bindAccountSingle = pushProvider.bindAccount(getOAID());
|
||
|
||
// 绑定标签
|
||
Single<String> bindTagSingle = !TextUtils.isEmpty(mChannel)
|
||
? pushProvider.bindChannel(mChannel).subscribeOn(Schedulers.io()) : null;
|
||
|
||
// 初始化推送
|
||
pushProvider
|
||
.register(this)
|
||
.flatMap((response) -> {
|
||
// 绑定别名、标签和账号任意一个流程失败,都不会影响其他流程的执行
|
||
Single<String> single = bindAccountSingle
|
||
.onErrorReturn((e) -> response);
|
||
|
||
if (bindTagSingle != null) {
|
||
single = single
|
||
.zipWith(bindTagSingle.onErrorReturn((e) -> response), (s1, s2) -> response);
|
||
}
|
||
|
||
if (UserManager.getInstance().isLoggedIn()) {
|
||
String userId = UserManager.getInstance().getUserId();
|
||
// 绑定别名
|
||
Single<String> bindAliasSingle = pushProvider
|
||
.bindAlias(userId)
|
||
.subscribeOn(Schedulers.io());
|
||
single = single.zipWith(bindAliasSingle.onErrorReturn((e) -> response), (s1, s2) -> response);
|
||
}
|
||
return single;
|
||
})
|
||
.subscribeOn(Schedulers.io())
|
||
.observeOn(AndroidSchedulers.mainThread())
|
||
.subscribe(
|
||
s -> Utils.log("IPushProvider->init push succeed, response: " + s + ",channel: " + getChannel()),
|
||
e -> Utils.log("IPushProvider->init push failed, error: " + e + ", channel: " + getChannel())
|
||
);
|
||
|
||
}
|
||
|
||
private void initPackageChangesReceiver() {
|
||
ProcessLifecycleOwner.get().getLifecycle().addObserver(PackageChangeHelper.INSTANCE);
|
||
}
|
||
|
||
private void initConnectivityChangesReceiver() {
|
||
NetworkStateReceiver receiver = new NetworkStateReceiver();
|
||
IntentFilter intentFilter = new IntentFilter();
|
||
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
|
||
this.registerReceiver(receiver, intentFilter);
|
||
}
|
||
|
||
private void initTimeConsumingAction() {
|
||
AppExecutor.getIoExecutor().execute(() -> {
|
||
// 初始化全局下载监听
|
||
DownloadObserver.initObserver();
|
||
|
||
if ("miui".equals(Build.MANUFACTURER) && Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||
DisplayUtils.sShouldUseLegacyMiuiStatusBarMethod = true;
|
||
}
|
||
|
||
// 避免在华为设备上出现 `Register too many Broadcast Receivers` 异常,可见 https://github.com/llew2011/HuaWeiVerifier
|
||
LoadedApkHuaWei.hookHuaWeiVerifier(this);
|
||
|
||
DownloadMessageHandler.INSTANCE.init(SimpleDownloadDatabase.getInstance().downloadDao());
|
||
VHelper.INSTANCE.preparePluginUpdate();
|
||
});
|
||
}
|
||
|
||
|
||
public void initFresco() {
|
||
// 系统版本低于 6.0 以及设备内存小于 2500M 的设备禁用动图
|
||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M
|
||
|| (deviceRamSize != 0 && deviceRamSize < 2500)) {
|
||
ImageUtils.disableAnimatedImage();
|
||
}
|
||
|
||
// 在 5.0 & 5.1 设备上 disable native code,原因是应用附带了 arm64 的 SO 以后在部分 5.0/5.1 设备
|
||
// 会出现找不到 arm64 so 的情况,具体可见
|
||
// https://sentry.ghzs.com/organizations/lightgame/issues/53107/
|
||
// 所以这里尝试在 5.0 & 5.1 设备上关闭 fresco 的 native 解码
|
||
ImagePipelineConfig.Builder pipelineConfigBuilder = ImagePipelineConfig.newBuilder(this);
|
||
ImageDecoderConfig.Builder decodeConfigBuilder = ImageDecoderConfig.newBuilder();
|
||
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP
|
||
|| Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||
pipelineConfigBuilder.setMemoryChunkType(MemoryChunkType.BUFFER_MEMORY)
|
||
.setImageTranscoderType(ImageTranscoderType.JAVA_TRANSCODER);
|
||
|
||
decodeConfigBuilder.overrideDecoder(DefaultImageFormats.GIF, new GifDecoder()).build();
|
||
|
||
String manufacture = Build.MANUFACTURER.toLowerCase();
|
||
|
||
// OPPO 和 VIVO 的 5.1.1 设备还会去加载 WEBP_ANIMATED 的 SO,
|
||
// 实测没有发现有地方使用 WEBP_ANIMATED 的图片,这里用空占位图来替换 WEBP 动图
|
||
if ("oppo".equals(manufacture) || "vivo".equals(manufacture)) {
|
||
decodeConfigBuilder.overrideDecoder(DefaultImageFormats.WEBP_ANIMATED, new EmptyDecoder()).build();
|
||
}
|
||
|
||
pipelineConfigBuilder
|
||
.setImageDecoderConfig(decodeConfigBuilder.build())
|
||
.experiment()
|
||
.setNativeCodeDisabled(true);
|
||
}
|
||
|
||
try {
|
||
BigImageViewer.initialize(FrescoImageLoader.with(this, pipelineConfigBuilder.build()));
|
||
} catch (Throwable e) {
|
||
e.printStackTrace();
|
||
}
|
||
}
|
||
|
||
private void retrieveVGameInfoIfNeeded() {
|
||
VHelper.init(this);
|
||
}
|
||
|
||
/**
|
||
* 移除已安装但还在本地数据库中的包
|
||
*/
|
||
private void removeInstalledButRemainedPackages() {
|
||
AppExecutor.getIoExecutor().execute(() -> {
|
||
for (DownloadEntity downloadEntity : DownloadManager.getInstance().getAllDownloadEntity()) {
|
||
if (downloadEntity.getStatus() != DownloadStatus.done) {
|
||
continue;
|
||
}
|
||
|
||
try {
|
||
String versionName = downloadEntity.getVersionName();
|
||
String packageName = downloadEntity.getPackageName();
|
||
// 这里暴力删除会影响畅玩游戏快速安装功能,但先不管了
|
||
if (versionName != null
|
||
&& packageName != null
|
||
&& versionName.equals(PackageUtils.getVersionNameByPackageName(packageName))) {
|
||
DownloadManager.getInstance().cancel(downloadEntity.getUrl());
|
||
}
|
||
} catch (Exception ignored) {
|
||
// 不在乎删除的结果,尝试即可
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
public boolean isReinstallTheSameVersion() {
|
||
return mIsReinstallTheSameVersion;
|
||
}
|
||
|
||
public void setIsReinstallTheSameVersion(boolean value) {
|
||
mIsReinstallTheSameVersion = value;
|
||
}
|
||
|
||
public Application getApplication() {
|
||
return this;
|
||
}
|
||
|
||
/**
|
||
* 判断当前用户是否是已经同意过隐私政策协议
|
||
* 覆盖安装进入过首页的也当作是同意了
|
||
*/
|
||
public static boolean isUserAcceptPrivacyPolicy(Context context) {
|
||
return !PreferenceManager.getDefaultSharedPreferences(context).getBoolean(Constants.SP_NEW_FIRST_LAUNCH_VERSION + PackageUtils.getGhVersionName(), true)
|
||
|| !SPUtils.getBooleanWithContext(context, Constants.SP_BRAND_NEW_USER, true)
|
||
|| SPUtils.getBooleanWithContext(context, Constants.SP_IS_USER_ACCEPTED_PRIVACY_STATEMENT, false);
|
||
}
|
||
|
||
public LunchType getLaunchType() {
|
||
if (mLaunchType == null) {
|
||
mLaunchType = DeviceTokenUtils.getLaunchType();
|
||
}
|
||
return mLaunchType;
|
||
}
|
||
|
||
public IFlavorProvider getFlavorProvider() {
|
||
return mFlavorProvider;
|
||
}
|
||
|
||
@Override
|
||
protected void attachBaseContext(Context base) {
|
||
super.attachBaseContext(base);
|
||
VaApp.get().attachBaseContext(this, base);
|
||
for (IApplication application : mApplicationList) {
|
||
application.attachBaseContext();
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onLowMemory() {
|
||
super.onLowMemory();
|
||
|
||
clearMemoryCaches();
|
||
|
||
for (IApplication application : mApplicationList) {
|
||
application.onLowMemory();
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onConfigurationChanged(@NonNull android.content.res.Configuration newConfig) {
|
||
super.onConfigurationChanged(newConfig);
|
||
for (IApplication application : mApplicationList) {
|
||
application.onConfigurationChanged(newConfig);
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onTerminate() {
|
||
super.onTerminate();
|
||
for (IApplication application : mApplicationList) {
|
||
application.onTerminate();
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onTrimMemory(int level) {
|
||
super.onTrimMemory(level);
|
||
|
||
if (level >= TRIM_MEMORY_MODERATE) {
|
||
clearMemoryCaches();
|
||
}
|
||
|
||
for (IApplication application : mApplicationList) {
|
||
application.onTrimMemory(level);
|
||
}
|
||
}
|
||
|
||
private void clearMemoryCaches() {
|
||
// 有一定机率在 ImagePipelineFactory 还没完成初始化就被调用,
|
||
// 直接粗暴地 catch 异常 "ImagePipelineFactory was not initialized!"
|
||
// 毕竟这里只是内存不足时候的操作,不用保证执行结果
|
||
try {
|
||
ImagePipelineFactory.getInstance().getImagePipeline().clearMemoryCaches();
|
||
} catch (NullPointerException e) {
|
||
e.printStackTrace();
|
||
}
|
||
ImageUtils.clearCaches();
|
||
}
|
||
|
||
}
|