diff --git a/app/build.gradle b/app/build.gradle index c677c38..ef055d0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,8 @@ android { applicationId "com.rehome.zhdcoa" minSdk 24 targetSdk 35 - versionCode 20 - versionName "2.3.6" + versionCode 21 + versionName "2.3.7" multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" //每个应用拥有不同的authorities,防止相同的在同一个手机上无法同时安装 diff --git a/app/src/main/java/com/rehome/zhdcoa/ui/activity/MainActivity.kt b/app/src/main/java/com/rehome/zhdcoa/ui/activity/MainActivity.kt index 1556c7c..3e665d2 100644 --- a/app/src/main/java/com/rehome/zhdcoa/ui/activity/MainActivity.kt +++ b/app/src/main/java/com/rehome/zhdcoa/ui/activity/MainActivity.kt @@ -16,9 +16,13 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentPagerAdapter import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.viewpager.widget.ViewPager +import com.azhon.appupdate.base.BaseHttpDownloadManager +import com.azhon.appupdate.base.bean.DownloadStatus +import com.azhon.appupdate.config.Constant import com.azhon.appupdate.listener.OnButtonClickListener import com.azhon.appupdate.listener.OnDownloadListener import com.azhon.appupdate.manager.DownloadManager +import com.azhon.appupdate.manager.HttpDownloadManager import com.jauker.widget.BadgeView import com.rehome.zhdcoa.App import com.rehome.zhdcoa.AppManager @@ -40,6 +44,7 @@ import com.tencent.tbs.reader.TbsFileInterfaceImpl import com.yolanda.nohttp.NoHttp import com.yolanda.nohttp.RequestMethod import com.yolanda.nohttp.rest.Response +import kotlinx.coroutines.flow.Flow import me.leolin.shortcutbadger.ShortcutBadger import java.io.File import java.util.* @@ -468,7 +473,8 @@ class MainActivity : BaseActivityOaToolbarViewBinding() { description: String, versionCode: String ) { - manager = DownloadManager.Builder(this).run { + val builder = DownloadManager.Builder(this) + manager = builder.httpManager(AppUpdateHttpDownloadManager(application.externalCacheDir?.path?:String.format(Constant.APK_PATH, application.packageName),this)).run { apkUrl(downloadURL) apkName("珠电e办公.apk") smallIcon(R.mipmap.ic_launcher) diff --git a/app/src/main/java/com/rehome/zhdcoa/utils/AppUpdateHttpDownloadManager.kt b/app/src/main/java/com/rehome/zhdcoa/utils/AppUpdateHttpDownloadManager.kt new file mode 100644 index 0000000..d103fa7 --- /dev/null +++ b/app/src/main/java/com/rehome/zhdcoa/utils/AppUpdateHttpDownloadManager.kt @@ -0,0 +1,179 @@ +package com.rehome.zhdcoa.utils + + +import android.content.Context +import com.azhon.appupdate.base.BaseHttpDownloadManager +import com.azhon.appupdate.base.bean.DownloadStatus +import com.azhon.appupdate.config.Constant +import com.azhon.appupdate.util.LogUtil +import com.rehome.zhdcoa.R +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* +import java.io.File +import java.io.FileOutputStream +import java.net.HttpURLConnection +import java.net.SocketTimeoutException +import java.net.URL +import java.security.KeyStore +import java.security.SecureRandom +import java.security.cert.CertificateFactory +import java.security.cert.X509Certificate +import javax.net.ssl.HostnameVerifier +import javax.net.ssl.HttpsURLConnection +import javax.net.ssl.SSLContext +import javax.net.ssl.SSLSocketFactory +import javax.net.ssl.TrustManager +import javax.net.ssl.TrustManagerFactory +import javax.net.ssl.X509TrustManager + + +/** + * ProjectName: AppUpdate + * PackageName: com.azhon.appupdate.manager + * FileName: HttpDownloadManager + * CreateDate: 2022/4/7 on 14:29 + * Desc: + * + * @author azhon + */ + +@Suppress("BlockingMethodInNonBlockingContext") +class AppUpdateHttpDownloadManager(private val path: String,private val context: Context) : BaseHttpDownloadManager() { + companion object { + private const val TAG = "HttpDownloadManager" + } + + private var shutdown: Boolean = false + + override fun download(apkUrl: String, apkName: String): Flow { + LogUtil.i("app","-------------apkUrl----------") + LogUtil.i("app",apkUrl) + trustAllHosts(apkUrl) + shutdown = false + File(path, apkName).let { + if (it.exists()) it.delete() + } + return flow { + emit(DownloadStatus.Start) + connectToDownload(apkUrl, apkName, this) + }.catch { + emit(DownloadStatus.Error(it)) + }.flowOn(Dispatchers.IO) + } + + private suspend fun connectToDownload( + apkUrl: String, apkName: String, flow: FlowCollector + ) { + val con = URL(apkUrl).openConnection() as HttpURLConnection + con.apply { + requestMethod = "GET" + readTimeout = Constant.HTTP_TIME_OUT + connectTimeout = Constant.HTTP_TIME_OUT + setRequestProperty("Accept-Encoding", "identity") + } + if (con.responseCode == HttpURLConnection.HTTP_OK) { + val inStream = con.inputStream + val length = con.contentLength + var len: Int + var progress = 0 + val buffer = ByteArray(1024 * 2) + val file = File(path, apkName) + FileOutputStream(file).use { out -> + while (inStream.read(buffer).also { len = it } != -1 && !shutdown) { + out.write(buffer, 0, len) + progress += len + flow.emit(DownloadStatus.Downloading(length, progress)) + } + out.flush() + } + inStream.close() + if (shutdown) { + flow.emit(DownloadStatus.Cancel) + } else { + flow.emit(DownloadStatus.Done(file)) + } + } else if (con.responseCode == HttpURLConnection.HTTP_MOVED_PERM + || con.responseCode == HttpURLConnection.HTTP_MOVED_TEMP + ) { + con.disconnect() + val locationUrl = con.getHeaderField("Location") + LogUtil.d( + TAG, + "The current url is the redirect Url, the redirected url is $locationUrl" + ) + connectToDownload(locationUrl, apkName, flow) + } else { + val e = SocketTimeoutException("Error: Http response code = ${con.responseCode}") + flow.emit(DownloadStatus.Error(e)) + } + con.disconnect() + } + + /** + * fix https url (SSLHandshakeException) exception + */ + private fun trustAllHosts(url:String) { + if (url.startsWith("https://219.131.195.3:7100") || url.startsWith("https://219.131.195.3:7011") || url.startsWith( + "https://219.131.195.3:7081" + ) || url.startsWith("https://219.131.195.3:7082") + ) { + HttpsURLConnection.setDefaultSSLSocketFactory(getSSLSocketFactory(context)) + HttpsURLConnection.setDefaultHostnameVerifier { _, _ -> true } + }else{ + val manager: TrustManager = object : X509TrustManager { + override fun getAcceptedIssuers(): Array { + return arrayOf() + } + + override fun checkClientTrusted(chain: Array?, authType: String?) { + LogUtil.d(TAG, "checkClientTrusted") + } + + override fun checkServerTrusted(chain: Array?, authType: String?) { + LogUtil.d(TAG, "checkServerTrusted") + } + } + try { + val sslContext = SSLContext.getInstance("TLS") + sslContext.init(null, arrayOf(manager), SecureRandom()) + HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.socketFactory) + } catch (e: Exception) { + LogUtil.e(TAG, "trustAllHosts error: $e") + } + } + } + + override fun cancel() { + shutdown = true + } + + override fun release() { + cancel() + } + + fun getSSLSocketFactory(context: Context): SSLSocketFactory? { + try { + val certificateFactory = CertificateFactory.getInstance("X.509") + val keyStore = KeyStore.getInstance(KeyStore.getDefaultType()) + keyStore.load(null) + val certificateAlias = 0.toString() + keyStore.setCertificateEntry( + certificateAlias, certificateFactory.generateCertificate + (context.resources.openRawResource(R.raw.domain)) + ) + val sslContext = SSLContext.getInstance("TLS") + val trustManagerFactory = + TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) + trustManagerFactory.init(keyStore) + sslContext.init( + null, + trustManagerFactory.trustManagers, + SecureRandom() + ) + return sslContext.socketFactory + } catch (ex: java.lang.Exception) { + ex.printStackTrace() + } + return null + } +} \ No newline at end of file