新增AppUpgrade功能
This commit is contained in:
@@ -175,8 +175,8 @@ android {
|
|||||||
def Lombok = "1.18.10"
|
def Lombok = "1.18.10"
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar','*.aar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
|
||||||
implementation fileTree(dir: 'aliyun-libs', include: ['*.jar','*.aar'])
|
implementation fileTree(dir: 'aliyun-libs', include: ['*.jar', '*.aar'])
|
||||||
testImplementation 'junit:junit:4.13.1'
|
testImplementation 'junit:junit:4.13.1'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
||||||
|
|
||||||
@@ -243,9 +243,9 @@ dependencies {
|
|||||||
implementation 'com.huawei.hms:push:6.5.0.300'
|
implementation 'com.huawei.hms:push:6.5.0.300'
|
||||||
//魅族推送
|
//魅族推送
|
||||||
implementation 'com.meizu.flyme.internet:push-internal:4.1.0'
|
implementation 'com.meizu.flyme.internet:push-internal:4.1.0'
|
||||||
//oppo推送需要
|
//oppo推送需要
|
||||||
implementation 'commons-codec:commons-codec:1.6'
|
implementation 'commons-codec:commons-codec:1.6'
|
||||||
|
|
||||||
api 'com.tencent.vasdolly:helper:3.0.3'
|
api 'com.tencent.vasdolly:helper:3.0.3'
|
||||||
implementation "io.github.tencent:vap:2.0.24"
|
implementation "io.github.tencent:vap:2.0.24"
|
||||||
|
|
||||||
@@ -254,7 +254,7 @@ dependencies {
|
|||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
flatDir {
|
flatDir {
|
||||||
dirs 'aliyun-libs','com.huawei.agconnect'
|
dirs 'aliyun-libs', 'com.huawei.agconnect'
|
||||||
}
|
}
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
|
||||||
|
@@ -270,8 +270,8 @@
|
|||||||
<!-- 云信继承vivo推送 start-->
|
<!-- 云信继承vivo推送 start-->
|
||||||
<service
|
<service
|
||||||
android:name="com.vivo.push.sdk.service.CommandClientService"
|
android:name="com.vivo.push.sdk.service.CommandClientService"
|
||||||
android:permission="com.push.permission.UPSTAGESERVICE"
|
android:exported="true"
|
||||||
android:exported="true" />
|
android:permission="com.push.permission.UPSTAGESERVICE" />
|
||||||
<activity
|
<activity
|
||||||
android:name="com.vivo.push.sdk.LinkProxyClientActivity"
|
android:name="com.vivo.push.sdk.LinkProxyClientActivity"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
@@ -1286,6 +1286,25 @@
|
|||||||
android:name=".vip.VipRankActivity"
|
android:name=".vip.VipRankActivity"
|
||||||
android:screenOrientation="portrait" />
|
android:screenOrientation="portrait" />
|
||||||
|
|
||||||
|
<service android:name="com.tongdaxing.erban.upgrade.service.DownloadService" />
|
||||||
|
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name="com.tongdaxing.erban.upgrade.config.AppUpdateFileProvider"
|
||||||
|
android:authorities="${applicationId}.fileProvider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/app_update_file" />
|
||||||
|
|
||||||
|
</provider>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name="com.tongdaxing.erban.upgrade.view.UpdateDialogActivity"
|
||||||
|
android:theme="@style/UpdateDialog" />
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
@@ -2,13 +2,16 @@ package com.tongdaxing.erban.upgrade;
|
|||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
|
|
||||||
import com.trello.rxlifecycle3.android.ActivityEvent;
|
|
||||||
import com.trello.rxlifecycle3.components.support.RxAppCompatActivity;
|
|
||||||
import com.mango.core.upgrade.bean.NewestVersionInfo;
|
import com.mango.core.upgrade.bean.NewestVersionInfo;
|
||||||
import com.mango.core.upgrade.bean.UpgradeCache;
|
import com.mango.core.upgrade.bean.UpgradeCache;
|
||||||
import com.mango.core.upgrade.model.UpgradeModel;
|
import com.mango.core.upgrade.model.UpgradeModel;
|
||||||
import com.mango.core.utils.ActivityUtil;
|
import com.mango.core.utils.ActivityUtil;
|
||||||
|
import com.mango.moshen.R;
|
||||||
|
import com.mango.xchat_android_library.utils.JavaUtil;
|
||||||
import com.mango.xchat_android_library.utils.SingleToastUtil;
|
import com.mango.xchat_android_library.utils.SingleToastUtil;
|
||||||
|
import com.tongdaxing.erban.upgrade.manager.DownloadManager;
|
||||||
|
import com.trello.rxlifecycle3.android.ActivityEvent;
|
||||||
|
import com.trello.rxlifecycle3.components.support.RxAppCompatActivity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author jack
|
* @author jack
|
||||||
@@ -18,9 +21,8 @@ import com.mango.xchat_android_library.utils.SingleToastUtil;
|
|||||||
public class AppUpgradeHelper {
|
public class AppUpgradeHelper {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @param isUserAuto ture 表示,是用户主动发起的请求,比如设置页点更新
|
* @param isUserAuto ture 表示,是用户主动发起的请求,比如设置页点更新
|
||||||
* @param isPush ture 表示是后台推送
|
* @param isPush ture 表示是后台推送
|
||||||
*/
|
*/
|
||||||
@SuppressLint("CheckResult")
|
@SuppressLint("CheckResult")
|
||||||
public static void checkAppUpgrade(RxAppCompatActivity activity, boolean isUserAuto,
|
public static void checkAppUpgrade(RxAppCompatActivity activity, boolean isUserAuto,
|
||||||
@@ -73,10 +75,17 @@ public class AppUpgradeHelper {
|
|||||||
}
|
}
|
||||||
//如果是强更,一定要弹窗
|
//如果是强更,一定要弹窗
|
||||||
if (forceUpdate || needShow) {
|
if (forceUpdate || needShow) {
|
||||||
AppUpdateDialog appUpdateDialog = new AppUpdateDialog();
|
DownloadManager manager = new DownloadManager.Builder(activity)
|
||||||
appUpdateDialog.setNewestVersionInfo(newestVersionInfo);
|
.apkUrl(newestVersionInfo.getUpdateDownloadLink())
|
||||||
appUpdateDialog.show(activity.getSupportFragmentManager());
|
.apkName("magic_v" + newestVersionInfo.getUpdateVersion() + ".apk")
|
||||||
UpgradeModel.get().setHasShowDialog(true);
|
.apkMD5(newestVersionInfo.getUpdateFileMd5())
|
||||||
|
.apkVersionName(newestVersionInfo.getUpdateVersion())
|
||||||
|
.smallIcon(R.mipmap.app_logo)
|
||||||
|
.forcedUpgrade(forceUpdate)
|
||||||
|
.apkVersionCode(Integer.MAX_VALUE)
|
||||||
|
.apkDescription(newestVersionInfo.getUpdateVersionDesc())
|
||||||
|
.build();
|
||||||
|
manager.download();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@@ -0,0 +1,34 @@
|
|||||||
|
package com.tongdaxing.erban.upgrade.base
|
||||||
|
|
||||||
|
import com.tongdaxing.erban.upgrade.base.bean.DownloadStatus
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProjectName: AppUpdate
|
||||||
|
* PackageName: com.tongdaxing.erban.upgrade.base
|
||||||
|
* FileName: BaseHttpDownloadManager
|
||||||
|
* CreateDate: 2022/4/7 on 10:24
|
||||||
|
* Desc:
|
||||||
|
*
|
||||||
|
* @author azhon
|
||||||
|
*/
|
||||||
|
|
||||||
|
abstract class BaseHttpDownloadManager {
|
||||||
|
/**
|
||||||
|
* download apk from apkUrl
|
||||||
|
*
|
||||||
|
* @param apkUrl
|
||||||
|
* @param apkName
|
||||||
|
*/
|
||||||
|
abstract fun download(apkUrl: String, apkName: String): Flow<DownloadStatus>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cancel download apk
|
||||||
|
*/
|
||||||
|
abstract fun cancel()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* release memory
|
||||||
|
*/
|
||||||
|
abstract fun release()
|
||||||
|
}
|
@@ -0,0 +1,28 @@
|
|||||||
|
package com.tongdaxing.erban.upgrade.base.bean
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProjectName: AppUpdate
|
||||||
|
* PackageName: com.tongdaxing.erban.upgrade.base.bean
|
||||||
|
* FileName: DownloadStatus
|
||||||
|
* CreateDate: 2022/4/14 on 11:18
|
||||||
|
* Desc:
|
||||||
|
*
|
||||||
|
* @author azhon
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
sealed class DownloadStatus {
|
||||||
|
|
||||||
|
object Start : DownloadStatus()
|
||||||
|
|
||||||
|
data class Downloading(val max: Int, val progress: Int) : DownloadStatus()
|
||||||
|
|
||||||
|
class Done(val apk: File) : DownloadStatus()
|
||||||
|
|
||||||
|
object Cancel : DownloadStatus()
|
||||||
|
|
||||||
|
data class Error(val e: Throwable) : DownloadStatus()
|
||||||
|
}
|
@@ -0,0 +1,16 @@
|
|||||||
|
package com.tongdaxing.erban.upgrade.config
|
||||||
|
|
||||||
|
import androidx.core.content.FileProvider
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProjectName: AppUpdate
|
||||||
|
* PackageName: com.tongdaxing.erban.upgrade.config
|
||||||
|
* FileName: AppUpdateFileProvider
|
||||||
|
* CreateDate: 2022/4/7 on 10:30
|
||||||
|
* Desc:
|
||||||
|
*
|
||||||
|
* @author azhon
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
class AppUpdateFileProvider : FileProvider()
|
@@ -0,0 +1,59 @@
|
|||||||
|
package com.tongdaxing.erban.upgrade.config
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProjectName: AppUpdate
|
||||||
|
* PackageName: com.tongdaxing.erban.upgrade.config
|
||||||
|
* FileName: Constant
|
||||||
|
* CreateDate: 2022/4/7 on 10:28
|
||||||
|
* Desc:
|
||||||
|
*
|
||||||
|
* @author azhon
|
||||||
|
*/
|
||||||
|
|
||||||
|
object Constant {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Http timeout(ms)
|
||||||
|
*/
|
||||||
|
const val HTTP_TIME_OUT = 30_000
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logcat tag
|
||||||
|
*/
|
||||||
|
const val TAG = "AppUpdate."
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apk file extension
|
||||||
|
*/
|
||||||
|
const val APK_SUFFIX = ".apk"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Coroutine Name
|
||||||
|
*/
|
||||||
|
const val COROUTINE_NAME = "app-update-coroutine"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification channel id
|
||||||
|
*/
|
||||||
|
const val DEFAULT_CHANNEL_ID = "appUpdate"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification id
|
||||||
|
*/
|
||||||
|
const val DEFAULT_NOTIFY_ID = 1011
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification channel name
|
||||||
|
*/
|
||||||
|
const val DEFAULT_CHANNEL_NAME = "AppUpdate"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compat Android N file uri
|
||||||
|
*/
|
||||||
|
var AUTHORITIES: String? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apk path
|
||||||
|
*/
|
||||||
|
var APK_PATH = "/storage/emulated/0/Android/data/%s/cache"
|
||||||
|
}
|
@@ -0,0 +1,40 @@
|
|||||||
|
package com.tongdaxing.erban.upgrade.listener
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.Application
|
||||||
|
import android.os.Bundle
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProjectName: AppUpdate
|
||||||
|
* PackageName: com.tongdaxing.erban.upgrade.listener
|
||||||
|
* FileName: LifecycleCallbacksAdapter
|
||||||
|
* CreateDate: 2022/4/8 on 11:26
|
||||||
|
* Desc:
|
||||||
|
*
|
||||||
|
* @author azhon
|
||||||
|
*/
|
||||||
|
|
||||||
|
abstract class LifecycleCallbacksAdapter : Application.ActivityLifecycleCallbacks {
|
||||||
|
|
||||||
|
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityStarted(activity: Activity) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityResumed(activity: Activity) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityPaused(activity: Activity) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityStopped(activity: Activity) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityDestroyed(activity: Activity) {
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,28 @@
|
|||||||
|
package com.tongdaxing.erban.upgrade.listener
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProjectName: AppUpdate
|
||||||
|
* PackageName: com.tongdaxing.erban.upgrade.listener
|
||||||
|
* FileName: OnButtonClickListener
|
||||||
|
* CreateDate: 2022/4/7 on 15:56
|
||||||
|
* Desc:
|
||||||
|
*
|
||||||
|
* @author azhon
|
||||||
|
*/
|
||||||
|
|
||||||
|
interface OnButtonClickListener {
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* click update button
|
||||||
|
*/
|
||||||
|
const val UPDATE = 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* click cancel button
|
||||||
|
*/
|
||||||
|
const val CANCEL = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onButtonClick(id: Int)
|
||||||
|
}
|
@@ -0,0 +1,43 @@
|
|||||||
|
package com.tongdaxing.erban.upgrade.listener
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProjectName: AppUpdate
|
||||||
|
* PackageName: com.tongdaxing.erban.upgrade.listener
|
||||||
|
* FileName: OnDownloadListener
|
||||||
|
* CreateDate: 2022/4/7 on 10:27
|
||||||
|
* Desc:
|
||||||
|
*
|
||||||
|
* @author azhon
|
||||||
|
*/
|
||||||
|
|
||||||
|
interface OnDownloadListener {
|
||||||
|
/**
|
||||||
|
* start download
|
||||||
|
*/
|
||||||
|
fun start()
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param max file length
|
||||||
|
* @param progress downloaded file size
|
||||||
|
*/
|
||||||
|
fun downloading(max: Int, progress: Int)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param apk
|
||||||
|
*/
|
||||||
|
fun done(apk: File)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cancel download
|
||||||
|
*/
|
||||||
|
fun cancel()
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param e
|
||||||
|
*/
|
||||||
|
fun error(e: Throwable)
|
||||||
|
}
|
@@ -0,0 +1,31 @@
|
|||||||
|
package com.tongdaxing.erban.upgrade.listener
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProjectName: AppUpdate
|
||||||
|
* PackageName: com.tongdaxing.erban.upgrade.listener
|
||||||
|
* FileName: OnDownloadListenerAdapter
|
||||||
|
* CreateDate: 2022/4/8 on 10:58
|
||||||
|
* Desc:
|
||||||
|
*
|
||||||
|
* @author azhon
|
||||||
|
*/
|
||||||
|
|
||||||
|
abstract class OnDownloadListenerAdapter : OnDownloadListener {
|
||||||
|
override fun start() {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun downloading(max: Int, progress: Int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun done(apk: File) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun cancel() {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun error(e: Throwable) {
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,441 @@
|
|||||||
|
package com.tongdaxing.erban.upgrade.manager
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.Application
|
||||||
|
import android.app.NotificationChannel
|
||||||
|
import android.content.Intent
|
||||||
|
import android.widget.Toast
|
||||||
|
import com.mango.moshen.R
|
||||||
|
import com.tongdaxing.erban.upgrade.base.BaseHttpDownloadManager
|
||||||
|
import com.tongdaxing.erban.upgrade.config.Constant
|
||||||
|
import com.tongdaxing.erban.upgrade.listener.LifecycleCallbacksAdapter
|
||||||
|
import com.tongdaxing.erban.upgrade.listener.OnButtonClickListener
|
||||||
|
import com.tongdaxing.erban.upgrade.listener.OnDownloadListener
|
||||||
|
import com.tongdaxing.erban.upgrade.service.DownloadService
|
||||||
|
import com.tongdaxing.erban.upgrade.util.ApkUtil
|
||||||
|
import com.tongdaxing.erban.upgrade.util.LogUtil
|
||||||
|
import com.tongdaxing.erban.upgrade.view.UpdateDialogActivity
|
||||||
|
import java.io.Serializable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProjectName: AppUpdate
|
||||||
|
* PackageName: com.tongdaxing.erban.upgrade.manager
|
||||||
|
* FileName: DownloadManager
|
||||||
|
* CreateDate: 2022/4/7 on 10:36
|
||||||
|
* Desc:
|
||||||
|
*
|
||||||
|
* @author azhon
|
||||||
|
*/
|
||||||
|
class DownloadManager private constructor(builder: Builder) : Serializable {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "DownloadManager"
|
||||||
|
private var instance: DownloadManager? = null
|
||||||
|
|
||||||
|
fun getInstance(builder: Builder? = null): DownloadManager? {
|
||||||
|
if (instance != null && builder != null) {
|
||||||
|
instance!!.release()
|
||||||
|
}
|
||||||
|
if (instance == null) {
|
||||||
|
if (builder == null) return null
|
||||||
|
instance = DownloadManager(builder)
|
||||||
|
}
|
||||||
|
return instance!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var application: Application = builder.application
|
||||||
|
var contextClsName: String = builder.contextClsName
|
||||||
|
var downloadState: Boolean = false
|
||||||
|
var apkUrl: String
|
||||||
|
var apkName: String
|
||||||
|
var apkVersionCode: Int
|
||||||
|
var apkVersionName: String
|
||||||
|
var downloadPath: String
|
||||||
|
var showNewerToast: Boolean
|
||||||
|
var smallIcon: Int
|
||||||
|
var apkDescription: String
|
||||||
|
var apkSize: String
|
||||||
|
var apkMD5: String
|
||||||
|
var httpManager: BaseHttpDownloadManager?
|
||||||
|
var notificationChannel: NotificationChannel?
|
||||||
|
var onDownloadListeners: MutableList<OnDownloadListener>
|
||||||
|
var onButtonClickListener: OnButtonClickListener?
|
||||||
|
var showNotification: Boolean
|
||||||
|
var jumpInstallPage: Boolean
|
||||||
|
var showBgdToast: Boolean
|
||||||
|
var forcedUpgrade: Boolean
|
||||||
|
var notifyId: Int
|
||||||
|
var dialogImage: Int
|
||||||
|
var dialogButtonColor: Int
|
||||||
|
var dialogButtonTextColor: Int
|
||||||
|
var dialogProgressBarColor: Int
|
||||||
|
|
||||||
|
|
||||||
|
init {
|
||||||
|
apkUrl = builder.apkUrl
|
||||||
|
apkName = builder.apkName
|
||||||
|
apkVersionCode = builder.apkVersionCode
|
||||||
|
apkVersionName = builder.apkVersionName
|
||||||
|
downloadPath =
|
||||||
|
builder.downloadPath ?: String.format(Constant.APK_PATH, application.packageName)
|
||||||
|
showNewerToast = builder.showNewerToast
|
||||||
|
smallIcon = builder.smallIcon
|
||||||
|
apkDescription = builder.apkDescription
|
||||||
|
apkSize = builder.apkSize
|
||||||
|
apkMD5 = builder.apkMD5
|
||||||
|
httpManager = builder.httpManager
|
||||||
|
notificationChannel = builder.notificationChannel
|
||||||
|
onDownloadListeners = builder.onDownloadListeners
|
||||||
|
onButtonClickListener = builder.onButtonClickListener
|
||||||
|
showNotification = builder.showNotification
|
||||||
|
jumpInstallPage = builder.jumpInstallPage
|
||||||
|
showBgdToast = builder.showBgdToast
|
||||||
|
forcedUpgrade = builder.forcedUpgrade
|
||||||
|
notifyId = builder.notifyId
|
||||||
|
dialogImage = builder.dialogImage
|
||||||
|
dialogButtonColor = builder.dialogButtonColor
|
||||||
|
dialogButtonTextColor = builder.dialogButtonTextColor
|
||||||
|
dialogProgressBarColor = builder.dialogProgressBarColor
|
||||||
|
// Fix memory leak
|
||||||
|
application.registerActivityLifecycleCallbacks(object : LifecycleCallbacksAdapter() {
|
||||||
|
override fun onActivityDestroyed(activity: Activity) {
|
||||||
|
super.onActivityDestroyed(activity)
|
||||||
|
if (contextClsName == activity.javaClass.name) {
|
||||||
|
clearListener()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start download
|
||||||
|
*/
|
||||||
|
fun download() {
|
||||||
|
if (!checkParams()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (checkVersionCode()) {
|
||||||
|
application.startService(Intent(application, DownloadService::class.java))
|
||||||
|
} else {
|
||||||
|
if (apkVersionCode > ApkUtil.getVersionCode(application)) {
|
||||||
|
application.startActivity(
|
||||||
|
Intent(application, UpdateDialogActivity::class.java)
|
||||||
|
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
if (showNewerToast) {
|
||||||
|
Toast.makeText(application, R.string.latest_version, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
LogUtil.d(TAG, application.resources.getString(R.string.latest_version))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkParams(): Boolean {
|
||||||
|
if (apkUrl.isEmpty()) {
|
||||||
|
LogUtil.e(TAG, "apkUrl can not be empty!")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (apkName.isEmpty()) {
|
||||||
|
LogUtil.e(TAG, "apkName can not be empty!")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!apkName.endsWith(Constant.APK_SUFFIX)) {
|
||||||
|
LogUtil.e(TAG, "apkName must endsWith .apk!")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (smallIcon == -1) {
|
||||||
|
LogUtil.e(TAG, "smallIcon can not be empty!");
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
Constant.AUTHORITIES = "${application.packageName}.fileProvider"
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the set apkVersionCode if it is not the default then use the built-in dialog
|
||||||
|
* If it is the default value Int.MIN_VALUE, directly start the service download
|
||||||
|
*/
|
||||||
|
private fun checkVersionCode(): Boolean {
|
||||||
|
if (apkVersionCode == Int.MIN_VALUE) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (apkDescription.isEmpty()) {
|
||||||
|
LogUtil.e(TAG, "apkDescription can not be empty!")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cancel() {
|
||||||
|
httpManager?.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* release objects
|
||||||
|
*/
|
||||||
|
internal fun release() {
|
||||||
|
httpManager?.release()
|
||||||
|
clearListener()
|
||||||
|
instance = null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun clearListener() {
|
||||||
|
onButtonClickListener = null
|
||||||
|
onDownloadListeners.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Builder constructor(activity: Activity) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* library context
|
||||||
|
*/
|
||||||
|
internal var application: Application = activity.application
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fix the memory leak caused by Activity destroy
|
||||||
|
*/
|
||||||
|
internal var contextClsName: String = activity.javaClass.name
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apk download url
|
||||||
|
*/
|
||||||
|
internal var apkUrl = ""
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apk file name on disk
|
||||||
|
*/
|
||||||
|
internal var apkName = ""
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The apk versionCode that needs to be downloaded
|
||||||
|
*/
|
||||||
|
internal var apkVersionCode = Int.MIN_VALUE
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The versionName of the dialog reality
|
||||||
|
*/
|
||||||
|
internal var apkVersionName = ""
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The file path where the Apk is saved
|
||||||
|
* eg: /storage/emulated/0/Android/data/ your packageName /cache
|
||||||
|
*/
|
||||||
|
internal var downloadPath = application.externalCacheDir?.path
|
||||||
|
|
||||||
|
/**
|
||||||
|
* whether to tip to user "Currently the latest version!"
|
||||||
|
*/
|
||||||
|
internal var showNewerToast = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification icon resource
|
||||||
|
*/
|
||||||
|
internal var smallIcon = -1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* New version description information
|
||||||
|
*/
|
||||||
|
internal var apkDescription = ""
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apk Size,Unit MB
|
||||||
|
*/
|
||||||
|
internal var apkSize = ""
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apk md5 file verification(32-bit) verification repeated download
|
||||||
|
*/
|
||||||
|
internal var apkMD5 = ""
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apk download manager
|
||||||
|
*/
|
||||||
|
internal var httpManager: BaseHttpDownloadManager? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The following are unimportant filed
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* adapter above Android O notification
|
||||||
|
*/
|
||||||
|
internal var notificationChannel: NotificationChannel? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* download listeners
|
||||||
|
*/
|
||||||
|
internal var onDownloadListeners = mutableListOf<OnDownloadListener>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dialog button click listener
|
||||||
|
*/
|
||||||
|
internal var onButtonClickListener: OnButtonClickListener? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to show the progress of the notification
|
||||||
|
*/
|
||||||
|
internal var showNotification = true
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the installation page will pop up automatically after the download is complete
|
||||||
|
*/
|
||||||
|
internal var jumpInstallPage = true
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does the download start tip "Downloading a new version in the background..."
|
||||||
|
*/
|
||||||
|
internal var showBgdToast = true
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to force an upgrade
|
||||||
|
*/
|
||||||
|
internal var forcedUpgrade = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification id
|
||||||
|
*/
|
||||||
|
internal var notifyId = Constant.DEFAULT_NOTIFY_ID
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dialog background Image resource
|
||||||
|
*/
|
||||||
|
internal var dialogImage = -1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dialog button background color
|
||||||
|
*/
|
||||||
|
internal var dialogButtonColor = -1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dialog button text color
|
||||||
|
*/
|
||||||
|
internal var dialogButtonTextColor = -1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dialog progress bar color and progress-text color
|
||||||
|
*/
|
||||||
|
internal var dialogProgressBarColor = -1
|
||||||
|
|
||||||
|
|
||||||
|
fun apkUrl(apkUrl: String): Builder {
|
||||||
|
this.apkUrl = apkUrl
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun apkName(apkName: String): Builder {
|
||||||
|
this.apkName = apkName
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun apkVersionCode(apkVersionCode: Int): Builder {
|
||||||
|
this.apkVersionCode = apkVersionCode
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun apkVersionName(apkVersionName: String): Builder {
|
||||||
|
this.apkVersionName = apkVersionName
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showNewerToast(showNewerToast: Boolean): Builder {
|
||||||
|
this.showNewerToast = showNewerToast
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun smallIcon(smallIcon: Int): Builder {
|
||||||
|
this.smallIcon = smallIcon
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun apkDescription(apkDescription: String): Builder {
|
||||||
|
this.apkDescription = apkDescription
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun apkSize(apkSize: String): Builder {
|
||||||
|
this.apkSize = apkSize
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun apkMD5(apkMD5: String): Builder {
|
||||||
|
this.apkMD5 = apkMD5
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun httpManager(httpManager: BaseHttpDownloadManager): Builder {
|
||||||
|
this.httpManager = httpManager
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun notificationChannel(notificationChannel: NotificationChannel): Builder {
|
||||||
|
this.notificationChannel = notificationChannel
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onButtonClickListener(onButtonClickListener: OnButtonClickListener): Builder {
|
||||||
|
this.onButtonClickListener = onButtonClickListener
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onDownloadListener(onDownloadListener: OnDownloadListener): Builder {
|
||||||
|
this.onDownloadListeners.add(onDownloadListener)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showNotification(showNotification: Boolean): Builder {
|
||||||
|
this.showNotification = showNotification
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun jumpInstallPage(jumpInstallPage: Boolean): Builder {
|
||||||
|
this.jumpInstallPage = jumpInstallPage
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showBgdToast(showBgdToast: Boolean): Builder {
|
||||||
|
this.showBgdToast = showBgdToast
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun forcedUpgrade(forcedUpgrade: Boolean): Builder {
|
||||||
|
this.forcedUpgrade = forcedUpgrade
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun notifyId(notifyId: Int): Builder {
|
||||||
|
this.notifyId = notifyId
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dialogImage(dialogImage: Int): Builder {
|
||||||
|
this.dialogImage = dialogImage
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dialogButtonColor(dialogButtonColor: Int): Builder {
|
||||||
|
this.dialogButtonColor = dialogButtonColor
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dialogButtonTextColor(dialogButtonTextColor: Int): Builder {
|
||||||
|
this.dialogButtonTextColor = dialogButtonTextColor
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dialogProgressBarColor(dialogProgressBarColor: Int): Builder {
|
||||||
|
this.dialogProgressBarColor = dialogProgressBarColor
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun enableLog(enable: Boolean): Builder {
|
||||||
|
LogUtil.enable(enable)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun build(): DownloadManager {
|
||||||
|
return getInstance(this)!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,135 @@
|
|||||||
|
package com.tongdaxing.erban.upgrade.manager
|
||||||
|
|
||||||
|
import com.tongdaxing.erban.upgrade.base.BaseHttpDownloadManager
|
||||||
|
import com.tongdaxing.erban.upgrade.base.bean.DownloadStatus
|
||||||
|
import com.tongdaxing.erban.upgrade.config.Constant
|
||||||
|
import com.tongdaxing.erban.upgrade.util.LogUtil
|
||||||
|
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.SecureRandom
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
|
import javax.net.ssl.HttpsURLConnection
|
||||||
|
import javax.net.ssl.SSLContext
|
||||||
|
import javax.net.ssl.TrustManager
|
||||||
|
import javax.net.ssl.X509TrustManager
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProjectName: AppUpdate
|
||||||
|
* PackageName: com.tongdaxing.erban.upgrade.manager
|
||||||
|
* FileName: HttpDownloadManager
|
||||||
|
* CreateDate: 2022/4/7 on 14:29
|
||||||
|
* Desc:
|
||||||
|
*
|
||||||
|
* @author azhon
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Suppress("BlockingMethodInNonBlockingContext")
|
||||||
|
class HttpDownloadManager(private val path: String) : BaseHttpDownloadManager() {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "HttpDownloadManager"
|
||||||
|
}
|
||||||
|
|
||||||
|
private var shutdown: Boolean = false
|
||||||
|
|
||||||
|
override fun download(apkUrl: String, apkName: String): Flow<DownloadStatus> {
|
||||||
|
trustAllHosts()
|
||||||
|
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<DownloadStatus>
|
||||||
|
) {
|
||||||
|
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() {
|
||||||
|
val manager: TrustManager = object : X509TrustManager {
|
||||||
|
override fun getAcceptedIssuers(): Array<X509Certificate> {
|
||||||
|
return arrayOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) {
|
||||||
|
LogUtil.d(TAG, "checkClientTrusted")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun checkServerTrusted(chain: Array<out X509Certificate>?, 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()
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,188 @@
|
|||||||
|
package com.tongdaxing.erban.upgrade.service
|
||||||
|
|
||||||
|
import android.app.Service
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.IBinder
|
||||||
|
import android.widget.Toast
|
||||||
|
import com.mango.moshen.R
|
||||||
|
import com.tongdaxing.erban.upgrade.base.bean.DownloadStatus
|
||||||
|
import com.tongdaxing.erban.upgrade.config.Constant
|
||||||
|
import com.tongdaxing.erban.upgrade.listener.OnDownloadListener
|
||||||
|
import com.tongdaxing.erban.upgrade.manager.DownloadManager
|
||||||
|
import com.tongdaxing.erban.upgrade.manager.HttpDownloadManager
|
||||||
|
import com.tongdaxing.erban.upgrade.util.ApkUtil
|
||||||
|
import com.tongdaxing.erban.upgrade.util.FileUtil
|
||||||
|
import com.tongdaxing.erban.upgrade.util.LogUtil
|
||||||
|
import com.tongdaxing.erban.upgrade.util.NotificationUtil
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProjectName: AppUpdate
|
||||||
|
* PackageName: com.tongdaxing.erban.upgrade.service
|
||||||
|
* FileName: DownloadService
|
||||||
|
* CreateDate: 2022/4/7 on 11:42
|
||||||
|
* Desc:
|
||||||
|
*
|
||||||
|
* @author azhon
|
||||||
|
*/
|
||||||
|
|
||||||
|
class DownloadService : Service(), OnDownloadListener {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "DownloadService"
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var manager: DownloadManager
|
||||||
|
private var lastProgress = 0
|
||||||
|
|
||||||
|
override fun onBind(intent: Intent?): IBinder? = null
|
||||||
|
|
||||||
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
|
if (intent == null) {
|
||||||
|
return START_NOT_STICKY
|
||||||
|
}
|
||||||
|
init()
|
||||||
|
return super.onStartCommand(intent, flags, startId)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun init() {
|
||||||
|
val tempManager = DownloadManager.getInstance()
|
||||||
|
if (tempManager == null) {
|
||||||
|
LogUtil.e(TAG, "An exception occurred by DownloadManager=null,please check your code!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
manager = tempManager
|
||||||
|
FileUtil.createDirDirectory(manager.downloadPath)
|
||||||
|
|
||||||
|
val enable = NotificationUtil.notificationEnable(this@DownloadService)
|
||||||
|
LogUtil.d(
|
||||||
|
TAG,
|
||||||
|
if (enable) "Notification switch status: opened" else " Notification switch status: closed"
|
||||||
|
)
|
||||||
|
if (checkApkMd5()) {
|
||||||
|
LogUtil.d(TAG, "Apk already exist and install it directly.")
|
||||||
|
//install apk
|
||||||
|
done(File(manager.downloadPath, manager.apkName))
|
||||||
|
} else {
|
||||||
|
LogUtil.d(TAG, "Apk don't exist will start download.")
|
||||||
|
download()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the Apk has been downloaded, don't download again
|
||||||
|
*/
|
||||||
|
private fun checkApkMd5(): Boolean {
|
||||||
|
val file = File(manager.downloadPath, manager.apkName)
|
||||||
|
if (file.exists()) {
|
||||||
|
return FileUtil.md5(file).equals(manager.apkMD5, ignoreCase = true)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
private fun download() {
|
||||||
|
if (manager.downloadState) {
|
||||||
|
LogUtil.e(TAG, "Currently downloading, please download again!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (manager.httpManager == null) {
|
||||||
|
manager.httpManager = HttpDownloadManager(manager.downloadPath)
|
||||||
|
}
|
||||||
|
GlobalScope.launch(Dispatchers.Main + CoroutineName(Constant.COROUTINE_NAME)) {
|
||||||
|
manager.httpManager!!.download(manager.apkUrl, manager.apkName)
|
||||||
|
.collect {
|
||||||
|
when (it) {
|
||||||
|
is DownloadStatus.Start -> start()
|
||||||
|
is DownloadStatus.Downloading -> downloading(it.max, it.progress)
|
||||||
|
is DownloadStatus.Done -> done(it.apk)
|
||||||
|
is DownloadStatus.Cancel -> this@DownloadService.cancel()
|
||||||
|
is DownloadStatus.Error -> error(it.e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
manager.downloadState = true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun start() {
|
||||||
|
LogUtil.i(TAG, "download start")
|
||||||
|
if (manager.showBgdToast) {
|
||||||
|
Toast.makeText(this, R.string.background_downloading, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
if (manager.showNotification) {
|
||||||
|
NotificationUtil.showNotification(
|
||||||
|
this@DownloadService, manager.smallIcon,
|
||||||
|
resources.getString(R.string.start_download),
|
||||||
|
resources.getString(R.string.start_download_hint)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
manager.onDownloadListeners.forEach { it.start() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun downloading(max: Int, progress: Int) {
|
||||||
|
if (manager.showNotification) {
|
||||||
|
val curr = (progress / max.toDouble() * 100.0).toInt()
|
||||||
|
if (curr == lastProgress) return
|
||||||
|
LogUtil.i(TAG, "downloading max: $max --- progress: $progress")
|
||||||
|
lastProgress = curr
|
||||||
|
val content = if (curr < 0) "" else "$curr%"
|
||||||
|
NotificationUtil.showProgressNotification(
|
||||||
|
this@DownloadService, manager.smallIcon,
|
||||||
|
resources.getString(R.string.start_downloading),
|
||||||
|
content, if (max == -1) -1 else 100, curr
|
||||||
|
)
|
||||||
|
}
|
||||||
|
manager.onDownloadListeners.forEach { it.downloading(max, progress) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun done(apk: File) {
|
||||||
|
LogUtil.d(TAG, "apk downloaded to ${apk.path}")
|
||||||
|
manager.downloadState = false
|
||||||
|
//If it is android Q (api=29) and above, (showNotification=false) will also send a
|
||||||
|
// download completion notification
|
||||||
|
if (manager.showNotification || Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
NotificationUtil.showDoneNotification(
|
||||||
|
this@DownloadService, manager.smallIcon,
|
||||||
|
resources.getString(R.string.download_completed),
|
||||||
|
resources.getString(R.string.click_hint),
|
||||||
|
Constant.AUTHORITIES!!, apk
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (manager.jumpInstallPage) {
|
||||||
|
ApkUtil.installApk(this@DownloadService, Constant.AUTHORITIES!!, apk)
|
||||||
|
}
|
||||||
|
manager.onDownloadListeners.forEach { it.done(apk) }
|
||||||
|
|
||||||
|
// release objects
|
||||||
|
releaseResources()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun cancel() {
|
||||||
|
LogUtil.i(TAG, "download cancel")
|
||||||
|
manager.downloadState = false
|
||||||
|
if (manager.showNotification) {
|
||||||
|
NotificationUtil.cancelNotification(this@DownloadService)
|
||||||
|
}
|
||||||
|
manager.onDownloadListeners.forEach { it.cancel() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun error(e: Throwable) {
|
||||||
|
LogUtil.e(TAG, "download error: $e")
|
||||||
|
manager.downloadState = false
|
||||||
|
if (manager.showNotification) {
|
||||||
|
NotificationUtil.showErrorNotification(
|
||||||
|
this@DownloadService, manager.smallIcon,
|
||||||
|
resources.getString(R.string.download_error),
|
||||||
|
resources.getString(R.string.continue_downloading),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
manager.onDownloadListeners.forEach { it.error(e) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun releaseResources() {
|
||||||
|
manager.release()
|
||||||
|
stopSelf()
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,82 @@
|
|||||||
|
package com.tongdaxing.erban.upgrade.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.core.content.FileProvider
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProjectName: AppUpdate
|
||||||
|
* PackageName: com.tongdaxing.erban.upgrade.util
|
||||||
|
* FileName: ApkUtil
|
||||||
|
* CreateDate: 2022/4/7 on 17:02
|
||||||
|
* Desc:
|
||||||
|
*
|
||||||
|
* @author azhon
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ApkUtil {
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* install package form file
|
||||||
|
*/
|
||||||
|
fun installApk(context: Context, authorities: String, apk: File) {
|
||||||
|
context.startActivity(createInstallIntent(context, authorities, apk))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createInstallIntent(context: Context, authorities: String, apk: File): Intent {
|
||||||
|
val intent = Intent().apply {
|
||||||
|
action = Intent.ACTION_VIEW
|
||||||
|
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
addCategory(Intent.CATEGORY_DEFAULT)
|
||||||
|
}
|
||||||
|
val uri: Uri
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
uri = FileProvider.getUriForFile(context, authorities, apk)
|
||||||
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
} else {
|
||||||
|
uri = Uri.fromFile(apk)
|
||||||
|
}
|
||||||
|
intent.setDataAndType(uri, "application/vnd.android.package-archive")
|
||||||
|
return intent
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getVersionCode(context: Context): Long {
|
||||||
|
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
|
||||||
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
|
packageInfo.longVersionCode
|
||||||
|
} else {
|
||||||
|
return packageInfo.versionCode.toLong()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteOldApk(context: Context, oldApkPath: String): Boolean {
|
||||||
|
val curVersionCode = getVersionCode(context)
|
||||||
|
try {
|
||||||
|
val apk = File(oldApkPath)
|
||||||
|
if (apk.exists()) {
|
||||||
|
val oldVersionCode = getVersionCodeByPath(context, oldApkPath)
|
||||||
|
if (curVersionCode > oldVersionCode) {
|
||||||
|
return apk.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getVersionCodeByPath(context: Context, path: String): Long {
|
||||||
|
val packageInfo =
|
||||||
|
context.packageManager.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES)
|
||||||
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
|
packageInfo?.longVersionCode ?: 1
|
||||||
|
} else {
|
||||||
|
return packageInfo?.versionCode?.toLong() ?: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,23 @@
|
|||||||
|
package com.tongdaxing.erban.upgrade.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProjectName: AppUpdate
|
||||||
|
* PackageName: com.tongdaxing.erban.upgrade.util
|
||||||
|
* FileName: DensityUtil
|
||||||
|
* CreateDate: 2022/4/7 on 17:52
|
||||||
|
* Desc:
|
||||||
|
*
|
||||||
|
* @author azhon
|
||||||
|
*/
|
||||||
|
|
||||||
|
class DensityUtil {
|
||||||
|
companion object {
|
||||||
|
fun dip2px(context: Context, dpValue: Float): Float {
|
||||||
|
val scale = context.resources.displayMetrics.density
|
||||||
|
return dpValue * scale + 0.5f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,47 @@
|
|||||||
|
package com.tongdaxing.erban.upgrade.util
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileInputStream
|
||||||
|
import java.math.BigInteger
|
||||||
|
import java.security.MessageDigest
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProjectName: AppUpdate
|
||||||
|
* PackageName: com.tongdaxing.erban.upgrade.util
|
||||||
|
* FileName: FileUtil
|
||||||
|
* CreateDate: 2022/4/7 on 11:52
|
||||||
|
* Desc:
|
||||||
|
*
|
||||||
|
* @author azhon
|
||||||
|
*/
|
||||||
|
|
||||||
|
class FileUtil {
|
||||||
|
companion object {
|
||||||
|
fun createDirDirectory(path: String) {
|
||||||
|
File(path).let {
|
||||||
|
if (!it.exists()) {
|
||||||
|
it.mkdirs()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun md5(file: File): String {
|
||||||
|
try {
|
||||||
|
val buffer = ByteArray(1024)
|
||||||
|
var len: Int
|
||||||
|
val digest = MessageDigest.getInstance("MD5")
|
||||||
|
val inStream = FileInputStream(file)
|
||||||
|
while (inStream.read(buffer).also { len = it } != -1) {
|
||||||
|
digest.update(buffer, 0, len)
|
||||||
|
}
|
||||||
|
inStream.close()
|
||||||
|
val bigInt = BigInteger(1, digest.digest())
|
||||||
|
return bigInt.toString(16).toUpperCase(Locale.ROOT)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,39 @@
|
|||||||
|
package com.tongdaxing.erban.upgrade.util
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import com.tongdaxing.erban.upgrade.config.Constant
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProjectName: AppUpdate
|
||||||
|
* PackageName: com.tongdaxing.erban.upgrade.util
|
||||||
|
* FileName: LogUtil
|
||||||
|
* CreateDate: 2022/4/7 on 11:23
|
||||||
|
* Desc:
|
||||||
|
*
|
||||||
|
* @author azhon
|
||||||
|
*/
|
||||||
|
|
||||||
|
class LogUtil {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
var b = true
|
||||||
|
|
||||||
|
fun enable(enable: Boolean) {
|
||||||
|
b = enable
|
||||||
|
}
|
||||||
|
|
||||||
|
fun e(tag: String, msg: String) {
|
||||||
|
if (b) Log.e(Constant.TAG + tag, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun d(tag: String, msg: String) {
|
||||||
|
if (b) Log.d(Constant.TAG + tag, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun i(tag: String, msg: String) {
|
||||||
|
if (b) Log.i(Constant.TAG + tag, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,163 @@
|
|||||||
|
package com.tongdaxing.erban.upgrade.util
|
||||||
|
|
||||||
|
import android.app.Notification
|
||||||
|
import android.app.NotificationChannel
|
||||||
|
import android.app.NotificationManager
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
|
import androidx.core.app.NotificationManagerCompat
|
||||||
|
import com.tongdaxing.erban.upgrade.config.Constant
|
||||||
|
import com.tongdaxing.erban.upgrade.manager.DownloadManager
|
||||||
|
import com.tongdaxing.erban.upgrade.service.DownloadService
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProjectName: AppUpdate
|
||||||
|
* PackageName: com.tongdaxing.erban.upgrade.util
|
||||||
|
* FileName: NotificationUtil
|
||||||
|
* CreateDate: 2022/4/7 on 13:36
|
||||||
|
* Desc:
|
||||||
|
*
|
||||||
|
* @author azhon
|
||||||
|
*/
|
||||||
|
class NotificationUtil {
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
fun notificationEnable(context: Context): Boolean {
|
||||||
|
return NotificationManagerCompat.from(context).areNotificationsEnabled()
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
|
private fun getNotificationChannelId(): String {
|
||||||
|
val channel = DownloadManager.getInstance()?.notificationChannel
|
||||||
|
return if (channel == null) {
|
||||||
|
Constant.DEFAULT_CHANNEL_ID
|
||||||
|
} else {
|
||||||
|
channel.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun builderNotification(
|
||||||
|
context: Context, icon: Int, title: String, content: String
|
||||||
|
): NotificationCompat.Builder {
|
||||||
|
var channelId = ""
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
channelId = getNotificationChannelId()
|
||||||
|
}
|
||||||
|
return NotificationCompat.Builder(context, channelId)
|
||||||
|
.setSmallIcon(icon)
|
||||||
|
.setContentTitle(title)
|
||||||
|
.setWhen(System.currentTimeMillis())
|
||||||
|
.setContentText(content)
|
||||||
|
.setAutoCancel(false)
|
||||||
|
.setOngoing(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showNotification(context: Context, icon: Int, title: String, content: String) {
|
||||||
|
val manager =
|
||||||
|
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
afterO(manager)
|
||||||
|
}
|
||||||
|
val notify = builderNotification(context, icon, title, content)
|
||||||
|
.setDefaults(Notification.DEFAULT_SOUND)
|
||||||
|
.build()
|
||||||
|
manager.notify(
|
||||||
|
DownloadManager.getInstance()?.notifyId ?: Constant.DEFAULT_NOTIFY_ID,
|
||||||
|
notify
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* send a downloading Notification
|
||||||
|
*/
|
||||||
|
fun showProgressNotification(
|
||||||
|
context: Context, icon: Int, title: String, content: String, max: Int, progress: Int
|
||||||
|
) {
|
||||||
|
val manager =
|
||||||
|
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
val notify = builderNotification(context, icon, title, content)
|
||||||
|
.setProgress(max, progress, max == -1).build()
|
||||||
|
manager.notify(
|
||||||
|
DownloadManager.getInstance()?.notifyId ?: Constant.DEFAULT_NOTIFY_ID,
|
||||||
|
notify
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* send a downloaded Notification
|
||||||
|
*/
|
||||||
|
fun showDoneNotification(
|
||||||
|
context: Context, icon: Int, title: String, content: String,
|
||||||
|
authorities: String, apk: File
|
||||||
|
) {
|
||||||
|
val manager =
|
||||||
|
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
manager.cancel(DownloadManager.getInstance()?.notifyId ?: Constant.DEFAULT_NOTIFY_ID)
|
||||||
|
val intent = ApkUtil.createInstallIntent(context, authorities, apk)
|
||||||
|
val pi = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
|
||||||
|
val notify = builderNotification(context, icon, title, content)
|
||||||
|
.setContentIntent(pi)
|
||||||
|
.build()
|
||||||
|
notify.flags = notify.flags or Notification.FLAG_AUTO_CANCEL
|
||||||
|
manager.notify(
|
||||||
|
DownloadManager.getInstance()?.notifyId ?: Constant.DEFAULT_NOTIFY_ID,
|
||||||
|
notify
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* send a error Notification
|
||||||
|
*/
|
||||||
|
fun showErrorNotification(
|
||||||
|
context: Context, icon: Int, title: String, content: String
|
||||||
|
) {
|
||||||
|
val manager =
|
||||||
|
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
afterO(manager)
|
||||||
|
}
|
||||||
|
val intent = Intent(context, DownloadService::class.java)
|
||||||
|
val pi = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
|
||||||
|
val notify = builderNotification(context, icon, title, content)
|
||||||
|
.setAutoCancel(true)
|
||||||
|
.setOngoing(false)
|
||||||
|
.setContentIntent(pi)
|
||||||
|
.setDefaults(Notification.DEFAULT_SOUND)
|
||||||
|
.build()
|
||||||
|
manager.notify(
|
||||||
|
DownloadManager.getInstance()?.notifyId ?: Constant.DEFAULT_NOTIFY_ID,
|
||||||
|
notify
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cancel Notification by id
|
||||||
|
*/
|
||||||
|
fun cancelNotification(context: Context) {
|
||||||
|
val manager =
|
||||||
|
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
manager.cancel(DownloadManager.getInstance()?.notifyId ?: Constant.DEFAULT_NOTIFY_ID)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
|
private fun afterO(manager: NotificationManager) {
|
||||||
|
var channel = DownloadManager.getInstance()?.notificationChannel
|
||||||
|
if (channel == null) {
|
||||||
|
channel = NotificationChannel(
|
||||||
|
Constant.DEFAULT_CHANNEL_ID, Constant.DEFAULT_CHANNEL_NAME,
|
||||||
|
NotificationManager.IMPORTANCE_LOW
|
||||||
|
).apply {
|
||||||
|
enableLights(true)
|
||||||
|
setShowBadge(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
manager.createNotificationChannel(channel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,479 @@
|
|||||||
|
package com.tongdaxing.erban.upgrade.view;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.RectF;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import com.mango.moshen.R;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by daimajia on 14-4-30.
|
||||||
|
* <a href="https://github.com/daimajia/NumberProgressBar/}"/>
|
||||||
|
*/
|
||||||
|
public class NumberProgressBar extends View {
|
||||||
|
|
||||||
|
private int mMaxProgress = 100;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current progress, can not exceed the max progress.
|
||||||
|
*/
|
||||||
|
private int mCurrentProgress = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The progress area bar color.
|
||||||
|
*/
|
||||||
|
private int mReachedBarColor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bar unreached area color.
|
||||||
|
*/
|
||||||
|
private int mUnreachedBarColor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The progress text color.
|
||||||
|
*/
|
||||||
|
private int mTextColor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The progress text size.
|
||||||
|
*/
|
||||||
|
private float mTextSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The height of the reached area.
|
||||||
|
*/
|
||||||
|
private float mReachedBarHeight;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The height of the unreached area.
|
||||||
|
*/
|
||||||
|
private float mUnreachedBarHeight;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The suffix of the number.
|
||||||
|
*/
|
||||||
|
private String mSuffix = "%";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The prefix.
|
||||||
|
*/
|
||||||
|
private String mPrefix = "";
|
||||||
|
|
||||||
|
|
||||||
|
private final int default_text_color = Color.rgb(255, 137, 91);
|
||||||
|
private final int default_reached_color = Color.rgb(255, 137, 91);
|
||||||
|
private final int default_unreached_color = Color.rgb(204, 204, 204);
|
||||||
|
private final float default_progress_text_offset;
|
||||||
|
private final float default_text_size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For save and restore instance of progressbar.
|
||||||
|
*/
|
||||||
|
private static final String INSTANCE_STATE = "saved_instance";
|
||||||
|
private static final String INSTANCE_TEXT_COLOR = "text_color";
|
||||||
|
private static final String INSTANCE_TEXT_SIZE = "text_size";
|
||||||
|
private static final String INSTANCE_REACHED_BAR_HEIGHT = "reached_bar_height";
|
||||||
|
private static final String INSTANCE_REACHED_BAR_COLOR = "reached_bar_color";
|
||||||
|
private static final String INSTANCE_UNREACHED_BAR_HEIGHT = "unreached_bar_height";
|
||||||
|
private static final String INSTANCE_UNREACHED_BAR_COLOR = "unreached_bar_color";
|
||||||
|
private static final String INSTANCE_MAX = "max";
|
||||||
|
private static final String INSTANCE_PROGRESS = "progress";
|
||||||
|
private static final String INSTANCE_SUFFIX = "suffix";
|
||||||
|
private static final String INSTANCE_PREFIX = "prefix";
|
||||||
|
private static final String INSTANCE_TEXT_VISIBILITY = "text_visibility";
|
||||||
|
|
||||||
|
private static final int PROGRESS_TEXT_VISIBLE = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The width of the text that to be drawn.
|
||||||
|
*/
|
||||||
|
private float mDrawTextWidth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The drawn text start.
|
||||||
|
*/
|
||||||
|
private float mDrawTextStart;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The drawn text end.
|
||||||
|
*/
|
||||||
|
private float mDrawTextEnd;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The text that to be drawn in onDraw().
|
||||||
|
*/
|
||||||
|
private String mCurrentDrawText;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Paint of the reached area.
|
||||||
|
*/
|
||||||
|
private Paint mReachedBarPaint;
|
||||||
|
/**
|
||||||
|
* The Paint of the unreached area.
|
||||||
|
*/
|
||||||
|
private Paint mUnreachedBarPaint;
|
||||||
|
/**
|
||||||
|
* The Paint of the progress text.
|
||||||
|
*/
|
||||||
|
private Paint mTextPaint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unreached bar area to draw rect.
|
||||||
|
*/
|
||||||
|
private RectF mUnreachedRectF = new RectF(0, 0, 0, 0);
|
||||||
|
/**
|
||||||
|
* Reached bar area rect.
|
||||||
|
*/
|
||||||
|
private RectF mReachedRectF = new RectF(0, 0, 0, 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The progress text offset.
|
||||||
|
*/
|
||||||
|
private float mOffset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if need to draw unreached area.
|
||||||
|
*/
|
||||||
|
private boolean mDrawUnreachedBar = true;
|
||||||
|
|
||||||
|
private boolean mDrawReachedBar = true;
|
||||||
|
|
||||||
|
private boolean mIfDrawText = true;
|
||||||
|
|
||||||
|
public enum ProgressTextVisibility {
|
||||||
|
Visible, Invisible
|
||||||
|
}
|
||||||
|
|
||||||
|
public NumberProgressBar(Context context) {
|
||||||
|
this(context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NumberProgressBar(Context context, AttributeSet attrs) {
|
||||||
|
this(context, attrs, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NumberProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
|
||||||
|
mReachedBarHeight = dp2px(1.5f);
|
||||||
|
mUnreachedBarHeight = dp2px(1.0f);
|
||||||
|
default_text_size = sp2px(10);
|
||||||
|
default_progress_text_offset = dp2px(3.0f);
|
||||||
|
|
||||||
|
//load styled attributes.
|
||||||
|
final TypedArray attributes = context.getTheme().obtainStyledAttributes(attrs, R.styleable.NumberProgressBar,
|
||||||
|
defStyleAttr, 0);
|
||||||
|
|
||||||
|
mReachedBarColor = attributes.getColor(R.styleable.NumberProgressBar_progress_reached_color, default_reached_color);
|
||||||
|
mUnreachedBarColor = attributes.getColor(R.styleable.NumberProgressBar_progress_unreached_color, default_unreached_color);
|
||||||
|
mTextColor = attributes.getColor(R.styleable.NumberProgressBar_progress_text_color, default_text_color);
|
||||||
|
mTextSize = attributes.getDimension(R.styleable.NumberProgressBar_progress_text_size, default_text_size);
|
||||||
|
attributes.recycle();
|
||||||
|
initializePainters();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getSuggestedMinimumWidth() {
|
||||||
|
return (int) mTextSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getSuggestedMinimumHeight() {
|
||||||
|
return Math.max((int) mTextSize, Math.max((int) mReachedBarHeight, (int) mUnreachedBarHeight));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
setMeasuredDimension(measure(widthMeasureSpec, true), measure(heightMeasureSpec, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int measure(int measureSpec, boolean isWidth) {
|
||||||
|
int result;
|
||||||
|
int mode = MeasureSpec.getMode(measureSpec);
|
||||||
|
int size = MeasureSpec.getSize(measureSpec);
|
||||||
|
int padding = isWidth ? getPaddingLeft() + getPaddingRight() : getPaddingTop() + getPaddingBottom();
|
||||||
|
if (mode == MeasureSpec.EXACTLY) {
|
||||||
|
result = size;
|
||||||
|
} else {
|
||||||
|
result = isWidth ? getSuggestedMinimumWidth() : getSuggestedMinimumHeight();
|
||||||
|
result += padding;
|
||||||
|
if (mode == MeasureSpec.AT_MOST) {
|
||||||
|
if (isWidth) {
|
||||||
|
result = Math.max(result, size);
|
||||||
|
} else {
|
||||||
|
result = Math.min(result, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(Canvas canvas) {
|
||||||
|
if (mIfDrawText) {
|
||||||
|
calculateDrawRectF();
|
||||||
|
} else {
|
||||||
|
calculateDrawRectFWithoutProgressText();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDrawReachedBar) {
|
||||||
|
canvas.drawRect(mReachedRectF, mReachedBarPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDrawUnreachedBar) {
|
||||||
|
canvas.drawRect(mUnreachedRectF, mUnreachedBarPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mIfDrawText)
|
||||||
|
canvas.drawText(mCurrentDrawText, mDrawTextStart, mDrawTextEnd, mTextPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializePainters() {
|
||||||
|
mReachedBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
mReachedBarPaint.setColor(mReachedBarColor);
|
||||||
|
|
||||||
|
mUnreachedBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
mUnreachedBarPaint.setColor(mUnreachedBarColor);
|
||||||
|
|
||||||
|
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
mTextPaint.setColor(mTextColor);
|
||||||
|
mTextPaint.setTextSize(mTextSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void calculateDrawRectFWithoutProgressText() {
|
||||||
|
mReachedRectF.left = getPaddingLeft();
|
||||||
|
mReachedRectF.top = getHeight() / 2.0f - mReachedBarHeight / 2.0f;
|
||||||
|
mReachedRectF.right = (getWidth() - getPaddingLeft() - getPaddingRight()) / (getMax() * 1.0f) * getProgress() + getPaddingLeft();
|
||||||
|
mReachedRectF.bottom = getHeight() / 2.0f + mReachedBarHeight / 2.0f;
|
||||||
|
|
||||||
|
mUnreachedRectF.left = mReachedRectF.right;
|
||||||
|
mUnreachedRectF.right = getWidth() - getPaddingRight();
|
||||||
|
mUnreachedRectF.top = getHeight() / 2.0f + -mUnreachedBarHeight / 2.0f;
|
||||||
|
mUnreachedRectF.bottom = getHeight() / 2.0f + mUnreachedBarHeight / 2.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void calculateDrawRectF() {
|
||||||
|
|
||||||
|
mCurrentDrawText = String.format("%d", getProgress() * 100 / getMax());
|
||||||
|
mCurrentDrawText = mPrefix + mCurrentDrawText + mSuffix;
|
||||||
|
mDrawTextWidth = mTextPaint.measureText(mCurrentDrawText);
|
||||||
|
|
||||||
|
if (getProgress() == 0) {
|
||||||
|
mDrawReachedBar = false;
|
||||||
|
mDrawTextStart = getPaddingLeft();
|
||||||
|
} else {
|
||||||
|
mDrawReachedBar = true;
|
||||||
|
mReachedRectF.left = getPaddingLeft();
|
||||||
|
mReachedRectF.top = getHeight() / 2.0f - mReachedBarHeight / 2.0f;
|
||||||
|
mReachedRectF.right = (getWidth() - getPaddingLeft() - getPaddingRight()) / (getMax() * 1.0f) * getProgress() - mOffset + getPaddingLeft();
|
||||||
|
mReachedRectF.bottom = getHeight() / 2.0f + mReachedBarHeight / 2.0f;
|
||||||
|
mDrawTextStart = (mReachedRectF.right + mOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
mDrawTextEnd = (int) ((getHeight() / 2.0f) - ((mTextPaint.descent() + mTextPaint.ascent()) / 2.0f));
|
||||||
|
|
||||||
|
if ((mDrawTextStart + mDrawTextWidth) >= getWidth() - getPaddingRight()) {
|
||||||
|
mDrawTextStart = getWidth() - getPaddingRight() - mDrawTextWidth;
|
||||||
|
mReachedRectF.right = mDrawTextStart - mOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
float unreachedBarStart = mDrawTextStart + mDrawTextWidth + mOffset;
|
||||||
|
if (unreachedBarStart >= getWidth() - getPaddingRight()) {
|
||||||
|
mDrawUnreachedBar = false;
|
||||||
|
} else {
|
||||||
|
mDrawUnreachedBar = true;
|
||||||
|
mUnreachedRectF.left = unreachedBarStart;
|
||||||
|
mUnreachedRectF.right = getWidth() - getPaddingRight();
|
||||||
|
mUnreachedRectF.top = getHeight() / 2.0f + -mUnreachedBarHeight / 2.0f;
|
||||||
|
mUnreachedRectF.bottom = getHeight() / 2.0f + mUnreachedBarHeight / 2.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get progress text color.
|
||||||
|
*
|
||||||
|
* @return progress text color.
|
||||||
|
*/
|
||||||
|
public int getTextColor() {
|
||||||
|
return mTextColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get progress text size.
|
||||||
|
*
|
||||||
|
* @return progress text size.
|
||||||
|
*/
|
||||||
|
public float getProgressTextSize() {
|
||||||
|
return mTextSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getUnreachedBarColor() {
|
||||||
|
return mUnreachedBarColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getReachedBarColor() {
|
||||||
|
return mReachedBarColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getProgress() {
|
||||||
|
return mCurrentProgress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMax() {
|
||||||
|
return mMaxProgress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getReachedBarHeight() {
|
||||||
|
return mReachedBarHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getUnreachedBarHeight() {
|
||||||
|
return mUnreachedBarHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProgressTextSize(float textSize) {
|
||||||
|
this.mTextSize = textSize;
|
||||||
|
mTextPaint.setTextSize(mTextSize);
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProgressTextColor(int textColor) {
|
||||||
|
this.mTextColor = textColor;
|
||||||
|
mTextPaint.setColor(mTextColor);
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUnreachedBarColor(int barColor) {
|
||||||
|
this.mUnreachedBarColor = barColor;
|
||||||
|
mUnreachedBarPaint.setColor(mUnreachedBarColor);
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReachedBarColor(int progressColor) {
|
||||||
|
this.mReachedBarColor = progressColor;
|
||||||
|
mReachedBarPaint.setColor(mReachedBarColor);
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReachedBarHeight(float height) {
|
||||||
|
mReachedBarHeight = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUnreachedBarHeight(float height) {
|
||||||
|
mUnreachedBarHeight = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMax(int maxProgress) {
|
||||||
|
if (maxProgress > 0) {
|
||||||
|
this.mMaxProgress = maxProgress;
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSuffix(String suffix) {
|
||||||
|
if (suffix == null) {
|
||||||
|
mSuffix = "";
|
||||||
|
} else {
|
||||||
|
mSuffix = suffix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSuffix() {
|
||||||
|
return mSuffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrefix(String prefix) {
|
||||||
|
if (prefix == null)
|
||||||
|
mPrefix = "";
|
||||||
|
else {
|
||||||
|
mPrefix = prefix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPrefix() {
|
||||||
|
return mPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void incrementProgressBy(int by) {
|
||||||
|
if (by > 0) {
|
||||||
|
setProgress(getProgress() + by);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProgress(int progress) {
|
||||||
|
if (progress <= getMax() && progress >= 0) {
|
||||||
|
this.mCurrentProgress = progress;
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Parcelable onSaveInstanceState() {
|
||||||
|
final Bundle bundle = new Bundle();
|
||||||
|
bundle.putParcelable(INSTANCE_STATE, super.onSaveInstanceState());
|
||||||
|
bundle.putInt(INSTANCE_TEXT_COLOR, getTextColor());
|
||||||
|
bundle.putFloat(INSTANCE_TEXT_SIZE, getProgressTextSize());
|
||||||
|
bundle.putFloat(INSTANCE_REACHED_BAR_HEIGHT, getReachedBarHeight());
|
||||||
|
bundle.putFloat(INSTANCE_UNREACHED_BAR_HEIGHT, getUnreachedBarHeight());
|
||||||
|
bundle.putInt(INSTANCE_REACHED_BAR_COLOR, getReachedBarColor());
|
||||||
|
bundle.putInt(INSTANCE_UNREACHED_BAR_COLOR, getUnreachedBarColor());
|
||||||
|
bundle.putInt(INSTANCE_MAX, getMax());
|
||||||
|
bundle.putInt(INSTANCE_PROGRESS, getProgress());
|
||||||
|
bundle.putString(INSTANCE_SUFFIX, getSuffix());
|
||||||
|
bundle.putString(INSTANCE_PREFIX, getPrefix());
|
||||||
|
bundle.putBoolean(INSTANCE_TEXT_VISIBILITY, getProgressTextVisibility());
|
||||||
|
return bundle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRestoreInstanceState(Parcelable state) {
|
||||||
|
if (state instanceof Bundle) {
|
||||||
|
final Bundle bundle = (Bundle) state;
|
||||||
|
mTextColor = bundle.getInt(INSTANCE_TEXT_COLOR);
|
||||||
|
mTextSize = bundle.getFloat(INSTANCE_TEXT_SIZE);
|
||||||
|
mReachedBarHeight = bundle.getFloat(INSTANCE_REACHED_BAR_HEIGHT);
|
||||||
|
mUnreachedBarHeight = bundle.getFloat(INSTANCE_UNREACHED_BAR_HEIGHT);
|
||||||
|
mReachedBarColor = bundle.getInt(INSTANCE_REACHED_BAR_COLOR);
|
||||||
|
mUnreachedBarColor = bundle.getInt(INSTANCE_UNREACHED_BAR_COLOR);
|
||||||
|
initializePainters();
|
||||||
|
setMax(bundle.getInt(INSTANCE_MAX));
|
||||||
|
setProgress(bundle.getInt(INSTANCE_PROGRESS));
|
||||||
|
setPrefix(bundle.getString(INSTANCE_PREFIX));
|
||||||
|
setSuffix(bundle.getString(INSTANCE_SUFFIX));
|
||||||
|
setProgressTextVisibility(bundle.getBoolean(INSTANCE_TEXT_VISIBILITY) ? ProgressTextVisibility.Visible : ProgressTextVisibility.Invisible);
|
||||||
|
super.onRestoreInstanceState(bundle.getParcelable(INSTANCE_STATE));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
super.onRestoreInstanceState(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float dp2px(float dp) {
|
||||||
|
final float scale = getResources().getDisplayMetrics().density;
|
||||||
|
return dp * scale + 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float sp2px(float sp) {
|
||||||
|
final float scale = getResources().getDisplayMetrics().scaledDensity;
|
||||||
|
return sp * scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProgressTextVisibility(ProgressTextVisibility visibility) {
|
||||||
|
mIfDrawText = visibility == ProgressTextVisibility.Visible;
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getProgressTextVisibility() {
|
||||||
|
return mIfDrawText;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,197 @@
|
|||||||
|
package com.tongdaxing.erban.upgrade.view
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.drawable.GradientDrawable
|
||||||
|
import android.graphics.drawable.StateListDrawable
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.Gravity
|
||||||
|
import android.view.View
|
||||||
|
import android.view.WindowManager
|
||||||
|
import android.widget.Button
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import com.mango.moshen.R
|
||||||
|
import com.tongdaxing.erban.upgrade.config.Constant
|
||||||
|
import com.tongdaxing.erban.upgrade.listener.OnButtonClickListener
|
||||||
|
import com.tongdaxing.erban.upgrade.listener.OnDownloadListenerAdapter
|
||||||
|
import com.tongdaxing.erban.upgrade.manager.DownloadManager
|
||||||
|
import com.tongdaxing.erban.upgrade.service.DownloadService
|
||||||
|
import com.tongdaxing.erban.upgrade.util.ApkUtil
|
||||||
|
import com.tongdaxing.erban.upgrade.util.DensityUtil
|
||||||
|
import com.tongdaxing.erban.upgrade.util.LogUtil
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProjectName: AppUpdate
|
||||||
|
* PackageName: com.tongdaxing.erban.upgrade.view
|
||||||
|
* FileName: UpdateDialogActivity
|
||||||
|
* CreateDate: 2022/4/7 on 17:40
|
||||||
|
* Desc:
|
||||||
|
*
|
||||||
|
* @author azhon
|
||||||
|
*/
|
||||||
|
|
||||||
|
class UpdateDialogActivity : AppCompatActivity(), View.OnClickListener {
|
||||||
|
|
||||||
|
private val install = 0x45
|
||||||
|
private val error = 0x46
|
||||||
|
private lateinit var manager: DownloadManager
|
||||||
|
private lateinit var apk: File
|
||||||
|
private lateinit var progressBar: NumberProgressBar
|
||||||
|
private lateinit var btnUpdate: Button
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "UpdateDialogActivity"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
overridePendingTransition(0, 0)
|
||||||
|
title = ""
|
||||||
|
setContentView(R.layout.dialog_update)
|
||||||
|
init()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun init() {
|
||||||
|
val tempManager = DownloadManager.getInstance()
|
||||||
|
if (tempManager == null) {
|
||||||
|
LogUtil.e(TAG, "An exception occurred by DownloadManager=null,please check your code!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
manager = tempManager
|
||||||
|
if (manager.forcedUpgrade) {
|
||||||
|
manager.onDownloadListeners.add(listenerAdapter)
|
||||||
|
}
|
||||||
|
setWindowSize()
|
||||||
|
initView()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initView() {
|
||||||
|
val ibClose = findViewById<View>(R.id.ib_close)
|
||||||
|
val vLine = findViewById<View>(R.id.line)
|
||||||
|
val ivBg = findViewById<ImageView>(R.id.iv_bg)
|
||||||
|
val tvTitle = findViewById<TextView>(R.id.tv_title)
|
||||||
|
val tvSize = findViewById<TextView>(R.id.tv_size)
|
||||||
|
val tvDescription = findViewById<TextView>(R.id.tv_description)
|
||||||
|
progressBar = findViewById(R.id.np_bar)
|
||||||
|
btnUpdate = findViewById(R.id.btn_update)
|
||||||
|
progressBar.visibility = if (manager.forcedUpgrade) View.VISIBLE else View.GONE
|
||||||
|
btnUpdate.tag = 0
|
||||||
|
btnUpdate.setOnClickListener(this)
|
||||||
|
ibClose.setOnClickListener(this)
|
||||||
|
if (manager.dialogImage != -1) {
|
||||||
|
ivBg.setBackgroundResource(manager.dialogImage)
|
||||||
|
}
|
||||||
|
if (manager.dialogButtonTextColor != -1) {
|
||||||
|
btnUpdate.setTextColor(manager.dialogButtonTextColor)
|
||||||
|
}
|
||||||
|
if (manager.dialogProgressBarColor != -1) {
|
||||||
|
progressBar.reachedBarColor = manager.dialogProgressBarColor
|
||||||
|
progressBar.setProgressTextColor(manager.dialogProgressBarColor)
|
||||||
|
}
|
||||||
|
if (manager.dialogButtonColor != -1) {
|
||||||
|
val colorDrawable = GradientDrawable().apply {
|
||||||
|
setColor(manager.dialogButtonColor)
|
||||||
|
cornerRadius = DensityUtil.dip2px(this@UpdateDialogActivity, 3f)
|
||||||
|
}
|
||||||
|
val drawable = StateListDrawable().apply {
|
||||||
|
addState(intArrayOf(android.R.attr.state_pressed), colorDrawable)
|
||||||
|
addState(IntArray(0), colorDrawable)
|
||||||
|
}
|
||||||
|
btnUpdate.background = drawable
|
||||||
|
}
|
||||||
|
if (manager.forcedUpgrade) {
|
||||||
|
vLine.visibility = View.GONE
|
||||||
|
ibClose.visibility = View.GONE
|
||||||
|
}
|
||||||
|
if (manager.apkVersionName.isNotEmpty()) {
|
||||||
|
tvTitle.text =
|
||||||
|
String.format(resources.getString(R.string.dialog_new), manager.apkVersionName)
|
||||||
|
}
|
||||||
|
if (manager.apkSize.isNotEmpty()) {
|
||||||
|
tvSize.text =
|
||||||
|
String.format(resources.getString(R.string.dialog_new_size), manager.apkSize)
|
||||||
|
tvSize.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
tvDescription.text = manager.apkDescription
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setWindowSize() {
|
||||||
|
val attributes = window.attributes
|
||||||
|
attributes.width = (resources.displayMetrics.widthPixels * 0.75f).toInt()
|
||||||
|
attributes.height = WindowManager.LayoutParams.WRAP_CONTENT
|
||||||
|
attributes.gravity = Gravity.CENTER
|
||||||
|
window.attributes = attributes
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClick(v: View?) {
|
||||||
|
when (v?.id) {
|
||||||
|
R.id.ib_close -> {
|
||||||
|
if (!manager.forcedUpgrade) {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
manager.onButtonClickListener?.onButtonClick(OnButtonClickListener.CANCEL)
|
||||||
|
}
|
||||||
|
R.id.btn_update -> {
|
||||||
|
if (btnUpdate.tag == install) {
|
||||||
|
ApkUtil.installApk(this, Constant.AUTHORITIES!!, apk)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (manager.forcedUpgrade) {
|
||||||
|
btnUpdate.isEnabled = false
|
||||||
|
btnUpdate.text = resources.getString(R.string.background_downloading)
|
||||||
|
} else {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
manager.onButtonClickListener?.onButtonClick(OnButtonClickListener.UPDATE)
|
||||||
|
startService(Intent(this, DownloadService::class.java))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed() {
|
||||||
|
if (manager.forcedUpgrade) return
|
||||||
|
super.onBackPressed()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun finish() {
|
||||||
|
super.finish()
|
||||||
|
overridePendingTransition(0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val listenerAdapter: OnDownloadListenerAdapter = object : OnDownloadListenerAdapter() {
|
||||||
|
override fun start() {
|
||||||
|
btnUpdate.isEnabled = false
|
||||||
|
btnUpdate.text = resources.getString(R.string.background_downloading)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun downloading(max: Int, progress: Int) {
|
||||||
|
if (max != -1) {
|
||||||
|
val curr = (progress / max.toDouble() * 100.0).toInt()
|
||||||
|
progressBar.progress = curr
|
||||||
|
} else {
|
||||||
|
progressBar.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun done(apk: File) {
|
||||||
|
this@UpdateDialogActivity.apk = apk
|
||||||
|
btnUpdate.tag = install
|
||||||
|
btnUpdate.isEnabled = true
|
||||||
|
btnUpdate.text = resources.getString(R.string.click_hint)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun error(e: Throwable) {
|
||||||
|
btnUpdate.tag = error
|
||||||
|
btnUpdate.isEnabled = true
|
||||||
|
btnUpdate.text = resources.getString(R.string.continue_downloading)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
manager.onDownloadListeners.remove(listenerAdapter)
|
||||||
|
}
|
||||||
|
}
|
15
app/src/module_upgrade_app/res/drawable/bg_button.xml
Normal file
15
app/src/module_upgrade_app/res/drawable/bg_button.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:state_pressed="true">
|
||||||
|
<shape>
|
||||||
|
<corners android:radius="3dp" />
|
||||||
|
<solid android:color="#ff895b" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
<item android:state_pressed="false">
|
||||||
|
<shape>
|
||||||
|
<corners android:radius="3dp" />
|
||||||
|
<solid android:color="#ff895b" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</selector>
|
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<corners
|
||||||
|
android:bottomLeftRadius="6dp"
|
||||||
|
android:bottomRightRadius="6dp" />
|
||||||
|
<solid android:color="@android:color/white" />
|
||||||
|
</shape>
|
BIN
app/src/module_upgrade_app/res/drawable/ic_dialog_close.png
Normal file
BIN
app/src/module_upgrade_app/res/drawable/ic_dialog_close.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.3 KiB |
BIN
app/src/module_upgrade_app/res/drawable/ic_dialog_default.png
Normal file
BIN
app/src/module_upgrade_app/res/drawable/ic_dialog_default.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 38 KiB |
97
app/src/module_upgrade_app/res/layout/dialog_update.xml
Normal file
97
app/src/module_upgrade_app/res/layout/dialog_update.xml
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@android:color/transparent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/iv_bg"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="140dp"
|
||||||
|
android:background="@drawable/ic_dialog_default" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/bg_white_radius_6"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:textColor="@android:color/black"
|
||||||
|
android:textSize="15sp"
|
||||||
|
tools:text="发现新版v2.0.1可以下载啦!" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_size"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:textColor="#757575"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:text="新版本大小:5M" />
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="90dp"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:overScrollMode="never">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_description"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:lineSpacingMultiplier="1.1"
|
||||||
|
android:textColor="#757575"
|
||||||
|
android:textSize="14sp"
|
||||||
|
tools:text="" />
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
<com.tongdaxing.erban.upgrade.view.NumberProgressBar
|
||||||
|
android:id="@+id/np_bar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginRight="16dp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btn_update"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:background="@drawable/bg_button"
|
||||||
|
android:text="@string/update"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@android:color/white" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/line"
|
||||||
|
android:layout_width="2dp"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:background="@android:color/white" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/ib_close"
|
||||||
|
android:layout_width="30dp"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:background="@drawable/ic_dialog_close" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
@@ -3,4 +3,16 @@
|
|||||||
<string name="tips_downloading_percent">·正在更新%s·</string>
|
<string name="tips_downloading_percent">·正在更新%s·</string>
|
||||||
<string name="title_upgrade_app">版本更新</string>
|
<string name="title_upgrade_app">版本更新</string>
|
||||||
<string name="title_upgrade_app_en">VERSION UPDATE</string>
|
<string name="title_upgrade_app_en">VERSION UPDATE</string>
|
||||||
|
<string name="latest_version">当前已是最新版本!</string>
|
||||||
|
<string name="start_download">开始下载</string>
|
||||||
|
<string name="start_download_hint">可稍后查看下载进度</string>
|
||||||
|
<string name="start_downloading">正在下载新版本</string>
|
||||||
|
<string name="download_completed">下载完成</string>
|
||||||
|
<string name="click_hint">点击进行安装</string>
|
||||||
|
<string name="download_error">下载出错</string>
|
||||||
|
<string name="continue_downloading">点击继续下载</string>
|
||||||
|
<string name="background_downloading">正在后台下载新版本…</string>
|
||||||
|
<string name="dialog_new">发现新版本%s可以下载啦!</string>
|
||||||
|
<string name="dialog_new_size">新版本大小:%s</string>
|
||||||
|
<string name="update">升级</string>
|
||||||
</resources>
|
</resources>
|
25
app/src/module_upgrade_app/res/values/styles.xml
Normal file
25
app/src/module_upgrade_app/res/values/styles.xml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<style name="DialogActivity" parent="Theme.AppCompat">
|
||||||
|
<item name="android:windowBackground">@android:color/transparent</item>
|
||||||
|
<item name="android:windowFrame">@null</item>
|
||||||
|
<item name="android:windowNoTitle">true</item>
|
||||||
|
<item name="android:windowIsFloating">true</item>
|
||||||
|
<item name="android:windowIsTranslucent">true</item>
|
||||||
|
<item name="android:windowContentOverlay">@null</item>
|
||||||
|
<item name="android:backgroundDimEnabled">false</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="UpdateDialog" parent="DialogActivity">
|
||||||
|
<item name="android:backgroundDimEnabled">true</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<declare-styleable name="NumberProgressBar">
|
||||||
|
<attr name="progress_unreached_color" format="color" />
|
||||||
|
<attr name="progress_reached_color" format="color" />
|
||||||
|
|
||||||
|
<attr name="progress_text_size" format="dimension" />
|
||||||
|
<attr name="progress_text_color" format="color" />
|
||||||
|
</declare-styleable>
|
||||||
|
</resources>
|
9
app/src/module_upgrade_app/res/xml/app_update_file.xml
Normal file
9
app/src/module_upgrade_app/res/xml/app_update_file.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<paths>
|
||||||
|
<external-path
|
||||||
|
name="app_update_external"
|
||||||
|
path="/" />
|
||||||
|
<external-cache-path
|
||||||
|
name="app_update_cache"
|
||||||
|
path="/" />
|
||||||
|
</paths>
|
@@ -25,5 +25,5 @@ only_arm64=true
|
|||||||
|
|
||||||
channel_file=channel.txt
|
channel_file=channel.txt
|
||||||
|
|
||||||
version_name=5.5.0
|
version_name=5.3.0
|
||||||
version_code=550
|
version_code=500
|
Reference in New Issue
Block a user