You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
FYReader/app/src/main/java/xyz/fycz/myreader/application/App.java

518 lines
20 KiB

package xyz.fycz.myreader.application;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.Application;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;
import android.webkit.WebView;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import com.liulishuo.filedownloader.FileDownloader;
import com.liulishuo.filedownloader.connection.FileDownloadUrlConnection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import java.io.File;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import io.reactivex.internal.functions.Functions;
import io.reactivex.plugins.RxJavaPlugins;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.common.URLCONST;
import xyz.fycz.myreader.entity.Setting;
import xyz.fycz.myreader.model.source.BookSourceManager;
import xyz.fycz.myreader.ui.activity.MainActivity;
import xyz.fycz.myreader.ui.dialog.APPDownloadTip;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.ui.fragment.BookcaseFragment;
import xyz.fycz.myreader.util.HttpUtil;
import xyz.fycz.myreader.util.SharedPreUtils;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.util.ToastUtils;
import xyz.fycz.myreader.util.utils.FileUtils;
import xyz.fycz.myreader.util.utils.NetworkUtils;
import xyz.fycz.myreader.util.utils.OkHttpUtils;
import xyz.fycz.myreader.ui.dialog.UpdateDialog;
public class App extends Application {
public static final String TAG = App.class.getSimpleName();
private static final Handler handler = new Handler();
private static App application;
private ExecutorService mFixedThreadPool;
private static boolean debug;
@Override
public void onCreate() {
super.onCreate();
application = this;
debug = isApkInDebug(this);
firstInit();
HttpUtil.trustAllHosts();//信任所有证书
RxJavaPlugins.setErrorHandler(Functions.emptyConsumer());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
webviewSetPath(this);
}
FileDownloader.setupOnApplicationOnCreate(this)
.connectionCreator(new FileDownloadUrlConnection
.Creator(new FileDownloadUrlConnection.Configuration()
.connectTimeout(15_000) // set connection timeout.
.readTimeout(15_000) // set read timeout.
))
.commit();
// handleSSLHandshake();
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel();
}
mFixedThreadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());//初始化线程池
initNightTheme();
// LLog.init(APPCONST.LOG_DIR);
}
private void firstInit() {
SharedPreUtils sru = SharedPreUtils.getInstance();
if (!sru.getBoolean("firstInit")) {
BookSourceManager.initDefaultSources();
sru.putBoolean("firstInit", true);
}
}
public void initNightTheme() {
if (isNightFS()) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
} else {
if (isNightTheme()) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
} else {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
}
}
}
public boolean isNightTheme() {
return !SysManager.getSetting().isDayStyle();
}
public boolean isNightFS() {
return SharedPreUtils.getInstance().getBoolean(getString(R.string.isNightFS), false);
}
/**
* 设置夜间模式
*
* @param isNightMode
*/
public void setNightTheme(boolean isNightMode) {
SharedPreUtils.getInstance().putBoolean(getmContext().getString(R.string.isNightFS), false);
Setting setting = SysManager.getSetting();
setting.setDayStyle(!isNightMode);
SysManager.saveSetting(setting);
App.getApplication().initNightTheme();
}
@SuppressLint("TrulyRandom")
public static void handleSSLHandshake() {
try {
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}};
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);
} catch (Exception ignored) {
}
}
public static Context getmContext() {
return application;
}
public void newThread(Runnable runnable) {
try {
mFixedThreadPool.execute(runnable);
} catch (Exception e) {
//e.printStackTrace();
mFixedThreadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());//初始化线程池
mFixedThreadPool.execute(runnable);
}
}
public void shutdownThreadPool() {
mFixedThreadPool.shutdownNow();
}
@TargetApi(26)
private void createNotificationChannel() {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel downloadChannel = new NotificationChannel(APPCONST.channelIdDownload, "下载通知", NotificationManager.IMPORTANCE_LOW);
downloadChannel.enableLights(true);//是否在桌面icon右上角展示小红点
downloadChannel.setLightColor(Color.RED);//小红点颜色
downloadChannel.setShowBadge(false); //是否在久按桌面图标时显示此渠道的通知
notificationManager.createNotificationChannel(downloadChannel);
NotificationChannel readChannel = new NotificationChannel(APPCONST.channelIdRead, "朗读通知", NotificationManager.IMPORTANCE_LOW);
readChannel.enableLights(true);//是否在桌面icon右上角展示小红点
readChannel.setLightColor(Color.RED);//小红点颜色
readChannel.setShowBadge(false); //是否在久按桌面图标时显示此渠道的通知
notificationManager.createNotificationChannel(readChannel);
}
/**
* 主线程执行
*
* @param runnable
*/
public static void runOnUiThread(Runnable runnable) {
handler.post(runnable);
}
public static Handler getHandler() {
return handler;
}
public static App getApplication() {
return application;
}
private boolean isFolderExist(String dir) {
File folder = Environment.getExternalStoragePublicDirectory(dir);
return (folder.exists() && folder.isDirectory()) || folder.mkdirs();
}
/**
* 获取app版本号
*
* @return
*/
public static int getVersionCode() {
try {
PackageManager manager = application.getPackageManager();
PackageInfo info = manager.getPackageInfo(application.getPackageName(), 0);
return info.versionCode;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取app版本号(String)
*
* @return
*/
public static String getStrVersionName() {
try {
PackageManager manager = application.getPackageManager();
PackageInfo info = manager.getPackageInfo(application.getPackageName(), 0);
return info.versionName;
} catch (Exception e) {
e.printStackTrace();
return "1.0.0";
}
}
/**
* 获取apk包的信息:版本号,名称,图标等
*
* @param absPath apk包的绝对路径
*/
public static int apkInfo(String absPath) {
PackageManager pm = application.getPackageManager();
PackageInfo pkgInfo = pm.getPackageArchiveInfo(absPath, PackageManager.GET_ACTIVITIES);
if (pkgInfo != null) {
int versionCode = pkgInfo.versionCode;
Log.i(TAG, String.format("PkgInfo: %s", versionCode));
return versionCode;
}
return 0;
}
/**
* 检查更新
*/
public static void checkVersionByServer(final AppCompatActivity activity, final boolean isManualCheck) {
App.getApplication().newThread(() -> {
try {
String url = "https://shimo.im/docs/cqkgjPRRydYYhQKt/read";
if (getApplication().debug) {
url = "https://shimo.im/docs/zfzpda7MUGskOC9v/read";
}
String html = OkHttpUtils.getHtml(url);
Document doc = Jsoup.parse(html);
String content = doc.getElementsByClass("ql-editor").text();
if (StringHelper.isEmpty(content)) {
content = OkHttpUtils.getUpdateInfo();
if (StringHelper.isEmpty(content)) {
if (isManualCheck || NetworkUtils.isNetWorkAvailable()) {
ToastUtils.showError("检查更新失败!");
}
return;
}
}
String[] contents = content.split(";");
int newestVersion = 0;
String updateContent = "";
String downloadLink = null;
boolean isForceUpdate = false;
int forceUpdateVersion;
StringBuilder s = new StringBuilder();
newestVersion = Integer.parseInt(contents[0].substring(contents[0].indexOf(":") + 1));
isForceUpdate = Boolean.parseBoolean(contents[1].substring(contents[1].indexOf(":") + 1));
downloadLink = contents[2].substring(contents[2].indexOf(":") + 1).trim();
updateContent = contents[3].substring(contents[3].indexOf(":") + 1);
SharedPreUtils spu = SharedPreUtils.getInstance();
spu.putString(getmContext().getString(R.string.lanzousKeyStart), contents[4].substring(contents[4].indexOf(":") + 1));
String newSplashTime = contents[5].substring(contents[5].indexOf(":") + 1);
String oldSplashTime = spu.getString("splashTime");
spu.putBoolean("needUdSI", !oldSplashTime.equals(newSplashTime));
spu.putString("splashTime", contents[5].substring(contents[5].indexOf(":") + 1));
spu.putString("splashImageUrl", contents[6].substring(contents[6].indexOf(":") + 1));
spu.putString("splashImageMD5", contents[7].substring(contents[7].indexOf(":") + 1));
forceUpdateVersion = Integer.parseInt(contents[8].substring(contents[8].indexOf(":") + 1));
spu.putInt("forceUpdateVersion", forceUpdateVersion);
int versionCode = getVersionCode();
isForceUpdate = isForceUpdate && forceUpdateVersion > versionCode;
if (!StringHelper.isEmpty(downloadLink)) {
spu.putString(getmContext().getString(R.string.downloadLink), downloadLink);
} else {
spu.putString(getmContext().getString(R.string.downloadLink), URLCONST.APP_DIR_UR);
}
String[] updateContents = updateContent.split("/");
for (String string : updateContents) {
s.append(string);
s.append("<br>");
}
Log.i("检查更新,最新版本", newestVersion + "");
if (newestVersion > versionCode) {
Setting setting = SysManager.getSetting();
if (isManualCheck || setting.getNewestVersionCode() < newestVersion || isForceUpdate) {
setting.setNewestVersionCode(newestVersion);
SysManager.saveSetting(setting);
getApplication().updateApp2(activity, downloadLink, newestVersion, s.toString(), isForceUpdate
);
}
} else if (isManualCheck) {
ToastUtils.showSuccess("已经是最新版本!");
}
} catch (Exception e) {
e.printStackTrace();
Log.e("检查更新失败!", "" + e.getLocalizedMessage());
if (isManualCheck || NetworkUtils.isNetWorkAvailable()) {
ToastUtils.showError("检查更新失败!");
}
}
});
}
/**
* App自动升级
*
* @param activity
* @param versionCode
*/
public void updateApp(final AppCompatActivity activity, final String url, final int versionCode, String message,
final boolean isForceUpdate, final BookcaseFragment mBookcaseFragment) {
//String version = (versionCode / 100 % 10) + "." + (versionCode / 10 % 10) + "." + (versionCode % 10);
String cancelTitle;
if (isForceUpdate) {
cancelTitle = "退出";
} else {
cancelTitle = "忽略此版本";
}
if (mBookcaseFragment == null) {
DialogCreator.createCommonDialog(activity, "发现新版本:", message, true, "取消", "立即更新", null,
(dialog, which) -> goDownload(activity, url));
return;
}
DialogCreator.createThreeButtonDialog(activity, "发现新版本:", message, !isForceUpdate, cancelTitle, "直接下载",
"浏览器下载", (dialog, which) -> {
if (isForceUpdate) {
activity.finish();
}
}, (dialog, which) -> {
if (activity instanceof MainActivity) {
MainActivity mainActivity = (MainActivity) activity;
mainActivity.getViewPagerMain().setCurrentItem(0);
String filePath = APPCONST.UPDATE_APK_FILE_DIR + "FYReader.apk";
if (apkInfo(filePath) == versionCode) {
mainActivity.installApk(FileUtils.getFile(filePath), isForceUpdate);
return;
}
}
if (url == null || "".equals(url)) {
ToastUtils.showError("获取链接失败,请前往浏览器下载!");
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(URLCONST.APP_DIR_UR));
activity.startActivity(intent);
if (isForceUpdate) {
activity.finish();
}
} else {
APPDownloadTip downloadTip = new APPDownloadTip(url, mBookcaseFragment, activity, isForceUpdate);
downloadTip.downloadApp();
}
}, (dialog, which) -> {
goDownload(activity, url);
if (isForceUpdate) {
activity.finish();
}
});
}
public void updateApp2(final AppCompatActivity activity, final String url, final int versionCode, String message,
final boolean isForceUpdate) {
//String version = (versionCode / 100 % 10) + "." + (versionCode / 10 % 10) + "." + (versionCode % 10);
int hun = versionCode / 100;
int ten = versionCode / 10 % 10;
int one = versionCode % 10;
String versionName = "v" + hun + "." + ten + "." + one;
UpdateDialog updateDialog = new UpdateDialog.Builder()
.setVersion(versionName)
.setContent(message)
.setCancelable(!isForceUpdate)
.setDownloadUrl(url)
.setContentHtml(true)
.setDebug(true)
.build();
updateDialog.showUpdateDialog(activity);
}
private void goDownload(Activity activity, String url) {
String downloadLink = url;
if (url == null || "".equals(url)) {
downloadLink = URLCONST.APP_DIR_UR;
}
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(downloadLink));
activity.startActivity(intent);
}
/**
* 判断当前应用是否是debug状态
*/
public static boolean isApkInDebug(Context context) {
try {
ApplicationInfo info = context.getApplicationInfo();
return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
} catch (Exception e) {
return false;
}
}
/**
* 判断Activity是否Destroy
*
* @param mActivity
* @return
*/
public static boolean isDestroy(Activity mActivity) {
return mActivity == null || mActivity.isFinishing() || mActivity.isDestroyed();
}
/****************
*
* 发起添加群流程。群号:风月读书交流群(1085028304) 的 key 为: 8PIOnHFuH6A38hgxvD_Rp2Bu-Ke1ToBn
* 调用 joinQQGroup(8PIOnHFuH6A38hgxvD_Rp2Bu-Ke1ToBn) 即可发起手Q客户端申请加群 风月读书交流群(1085028304)
*
* @param key 由官网生成的key
* @return 返回true表示呼起手Q成功,返回false表示呼起失败
******************/
public static boolean joinQQGroup(Context context, String key) {
Intent intent = new Intent();
intent.setData(Uri.parse("mqqopensdkapi://bizAgent/qm/qr?url=http%3A%2F%2Fqm.qq.com%2Fcgi-bin%2Fqm%2Fqr%3Ffrom%3Dapp%26p%3Dandroid%26jump_from%3Dwebapi%26k%3D" + key));
// 此Flag可根据具体产品需要自定义,如设置,则在加群界面按返回,返回手Q主界面,不设置,按返回会返回到呼起产品界面 //intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
try {
context.startActivity(intent);
return true;
} catch (Exception e) {
// 未安装手Q或安装的版本不支持
return false;
}
}
@RequiresApi(api = 28)
public void webviewSetPath(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
String processName = getProcessName(context);
if (!getApplicationContext().getPackageName().equals(processName)) {//判断不等于默认进程名称
WebView.setDataDirectorySuffix(processName);
}
}
}
public String getProcessName(Context context) {
if (context == null) return null;
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningAppProcessInfo processInfo : manager.getRunningAppProcesses()) {
if (processInfo.pid == android.os.Process.myPid()) {
return processInfo.processName;
}
}
return null;
}
public static boolean isDebug() {
return debug;
}
}