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.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.gh.common.util.AdHelper;
import com.gh.gamecenter.login.interceptor.LoginInterceptor;
import com.therouter.TheRouter;
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.interceptor.MainInterceptor;
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.entity.LinkEntity;
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.IAcceleratorProvider;
import com.gh.gamecenter.core.provider.IFlavorProvider;
import com.gh.gamecenter.core.provider.IPushProvider;
import com.gh.gamecenter.core.provider.IQGameProvider;
import com.gh.gamecenter.core.utils.GsonUtils;
import com.gh.gamecenter.core.utils.SPUtils;
import com.gh.gamecenter.login.interceptor.LoginInterceptor;
import com.gh.gamecenter.login.user.UserManager;
import com.gh.gamecenter.login.user.UserRepository;
import com.gh.gamecenter.login.utils.QuickLoginHelper;
import com.gh.gamecenter.oaid.OAIDHelper;
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.halo.assistant.accelerator.repository.AcceleratorDataHolder;
import com.lg.ndownload.DownloadCore;
import com.lightgame.download.DownloadEntity;
import com.lightgame.download.DownloadStatus;
import com.lightgame.utils.Utils;
import com.llew.huawei.verifier.LoadedApkHuaWei;
import com.shuyu.gsyvideoplayer.cache.CacheFactory;
import com.shuyu.gsyvideoplayer.player.PlayerFactory;
import com.therouter.TheRouter;
import com.therouter.TheRouterKt;
import com.therouter.router.NavigatorKt;

import java.lang.reflect.Method;
import java.util.List;
import java.util.ServiceLoader;

import io.reactivex.Single;
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 isDisableSplashAdTemporarily = false; // 是否处于暂时禁用热启动开屏广告状态
    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();

        // 每个进程都用自己的进程名作为后缀的文件夹来存 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();
            }
        }

        mInstance = this;

        initRouter();

        for (IApplication application : mApplicationList) {
            application.onCreate(mInstance);
        }

        fixDebugIssueInAndroid14();

        ExtensionsKt.doOnMainProcessOnly(() -> {
            // 似乎只是 load SO 不涉及方法调用，所以可以在隐私政策前调用吧？
            OAIDHelper.INSTANCE.doSystemLoad();

            PlayerFactory.setPlayManager(Exo2PlayerManager.class);
            CacheFactory.setCacheManager(ExoPlayerCacheManager.class);

            isNewForThisVersion =
                    PreferenceManager.getDefaultSharedPreferences(this).getBoolean(Constants.SP_NEW_FIRST_LAUNCH_VERSION + PackageUtils.getGhVersionName(), true);

            Tracker.init(this);

            AppExecutor.getIoExecutor().execute(() -> {
                // fresco 初始化约耗费 300ms，所以放到工作线程初始化更合理一些
                initFresco();

                initDataHelper();
                DownloadCore.init(this);

                deviceRamSize = DeviceUtils.getTotalRamSizeOfDevice(this);
                mChannel = mFlavorProvider.getChannelStr(this);

                // 初始化推送
                initPushEngine();

                // 异步初始化 SP
                SPUtils.getString("");
            });

            RxJavaPlugins.setIoSchedulerHandler(scheduler -> AppExecutor.INSTANCE.getCachedScheduler());
            RxJavaPlugins.setComputationSchedulerHandler(scheduler -> AppExecutor.INSTANCE.getComputationScheduler());

            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;
        });
    }

    private void initRouter() {
        TheRouter.init(this);
        NavigatorKt.addRouterReplaceInterceptor(new MainInterceptor());
        NavigatorKt.addRouterReplaceInterceptor(new LoginInterceptor());
    }

    private void fixDebugIssueInAndroid14() {
        if (BuildConfig.DEBUG && Build.VERSION.SDK_INT == Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
            try {
                Class<?> clazz = Class.forName("com.wy.lib.wytrace.ArtMethodTrace");
                Method method = clazz.getDeclaredMethod("fix14debugApp", Context.class);
                method.setAccessible(true);
                method.invoke(null, this);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 需要延迟初始化的东西，以下代码调用都放置到用户同意了隐私政策之后
     */
    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, getChannel());
        // 初始化 一键登录
        QuickLoginHelper.init(this);

        // 初始化广告 SDK
        AdDelegateHelper.INSTANCE.initAdSdk(this);

        SignatureRepository.INSTANCE.init();

        // 初始化奇游sdk需要放在 UserRepository.getInstance().init() 前面，必须先初始化sdk才能 setQyToken
        IAcceleratorProvider acceleratorProvider = TheRouter.get(IAcceleratorProvider.class);
        if (acceleratorProvider != null) {
            acceleratorProvider.init(EnvHelper.isDevEnv(), this, PackageUtils.getGhVersionName());
        }

        // 刷新内存中的用户信息，避免应用进程重建时因没有用户信息数据而显示为掉登录状态
        // 同时避免请求实名信息时没有用户信息导致实名信息变更为设备实名的问题
        UserRepository.getInstance().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();

                DataUtils.bindValidOaidToSensor(true);

                if (getGid() != null) {
                    DataUtils.getDeviceCertification(getGid());
                }

                registerPushEngine();

                // 上报设备安装事件
                if (isNewForThisVersion) {
                    final LunchType launchType = getLaunchType();
                    LogUtils.uploadDevice(launchType);

                    ActivationHelper.sendActivationInfo();
                } else {
                    // 发送次日留存信息
                    ActivationHelper.sendRetentionInfo();
                }
                return null;
            });
        }

        // 获取 settings 配置
        com.gh.common.constant.Config.getGhzsSettings();

        AdHelper.getIdfa(BuildConfig.VERSION_NAME, getChannel());

        // 5 秒获取后台配置的获取应用列表限制接口结果超时后，回落到关闭状态
        PackageHelper.INSTANCE.ignoreInstalledPackageApiSwitchAfterTimeout(5000L);

        String localTemporaryDeviceId = SPUtils.getString(Constants.SP_TEMPORARY_DEVICE_ID);
        if (!TextUtils.isEmpty(localTemporaryDeviceId)) {
            HaloApp.getInstance().setLocalTemporaryDeviceId(localTemporaryDeviceId);
        }

        // 初始化下载服务，恢复下载任务
        DownloadManager.getInstance().initDownloadService();

        GlobalPriorityChainHelper.INSTANCE.preStart(isNewForThisVersion);

        MainWrapperRepository.Companion.getInstance().init();

        // QQ小游戏开放互联初始化
        IQGameProvider provider = TheRouter.get(IQGameProvider.class);

        if (provider != null) {
            provider.initOpenSdkLogin(
                    this,
                    com.gh.common.constant.Config.WECHAT_APPID,
                    com.gh.common.constant.Config.TENCENT_APPID
            );
        }

        AppExecutor.getUiExecutor().executeWithDelay(() -> {
            FixedRateJobHelper.begin();

            if (PackageHelper.INSTANCE.isGetInstalledPackagesAgreed()) {
                PackageHelper.INSTANCE.initPackageRelatedData();
            }

            initReceiver();
            initPackageChangesReceiver();
            initConnectivityChangesReceiver();

            initTimeConsumingAction();

            // 注册回调以用于做各种统计
            ProcessLifecycleOwner.get().getLifecycle().addObserver(new ProcessorLifeCycleOwner());

            // 初始化畅玩相关数据
            VHelper.init();

            // 开发环境不要强制捕获相关异常,这些异常通常是需要处理的
            if (!BuildConfig.DEBUG) {
                RxJavaPlugins.setErrorHandler(throwable -> {
                    if (Config.DEFAULT_CHANNEL.equals(mChannel)) {
                        Utils.toast(this, "当前页面未捕获的异常:" + throwable.toString());
                    }
                });
            }
        }, delay);

        AcceleratorDataHolder.Companion.getInstance().init();
    }

    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();
            }
        }
        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 = TheRouter.get(IPushProvider.class);
        if (pushProvider != null) {
            pushProvider.initialize(this);
            pushProvider.setOnPushOpenedCallback((id, title, content, linkType, linkId) -> {
                SensorsBridge.trackJiGuangPushClick(id, title, content, linkType, linkId);
                put(Constants.PUSH_MESSAGE_ID, id);
                LinkEntity link = new LinkEntity();
                link.setLink(linkId);
                link.setType(linkType);
                put(Constants.PUSH_LINK_ENTITY, link);
                return null;
            });
        }
    }

    /**
     * 同意隐私政策后，注册推送功能
     */
    @SuppressLint("CheckResult")
    private void registerPushEngine() {
        IPushProvider pushProvider = TheRouter.get(IPushProvider.class);

        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())
                .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(() -> {
            // 初始化地区设置
            RegionSettingHelper.getRegionSetting();

            // 初始化全局下载监听
            DownloadObserver.initObserver();

            // 移除已安装但还在本地数据库中的包(避免因为没有监听到安装结果导致安装包没有删除的问题)
            removeInstalledButRemainedPackages();

            // 避免在华为设备上出现 `Register too many Broadcast Receivers` 异常，可见 https://github.com/llew2011/HuaWeiVerifier
            LoadedApkHuaWei.hookHuaWeiVerifier(this);

            DownloadMessageHandler.INSTANCE.init(SimpleDownloadDatabase.getInstance().downloadDao());

            // 初始化 WebView ABI 列表
            getWebviewAbiList();
        });
    }

    public void initFresco() {
        if (ImageUtils.isFrescoInitialized()) return;

        // 系统版本低于 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 EmptyDecoder()).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 removeInstalledButRemainedPackages() {
        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);
        TheRouterKt.setTheRouterUseAutoInit(false);
        for (IApplication application : mApplicationList) {
            application.attachBaseContext(base);
        }
    }

    @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();
    }

}
