Compare commits
480 Commits
v3.7.4
...
v4.0.0-bug
| Author | SHA1 | Date | |
|---|---|---|---|
| 0dcf86ce1c | |||
| 961d4ebb5c | |||
| 54b947504c | |||
| ee7f102d34 | |||
| 135b55f6f9 | |||
| 17782a500f | |||
| 64a64960ac | |||
| d4be850e68 | |||
| 414064699a | |||
| 99878606d5 | |||
| e1cfccfcc0 | |||
| 4c2a46875d | |||
| 6ce64892e4 | |||
| 92e1c55913 | |||
| b0a4aa66f6 | |||
| aad92e529e | |||
| 6e03e75110 | |||
| cc53a1f3d6 | |||
| 25cd086298 | |||
| b27ecab969 | |||
| db1c0d954f | |||
| 03d20aed61 | |||
| c2fb1a58db | |||
| 55998c3c4d | |||
| 1ef8fa5deb | |||
| d0a2868e5a | |||
| 49c2366a89 | |||
| cb7d985195 | |||
| bead0bded1 | |||
| cc92d5f639 | |||
| 84c281122e | |||
| bb4a35a6a9 | |||
| 71ae36fbf0 | |||
| 04864e059c | |||
| 7a47184af3 | |||
| 48e2831e96 | |||
| c1caa23b10 | |||
| 443cc6007d | |||
| 7d99923960 | |||
| 7342d0706f | |||
| c15c194feb | |||
| 3b6a5b5a20 | |||
| f9028ea8a5 | |||
| 8cd06a80ba | |||
| 6c80266abb | |||
| ca3c1fa8c2 | |||
| 2b7deaef2c | |||
| 42f1898bd1 | |||
| 0bdff3eac7 | |||
| a00f6660a1 | |||
| 67d1ad497f | |||
| f1bbf4e9fc | |||
| 00ff5e6a94 | |||
| 7f77a8fd63 | |||
| 7c1267148b | |||
| 2dbb7b67cb | |||
| c41db52b05 | |||
| 72f3a262ca | |||
| 2fbe6a4937 | |||
| c6fb63afe6 | |||
| 2e05320ddf | |||
| a3f86ab604 | |||
| 5d2efda925 | |||
| 4a30ac04b7 | |||
| 1097068393 | |||
| df88502c3d | |||
| b8df485a7e | |||
| a8a66a7b1e | |||
| a1548faeda | |||
| 34f452be6e | |||
| bef04854e3 | |||
| 757782e43b | |||
| 8a69f565a3 | |||
| be20d1fec6 | |||
| 7a71e457ef | |||
| 3af9fa0a10 | |||
| ff8b3efde9 | |||
| 161f63a664 | |||
| 3206e04639 | |||
| f1dea0f200 | |||
| d94a83a7c3 | |||
| 6bbc7104d9 | |||
| a2180196ee | |||
| a6dc4c7891 | |||
| fa7355c5c6 | |||
| 0140c10f15 | |||
| 796da0e673 | |||
| 743f78096a | |||
| 52d726b6f3 | |||
| bed4450900 | |||
| c2ab369d0c | |||
| 32a7f37b6a | |||
| b299199579 | |||
| 7091ea179d | |||
| dceb582bb1 | |||
| 6e2e9f7dcf | |||
| c27025269e | |||
| cf1411b412 | |||
| b167f5bb8b | |||
| 87d0fd9edb | |||
| b59a169e10 | |||
| 941e3985b0 | |||
| 127fb76c19 | |||
| 5f3028f795 | |||
| b596f04dba | |||
| 0bb96ecf12 | |||
| 60f21f6113 | |||
| 051000e4a8 | |||
| 14e8020147 | |||
| a99b1499a2 | |||
| 6a1c01556b | |||
| c41327640b | |||
| 7df5834ebc | |||
| ebafc2f98f | |||
| 92c9d81171 | |||
| 7d34a3972d | |||
| 90f1254c6c | |||
| 8b71d68749 | |||
| 17c0cf52b6 | |||
| 045be4ff09 | |||
| af7948fcb5 | |||
| e1c12d5007 | |||
| 8676b20fbb | |||
| 54db9fb910 | |||
| 0eaee1ae63 | |||
| e141dfb6b5 | |||
| b14b8c9f66 | |||
| b4b589f307 | |||
| 7a74bc0d04 | |||
| b826c0a9ad | |||
| a197b59200 | |||
| c25503d7f8 | |||
| 2c0449dcf6 | |||
| 59655daa3b | |||
| 1c36d7b923 | |||
| a51175620c | |||
| 7b95c265df | |||
| a11bb9c210 | |||
| 72227a5591 | |||
| f9f2eb4c05 | |||
| e9c57df5fb | |||
| 158c47d94b | |||
| 7616435098 | |||
| 69443ff457 | |||
| 6a257c0a9f | |||
| eddd56e53a | |||
| de6648867b | |||
| 567a6ceda5 | |||
| 59330bcd7e | |||
| 36803f24bc | |||
| 46458e2667 | |||
| d0aa6e1fe5 | |||
| 9bef919123 | |||
| 88a0603d50 | |||
| 106dbb3d8d | |||
| 4214d924a4 | |||
| c3b40f9264 | |||
| 69f00626a2 | |||
| c485b1f7f8 | |||
| 7186c73a64 | |||
| 88f9f65fb6 | |||
| 714573ec6b | |||
| a979445ffc | |||
| 3cf38eb3fc | |||
| 94ceca35bf | |||
| 2bd4eb2004 | |||
| 355e71e620 | |||
| 7d9520d97b | |||
| ce0957cea7 | |||
| c93169fe97 | |||
| 485dd96e93 | |||
| 9b0f0892ff | |||
| 9a04f3ae38 | |||
| 968d668afa | |||
| 85d19ed2bd | |||
| 28e490f8d4 | |||
| 298ae7f657 | |||
| d50f29b3dc | |||
| f167eff21c | |||
| 69d4f00807 | |||
| 904dc4fe2e | |||
| c870cc683d | |||
| deb29fd2e4 | |||
| 19f1bc947a | |||
| 60d1b47c66 | |||
| d67b845dfd | |||
| 6f1c7c7ded | |||
| 46e9118d73 | |||
| 8aa1557b7a | |||
| 6058c3f8c1 | |||
| d409e6f2a9 | |||
| a4d272c556 | |||
| 911d7c04ec | |||
| afc379d449 | |||
| 18feca49e2 | |||
| 792b19d40d | |||
| 108d05edea | |||
| d8882da17b | |||
| f120dfda15 | |||
| dd60ed7abd | |||
| d0b478c743 | |||
| defe69ba18 | |||
| 32d748efae | |||
| f194dc9bf6 | |||
| 075fbd77b2 | |||
| edb3e3713a | |||
| c70648e038 | |||
| 966f4ee57d | |||
| f90ced4c31 | |||
| 6bda4d80bf | |||
| 3ecb6b30c9 | |||
| 3c337b618c | |||
| 40a90bbcfa | |||
| e54b124252 | |||
| 18923efe23 | |||
| 5c5ad63cc6 | |||
| d5fd444ae5 | |||
| 4a22f70e30 | |||
| 9805bd4caa | |||
| 0690f13bea | |||
| da8faea4c8 | |||
| ff927bab09 | |||
| e3e5def3b6 | |||
| ba246585ad | |||
| f242a6a3bc | |||
| 0c89e76b39 | |||
| 1fa82e72f1 | |||
| b4e6b04e47 | |||
| 002f824f5c | |||
| 1aaf7f8de1 | |||
| 82ba19842c | |||
| 32ed1bf622 | |||
| 0d4838c536 | |||
| 11527b46f9 | |||
| 20c1957b59 | |||
| ed2630e016 | |||
| 8cc2ac2cad | |||
| 37745ca548 | |||
| 29edcdca4b | |||
| 92669d6974 | |||
| 351a64f592 | |||
| a29e56543b | |||
| 6db3e8c671 | |||
| 20c4d24698 | |||
| 114e185bcb | |||
| a293bff964 | |||
| 98b4d9f95d | |||
| 8489e87586 | |||
| 6f7ee2179d | |||
| ea6b442a48 | |||
| 12d26edd76 | |||
| 27053eecd7 | |||
| 151d5b9c1f | |||
| d35c558658 | |||
| baa4dc7a23 | |||
| 9b565c32d1 | |||
| 243dd26d73 | |||
| 263234fecb | |||
| 0ba598ee67 | |||
| 29430b9967 | |||
| 2038b16dce | |||
| 9ea793397b | |||
| 6061938129 | |||
| 62277aa525 | |||
| 1c118b06c9 | |||
| a41c315e36 | |||
| 40576e0012 | |||
| a582db52f4 | |||
| cb2656ad85 | |||
| da1c686d42 | |||
| b613fe2ba0 | |||
| 83e29b1cf3 | |||
| 5bbd23bf33 | |||
| 9f69b83b51 | |||
| 4f77781e24 | |||
| b23d589a29 | |||
| 7f16a69301 | |||
| 006da73737 | |||
| 3f998cdf75 | |||
| 3e94cd9d8c | |||
| 5922f92caf | |||
| f5f5909fde | |||
| 7e67357887 | |||
| d5750c6cb8 | |||
| b2983d5c96 | |||
| 5a19e46d26 | |||
| 8456a5a1d6 | |||
| b9329a092e | |||
| 6ecad9eebd | |||
| f32dd9efee | |||
| 48cbaf03b7 | |||
| 7f23cda97a | |||
| 49c1809aa2 | |||
| 173aa82134 | |||
| c57e8b2c76 | |||
| bb741013d4 | |||
| 5b014a5a1d | |||
| a85548ac33 | |||
| 77e689fec4 | |||
| fe942dcf73 | |||
| da3b700403 | |||
| fcd46cb2ea | |||
| e918b19e2e | |||
| afb8badc74 | |||
| 4e22a5c7bb | |||
| b5d2e02606 | |||
| 28afb4f14c | |||
| 165659a664 | |||
| 7b3d910b10 | |||
| 1e4a85719a | |||
| 4269cb37d2 | |||
| b7ce3e8c20 | |||
| 15a0240808 | |||
| 0aa68ea04b | |||
| 2d9b69fd1e | |||
| 48d6023e0d | |||
| 113ebb36e9 | |||
| d0c9bd8038 | |||
| 104586fb7f | |||
| 0612582a46 | |||
| 1bc5bb3ef6 | |||
| a3d0d91dd7 | |||
| 982e8f67bf | |||
| 0bb871bff2 | |||
| 0eb43d6552 | |||
| b4f29f4856 | |||
| 2719bff65f | |||
| 4fd68565d2 | |||
| b87972143d | |||
| 8241551438 | |||
| d7bb45f287 | |||
| 0cdec2a226 | |||
| dbb3078300 | |||
| 545c235b24 | |||
| 53d3cb320b | |||
| 38682f01a8 | |||
| a7d141efcc | |||
| 31c404021c | |||
| 631cec2fc6 | |||
| 13a76e8de7 | |||
| 1e98fa98c3 | |||
| be6eab0cae | |||
| 71ee8bfd29 | |||
| 66c9c02dc6 | |||
| 7880ee4aef | |||
| 6274d1f778 | |||
| 5e98813f92 | |||
| 1a30800fb4 | |||
| 454e6933f4 | |||
| e3072071b7 | |||
| 83a88326f0 | |||
| 27109a810f | |||
| 3d5bd424c5 | |||
| c7cd56e7be | |||
| 860769c44a | |||
| a83761b88d | |||
| a79fc9c0b1 | |||
| 877bd63d9d | |||
| bb9d0582c4 | |||
| df7e89e7e4 | |||
| a12593ea51 | |||
| 5ca0af4285 | |||
| 20bc38add4 | |||
| 89e72a0fea | |||
| efabaafa38 | |||
| fc25ad7ddb | |||
| 6b656cf446 | |||
| 26927661e7 | |||
| 15ba945751 | |||
| 8e0527b777 | |||
| b9dfa0a8a9 | |||
| c462973eb6 | |||
| b8cefd69c0 | |||
| 1c1537be8a | |||
| 1dad632c70 | |||
| 41600c7576 | |||
| 5f0074c88d | |||
| 907f2a2678 | |||
| 4a118bd51c | |||
| 4919b46e5d | |||
| 034a6662f9 | |||
| 3fb1c9f315 | |||
| aed4610afd | |||
| 345be8035a | |||
| 68c7b0ddaf | |||
| f39ce00152 | |||
| 0b6d1bd6df | |||
| dca7a44884 | |||
| d90cdf341e | |||
| 7828189ea0 | |||
| e4cd8da65c | |||
| 8cebbd3bbe | |||
| 63d9b354d4 | |||
| 254944fbfc | |||
| ec93080190 | |||
| ec638cb231 | |||
| 7c72623f90 | |||
| 2e58404e01 | |||
| 5eae896330 | |||
| 6c2dcc7d37 | |||
| d40e4e97c1 | |||
| 3164ed1b0e | |||
| b49fae0384 | |||
| 80544aff32 | |||
| 64235951c2 | |||
| f08fe7d855 | |||
| ff1a9e8694 | |||
| 2abf87628a | |||
| e835482abd | |||
| e78882e911 | |||
| b021d99f78 | |||
| 084781dc0a | |||
| ce83fed40d | |||
| 24d16a234f | |||
| 536af50a4f | |||
| 9eccc459d9 | |||
| 6e8310e4c9 | |||
| 35e79f938c | |||
| 10764ac3f4 | |||
| 2a6ecfa176 | |||
| 5b44c7f856 | |||
| 1e5e36a8c7 | |||
| 92dda8e64c | |||
| 27488e8084 | |||
| 72871e55c6 | |||
| 07c05b2191 | |||
| 89d2fb98fa | |||
| 6d1719b877 | |||
| 51599d5bb4 | |||
| aedaee8d3a | |||
| 82a859e820 | |||
| c1c655fcb0 | |||
| fabcce9a6c | |||
| 16feb963f6 | |||
| c7db58f6ea | |||
| 79ff887375 | |||
| e7ae608986 | |||
| 7ec4625d45 | |||
| 4f68f30576 | |||
| 135d717715 | |||
| 77c3131a69 | |||
| 7e74cbfbd1 | |||
| a2cd548d1e | |||
| 0a4d6122df | |||
| 23d1435ed2 | |||
| dd60900bcb | |||
| 0fbb0aa363 | |||
| 23539b9316 | |||
| a1460b07c6 | |||
| 02b6c2733b | |||
| 1d29d68aa2 | |||
| aa76a20065 | |||
| ec945919ba | |||
| e626e82945 | |||
| 6e238379f2 | |||
| 7a4ae83a2b | |||
| 46827b1489 | |||
| 762897521b | |||
| b8d74357b5 | |||
| 21c116ea5b | |||
| 2cf23b9a32 | |||
| 6b03098ac7 | |||
| bcd846024e | |||
| d98edc8f77 | |||
| faef2a5e4d | |||
| 3d88792d11 | |||
| f7fe7a5153 | |||
| 9aeb1624a6 | |||
| ec9c282618 | |||
| 7e1da213d5 | |||
| 8d9fd482b9 | |||
| c87bf1e613 | |||
| 81dc17530f | |||
| b53f769a0d | |||
| aadd2071b9 | |||
| fc7566d2c9 | |||
| 7f082b7a36 | |||
| bebda2de70 | |||
| 3e0887b5f9 | |||
| a8894cc464 |
@ -23,6 +23,10 @@ android {
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
dexOptions {
|
||||
// jumboMode = true
|
||||
javaMaxHeapSize "4g"
|
||||
@ -118,7 +122,6 @@ android {
|
||||
dimension "nonsense"
|
||||
buildConfigField "String", "API_HOST", "\"${API_HOST}\""
|
||||
buildConfigField "String", "COMMENT_HOST", "\"${COMMENT_HOST}\""
|
||||
buildConfigField "String", "DATA_HOST", "\"${DATA_HOST}\""
|
||||
|
||||
buildConfigField "String", "UMENG_APPKEY", "\"${UMENG_APPKEY}\""
|
||||
buildConfigField "String", "UMENG_MESSAGE_SECRET", "\"${UMENG_MESSAGE_SECRET}\""
|
||||
@ -136,7 +139,6 @@ android {
|
||||
|
||||
buildConfigField "String", "API_HOST", "\"${DEV_API_HOST}\""
|
||||
buildConfigField "String", "COMMENT_HOST", "\"${DEV_COMMENT_HOST}\""
|
||||
buildConfigField "String", "DATA_HOST", "\"${DEV_DATA_HOST}\""
|
||||
|
||||
buildConfigField "String", "UMENG_APPKEY", "\"${DEBUG_UMENG_APPKEY}\""
|
||||
buildConfigField "String", "UMENG_MESSAGE_SECRET", "\"${DEBUG_UMENG_MESSAGE_SECRET}\""
|
||||
@ -183,6 +185,7 @@ dependencies {
|
||||
debugImplementation "com.facebook.stetho:stetho:${stetho}"
|
||||
debugImplementation "com.facebook.stetho:stetho-okhttp3:${stetho}"
|
||||
debugImplementation "com.squareup.okhttp3:logging-interceptor:${okHttp}"
|
||||
debugImplementation "com.gu.android:toolargetool:${toolargetool}"
|
||||
|
||||
implementation "androidx.core:core:${core}"
|
||||
implementation "androidx.fragment:fragment:${fragment}"
|
||||
@ -192,14 +195,18 @@ dependencies {
|
||||
implementation "androidx.annotation:annotation:${annotation}"
|
||||
implementation "androidx.constraintlayout:constraintlayout:${constraintLayout}"
|
||||
implementation "androidx.recyclerview:recyclerview:${recyclerView}"
|
||||
implementation "androidx.lifecycle:lifecycle-runtime:${lifeCycle}"
|
||||
implementation "androidx.lifecycle:lifecycle-extensions:${lifeCycle}"
|
||||
kapt "androidx.lifecycle:lifecycle-compiler:${lifeCycle}"
|
||||
// implementation "androidx.lifecycle:lifecycle-runtime:${lifeCycle}"
|
||||
// implementation "androidx.lifecycle:lifecycle-extensions:${lifeCycle}"
|
||||
// kapt "androidx.lifecycle:lifecycle-compiler:${lifeCycle}"
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifeCycle"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifeCycle"
|
||||
implementation "androidx.lifecycle:lifecycle-common-java8:$lifeCycle"
|
||||
implementation "androidx.room:room-runtime:${room}"
|
||||
implementation "androidx.room:room-rxjava2:${room}"
|
||||
implementation "androidx.core:core-ktx:${ktx}"
|
||||
implementation "androidx.viewpager2:viewpager2:${viewpager2}"
|
||||
kapt "androidx.room:room-compiler:${room}"
|
||||
kapt "androidx.databinding:databinding-compiler:${databinding}"
|
||||
// kapt "androidx.databinding:databinding-compiler:${databinding}"
|
||||
|
||||
implementation "com.google.android.material:material:${material}"
|
||||
|
||||
@ -241,13 +248,13 @@ dependencies {
|
||||
// bugly with tinker support
|
||||
implementation "com.tencent.bugly:crashreport_upgrade:${buglyTinkerSupport}"
|
||||
|
||||
implementation 'com.google.android:flexbox:1.1.0'
|
||||
implementation "com.google.android:flexbox:${flexbox}"
|
||||
|
||||
implementation "pub.devrel:easypermissions:${easypermissions}"
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
|
||||
implementation 'com.contrarywind:Android-PickerView:4.1.8'
|
||||
implementation "com.contrarywind:Android-PickerView:${pickerView}"
|
||||
|
||||
implementation "com.scwang.smartrefresh:SmartRefreshLayout:${smartRefreshLayout}"
|
||||
implementation "net.cachapa.expandablelayout:expandablelayout:${expandableLayout}"
|
||||
@ -264,27 +271,25 @@ dependencies {
|
||||
exclude module: "gsyvideoplayer-androidvideocache"
|
||||
})
|
||||
implementation "com.shuyu:GSYVideoPlayer-exo2:$gsyVideo"
|
||||
// implementation "com.shuyu:gsyVideoPlayer-armv7a:$gsyVideo"
|
||||
// implementation "com.shuyu:gsyVideoPlayer-x86:$gsyVideo"
|
||||
|
||||
implementation "com.github.wendux:DSBridge-Android:$dsBridge"
|
||||
|
||||
implementation "android.arch.work:work-runtime:${workManager}"
|
||||
|
||||
implementation "com.llew.huawei:verifier:1.0.6"
|
||||
implementation "com.llew.huawei:verifier:${verifier}"
|
||||
|
||||
implementation "com.github.tbruyelle:rxpermissions:${rxPermissions}"
|
||||
|
||||
implementation 'com.ethanhua:skeleton:1.1.1'
|
||||
implementation 'io.supercharge:shimmerlayout:2.1.0'
|
||||
implementation "com.tencent.mm.opensdk:wechat-sdk-android-without-mta:5.3.1"
|
||||
implementation 'com.walkud.rom.checker:RomChecker:1.0.0'
|
||||
implementation "com.ethanhua:skeleton:${skeleton}"
|
||||
implementation "io.supercharge:shimmerlayout:${shimmerlayout}"
|
||||
implementation "com.tencent.mm.opensdk:wechat-sdk-android-without-mta:${mta}"
|
||||
implementation "com.walkud.rom.checker:RomChecker:${romChecker}"
|
||||
|
||||
debugImplementation "com.github.nichbar.chucker:library:$chucker"
|
||||
releaseImplementation "com.github.nichbar.chucker:library-no-op:$chucker"
|
||||
implementation "com.bytedance.applog:RangersAppLog-Lite-cn:$bytedanceApplog"
|
||||
|
||||
implementation 'com.aliyun.dpa:oss-android-sdk:2.9.2'
|
||||
implementation "com.aliyun.dpa:oss-android-sdk:${oss}"
|
||||
|
||||
implementation "com.airbnb.android:lottie:$lottie"
|
||||
|
||||
|
||||
@ -30,6 +30,8 @@ public class Injection {
|
||||
// init stetho
|
||||
Stetho.initializeWithDefaults(application);
|
||||
|
||||
// 监控Bundle大小,预防溢出(需要调试的时候再开启吧!)
|
||||
// TooLargeTool.startLogging(application);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -115,6 +115,10 @@
|
||||
android:name="com.gh.gamecenter.ShellActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.gamedetail.history.HistoryApkListActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.NewsDetailActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
@ -458,8 +462,8 @@
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.HelpAndFeedbackActivity"
|
||||
android:windowSoftInputMode="stateHidden"
|
||||
android:screenOrientation="portrait" />
|
||||
android:screenOrientation="portrait"
|
||||
android:windowSoftInputMode="stateHidden" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.help.HelpDetailActivity"
|
||||
@ -480,6 +484,14 @@
|
||||
android:name=".gamedetail.myrating.MyRatingActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.gamedetail.fuli.kaifu.ServersCalendarActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.QaActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<!-- 使用小米/华为推送弹窗功能提高推送成功率-->
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.PushProxyActivity"
|
||||
|
||||
@ -6,13 +6,16 @@
|
||||
<link rel="stylesheet" type="text/css" href="normalize.css">
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
<link rel="stylesheet" type="text/css" href="video-js.min.css">
|
||||
<!-- <link rel="stylesheet" href="https://static-web.ghzs.com/website-static/lib/video-js.min.css">--> <!--在web页面播放视频-->
|
||||
<!--<link rel="stylesheet" type="text/css" href="https://resource.ghzs.com/css/halo_app.css">-->
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<body style="overflow-x: hidden; word-break: break-all;">
|
||||
<div id="editor" contenteditable="false"></div>
|
||||
<script type="text/javascript" src="zepto.min.js"></script>
|
||||
<script type="text/javascript" src="rich_editor.js"></script>
|
||||
<script type="text/javascript" src="video.min.js"></script>
|
||||
<!--<script src="https://static-web.ghzs.com/website-static/lib/video.min.js"></script>--> <!--在web页面播放视频-->
|
||||
<!--<script type="text/javascript" src="content.js"></script>-->
|
||||
<!--<script type="text/javascript" src="https://resource.ghzs.com/js/halo_app.js"></script>-->
|
||||
</body>
|
||||
|
||||
1
app/src/main/assets/lottie/follow.json
Normal file
1
app/src/main/assets/lottie/follow.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -624,3 +624,18 @@ function customLinkgo(self) {
|
||||
// console.log(datas)
|
||||
window.OnLinkClickListener.onClick(datas)
|
||||
}
|
||||
|
||||
// 在web页面播放视频
|
||||
//RE.initArticleVideo = function(){
|
||||
// initArticleVideo()
|
||||
//}
|
||||
|
||||
function showNativeDialog(title, message, positive, negative, callback) {
|
||||
var jsCallbackCode = "(" + function (v) {
|
||||
window.onNativeDialogCallback(v);
|
||||
delete window.onNativeDialogCallback;
|
||||
}.toString() + ")";
|
||||
|
||||
window.onNativeDialogCallback = callback;
|
||||
window.NativeCallBack.showDialog(title, message, positive, negative, jsCallbackCode);
|
||||
}
|
||||
@ -1,6 +1,8 @@
|
||||
package com.gh.base;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
@ -93,8 +95,8 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
EventBus.getDefault().register(this);
|
||||
ButterKnife.bind(this);
|
||||
if (useEventBus()) EventBus.getDefault().register(this);
|
||||
if (useButterKnife()) ButterKnife.bind(this);
|
||||
mEntrance = getIntent().getStringExtra(KEY_ENTRANCE);
|
||||
if (TextUtils.isEmpty(mEntrance)) {
|
||||
mEntrance = Constants.ENTRANCE_UNKNOWN;
|
||||
@ -107,7 +109,7 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
EventBus.getDefault().unregister(this);
|
||||
if (useEventBus()) EventBus.getDefault().unregister(this);
|
||||
mBaseHandler.removeCallbacksAndMessages(null);
|
||||
super.onDestroy();
|
||||
}
|
||||
@ -187,13 +189,12 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 可凭借此回调确定当前 activity 已经执行了 finish() 处于 isFinishing 状态
|
||||
* 可在后续进行
|
||||
* 此回调可用于确认当前 activity 已经执行了 finish() 方法并处于 isFinishing 状态
|
||||
*/
|
||||
protected void onFinish() {
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -246,4 +247,22 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
|
||||
return StringUtils.buildString(entrance, "+(", path, ")");
|
||||
}
|
||||
|
||||
protected boolean useEventBus() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean useButterKnife() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resources getResources() {
|
||||
Resources resources = super.getResources();
|
||||
if (resources.getConfiguration().fontScale != 1.0f) {
|
||||
Configuration configuration = resources.getConfiguration();
|
||||
configuration.fontScale = 1.0f;
|
||||
resources.updateConfiguration(configuration, resources.getDisplayMetrics());
|
||||
}
|
||||
return resources;
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import androidx.fragment.app.Fragment;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import com.gh.base.adapter.FragmentAdapter;
|
||||
import com.gh.base.fragment.BaseFragment_TabLayout;
|
||||
import com.gh.common.view.TabIndicatorView;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
@ -45,7 +46,7 @@ public abstract class BaseActivity_TabLayout extends ToolBarActivity implements
|
||||
protected abstract void initTabTitleList(List<String> tabTitleList);
|
||||
|
||||
protected int provideIndicatorWidth() {
|
||||
return 65;
|
||||
return 20;
|
||||
}
|
||||
|
||||
protected View provideTabView(int position, String tabTitle) {
|
||||
@ -89,11 +90,14 @@ public abstract class BaseActivity_TabLayout extends ToolBarActivity implements
|
||||
for (int i = 0; i < mTabLayout.getTabCount(); i++) {
|
||||
TabLayout.Tab tab = mTabLayout.getTabAt(i);
|
||||
if (tab == null) continue;
|
||||
View tabView = provideTabView(i, tab.getText() != null ? tab.getText().toString() : "");
|
||||
if (tabView == null) continue;
|
||||
String tabTitle = tab.getText() != null ? tab.getText().toString() : "";
|
||||
View tabView = provideTabView(i, tabTitle);
|
||||
if (tabView == null)
|
||||
tabView = BaseFragment_TabLayout.createDefaultTabCustomView(tabTitle);
|
||||
tab.setCustomView(tabView);
|
||||
}
|
||||
|
||||
BaseFragment_TabLayout.initTabStyle(mTabLayout, mCheckedIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -65,7 +65,9 @@ public class GHActivityLifecycleCallbacksImpl implements ActivityLifecycleCallba
|
||||
|
||||
@Override
|
||||
public void onActivityDestroyed(Activity activity) {
|
||||
AppManager.getInstance().finishActivity(activity);
|
||||
if (activity.isFinishing()) {
|
||||
AppManager.getInstance().finishActivity(activity);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -9,9 +9,20 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.LayoutRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.gh.base.OnListClickListener;
|
||||
import com.gh.base.OnRequestCallBackListener;
|
||||
import com.gh.common.constant.Constants;
|
||||
import com.gh.common.syncpage.ISyncAdapterHandler;
|
||||
import com.gh.common.syncpage.SyncDataEntity;
|
||||
import com.gh.common.syncpage.SyncPageRepository;
|
||||
import com.gh.gamecenter.BuildConfig;
|
||||
import com.gh.gamecenter.eventbus.EBMiPush;
|
||||
import com.lightgame.OnTitleClickListener;
|
||||
@ -23,18 +34,14 @@ import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.LayoutRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import butterknife.ButterKnife;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import kotlin.Pair;
|
||||
|
||||
import static com.gh.common.util.EntranceUtils.KEY_ENTRANCE;
|
||||
|
||||
@ -138,9 +145,55 @@ public abstract class BaseFragment<T> extends Fragment implements OnRequestCallB
|
||||
} else {
|
||||
mCachedView = View.inflate(getContext(), getLayoutId(), null);
|
||||
}
|
||||
ButterKnife.bind(this, mCachedView);
|
||||
|
||||
if (useButterKnife()) {
|
||||
ButterKnife.bind(this, mCachedView);
|
||||
}
|
||||
|
||||
initView(mCachedView);
|
||||
|
||||
if (addSyncPageObserver()) {
|
||||
initSyncPageObserver();
|
||||
}
|
||||
}
|
||||
|
||||
private void initSyncPageObserver() {
|
||||
SyncPageRepository.INSTANCE.getSyncPageLiveData().observe(this, syncDataEntities -> {
|
||||
try {
|
||||
Utils.log("SyncPageRepository initSyncPageObserver->" + syncDataEntities);
|
||||
List<SyncDataEntity> readyRemoveList = new ArrayList<>();
|
||||
if (syncDataEntities == null || syncDataEntities.isEmpty()) return;
|
||||
RecyclerView.Adapter adapter = provideSyncAdapter();
|
||||
if (!(adapter instanceof ISyncAdapterHandler)) return;
|
||||
for (int i = 0; i < adapter.getItemCount(); i++) {
|
||||
Pair<String, Object> syncKey = ((ISyncAdapterHandler) adapter).getSyncData(i);
|
||||
if (syncKey == null) continue;
|
||||
for (SyncDataEntity syncDataEntity : syncDataEntities) {
|
||||
if (syncDataEntity.getSyncId().equals(syncKey.getFirst())) {
|
||||
boolean isSuccess = SyncPageRepository.INSTANCE.handleSyncData(syncKey.getSecond(), syncDataEntity);
|
||||
if (isSuccess) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
Utils.log("SyncPageRepository notify position->" + i);
|
||||
}
|
||||
adapter.notifyItemChanged(i);
|
||||
|
||||
if (syncDataEntity.getRemove()) {
|
||||
readyRemoveList.add(syncDataEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mBaseHandler.postDelayed(() -> SyncPageRepository.removeSyncData(readyRemoveList), 2000);
|
||||
} catch (Exception e) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
throw e;
|
||||
} else {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 必须的有subscribe才能register
|
||||
@ -183,11 +236,7 @@ public abstract class BaseFragment<T> extends Fragment implements OnRequestCallB
|
||||
}
|
||||
|
||||
public void toast(String msg) {
|
||||
try {
|
||||
Utils.toast(getContext(), msg);
|
||||
} catch (Exception ignore) {
|
||||
|
||||
}
|
||||
Utils.toast(getContext(), msg);
|
||||
}
|
||||
|
||||
public void toastLong(@StringRes int msg) {
|
||||
@ -255,4 +304,16 @@ public abstract class BaseFragment<T> extends Fragment implements OnRequestCallB
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected RecyclerView.Adapter provideSyncAdapter() {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected boolean addSyncPageObserver() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean useButterKnife() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,18 +1,28 @@
|
||||
package com.gh.base.fragment;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.CheckedTextView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.gh.base.adapter.FragmentAdapter;
|
||||
import com.gh.common.view.TabIndicatorView;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.normal.NormalFragment;
|
||||
import com.halo.assistant.HaloApp;
|
||||
import com.lightgame.utils.Utils;
|
||||
import com.lightgame.view.NoScrollableViewPager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -46,7 +56,7 @@ public abstract class BaseFragment_TabLayout extends NormalFragment implements V
|
||||
protected abstract void initTabTitleList(List<String> tabTitleList);
|
||||
|
||||
protected int provideIndicatorWidth() {
|
||||
return 65;
|
||||
return 20;
|
||||
}
|
||||
|
||||
protected View provideTabView(int position, String tabTitle) {
|
||||
@ -94,11 +104,71 @@ public abstract class BaseFragment_TabLayout extends NormalFragment implements V
|
||||
for (int i = 0; i < mTabLayout.getTabCount(); i++) {
|
||||
TabLayout.Tab tab = mTabLayout.getTabAt(i);
|
||||
if (tab == null) continue;
|
||||
View tabView = provideTabView(i, tab.getText() != null ? tab.getText().toString() : "");
|
||||
if (tabView == null) continue;
|
||||
String tabTitle = tab.getText() != null ? tab.getText().toString() : "";
|
||||
View tabView = provideTabView(i, tabTitle);
|
||||
if (tabView == null) tabView = createDefaultTabCustomView(tabTitle);
|
||||
tab.setCustomView(tabView);
|
||||
}
|
||||
|
||||
initTabStyle(mTabLayout, mCheckedIndex);
|
||||
}
|
||||
|
||||
public static void initTabStyle(TabLayout tabLayout, int currentItem) {
|
||||
// 默认选择addOnTabSelectedListener不会回调
|
||||
int tabCount = tabLayout.getTabCount();
|
||||
if (tabCount > 0) {
|
||||
TabLayout.Tab tab = tabLayout.getTabAt(currentItem);
|
||||
if (tab != null) updateTabStyle(tab, true);
|
||||
}
|
||||
|
||||
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
|
||||
@Override
|
||||
public void onTabSelected(TabLayout.Tab tab) {
|
||||
updateTabStyle(tab, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabUnselected(TabLayout.Tab tab) {
|
||||
updateTabStyle(tab, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabReselected(TabLayout.Tab tab) {
|
||||
updateTabStyle(tab, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void updateTabStyle(TabLayout.Tab tab, boolean isChecked) {
|
||||
View tabView = tab.getCustomView();
|
||||
if (tabView == null) {
|
||||
Utils.log("TabLayout->Tab样式不是通用样式,请检查");
|
||||
return;
|
||||
}
|
||||
|
||||
TextView tabTitle;
|
||||
if (tabView instanceof TextView) {
|
||||
tabTitle = (TextView) tabView;
|
||||
} else {
|
||||
tabTitle = tabView.findViewById(R.id.tab_title);
|
||||
}
|
||||
|
||||
if (tabTitle == null) {
|
||||
Utils.log("TabLayout->Tab样式不是通用样式,请检查");
|
||||
return;
|
||||
}
|
||||
tabTitle.setTypeface(isChecked ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT);
|
||||
}
|
||||
|
||||
// 如果不设置View的话,无法动态设置字体样式
|
||||
@NonNull
|
||||
public static View createDefaultTabCustomView(String title) {
|
||||
View view = LayoutInflater.from(HaloApp.getInstance().getApplication().getBaseContext()).inflate(R.layout.tab_item, null);
|
||||
View tabTitle = view.findViewById(R.id.tab_title);
|
||||
if (tabTitle instanceof CheckedTextView) {
|
||||
((CheckedTextView) tabTitle).setText(title);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -2,6 +2,7 @@ package com.gh.base.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import com.gh.gamecenter.normal.NormalFragment
|
||||
import com.lightgame.utils.Utils
|
||||
|
||||
/**
|
||||
* 懒加载(支持多层嵌套)
|
||||
@ -12,10 +13,10 @@ abstract class BaseLazyFragment : NormalFragment() {
|
||||
|
||||
private var isViewCreated = false
|
||||
|
||||
private var isSupportVisible = false
|
||||
protected var isSupportVisible = false
|
||||
|
||||
/**
|
||||
* 用于分发可见时间的时候父获取 fragment 是否隐藏
|
||||
* 用于分发可见时间的时候获取 父fragment 是否隐藏
|
||||
*
|
||||
* @return true fragment 不可见, false 父 fragment 可见
|
||||
*/
|
||||
@ -104,7 +105,7 @@ abstract class BaseLazyFragment : NormalFragment() {
|
||||
isSupportVisible = visible
|
||||
|
||||
if (visible) {
|
||||
if (mIsFirstVisible) {
|
||||
if (mIsFirstVisible && view != null) {
|
||||
mIsFirstVisible = false
|
||||
onFragmentFirstVisible()
|
||||
}
|
||||
|
||||
152
app/src/main/java/com/gh/base/fragment/BaseLazyTabFragment.kt
Normal file
152
app/src/main/java/com/gh/base/fragment/BaseLazyTabFragment.kt
Normal file
@ -0,0 +1,152 @@
|
||||
package com.gh.base.fragment
|
||||
|
||||
import android.content.Intent
|
||||
import android.graphics.Typeface
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.CheckedTextView
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import butterknife.BindView
|
||||
import com.gh.base.adapter.FragmentAdapter
|
||||
import com.gh.common.view.TabIndicatorView
|
||||
import com.gh.gamecenter.R
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.Utils
|
||||
import com.lightgame.view.NoScrollableViewPager
|
||||
|
||||
abstract class BaseLazyTabFragment : BaseLazyFragment(), ViewPager.OnPageChangeListener {
|
||||
|
||||
@BindView(R.id.fragment_tab_layout)
|
||||
lateinit var mTabLayout: TabLayout
|
||||
@BindView(R.id.fragment_view_pager)
|
||||
lateinit var mViewPager: NoScrollableViewPager
|
||||
@BindView(R.id.fragment_tab_indicator)
|
||||
lateinit var mTabIndicatorView: TabIndicatorView
|
||||
|
||||
var mFragmentsList: MutableList<Fragment> = arrayListOf()
|
||||
|
||||
var mTabTitleList: MutableList<String> = arrayListOf()
|
||||
|
||||
var mCheckedIndex = 0
|
||||
|
||||
abstract fun initFragmentList(fragments: MutableList<Fragment>)
|
||||
|
||||
abstract fun initTabTitleList(tabTitleList: MutableList<String>)
|
||||
|
||||
protected open fun provideIndicatorWidth(): Int {
|
||||
return 20
|
||||
}
|
||||
|
||||
protected open fun provideTabView(position: Int, tabTitle: String?): View? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun getLayoutId(): Int {
|
||||
return R.layout.fragment_tablayout_viewpager
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
val fragments = childFragmentManager.fragments
|
||||
if (fragments != null) {
|
||||
for (fragment in fragments) {
|
||||
fragment.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
if (arguments != null) mCheckedIndex = requireArguments().getInt(PAGE_INDEX, 0)
|
||||
}
|
||||
|
||||
override fun onFragmentFirstVisible() {
|
||||
super.onFragmentFirstVisible()
|
||||
|
||||
initFragmentList(mFragmentsList)
|
||||
initTabTitleList(mTabTitleList)
|
||||
|
||||
mViewPager.offscreenPageLimit = mFragmentsList.size
|
||||
mViewPager.addOnPageChangeListener(this)
|
||||
mViewPager.adapter = FragmentAdapter(childFragmentManager, mFragmentsList, mTabTitleList)
|
||||
mViewPager.currentItem = mCheckedIndex
|
||||
mTabLayout.setupWithViewPager(mViewPager)
|
||||
mTabIndicatorView.setupWithTabLayout(mTabLayout)
|
||||
mTabIndicatorView.setupWithViewPager(mViewPager)
|
||||
mTabIndicatorView.setIndicatorWidth(provideIndicatorWidth())
|
||||
for (i in 0 until mTabLayout.tabCount) {
|
||||
val tab = mTabLayout.getTabAt(i) ?: continue
|
||||
val tabTitle = if (tab.text != null) tab.text.toString() else ""
|
||||
var tabView = provideTabView(i, tabTitle)
|
||||
if (tabView == null) tabView = createDefaultTabCustomView(tabTitle)
|
||||
tab.customView = tabView
|
||||
}
|
||||
initTabStyle(mTabLayout, mCheckedIndex)
|
||||
}
|
||||
|
||||
open fun initTabStyle(tabLayout: TabLayout?, currentItem: Int) { // 默认选择addOnTabSelectedListener不会回调
|
||||
val tabCount = tabLayout!!.tabCount
|
||||
if (tabCount > 0) {
|
||||
val tab = tabLayout.getTabAt(currentItem)
|
||||
if (tab != null) updateTabStyle(tab, true)
|
||||
}
|
||||
tabLayout.addOnTabSelectedListener(object : OnTabSelectedListener {
|
||||
override fun onTabSelected(tab: TabLayout.Tab) {
|
||||
updateTabStyle(tab, true)
|
||||
}
|
||||
|
||||
override fun onTabUnselected(tab: TabLayout.Tab) {
|
||||
updateTabStyle(tab, false)
|
||||
}
|
||||
|
||||
override fun onTabReselected(tab: TabLayout.Tab) {
|
||||
updateTabStyle(tab, true)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
open fun updateTabStyle(tab: TabLayout.Tab, isChecked: Boolean) {
|
||||
val tabView = tab.customView
|
||||
if (tabView == null) {
|
||||
Utils.log("TabLayout->Tab样式不是通用样式,请检查")
|
||||
return
|
||||
}
|
||||
val tabTitle: TextView?
|
||||
tabTitle = if (tabView is TextView) {
|
||||
tabView
|
||||
} else {
|
||||
tabView.findViewById(R.id.tab_title)
|
||||
}
|
||||
if (tabTitle == null) {
|
||||
Utils.log("TabLayout->Tab样式不是通用样式,请检查")
|
||||
return
|
||||
}
|
||||
tabTitle.typeface = if (isChecked) Typeface.DEFAULT_BOLD else Typeface.DEFAULT
|
||||
}
|
||||
|
||||
// 如果不设置View的话,无法动态设置字体样式
|
||||
open fun createDefaultTabCustomView(title: String?): View {
|
||||
val view = LayoutInflater.from(HaloApp.getInstance().application.baseContext).inflate(R.layout.tab_item, null)
|
||||
val tabTitle = view.findViewById<View>(R.id.tab_title)
|
||||
if (tabTitle is CheckedTextView) {
|
||||
tabTitle.text = title
|
||||
}
|
||||
return view
|
||||
}
|
||||
|
||||
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
|
||||
|
||||
override fun onPageSelected(position: Int) {}
|
||||
|
||||
override fun onPageScrollStateChanged(state: Int) {}
|
||||
|
||||
companion object {
|
||||
const val PAGE_INDEX = "PAGE_INDEX"
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,8 @@ import android.os.Looper
|
||||
import com.gh.common.AppExecutor.ioExecutor
|
||||
import com.gh.common.AppExecutor.lightWeightIoExecutor
|
||||
import com.gh.common.AppExecutor.uiExecutor
|
||||
import java.util.concurrent.*
|
||||
import java.util.concurrent.Executor
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
/**
|
||||
* APP 线程池管理类
|
||||
@ -18,11 +19,11 @@ import java.util.concurrent.*
|
||||
object AppExecutor {
|
||||
|
||||
@JvmStatic
|
||||
var uiExecutor = MainThreadExecutor()
|
||||
val uiExecutor by lazy { MainThreadExecutor() }
|
||||
@JvmStatic
|
||||
var lightWeightIoExecutor = Executors.newSingleThreadExecutor()
|
||||
val lightWeightIoExecutor by lazy { Executors.newSingleThreadExecutor() }
|
||||
@JvmStatic
|
||||
var ioExecutor = Executors.newCachedThreadPool()
|
||||
val ioExecutor = Executors.newCachedThreadPool() // 用 by lazy 可能影响初始化速度
|
||||
|
||||
class MainThreadExecutor : Executor {
|
||||
private val mainThreadHandler = Handler(Looper.getMainLooper())
|
||||
|
||||
@ -6,9 +6,11 @@ import android.webkit.JavascriptInterface
|
||||
import androidx.annotation.Keep
|
||||
import com.gh.base.CurrentActivityHolder
|
||||
import com.gh.common.util.*
|
||||
import com.gh.gamecenter.BuildConfig
|
||||
import com.gh.gamecenter.LoginActivity
|
||||
import com.gh.gamecenter.ViewImageActivity
|
||||
import com.gh.gamecenter.entity.Badge
|
||||
import com.gh.gamecenter.entity.MtaEvent
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
import com.gh.gamecenter.retrofit.BiResponse
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
@ -76,6 +78,11 @@ class DefaultJsApi(var context: Context) {
|
||||
return HaloApp.getInstance().channel
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun getAppVersion(msg: Any): String {
|
||||
return BuildConfig.VERSION_NAME
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun bindWechat(msg: Any, handler: CompletionHandler<Any>) {
|
||||
context.ifLogin("浏览器") {
|
||||
@ -144,9 +151,6 @@ class DefaultJsApi(var context: Context) {
|
||||
context?.startActivity(ViewImageActivity.getBase64ViewImageIntent(context, true))
|
||||
}
|
||||
|
||||
@Keep
|
||||
internal data class MtaEvent(var name: String = "", var key: String = "", var value: String = "")
|
||||
|
||||
@Keep
|
||||
internal data class ImageEvent(var imageList: ArrayList<String> = arrayListOf(), var position: Int = 0)
|
||||
|
||||
|
||||
@ -15,6 +15,7 @@ import com.gh.gamecenter.WebActivity
|
||||
import com.gh.gamecenter.entity.CommunityEntity
|
||||
import com.gh.gamecenter.entity.VideoLinkEntity
|
||||
import com.gh.gamecenter.subject.SubjectActivity
|
||||
import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel
|
||||
import com.lightgame.utils.Utils
|
||||
|
||||
object DefaultWebViewUrlHandler {
|
||||
@ -43,7 +44,7 @@ object DefaultWebViewUrlHandler {
|
||||
|
||||
"qq" -> try {
|
||||
DirectUtils.directToQqConversation(context, id)
|
||||
} catch (e: Exception) {
|
||||
} catch (e: Throwable) {
|
||||
Utils.toast(context, "请检查是否已经安装手机QQ")
|
||||
e.printStackTrace()
|
||||
}
|
||||
@ -78,10 +79,18 @@ object DefaultWebViewUrlHandler {
|
||||
"community" -> {
|
||||
val community = CommunityEntity()
|
||||
community.id = id
|
||||
community.name = uri.getQueryParameter("name")
|
||||
community.name = uri.getQueryParameter("name") ?: ""
|
||||
DirectUtils.directToCommunity(context, community)
|
||||
}
|
||||
|
||||
"community_column" -> {
|
||||
val community = CommunityEntity()
|
||||
community.id = uri.getQueryParameter("community_id") ?: ""
|
||||
community.name = uri.getQueryParameter("community_name") ?: ""
|
||||
val columnId = uri.getQueryParameter("column_id") ?: ""
|
||||
DirectUtils.directToCommunityColumn(context, community, columnId, entrance, "文章链接")
|
||||
}
|
||||
|
||||
"answer" -> DirectUtils.directToAnswerDetail(context, id, entrance, "文章链接")
|
||||
|
||||
"communities" -> {
|
||||
@ -124,8 +133,17 @@ object DefaultWebViewUrlHandler {
|
||||
}
|
||||
EntranceUtils.HOST_USERHOME -> {
|
||||
val position = uri.getQueryParameter("position")
|
||||
DirectUtils.directToHomeActivity(context, id, if(position.isNullOrEmpty()) -1 else position.toInt(), entrance, "")
|
||||
DirectUtils.directToHomeActivity(context, id, if (position.isNullOrEmpty()) -1 else position.toInt(), entrance, "")
|
||||
}
|
||||
|
||||
EntranceUtils.HOST_VIDEO_MORE -> {
|
||||
val referer = uri.getQueryParameter("referer") ?: ""
|
||||
val type = uri.getQueryParameter("type") ?: ""
|
||||
val act = uri.getQueryParameter("act") ?: ""
|
||||
val loaction = if (TextUtils.isEmpty(act)) id else VideoDetailContainerViewModel.Location.VIDEO_ACTIVITY.value
|
||||
DirectUtils.directToVideoDetail(context, id, loaction, false, "", entrance, "", referer, type, act)
|
||||
}
|
||||
|
||||
else -> DialogUtils.showLowVersionDialog(context)
|
||||
}
|
||||
return true
|
||||
|
||||
@ -8,6 +8,7 @@ import com.gh.common.exposure.meta.MetaUtil
|
||||
import com.gh.common.util.edit
|
||||
import com.gh.common.util.toJson
|
||||
import com.gh.common.util.toObject
|
||||
import com.gh.common.util.tryWithDefaultCatch
|
||||
import com.gh.gamecenter.BuildConfig
|
||||
import com.gh.gamecenter.entity.AliasEntity
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
@ -35,33 +36,34 @@ object PushManager {
|
||||
|
||||
@JvmStatic
|
||||
fun init(channel: String) {
|
||||
//初始化友盟推送
|
||||
UMConfigure.init(mApplication,
|
||||
Config.UMENG_APPKEY, channel,
|
||||
UMConfigure.DEVICE_TYPE_PHONE,
|
||||
Config.UMENG_MESSAGE_SECRET)
|
||||
tryWithDefaultCatch {
|
||||
//初始化友盟推送
|
||||
UMConfigure.init(mApplication,
|
||||
Config.UMENG_APPKEY, channel,
|
||||
UMConfigure.DEVICE_TYPE_PHONE,
|
||||
Config.UMENG_MESSAGE_SECRET)
|
||||
|
||||
// 注册小米、华为和魅族通道
|
||||
MiPushRegistar.register(mApplication, Config.MIPUSH_APPID, Config.MIPUSH_APPKEY)
|
||||
HuaWeiRegister.register(mApplication)
|
||||
MeizuRegister.register(mApplication, BuildConfig.MEIZUPUSH_APPID, BuildConfig.MEIZUPUSH_APPKEY)
|
||||
// 注册小米、华为和魅族通道
|
||||
MiPushRegistar.register(mApplication, Config.MIPUSH_APPID, Config.MIPUSH_APPKEY)
|
||||
HuaWeiRegister.register(mApplication)
|
||||
MeizuRegister.register(mApplication, BuildConfig.MEIZUPUSH_APPID, BuildConfig.MEIZUPUSH_APPKEY)
|
||||
|
||||
//友盟推送
|
||||
val pushAgent = PushAgent.getInstance(mApplication)
|
||||
pushAgent.onAppStart() // 开启App统计
|
||||
//友盟推送
|
||||
val pushAgent = PushAgent.getInstance(mApplication)
|
||||
|
||||
//注册推送服务,每次调用register方法都会回调该接口
|
||||
registerDevice()
|
||||
//注册推送服务,每次调用register方法都会回调该接口
|
||||
runOnIoThread { registerDevice() }
|
||||
|
||||
val aliasInSp = PreferenceManager.getDefaultSharedPreferences(mApplication).getString(SP_PUSH_ALIAS, "")
|
||||
mPreviousAlias = aliasInSp?.toObject()
|
||||
val aliasInSp = PreferenceManager.getDefaultSharedPreferences(mApplication).getString(SP_PUSH_ALIAS, "")
|
||||
mPreviousAlias = aliasInSp?.toObject()
|
||||
|
||||
if (mPreviousAlias == null) {
|
||||
getAndSetAlias()
|
||||
if (mPreviousAlias == null) {
|
||||
getAndSetAlias()
|
||||
}
|
||||
|
||||
// 完全自定义处理(透传)
|
||||
pushAgent.setPushIntentServiceClass(GHUmengNotificationService::class.java)
|
||||
}
|
||||
|
||||
// 完全自定义处理(透传)
|
||||
pushAgent.setPushIntentServiceClass(GHUmengNotificationService::class.java)
|
||||
}
|
||||
|
||||
private fun registerDevice() {
|
||||
|
||||
@ -6,6 +6,7 @@ import android.os.Bundle
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
/**
|
||||
@ -113,7 +114,7 @@ class TimeElapsedHelper(val fragment: Fragment?, val activity: Activity?) {
|
||||
}
|
||||
|
||||
object TimeElapsedThreadHolder {
|
||||
val threadService = Executors.newSingleThreadExecutor()
|
||||
val threadService: ExecutorService by lazy { Executors.newSingleThreadExecutor() }
|
||||
}
|
||||
|
||||
interface TimeoutCallback {
|
||||
|
||||
16
app/src/main/java/com/gh/common/annotation/SyncIgnore.java
Normal file
16
app/src/main/java/com/gh/common/annotation/SyncIgnore.java
Normal file
@ -0,0 +1,16 @@
|
||||
package com.gh.common.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 标记那些不需要检查的同步字段
|
||||
*
|
||||
* 同步字段冲突时使用
|
||||
*/
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface SyncIgnore {
|
||||
}
|
||||
12
app/src/main/java/com/gh/common/annotation/SyncPage.java
Normal file
12
app/src/main/java/com/gh/common/annotation/SyncPage.java
Normal file
@ -0,0 +1,12 @@
|
||||
package com.gh.common.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface SyncPage {
|
||||
String[] syncNames();
|
||||
}
|
||||
@ -32,7 +32,6 @@ public class Config {
|
||||
|
||||
public static final String API_HOST = BuildConfig.API_HOST;
|
||||
public static final String COMMENT_HOST = BuildConfig.COMMENT_HOST;
|
||||
public static final String DATA_HOST = BuildConfig.DATA_HOST;
|
||||
|
||||
/**
|
||||
* 需要配置的请使用{@link PreferenceManager#getDefaultSharedPreferences(Context)}
|
||||
@ -235,6 +234,10 @@ public class Config {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isGameDomeSwitchOpen() {
|
||||
return getSettings() != null && getSettings().getGameDomeSwitch().equals("on");
|
||||
}
|
||||
|
||||
public static void fixHideFunction() {
|
||||
SharedPreferences preferences = PreferenceManager.
|
||||
getDefaultSharedPreferences(HaloApp.getInstance().getApplication());
|
||||
|
||||
@ -29,6 +29,8 @@ public class Constants {
|
||||
|
||||
public static final String EB_QUIT_LOGIN = "quit_login";
|
||||
|
||||
public static final String GAME_ID_DIVIDER = ":"; // 用于避免历史下载掺和到普通下载状态的 ID 修饰符
|
||||
|
||||
// 最近显示的弹窗信息
|
||||
public static final String SP_LAST_OPENING_ID = "last_opening_dialog_id";
|
||||
public static final String SP_LAST_OPENING_TIME = "last_opening_dialog_time";
|
||||
@ -62,6 +64,18 @@ public class Constants {
|
||||
public static final String SP_ADDONS_FUNCS_HAVE_READ = "addons_funcs_have_read";
|
||||
//视频非wifi提醒只提醒一次,重启恢复
|
||||
public static final String SP_NON_WIFI_TIPS = "non_wifi_tips";
|
||||
//首页视频最新tab提示
|
||||
public static final String SP_HOME_NEW_VIDEO_TIPS = "home_new_video";
|
||||
//游戏设备弹窗提示
|
||||
public static final String SP_DEVICE_REMIND = "device_remind";
|
||||
//是否是第一次弹出游戏设备弹窗提示
|
||||
public static final String SP_FIRST_DEVICE_REMIND = "first_device_remind";
|
||||
//游戏设备弹窗不再提示
|
||||
public static final String SP_NO_REMIND_AGAIN = "no_remind_again";
|
||||
//游戏详情过滤标签数据
|
||||
public static final String SP_FILTER_TAGS= "filter_tags";
|
||||
//实名认证弹窗分类数据
|
||||
public static final String SP_AUTH_DIALOG= "auth_dialog";
|
||||
|
||||
//手机号码匹配规则
|
||||
public static final String REGEX_MOBILE = "^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$";
|
||||
|
||||
@ -13,9 +13,16 @@ import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.databinding.BindingAdapter;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import com.facebook.drawee.view.SimpleDraweeView;
|
||||
import com.gh.base.OnViewClickListener;
|
||||
import com.gh.common.constant.Config;
|
||||
import com.gh.common.dialog.CertificationDialog;
|
||||
import com.gh.common.dialog.ReserveDialogFragment;
|
||||
import com.gh.common.exposure.ExposureEvent;
|
||||
import com.gh.common.exposure.ExposureUtils;
|
||||
@ -36,10 +43,10 @@ import com.gh.common.util.PackageUtils;
|
||||
import com.gh.common.util.PermissionHelper;
|
||||
import com.gh.common.util.PlatformUtils;
|
||||
import com.gh.common.util.ReservationHelper;
|
||||
import com.gh.common.view.DownloadDialog;
|
||||
import com.gh.common.view.DownloadProgressBar;
|
||||
import com.gh.common.view.DrawableView;
|
||||
import com.gh.download.DownloadManager;
|
||||
import com.gh.download.dialog.DownloadDialog;
|
||||
import com.gh.gamecenter.DownloadManagerActivity;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.WebActivity;
|
||||
@ -65,12 +72,6 @@ import java.text.SimpleDateFormat;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.databinding.BindingAdapter;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
/**
|
||||
* Created by khy on 12/02/18.
|
||||
*/
|
||||
@ -198,6 +199,17 @@ public class BindingAdapters {
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("textColorFromString")
|
||||
public static void textColorFromString(TextView tv, String hexString) {
|
||||
if (TextUtils.isEmpty(hexString)) return;
|
||||
|
||||
try {
|
||||
tv.setTextColor(Color.parseColor(hexString));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("visibleGone")
|
||||
public static void showHide(View view, Boolean show) {
|
||||
if (show != null && show) {
|
||||
@ -207,6 +219,32 @@ public class BindingAdapters {
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("goneIf")
|
||||
public static void goneIf(View view, Boolean gone) {
|
||||
if (gone != null && gone) {
|
||||
view.setVisibility(View.GONE);
|
||||
} else {
|
||||
view.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* lazy 的 paddingTop
|
||||
*/
|
||||
@BindingAdapter("lazyPaddingTop")
|
||||
public static void lazyPaddingTop(View view, int paddingTopInDp) {
|
||||
view.setPadding(view.getPaddingLeft(), DisplayUtils.dip2px(paddingTopInDp), view.getPaddingRight(), view.getPaddingBottom());
|
||||
}
|
||||
|
||||
@BindingAdapter("visibleInvisible")
|
||||
public static void visibleInvisible(View view, Boolean show) {
|
||||
if (show != null && show) {
|
||||
view.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
view.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("messageUnread")
|
||||
public static void setMessageUnread(TextView view, int unreadCount) {
|
||||
if (unreadCount < 100) {
|
||||
@ -358,17 +396,25 @@ public class BindingAdapters {
|
||||
case PLUGIN:
|
||||
if (gameEntity.getApk().size() == 1) {
|
||||
ApkEntity apk = gameEntity.getApk().get(0);
|
||||
DownloadDialogHelper.findAvailableDialogAndShow(
|
||||
v.getContext(),
|
||||
gameEntity,
|
||||
apk,
|
||||
DownloadDialogHelper.findAvailableDialogAndShow(v.getContext(), gameEntity, apk,
|
||||
() -> {
|
||||
DialogUtils.checkDownload(v.getContext(), apk.getSize(),
|
||||
isSubscribe -> download(progressBar, gameEntity, traceEvent, isSubscribe, entrance, location));
|
||||
CertificationDialog.showCertificationDialog(v.getContext(), gameEntity, () -> {
|
||||
DialogUtils.showVersionNumberDialog(v.getContext(), gameEntity, () -> {
|
||||
DialogUtils.checkDownload(v.getContext(), apk.getSize(),
|
||||
isSubscribe -> download(progressBar, gameEntity, traceEvent, isSubscribe, entrance, location));
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
DownloadDialog.getInstance(v.getContext()).showPopupWindow(v, gameEntity,
|
||||
entrance, location + gameEntity.getName(), traceEvent);
|
||||
CertificationDialog.showCertificationDialog(v.getContext(), gameEntity, () -> {
|
||||
DialogUtils.showVersionNumberDialog(v.getContext(), gameEntity, () -> {
|
||||
DownloadDialog.showDownloadDialog(
|
||||
v.getContext(),
|
||||
gameEntity,
|
||||
entrance,
|
||||
location + ":" + gameEntity.getName());
|
||||
});
|
||||
});
|
||||
}
|
||||
break;
|
||||
case LAUNCH_OR_OPEN:
|
||||
@ -376,8 +422,11 @@ public class BindingAdapters {
|
||||
DataUtils.onGameLaunchEvent(v.getContext(), gameEntity.getName(), gameEntity.getApk().get(0).getPlatform(), location);
|
||||
PackageUtils.launchApplicationByPackageName(v.getContext(), gameEntity.getApk().get(0).getPackageName());
|
||||
} else {
|
||||
DownloadDialog.getInstance(v.getContext()).showPopupWindow(v, gameEntity,
|
||||
entrance, location + gameEntity.getName(), traceEvent);
|
||||
DownloadDialog.showDownloadDialog(
|
||||
v.getContext(),
|
||||
gameEntity,
|
||||
entrance,
|
||||
location + ":" + gameEntity.getName());
|
||||
}
|
||||
break;
|
||||
case INSTALL_PLUGIN:
|
||||
@ -455,7 +504,7 @@ public class BindingAdapters {
|
||||
}
|
||||
progressBar.setDownloadType(DownloadProgressBar.DownloadType.NONE);
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
String status = GameUtils.getDownloadBtnText(progressBar.getContext(), gameEntity, PluginLocation.only_game);
|
||||
switch (status) {
|
||||
@ -511,6 +560,23 @@ public class BindingAdapters {
|
||||
}
|
||||
}
|
||||
|
||||
/*private static void download(DownloadProgressBar progressBar, GameEntity gameEntity, ExposureEvent traceEvent, @Nullable String entrance, @Nullable String location, View v) {
|
||||
if (gameEntity.getApk().size() == 1) {
|
||||
ApkEntity apk = gameEntity.getApk().get(0);
|
||||
DownloadDialogHelper.findAvailableDialogAndShow(
|
||||
v.getContext(),
|
||||
gameEntity,
|
||||
apk,
|
||||
() -> {
|
||||
DialogUtils.checkDownload(v.getContext(), apk.getSize(),
|
||||
isSubscribe -> download(progressBar, gameEntity, traceEvent, isSubscribe, entrance, location));
|
||||
});
|
||||
} else {
|
||||
DownloadDialog.getInstance(v.getContext()).showPopupWindow(v, gameEntity,
|
||||
entrance, location + gameEntity.getName(), traceEvent);
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
private static void updateReservation(DownloadProgressBar progressBar, GameEntity gameEntity) {
|
||||
// 显示预约
|
||||
@ -546,10 +612,10 @@ public class BindingAdapters {
|
||||
String msg = FileUtils.isCanDownload(progressBar.getContext(), apkEntity.getSize());
|
||||
if (TextUtils.isEmpty(msg)) {
|
||||
DataUtils.onGameDownloadEvent(progressBar.getContext(), gameEntity.getName(), apkEntity.getPlatform(), entrance, "下载开始", method);
|
||||
|
||||
|
||||
ExposureUtils.DownloadType downloadType = ExposureUtils.getDownloadType(apkEntity, method);
|
||||
ExposureEvent downloadExposureEvent = ExposureUtils.logADownloadExposureEvent(gameEntity, apkEntity.getPlatform(), traceEvent, downloadType);
|
||||
|
||||
|
||||
DownloadManager.createDownload(progressBar.getContext(),
|
||||
apkEntity,
|
||||
gameEntity,
|
||||
@ -567,27 +633,9 @@ public class BindingAdapters {
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter({"gameLabelList", "subjectTag"})
|
||||
public static void setGameLabelList(LinearLayout layout, GameEntity gameEntity, String subjectTag) {
|
||||
if (gameEntity == null) return;
|
||||
if (gameEntity.getTest() != null) {
|
||||
layout.removeAllViews();
|
||||
View testView = LayoutInflater.from(layout.getContext()).inflate(R.layout.game_test_label, null);
|
||||
TextView testType = testView.findViewById(R.id.test_type);
|
||||
TextView testTime = testView.findViewById(R.id.test_time);
|
||||
testType.setText(gameEntity.getTest().getType());
|
||||
testType.setBackgroundColor(ContextCompat.getColor(layout.getContext(), R.color.tag_yellow));
|
||||
|
||||
if (gameEntity.getTest().getStart() == 0) {
|
||||
testTime.setVisibility(View.GONE);
|
||||
} else {
|
||||
testTime.setText(GameViewUtils.getGameTestDate(gameEntity.getTest().getStart()));
|
||||
}
|
||||
layout.addView(testView);
|
||||
} else {
|
||||
GameViewUtils.setLabelList(layout.getContext(), layout, gameEntity.getTag(), subjectTag, gameEntity.getTagStyle());
|
||||
}
|
||||
|
||||
@BindingAdapter("gameLabelList")
|
||||
public static void setGameLabelList(LinearLayout layout, List<TagStyleEntity> tagStyle) {
|
||||
GameViewUtils.setLabelList(layout.getContext(), layout, tagStyle);
|
||||
}
|
||||
|
||||
@BindingAdapter("isRefreshing")
|
||||
|
||||
@ -15,6 +15,7 @@ abstract class BaseTrackableDialogFragment : BaseDialogFragment() {
|
||||
|
||||
abstract fun getEvent(): String
|
||||
abstract fun getKey(): String
|
||||
open fun getValue(): String = ""
|
||||
|
||||
// 区分此 dialog 是点击 dialog 外部取消的还是点击返回取消的
|
||||
private val mIsCanceledByClickOutsideOfDialog = AtomicBoolean(true)
|
||||
@ -47,6 +48,9 @@ abstract class BaseTrackableDialogFragment : BaseDialogFragment() {
|
||||
MtaHelper.onEventWithBasicDeviceInfo(getEvent(), getKey(), value)
|
||||
} else {
|
||||
MtaHelper.onEvent(getEvent(), getKey(), value)
|
||||
if (getValue().isNotEmpty()) {
|
||||
MtaHelper.onEvent(getEvent(), value, getValue())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
196
app/src/main/java/com/gh/common/dialog/CertificationDialog.kt
Normal file
196
app/src/main/java/com/gh/common/dialog/CertificationDialog.kt
Normal file
@ -0,0 +1,196 @@
|
||||
package com.gh.common.dialog
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.CheckBox
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.gh.common.avoidcallback.AvoidOnResultManager
|
||||
import com.gh.common.avoidcallback.Callback
|
||||
import com.gh.common.constant.Constants
|
||||
import com.gh.common.util.CheckLoginUtils
|
||||
import com.gh.common.util.DialogUtils
|
||||
import com.gh.common.util.GsonUtils
|
||||
import com.gh.common.util.SPUtils
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.UserInfoEditActivity
|
||||
import com.gh.gamecenter.WebActivity
|
||||
import com.gh.gamecenter.entity.AuthDialogEntity
|
||||
import com.gh.gamecenter.entity.AuthDialogLevel
|
||||
import com.gh.gamecenter.entity.DeviceDialogEntity
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
import com.gh.gamecenter.retrofit.BiResponse
|
||||
import com.gh.gamecenter.retrofit.Response
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.gh.gamecenter.user.UserViewModel
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.halo.assistant.fragment.user.UserInfoEditFragment
|
||||
import com.lightgame.utils.AppManager
|
||||
import com.tencent.bugly.beta.tinker.TinkerManager.getApplication
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.ResponseBody
|
||||
import retrofit2.HttpException
|
||||
|
||||
class CertificationDialog(context: Context, private val authDialogEntity: AuthDialogEntity, val gameId: String, val listener: DialogUtils.ConfirmListener) :
|
||||
Dialog(context, R.style.GhAlertDialog) {
|
||||
|
||||
private lateinit var view: View
|
||||
private lateinit var detailedDesTv: TextView
|
||||
private lateinit var noRemindAgainCb: CheckBox
|
||||
private lateinit var actionLeftTv: TextView
|
||||
private lateinit var actionRightTv: TextView
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
view = LayoutInflater.from(context).inflate(R.layout.dialog_sertification, null)
|
||||
setContentView(view)
|
||||
detailedDesTv = view.findViewById(R.id.detailedDesTv)
|
||||
noRemindAgainCb = view.findViewById(R.id.noRemindAgainCb)
|
||||
actionLeftTv = view.findViewById(R.id.actionLeftTv)
|
||||
actionRightTv = view.findViewById(R.id.actionRightTv)
|
||||
|
||||
detailedDesTv.paint.flags = Paint.UNDERLINE_TEXT_FLAG
|
||||
detailedDesTv.paint.isAntiAlias = true
|
||||
|
||||
detailedDesTv.setOnClickListener {
|
||||
context.startActivity(WebActivity.getIntentByUrl(context, authDialogEntity.link))
|
||||
}
|
||||
|
||||
when (authDialogEntity.level) {
|
||||
AuthDialogLevel.MUST_PASS.value -> {
|
||||
actionLeftTv.text = "暂不下载"
|
||||
actionRightTv.text = "去实名认证"
|
||||
noRemindAgainCb.visibility = View.GONE
|
||||
actionLeftTv.setOnClickListener {
|
||||
dismiss()
|
||||
}
|
||||
actionRightTv.setOnClickListener {
|
||||
if (UserManager.getInstance().isLoggedIn) {
|
||||
gotoAuthPage()
|
||||
} else {
|
||||
gotoLoginPage()
|
||||
}
|
||||
}
|
||||
}
|
||||
AuthDialogLevel.ALWAYS_HINT.value -> {
|
||||
actionLeftTv.text = "去实名认证"
|
||||
actionRightTv.text = "继续下载"
|
||||
noRemindAgainCb.visibility = View.GONE
|
||||
actionLeftTv.setOnClickListener {
|
||||
if (UserManager.getInstance().isLoggedIn) {
|
||||
gotoAuthPage()
|
||||
} else {
|
||||
gotoLoginPage()
|
||||
}
|
||||
}
|
||||
actionRightTv.setOnClickListener {
|
||||
listener.onConfirm()
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
AuthDialogLevel.OPTIONAL_HINT.value -> {
|
||||
actionLeftTv.text = "去实名认证"
|
||||
actionRightTv.text = "继续下载"
|
||||
noRemindAgainCb.visibility = View.VISIBLE
|
||||
actionLeftTv.setOnClickListener {
|
||||
if (noRemindAgainCb.isChecked) {
|
||||
SPUtils.setBoolean(gameId, true)
|
||||
}
|
||||
if (UserManager.getInstance().isLoggedIn) {
|
||||
gotoAuthPage()
|
||||
} else {
|
||||
gotoLoginPage()
|
||||
}
|
||||
}
|
||||
actionRightTv.setOnClickListener {
|
||||
if (noRemindAgainCb.isChecked) {
|
||||
SPUtils.getBoolean(gameId, true)
|
||||
}
|
||||
listener.onConfirm()
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//跳转登录页面
|
||||
private fun gotoLoginPage() {
|
||||
CheckLoginUtils.checkLogin(AppManager.getInstance().currentActivity() as AppCompatActivity,
|
||||
null, true, "实名认证弹窗") {
|
||||
if (UserManager.getInstance().isAuth) {
|
||||
listener.onConfirm()
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//跳转实名认证页面
|
||||
private fun gotoAuthPage() {
|
||||
AvoidOnResultManager.getInstance(AppManager.getInstance().currentActivity() as AppCompatActivity)
|
||||
.startForResult(UserInfoEditActivity.getIntent(context, UserViewModel.TYPE_ID_CARD), object : Callback {
|
||||
override fun onActivityResult(resultCode: Int, data: Intent?) {
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
val isAuthSuccess = data.getBooleanExtra(UserInfoEditFragment.AUTH_SUCCESS, false)
|
||||
if (isAuthSuccess) {
|
||||
listener.onConfirm()
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun showCertificationDialog(context: Context, game: GameEntity, listener: DialogUtils.ConfirmListener) {
|
||||
//1.先判断是否登录 是执行2 否执行3
|
||||
//2.判断是否实名认证 是终止 否执行3
|
||||
//3.判断是否需要弹出认证弹窗接口
|
||||
if (UserManager.getInstance().isLoggedIn) {
|
||||
if (UserManager.getInstance().isAuth) {//已实名认证
|
||||
listener.onConfirm()
|
||||
} else {
|
||||
authDialog(context, game, listener)
|
||||
}
|
||||
} else {
|
||||
authDialog(context, game, listener)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
private fun authDialog(context: Context, game: GameEntity, listener: DialogUtils.ConfirmListener) {
|
||||
var authDialog: AuthDialogEntity? = null
|
||||
if (game.authDialog != null) {
|
||||
authDialog = game.authDialog
|
||||
}
|
||||
if (authDialog == null) {
|
||||
val datas = SPUtils.getString(Constants.SP_AUTH_DIALOG)
|
||||
val type = object : TypeToken<List<AuthDialogEntity>>() {}.type
|
||||
val authDialogs = GsonUtils.gson.fromJson<List<AuthDialogEntity>>(datas, type)
|
||||
if (!authDialogs.isNullOrEmpty()) {
|
||||
authDialog = authDialogs.find { it.gameCategory == game.category }
|
||||
}
|
||||
}
|
||||
val isCloseAuthDialog = SPUtils.getBoolean(game.id, false)
|
||||
if (authDialog != null && (authDialog.level != AuthDialogLevel.OPTIONAL_HINT.value || !isCloseAuthDialog)) {
|
||||
val dialog = CertificationDialog(context, authDialog, game.id, listener)
|
||||
dialog.show()
|
||||
} else {
|
||||
listener.onConfirm()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
234
app/src/main/java/com/gh/common/dialog/DeviceRemindDialog.kt
Normal file
234
app/src/main/java/com/gh/common/dialog/DeviceRemindDialog.kt
Normal file
@ -0,0 +1,234 @@
|
||||
package com.gh.common.dialog
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Message
|
||||
import android.preference.PreferenceManager
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.facebook.drawee.view.SimpleDraweeView
|
||||
import com.gh.common.constant.Constants
|
||||
import com.gh.common.util.*
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.entity.DeviceDialogEntity
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.halo.assistant.fragment.SettingsFragment.AUTO_INSTALL_SP_KEY
|
||||
import com.lightgame.download.DataWatcher
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import com.lightgame.download.DownloadStatus
|
||||
import com.lightgame.utils.Utils
|
||||
import io.reactivex.disposables.Disposable
|
||||
import kotlinx.android.synthetic.main.dialog_device_remind.view.*
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
/**
|
||||
* 设备提醒弹窗
|
||||
*/
|
||||
class DeviceRemindDialog(context: Context, val entity: DeviceDialogEntity, val gameEntity: GameEntity) : Dialog(context, R.style.GhAlertDialog) {
|
||||
private lateinit var view: View
|
||||
private var currentPage = 0
|
||||
private var mSlideLooperInterval = 3000L
|
||||
private lateinit var mLooperHandle: LooperHandle
|
||||
private lateinit var mAdapter: BannerAdapter
|
||||
private var mDatas: ArrayList<String> = ArrayList()
|
||||
private val mSlideLooperKey = 100
|
||||
private var disposable: Disposable? = null
|
||||
private val dataWatcher = object : DataWatcher() {
|
||||
override fun onDataChanged(downloadEntity: DownloadEntity) {
|
||||
if (downloadEntity.status == DownloadStatus.done && downloadEntity.name == gameEntity.name) {
|
||||
val sp = PreferenceManager.getDefaultSharedPreferences(getContext())
|
||||
val autoInstall = sp.getBoolean(AUTO_INSTALL_SP_KEY, true)
|
||||
if (autoInstall) {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun showDeviceRemindDialog(context: Context, gameEntity: GameEntity) {
|
||||
val datas = SPUtils.getString(Constants.SP_DEVICE_REMIND)
|
||||
if (datas.isNotEmpty()) {
|
||||
val type = object : TypeToken<List<DeviceDialogEntity>>() {}.type
|
||||
val entitys = GsonUtils.gson.fromJson<List<DeviceDialogEntity>>(datas, type)
|
||||
//1.判断设备是否匹配
|
||||
val entity = entitys.find { it.manufacturer.toLowerCase().startsWith(Build.MANUFACTURER.toLowerCase()) }
|
||||
?: return
|
||||
//2.判断游戏不含剔除标签
|
||||
gameEntity.tagStyle.forEach {
|
||||
if (entity.excludeTags.contains(it.name)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
//3.不再弹出提示判断
|
||||
val isNoRemindAgain = SPUtils.getBoolean(Constants.SP_NO_REMIND_AGAIN, false)
|
||||
if (isNoRemindAgain) return
|
||||
|
||||
val dialog = DeviceRemindDialog(context, entity, gameEntity)
|
||||
dialog.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
view = LayoutInflater.from(context).inflate(R.layout.dialog_device_remind, null)
|
||||
setContentView(view)
|
||||
mDatas.addAll(entity.gallery)
|
||||
view.titleTv.text = entity.title
|
||||
view.contentTv.text = entity.content
|
||||
|
||||
view.bannerView.apply {
|
||||
orientation = ViewPager2.ORIENTATION_HORIZONTAL
|
||||
mAdapter = BannerAdapter()
|
||||
val recyclerView = getChildAt(0) as RecyclerView
|
||||
recyclerView.overScrollMode = RecyclerView.OVER_SCROLL_NEVER
|
||||
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
super.onPageSelected(position)
|
||||
currentPage = position
|
||||
slideIndicator(currentPage % mDatas.size)
|
||||
}
|
||||
})
|
||||
recyclerView.addOnItemTouchListener(object : RecyclerView.SimpleOnItemTouchListener() {
|
||||
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
|
||||
val isStop = e.action == MotionEvent.ACTION_DOWN || e.action == MotionEvent.ACTION_MOVE
|
||||
if (isStop) mAdapter.stopScroll() else mAdapter.startScroll()
|
||||
return false
|
||||
}
|
||||
})
|
||||
adapter = mAdapter
|
||||
mLooperHandle = LooperHandle(mAdapter)
|
||||
currentPage = (adapter as BannerAdapter).getActualFirstPositionInCenter()
|
||||
setCurrentItem(currentPage, false)
|
||||
if (mDatas.size > 1) {
|
||||
addIndicator()
|
||||
slideIndicator(currentPage % mDatas.size)
|
||||
autoPlay()
|
||||
}
|
||||
}
|
||||
val isFirst = SPUtils.getBoolean(Constants.SP_FIRST_DEVICE_REMIND, false)
|
||||
if (!isFirst) {
|
||||
view.cancelTv.isEnabled = false
|
||||
view.cancelTv.background = ContextCompat.getDrawable(context, R.drawable.button_round_f5f5f5)
|
||||
disposable = countDownTimer(3) { finish, time ->
|
||||
if (finish) {
|
||||
view.cancelTv.isEnabled = true
|
||||
view.cancelTv.background = ContextCompat.getDrawable(context, R.drawable.button_blue_oval)
|
||||
view.cancelTv.text = "我知道了"
|
||||
view.cancelTv.setTextColor(ContextCompat.getColor(context, R.color.white))
|
||||
} else {
|
||||
view.cancelTv.text = "我知道了(${time}S)"
|
||||
}
|
||||
}
|
||||
|
||||
SPUtils.setBoolean(Constants.SP_FIRST_DEVICE_REMIND, true)
|
||||
} else {
|
||||
view.noRemindAgainCb.visibility = View.VISIBLE
|
||||
view.cancelTv.text = "我知道了"
|
||||
view.cancelTv.isEnabled = true
|
||||
view.cancelTv.setTextColor(ContextCompat.getColor(context, R.color.white))
|
||||
view.cancelTv.background = ContextCompat.getDrawable(context, R.drawable.button_blue_oval)
|
||||
}
|
||||
view.cancelTv.setOnClickListener {
|
||||
SPUtils.setBoolean(Constants.SP_NO_REMIND_AGAIN, view.noRemindAgainCb.isChecked)
|
||||
dismiss()
|
||||
}
|
||||
DownloadManager.getInstance(context).addObserver(dataWatcher)
|
||||
}
|
||||
|
||||
private fun addIndicator() {
|
||||
view.indicatorLl.removeAllViews()
|
||||
mDatas.forEach { _ ->
|
||||
val indicatorView = ImageView(context).apply {
|
||||
setImageResource(R.drawable.selector_device_remind_indicator)
|
||||
val params = LinearLayout.LayoutParams(DisplayUtils.dip2px(8F), LinearLayout.LayoutParams.WRAP_CONTENT)
|
||||
params.leftMargin = DisplayUtils.dip2px(1F)
|
||||
params.rightMargin = DisplayUtils.dip2px(1F)
|
||||
layoutParams = params
|
||||
}
|
||||
view.indicatorLl.addView(indicatorView)
|
||||
}
|
||||
}
|
||||
|
||||
private fun slideIndicator(position: Int) {
|
||||
for (i in 0 until view.indicatorLl.childCount) {
|
||||
val childAt = view.indicatorLl.getChildAt(i)
|
||||
childAt.isSelected = i == position
|
||||
}
|
||||
}
|
||||
|
||||
private fun autoPlay() {
|
||||
mAdapter.startScroll()
|
||||
}
|
||||
|
||||
inner class BannerAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return object : RecyclerView.ViewHolder(LayoutInflater.from(context).inflate(R.layout.item_ad_banner, parent, false)) {}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = if (mDatas.size == 1) mDatas.size else Int.MAX_VALUE
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
val data = mDatas[position % mDatas.size]
|
||||
val view = holder.itemView as SimpleDraweeView
|
||||
ImageUtils.display(view, data)
|
||||
}
|
||||
|
||||
fun getActualFirstPositionInCenter(): Int {
|
||||
if (mDatas.size == 1) return 0
|
||||
var index = itemCount / 2
|
||||
if (index % mDatas.size != 0) {
|
||||
index -= (index % mDatas.size)
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
fun scrollToNextPage() {
|
||||
currentPage++
|
||||
view.bannerView.setCurrentItem(currentPage, true)
|
||||
}
|
||||
|
||||
fun startScroll() {
|
||||
mLooperHandle.removeMessages(mSlideLooperKey)
|
||||
mLooperHandle.sendEmptyMessageDelayed(mSlideLooperKey, mSlideLooperInterval)
|
||||
}
|
||||
|
||||
fun stopScroll() {
|
||||
mLooperHandle.removeMessages(mSlideLooperKey)
|
||||
}
|
||||
}
|
||||
|
||||
class LooperHandle(val mAdapter: BannerAdapter) : Handler() {
|
||||
private val mWeakReference: WeakReference<BannerAdapter> = WeakReference(mAdapter)
|
||||
override fun handleMessage(msg: Message?) {
|
||||
val adapter = mWeakReference.get()
|
||||
adapter?.scrollToNextPage()
|
||||
adapter?.startScroll()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
if (disposable != null && !disposable!!.isDisposed) {
|
||||
disposable!!.dispose()
|
||||
disposable = null
|
||||
}
|
||||
DownloadManager.getInstance(context).removeObserver(dataWatcher)
|
||||
}
|
||||
}
|
||||
@ -37,7 +37,7 @@ class NotificationHintDialogFragment : BaseTrackableDialogFragment() {
|
||||
titleTv.text = mNotificationHint?.title
|
||||
|
||||
contentContainer.removeAllViews()
|
||||
for (item in mNotificationHint?.content!!) {
|
||||
for (item in mNotificationHint?.content ?: arrayListOf()) {
|
||||
val tv = TextView(context)
|
||||
|
||||
tv.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT).apply {
|
||||
|
||||
@ -8,12 +8,13 @@ import com.gh.common.util.MtaHelper
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
open class TrackableDialog(context: Context,
|
||||
themeResId: Int,
|
||||
private var mEvent: String,
|
||||
private var mKey: String,
|
||||
private var mCancelValue: String? = null,
|
||||
private var mKeyBackValue: String? = null,
|
||||
private var mLogShowEvent: Boolean = true)
|
||||
themeResId: Int,
|
||||
private var mEvent: String,
|
||||
private var mKey: String,
|
||||
private var mValue: String? = null,
|
||||
private var mCancelValue: String? = null,
|
||||
private var mKeyBackValue: String? = null,
|
||||
private var mLogShowEvent: Boolean = true)
|
||||
: Dialog(context, themeResId) {
|
||||
|
||||
// 区分此 dialog 是点击 dialog 外部取消的还是点击返回取消的
|
||||
@ -25,6 +26,9 @@ open class TrackableDialog(context: Context,
|
||||
setOnCancelListener {
|
||||
if (mIsCanceledByClickOutsideOfDialog.get()) {
|
||||
MtaHelper.onEvent(mEvent, mKey, mCancelValue ?: "点击空白")
|
||||
if (!mValue.isNullOrEmpty()) {
|
||||
MtaHelper.onEvent(mEvent, "点击空白", mValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,6 +36,9 @@ open class TrackableDialog(context: Context,
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) {
|
||||
mIsCanceledByClickOutsideOfDialog.set(false)
|
||||
MtaHelper.onEvent(mEvent, mKey, mKeyBackValue ?: "点击返回")
|
||||
if (mValue != null) {
|
||||
MtaHelper.onEvent(mEvent, "点击返回", mValue)
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
@ -41,6 +48,9 @@ open class TrackableDialog(context: Context,
|
||||
super.show()
|
||||
if (mLogShowEvent) {
|
||||
MtaHelper.onEvent(mEvent, mKey, "出现弹窗")
|
||||
if (!mValue.isNullOrEmpty()) {
|
||||
MtaHelper.onEvent(mEvent, "出现弹窗", mValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -31,9 +31,9 @@ object ExposureManager {
|
||||
private val loghubHelper = LoghubHelper.getInstance()
|
||||
|
||||
// exposureCache 用来过滤掉具有相同 id 的曝光事件,避免重复发送事件
|
||||
private val exposureSet = hashSetOf<ExposureEvent>()
|
||||
private val exposureExecutor = Executors.newSingleThreadExecutor()
|
||||
private val exposureCache = FixedSizeLinkedHashSet<String>(300)
|
||||
private val exposureSet by lazy { hashSetOf<ExposureEvent>() }
|
||||
private val exposureExecutor by lazy { Executors.newSingleThreadExecutor() }
|
||||
private val exposureCache by lazy { FixedSizeLinkedHashSet<String>(300) }
|
||||
private val exposureDao by lazy { ExposureDatabase.buildDatabase(HaloApp.getInstance().application).logHubEventDao() }
|
||||
|
||||
@JvmStatic
|
||||
|
||||
@ -15,7 +15,7 @@ object LoghubUtils {
|
||||
|
||||
private lateinit var mApplication: Application
|
||||
|
||||
private val loghubEventSet = hashSetOf<LoghubEvent>()
|
||||
private val loghubEventSet by lazy { hashSetOf<LoghubEvent>() }
|
||||
private val loghubEventExecutor by lazy { Executors.newSingleThreadExecutor() }
|
||||
private val loghubEventDao by lazy { LoghubDatabase.buildDatabase(mApplication).logHubEventDao() }
|
||||
|
||||
@ -75,8 +75,16 @@ object LoghubUtils {
|
||||
}
|
||||
|
||||
val log = Log()
|
||||
log.PutContent("current time ", event.time)
|
||||
log.PutContent("content", event.content)
|
||||
// 特殊处理,以下logStore不需要用content包裹数据
|
||||
if (event.logStore == "collection" || event.logStore == "common" || event.logStore == "halo-api-device-installed") {
|
||||
val contentJson = JSONObject(event.content)
|
||||
for (key in contentJson.keys()) {
|
||||
log.PutContent(key, contentJson.get(key).toString())
|
||||
}
|
||||
} else {
|
||||
log.PutContent("current time ", event.time)
|
||||
log.PutContent("content", event.content)
|
||||
}
|
||||
logGroupHashMap[event.logStore]?.PutLog(log)
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
package com.gh.common.syncpage
|
||||
|
||||
interface ISyncAdapterHandler {
|
||||
|
||||
/**
|
||||
* @param position position to query
|
||||
* @return Pair first: item sync id
|
||||
* Pair second: item data entity
|
||||
*/
|
||||
fun getSyncData(position: Int): Pair<String, Any>?
|
||||
|
||||
}
|
||||
43
app/src/main/java/com/gh/common/syncpage/SyncDataEntity.kt
Normal file
43
app/src/main/java/com/gh/common/syncpage/SyncDataEntity.kt
Normal file
@ -0,0 +1,43 @@
|
||||
package com.gh.common.syncpage
|
||||
|
||||
import androidx.annotation.Keep
|
||||
|
||||
@Keep
|
||||
data class SyncDataEntity(
|
||||
/**
|
||||
* 标识一条数据的唯一ID
|
||||
*
|
||||
* 与[ISyncAdapterHandler.getSyncData]返回的Pair first一致
|
||||
*/
|
||||
val syncId: String,
|
||||
|
||||
/**
|
||||
* 需要同步的字段名
|
||||
*
|
||||
* 与@SyncPage注解的值一致
|
||||
*/
|
||||
val syncFieldName: String?,
|
||||
|
||||
/**
|
||||
* 需要同步的具体内容
|
||||
*/
|
||||
val syncFieldValue: Any?,
|
||||
|
||||
/**
|
||||
* 同步完一次是否自动删除
|
||||
*/
|
||||
val remove: Boolean = true,
|
||||
|
||||
/**
|
||||
* 是否需要查询同步实体的父级字段
|
||||
*
|
||||
* 由于反射可能会导致较大的性能消耗,默认关闭,具体按实际情况开启
|
||||
*/
|
||||
val checkInherited: Boolean = false,
|
||||
|
||||
/**
|
||||
* 是否需要查询同步实体的嵌套实体内容
|
||||
*
|
||||
* 由于反射可能会导致较大的性能消耗,默认关闭,具体按实际情况开启
|
||||
*/
|
||||
val checkFieldEntity: Boolean = false)
|
||||
@ -0,0 +1,20 @@
|
||||
package com.gh.common.syncpage
|
||||
|
||||
object SyncFieldConstants {
|
||||
|
||||
// 是否点赞
|
||||
const val ANSWER_VOTE = "ANSWER_VOTE"
|
||||
const val ARTICLE_VOTE = "ARTICLE_VOTE"
|
||||
|
||||
// 赞同数量
|
||||
const val ANSWER_VOTE_COUNT = "ANSWER_VOTE_COUNT"
|
||||
const val ARTICLE_VOTE_COUNT = "ARTICLE_VOTE_COUNT"
|
||||
|
||||
// 评论数量
|
||||
const val ANSWER_COMMENT_COUNT = "ANSWER_COMMENT_COUNT"
|
||||
const val ARTICLE_COMMENT_COUNT = "ARTICLE_COMMENT_COUNT"
|
||||
|
||||
// 回答数量
|
||||
const val ANSWER_COUNT = "ANSWER_COUNT"
|
||||
|
||||
}
|
||||
154
app/src/main/java/com/gh/common/syncpage/SyncPageRepository.kt
Normal file
154
app/src/main/java/com/gh/common/syncpage/SyncPageRepository.kt
Normal file
@ -0,0 +1,154 @@
|
||||
package com.gh.common.syncpage
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.common.annotation.SyncIgnore
|
||||
import com.gh.common.annotation.SyncPage
|
||||
import com.gh.common.util.debugOnly
|
||||
import com.gh.common.util.toJson
|
||||
import com.gh.common.util.tryCatchInRelease
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.Utils
|
||||
import java.lang.reflect.Field
|
||||
|
||||
/**
|
||||
* 页面之间实现数据同步(主要是单个字段的同步)
|
||||
*
|
||||
* 实现思路:
|
||||
* 1.把需要同步的数据以一个特殊ID作为唯一标识,并存于一个全局的仓库
|
||||
* 2.利用LiveData进行页面回调,再通过[ISyncAdapterHandler.getSyncData]找到需要被同步的数据
|
||||
* 3.最后利用反射进行数据替换,具体请见[SyncPageRepository.replaceSyncData]
|
||||
*
|
||||
* 具体的接入方式(以列表为例):
|
||||
* 1.通过[SyncPageRepository.postSyncData]提交同步数据
|
||||
* 2.在Fragment重写[BaseFragment.addSyncPageObserver]开启同步事件监听
|
||||
* 3.在Fragment重写[BaseFragment.provideSyncAdapter]提供获取数据的来源
|
||||
* - [BaseFragment.provideSyncAdapter]提供的Adapter必须实现[ISyncAdapterHandler]接口
|
||||
*
|
||||
*/
|
||||
object SyncPageRepository {
|
||||
|
||||
// 只有在新增操作时,才需要进行页面刷新
|
||||
val syncPageLiveData = MutableLiveData<List<SyncDataEntity>>()
|
||||
|
||||
val syncDataList = ArrayList<SyncDataEntity>()
|
||||
|
||||
// size: 防止删除刚刚添加的数据
|
||||
@JvmStatic
|
||||
fun clearSyncData() {
|
||||
tryCatchInRelease {
|
||||
synchronized(syncDataList) {
|
||||
if (syncDataList.size > 0) {
|
||||
debugOnly {
|
||||
Utils.log("SyncPageRepository clearSyncData 存在" + syncDataList.size + "条数据尚未处理,即将删除")
|
||||
}
|
||||
syncDataList.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun removeSyncData(list: List<SyncDataEntity>) {
|
||||
tryCatchInRelease {
|
||||
synchronized(syncDataList) {
|
||||
for (syncDataEntity in list) {
|
||||
val remove = syncDataList.remove(syncDataEntity)
|
||||
debugOnly {
|
||||
Utils.log("SyncPageRepository removeSyncData ->" + syncDataEntity.syncId +
|
||||
", fieldName->" + syncDataEntity.syncFieldName +
|
||||
", remove->" + remove)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debugOnly {
|
||||
Utils.log("SyncPageRepository removeSyncData ->" + syncDataList.size)
|
||||
}
|
||||
}
|
||||
|
||||
// 提交同步数据
|
||||
fun postSyncData(entity: SyncDataEntity) {
|
||||
tryCatchInRelease {
|
||||
synchronized(syncDataList) {
|
||||
// 检查是否存在重复操作
|
||||
for (syncDataEntity in syncDataList) {
|
||||
if (syncDataEntity.syncId == entity.syncId && syncDataEntity.syncFieldName == entity.syncFieldName) {
|
||||
syncDataList.remove(syncDataEntity)
|
||||
|
||||
debugOnly {
|
||||
Utils.log("SyncPageRepository postSyncData 存在重复操作->" + entity.toJson())
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
syncDataList.add(entity)
|
||||
syncPageLiveData.postValue(syncDataList)
|
||||
|
||||
debugOnly {
|
||||
Utils.log("SyncPageRepository postSyncData->" + entity.toJson())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun handleSyncData(rawData: Any, syncData: SyncDataEntity): Boolean {
|
||||
val fields = if (syncData.checkInherited) {
|
||||
getAllFieldsList(rawData::class.java)
|
||||
} else rawData::class.java.declaredFields.toList()
|
||||
|
||||
var isNeedNotify = false
|
||||
for (field in fields) {
|
||||
isNeedNotify = replaceSyncData(field, rawData, syncData)
|
||||
if (isNeedNotify) break
|
||||
}
|
||||
|
||||
if (!isNeedNotify) {
|
||||
debugOnly {
|
||||
Utils.log("SyncPageRepository sync failure-> " + syncData.syncFieldName)
|
||||
}
|
||||
}
|
||||
|
||||
return isNeedNotify
|
||||
}
|
||||
|
||||
private fun replaceSyncData(field: Field, extractObject: Any, syncData: SyncDataEntity): Boolean {
|
||||
field.getAnnotation(SyncPage::class.java)?.syncNames?.forEach { syncName ->
|
||||
if (syncName == syncData.syncFieldName && field.getAnnotation(SyncIgnore::class.java) == null) {
|
||||
field.isAccessible = true
|
||||
debugOnly {
|
||||
Utils.log("SyncPageRepository sync success-> " + syncData.syncFieldName + ", fieldName->" + field.name + ", fieldValue->" + field.get(extractObject))
|
||||
}
|
||||
field.set(extractObject, syncData.syncFieldValue)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if (syncData.checkFieldEntity) {
|
||||
// 递归查询
|
||||
val pkgName = field.type.getPackage()?.name ?: return false
|
||||
if (pkgName.contains(HaloApp.getInstance().application.packageName) && field.getAnnotation(SyncIgnore::class.java) == null) {
|
||||
field.isAccessible = true
|
||||
val extractEntityObject = field.get(extractObject) ?: return false
|
||||
field.type.declaredFields.forEach {
|
||||
if (replaceSyncData(it, extractEntityObject, syncData)) return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 包括父类 Field 对象
|
||||
private fun getAllFieldsList(cls: Class<*>?): List<Field> {
|
||||
if (cls == null) return arrayListOf()
|
||||
val allFields = arrayListOf<Field>()
|
||||
var currentClass = cls
|
||||
while (currentClass != null) {
|
||||
val declaredFields = currentClass.declaredFields
|
||||
allFields.addAll(declaredFields)
|
||||
currentClass = currentClass.superclass
|
||||
}
|
||||
return allFields
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,98 @@
|
||||
package com.gh.common.syncpage.example
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.constant.ItemViewType
|
||||
import com.gh.common.syncpage.ISyncAdapterHandler
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.adapter.viewholder.FooterViewHolder
|
||||
import com.gh.gamecenter.baselist.ListAdapter
|
||||
import com.gh.gamecenter.databinding.CommunityAnswerItemBinding
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
import com.gh.gamecenter.qa.answer.CommunityAnswerItemViewHolder
|
||||
import com.gh.gamecenter.qa.answer.detail.AnswerDetailActivity
|
||||
import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity
|
||||
import com.gh.gamecenter.qa.entity.AnswerEntity
|
||||
import com.gh.gamecenter.qa.entity.Questions
|
||||
import com.gh.gamecenter.qa.questions.detail.QuestionsDetailActivity
|
||||
|
||||
class ExampleAdapter(context: Context) : ListAdapter<AnswerEntity>(context), ISyncAdapterHandler {
|
||||
|
||||
override fun areItemsTheSame(oldItem: AnswerEntity?, newItem: AnswerEntity?): Boolean {
|
||||
return oldItem?.id == newItem?.id
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
if (position == itemCount - 1) return ItemViewType.ITEM_FOOTER
|
||||
return ItemViewType.ITEM_BODY
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val view: View
|
||||
return when (viewType) {
|
||||
ItemViewType.ITEM_FOOTER -> {
|
||||
view = mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false)
|
||||
FooterViewHolder(view)
|
||||
}
|
||||
else -> {
|
||||
view = mLayoutInflater.inflate(R.layout.community_answer_item, parent, false)
|
||||
CommunityAnswerItemViewHolder(CommunityAnswerItemBinding.bind(view))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return if (mEntityList.isNotEmpty()) mEntityList.size + FOOTER_ITEM_COUNT else 0
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (getItemViewType(position)) {
|
||||
ItemViewType.ITEM_BODY -> {
|
||||
val answer = mEntityList[position]
|
||||
if ("community_article" == answer.type) {
|
||||
val questions = Questions()
|
||||
questions.title = answer.articleTitle
|
||||
answer.questions = questions
|
||||
}
|
||||
|
||||
val answerViewHolder = holder as CommunityAnswerItemViewHolder
|
||||
val binding = answerViewHolder.binding
|
||||
answerViewHolder.bindAnswerItem(answer, "", getPath())
|
||||
binding.title.setOnClickListener {
|
||||
if ("community_article" == answer.type) {
|
||||
mContext.startActivity(ArticleDetailActivity.getIntent(mContext, UserManager.getInstance().community, answer.id!!, "", getPath()))
|
||||
} else {
|
||||
val questions = answer.questions
|
||||
mContext.startActivity(QuestionsDetailActivity.getIntent(mContext, questions.id, "", getPath()))
|
||||
}
|
||||
}
|
||||
|
||||
answerViewHolder.itemView.setOnClickListener {
|
||||
if ("community_article" == answer.type) {
|
||||
mContext.startActivity(ArticleDetailActivity.getIntent(mContext, UserManager.getInstance().community, answer.id!!, "", getPath()))
|
||||
} else {
|
||||
mContext.startActivity(AnswerDetailActivity.getIntent(mContext, answer.id, "", getPath()))
|
||||
}
|
||||
}
|
||||
}
|
||||
ItemViewType.ITEM_FOOTER -> {
|
||||
val footerViewHolder = holder as FooterViewHolder
|
||||
footerViewHolder.initItemPadding()
|
||||
footerViewHolder.initFooterViewHolder(mIsLoading, mIsNetworkError, mIsOver, R.string.ask_loadover_hint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSyncData(position: Int): Pair<String, AnswerEntity>? {
|
||||
if (position >= mEntityList.size) return null
|
||||
val entity = mEntityList[position]
|
||||
return Pair(entity.id ?: "", entity)
|
||||
}
|
||||
|
||||
fun getPath(): String {
|
||||
return "问答-推荐-按时间"
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
package com.gh.common.syncpage.example
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.view.VerticalItemDecoration
|
||||
import com.gh.gamecenter.baselist.ListFragment
|
||||
import com.gh.gamecenter.baselist.NormalListViewModel
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
import com.gh.gamecenter.qa.entity.AnswerEntity
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import io.reactivex.Observable
|
||||
|
||||
class ExampleFragment : ListFragment<AnswerEntity, NormalListViewModel<AnswerEntity>>() {
|
||||
|
||||
private var mAdapter: ExampleAdapter? = null
|
||||
|
||||
override fun provideListAdapter(): ExampleAdapter {
|
||||
if (mAdapter == null) {
|
||||
mAdapter = ExampleAdapter(requireContext())
|
||||
}
|
||||
return mAdapter!!
|
||||
}
|
||||
|
||||
override fun getItemDecoration(): RecyclerView.ItemDecoration {
|
||||
return VerticalItemDecoration(context, 8F, false)
|
||||
}
|
||||
|
||||
override fun provideDataObservable(page: Int): Observable<MutableList<AnswerEntity>> {
|
||||
return RetrofitManager.getInstance(context).api.getCommunitiesRecommendNewest(UserManager.getInstance().community.id, page)
|
||||
}
|
||||
|
||||
override fun provideListViewModel(): NormalListViewModel<AnswerEntity> {
|
||||
val factory = NormalListViewModel.Factory(HaloApp.getInstance().application, this)
|
||||
return ViewModelProviders.of(this, factory).get(NormalListViewModel::class.java) as NormalListViewModel<AnswerEntity>
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
// SyncPageRepository.syncPageLiveData.observe(this, Observer {
|
||||
// it ?: return@Observer
|
||||
// val adapter = mListRv.adapter
|
||||
// if (adapter !is ISyncAdapterHandler) return@Observer
|
||||
// for(position in 0 until adapter.itemCount) {
|
||||
// val syncKey = adapter.getSyncData(position)
|
||||
// for (syncDataEntity in it) {
|
||||
// if (syncDataEntity.syncId == syncKey?.first) {
|
||||
// val isSuccess = SyncPageRepository.handleSyncData(syncKey.second, syncDataEntity)
|
||||
// if (isSuccess) adapter.notifyItemChanged(position)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
}
|
||||
|
||||
override fun provideSyncAdapter(): RecyclerView.Adapter<*>? {
|
||||
return mListRv.adapter
|
||||
}
|
||||
|
||||
override fun addSyncPageObserver(): Boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,7 @@ import android.graphics.BitmapFactory
|
||||
import android.graphics.Matrix
|
||||
import com.gh.common.constant.Config
|
||||
import com.gh.gamecenter.entity.SettingsEntity
|
||||
import com.github.piasy.biv.metadata.ImageInfoExtractor
|
||||
import com.halo.assistant.HaloApp
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
@ -26,11 +27,16 @@ object CompressImageUtils {
|
||||
* 压缩图片并保存到目标文件
|
||||
* 该压缩方法是同步执行 请勿在主线程执行
|
||||
* 返回源文件的三种情况:小于特定值,图片类型为GIF,压缩失败
|
||||
* ---------------------------------------------------------------------------------------------
|
||||
* 关于压缩格式问题:
|
||||
* 1.图片详情对动态Webp/静态Webp的判断存在问题,导致部分静态Webp图片误判为动态Webp而直接委托Fresco处理,出现无法缩放问题
|
||||
* 2.为了解决上述问题,如果压缩是检测到是动态Webp时直接压缩为JPEG,这样就能解决图片详情无法缩放问题(todo 这样会导致动态Webp无法播放)
|
||||
*/
|
||||
@Throws(Exception::class)
|
||||
fun compressImageAndSaveToFile(imageFile: File, compressGif: Boolean): File {
|
||||
val imageType = ImageInfoExtractor.getImageType(imageFile)
|
||||
// 小于某一个设定的值时,直接返回原图
|
||||
if (imageFile.length() < getImageSetting().processLimitSize) {
|
||||
if (imageType != ImageInfoExtractor.TYPE_ANIMATED_WEBP && imageFile.length() < getImageSetting().processLimitSize) {
|
||||
return imageFile
|
||||
}
|
||||
|
||||
@ -49,7 +55,7 @@ object CompressImageUtils {
|
||||
} else if (options.outMimeType.contains("gif") && !compressGif) { // gif直接返回原图
|
||||
return imageFile
|
||||
} else {
|
||||
Bitmap.CompressFormat.WEBP
|
||||
Bitmap.CompressFormat.JPEG
|
||||
}
|
||||
|
||||
fileOutputStream = FileOutputStream(cacheDir)
|
||||
@ -165,6 +171,7 @@ object CompressImageUtils {
|
||||
enum class CompressType {
|
||||
// 0: 短边等比压缩至1280 H:长边 W:短边
|
||||
LIMIT_SHORT,
|
||||
|
||||
// 1: 取长边等比压缩至1280 H:短边 W: 长边
|
||||
LIMIT_LONG
|
||||
}
|
||||
@ -9,8 +9,6 @@ import com.gh.gamecenter.manager.DataCollectionManager;
|
||||
import com.gh.gamecenter.manager.PackagesManager;
|
||||
import com.lightgame.download.DownloadEntity;
|
||||
|
||||
import org.json.JSONArray;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ -169,24 +167,4 @@ public class DataCollectionUtils {
|
||||
map.put("type", args[2]);
|
||||
DataCollectionManager.onEvent(context, "concern", map);
|
||||
}
|
||||
|
||||
//上传推荐位数据
|
||||
public static void uploadPosition(Context context, String... args) {
|
||||
if (args.length < 3) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("page", args[0]);
|
||||
map.put("location", args[1]);
|
||||
map.put("name", args[2]);
|
||||
DataCollectionManager.onEvent(context, "position", map);
|
||||
}
|
||||
|
||||
//上传应用列表
|
||||
public static void uploadAppList(Context context, JSONArray applist) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("applist", applist);
|
||||
DataCollectionManager.onEvent(context, "applist", map);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -2,8 +2,7 @@ package com.gh.common.util;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.gh.gamecenter.retrofit.Response;
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager;
|
||||
import com.gh.common.loghub.LoghubUtils;
|
||||
import com.halo.assistant.HaloApp;
|
||||
import com.lightgame.download.DownloadEntity;
|
||||
import com.lightgame.utils.Util_System_Phone_State;
|
||||
@ -14,12 +13,6 @@ import org.json.JSONObject;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.ResponseBody;
|
||||
|
||||
/**
|
||||
* Created by LGT on 2016/12/8.
|
||||
* 日志上传工具类
|
||||
@ -68,12 +61,7 @@ public class DataLogUtils {
|
||||
params.put("time", String.valueOf(Utils.getTime(context)));
|
||||
params.put("content", new JSONObject(map).toString());
|
||||
|
||||
RequestBody body = RequestBody.create(MediaType.parse("application/json"),
|
||||
new JSONObject(params).toString());
|
||||
RetrofitManager.getInstance(context).getData().postLog(body)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Response<ResponseBody>());
|
||||
LoghubUtils.log(new JSONObject(params), "common", true);
|
||||
}
|
||||
|
||||
// 网络错误
|
||||
|
||||
@ -13,6 +13,7 @@ import com.gh.gamecenter.entity.LinkEntity;
|
||||
import com.gh.gamecenter.entity.PluginLocation;
|
||||
import com.gh.gamecenter.manager.PackagesManager;
|
||||
import com.lightgame.download.DownloadEntity;
|
||||
import com.lightgame.utils.Utils;
|
||||
|
||||
/**
|
||||
* Created by khy on 27/06/17.
|
||||
@ -22,6 +23,7 @@ import com.lightgame.download.DownloadEntity;
|
||||
public class DetailDownloadUtils {
|
||||
|
||||
public static void detailInitDownload(DetailViewHolder viewHolder, boolean isCheck) {
|
||||
String downloadAddWord = viewHolder.gameEntity.getDownloadAddWord();
|
||||
|
||||
if (viewHolder.gameEntity != null
|
||||
&& Config.isShowDownload(viewHolder.gameEntity.getId())
|
||||
@ -34,10 +36,11 @@ public class DetailDownloadUtils {
|
||||
|
||||
if (viewHolder.gameEntity.isReservable()) {
|
||||
if (!ReservationRepository.thisGameHasBeenReserved(viewHolder.gameEntity.getId())) {
|
||||
if (TextUtils.isEmpty(viewHolder.downloadAddWord)) {
|
||||
|
||||
if (TextUtils.isEmpty(downloadAddWord)) {
|
||||
viewHolder.mDownloadPb.setText(String.format("预约" + "《%s》", viewHolder.gameEntity.getName()));
|
||||
} else {
|
||||
viewHolder.mDownloadPb.setText(String.format("预约" + "《%s》%s", viewHolder.gameEntity.getName(), viewHolder.downloadAddWord));
|
||||
viewHolder.mDownloadPb.setText(String.format("预约" + "《%s》%s", viewHolder.gameEntity.getName(), downloadAddWord));
|
||||
}
|
||||
viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.RESERVABLE);
|
||||
} else {
|
||||
@ -49,10 +52,10 @@ public class DetailDownloadUtils {
|
||||
|
||||
if (viewHolder.gameEntity.getApk().isEmpty() || viewHolder.gameEntity.getDownloadOffStatus() != null) {
|
||||
LinkEntity h5LinkEntity = viewHolder.gameEntity.getH5Link();
|
||||
|
||||
|
||||
if (h5LinkEntity != null) {
|
||||
if ("play".equals(h5LinkEntity.getType())) {
|
||||
String defaultString = String.format("开始玩" + "《%s》", viewHolder.gameEntity.getName());
|
||||
String defaultString = String.format("开始玩" + "《%s》", viewHolder.gameEntity.getName());
|
||||
viewHolder.mDownloadPb.setText(TextUtils.isEmpty(h5LinkEntity.getText()) ? defaultString : h5LinkEntity.getText());
|
||||
} else {
|
||||
viewHolder.mDownloadPb.setText(TextUtils.isEmpty(h5LinkEntity.getText()) ? "查看" : h5LinkEntity.getText());
|
||||
@ -60,35 +63,39 @@ public class DetailDownloadUtils {
|
||||
viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.H5_GAME);
|
||||
} else {
|
||||
if ("dialog".equals(viewHolder.gameEntity.getDownloadOffStatus())) {
|
||||
viewHolder.mDownloadPb.setText(TextUtils.isEmpty(viewHolder.downloadOffText) ? "查看详情" : viewHolder.downloadOffText);
|
||||
viewHolder.mDownloadPb.setText(TextUtils.isEmpty(viewHolder.gameEntity.getDownloadOffText()) ? "查看详情" : viewHolder.gameEntity.getDownloadOffText());
|
||||
viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.NONE_WITH_HINT);
|
||||
} else {
|
||||
viewHolder.mDownloadPb.setText(TextUtils.isEmpty(viewHolder.downloadOffText) ? "暂无下载" : viewHolder.downloadOffText);
|
||||
viewHolder.mDownloadPb.setText(TextUtils.isEmpty(viewHolder.gameEntity.getDownloadOffText()) ? "暂无下载" : viewHolder.gameEntity.getDownloadOffText());
|
||||
viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.NONE);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else if (viewHolder.gameEntity.getApk().size() == 1) {
|
||||
String status = GameUtils.getDownloadBtnText(viewHolder.context, viewHolder.gameEntity, PluginLocation.only_game);
|
||||
switch (status) {
|
||||
case "插件化":
|
||||
viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.PLUGIN);
|
||||
break;
|
||||
case "打开":
|
||||
case "启动":
|
||||
viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.LAUNCH_OR_OPEN);
|
||||
break;
|
||||
default:
|
||||
viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.NORMAL);
|
||||
break;
|
||||
if (viewHolder.context.getString(R.string.pluggable).equals(status)) {
|
||||
viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.PLUGIN);
|
||||
} else if (viewHolder.context.getString(R.string.launch).equals(status)) {
|
||||
viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.LAUNCH_OR_OPEN);
|
||||
} else {
|
||||
viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.NORMAL);
|
||||
}
|
||||
|
||||
String downloadText;
|
||||
if (viewHolder.isNewsDetail) {
|
||||
viewHolder.mDownloadPb.setText(status);
|
||||
} else if (TextUtils.isEmpty(viewHolder.downloadAddWord)) {
|
||||
viewHolder.mDownloadPb.setText(String.format(status + "《%s》", viewHolder.gameEntity.getName()));
|
||||
downloadText = status;
|
||||
} else if (viewHolder.context.getString(R.string.pluggable).equals(status)) {
|
||||
downloadText = "升级" + (TextUtils.isEmpty(downloadAddWord) ? "" : "至" + downloadAddWord) + getDownloadSizeText(viewHolder);
|
||||
} else if (viewHolder.context.getString(R.string.launch).equals(status)) {
|
||||
downloadText = status + (TextUtils.isEmpty(downloadAddWord) ? "" : "-" + downloadAddWord);
|
||||
} else if (viewHolder.context.getString(R.string.attempt).equals(status)) {
|
||||
downloadText = status + getDownloadSizeText(viewHolder);
|
||||
} else {
|
||||
viewHolder.mDownloadPb.setText(String.format(status + "《%s》%s", viewHolder.gameEntity.getName(), viewHolder.downloadAddWord));
|
||||
downloadText = status + (TextUtils.isEmpty(downloadAddWord) ? "" : downloadAddWord) + getDownloadSizeText(viewHolder);
|
||||
}
|
||||
viewHolder.mDownloadPb.setText(downloadText);
|
||||
} else {
|
||||
viewHolder.mDownloadPb.setText("选择下载你的版本" + (TextUtils.isEmpty(downloadAddWord) ? "" : "-" + downloadAddWord) + " >");
|
||||
viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.NORMAL);
|
||||
}
|
||||
if (isCheck && viewHolder.gameEntity.getApk().size() == 1) {
|
||||
String url = viewHolder.gameEntity.getApk().get(0).getUrl();
|
||||
@ -100,6 +107,10 @@ public class DetailDownloadUtils {
|
||||
}
|
||||
}
|
||||
|
||||
private static String getDownloadSizeText(DetailViewHolder viewHolder) {
|
||||
return String.format("(%s)", viewHolder.gameEntity.getApk().get(0).getSize());
|
||||
}
|
||||
|
||||
public static void detailInvalidate(DetailViewHolder viewHolder) {
|
||||
viewHolder.mDownloadPb.setProgress((int) (viewHolder.downloadEntity.getPercent() * 10));
|
||||
DownloadEntity downloadEntity = viewHolder.downloadEntity;
|
||||
@ -128,7 +139,7 @@ public class DetailDownloadUtils {
|
||||
case done:
|
||||
viewHolder.mDownloadPb.setText(R.string.install);
|
||||
if (downloadEntity.isPluggable()
|
||||
&& PackagesManager.INSTANCE.isInstalled(downloadEntity.getPackageName())) {
|
||||
&& PackagesManager.isInstalled(downloadEntity.getPackageName())) {
|
||||
viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.INSTALL_PLUGIN);
|
||||
} else {
|
||||
viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.INSTALL_NORMAL);
|
||||
@ -143,6 +154,4 @@ public class DetailDownloadUtils {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.os.Build;
|
||||
import android.os.CountDownTimer;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.Html;
|
||||
@ -29,23 +30,24 @@ import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.facebook.drawee.generic.GenericDraweeHierarchy;
|
||||
import com.gh.common.AppExecutor;
|
||||
import com.gh.common.constant.Config;
|
||||
import com.gh.common.dialog.TrackableDialog;
|
||||
import com.gh.common.view.DrawableView;
|
||||
import com.gh.common.view.FixLinearLayoutManager;
|
||||
import com.gh.common.view.LimitHeightLinearLayout;
|
||||
import com.gh.common.view.MaxHeightNestedScrollView;
|
||||
import com.gh.gamecenter.AboutActivity;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.WebActivity;
|
||||
import com.gh.gamecenter.adapter.viewholder.PrivacyPolicyItemViewHolder;
|
||||
import com.gh.gamecenter.databinding.ImprintContentItemBinding;
|
||||
import com.gh.gamecenter.databinding.PrivacyItemBinding;
|
||||
import com.gh.gamecenter.entity.ApkEntity;
|
||||
import com.gh.gamecenter.entity.GameEntity;
|
||||
import com.gh.gamecenter.entity.PrivacyPolicyEntity;
|
||||
import com.gh.gamecenter.entity.SettingsEntity;
|
||||
import com.gh.gamecenter.entity.TrackableEntity;
|
||||
import com.halo.assistant.HaloApp;
|
||||
import com.halo.assistant.fragment.SettingsFragment;
|
||||
@ -53,9 +55,17 @@ import com.lightgame.adapter.BaseRecyclerAdapter;
|
||||
import com.lightgame.utils.AppManager;
|
||||
import com.lightgame.utils.Utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public class DialogUtils {
|
||||
|
||||
public static Dialog showWaitDialog(Context context, String msg) {
|
||||
@ -1097,6 +1107,7 @@ public class DialogUtils {
|
||||
R.style.GhAlertDialog,
|
||||
trackableEntity.getEvent(),
|
||||
trackableEntity.getKey(),
|
||||
trackableEntity.getValue(),
|
||||
trackableEntity.getCancelValue(),
|
||||
trackableEntity.getKeyBackValue(),
|
||||
trackableEntity.getLogShowEvent());
|
||||
@ -1224,6 +1235,114 @@ public class DialogUtils {
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
public static void showVersionNumberDialog(Context context, GameEntity gameEntity, @NonNull ConfirmListener listener) {
|
||||
context = checkDialogContext(context);
|
||||
|
||||
if (!gameEntity.isShowVersionNumber()) {
|
||||
listener.onConfirm();
|
||||
} else {
|
||||
final Dialog dialog = new Dialog(context, R.style.GhAlertDialog);
|
||||
|
||||
View contentView = LayoutInflater.from(context).inflate(R.layout.dialog_version_number, null);
|
||||
|
||||
TextView contentTv = contentView.findViewById(R.id.contentTv);
|
||||
TextView cancelTv = contentView.findViewById(R.id.cancelTv);
|
||||
TextView continueTv = contentView.findViewById(R.id.continueTv);
|
||||
|
||||
contentTv.setText(gameEntity.getVersionNumberString());
|
||||
cancelTv.setOnClickListener(v -> dialog.dismiss());
|
||||
continueTv.setOnClickListener(v -> {
|
||||
listener.onConfirm();
|
||||
dialog.dismiss();
|
||||
});
|
||||
|
||||
Window window = dialog.getWindow();
|
||||
if (window != null) {
|
||||
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||
}
|
||||
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
dialog.setContentView(contentView);
|
||||
dialog.show();
|
||||
}
|
||||
}
|
||||
|
||||
public static void showImprintDialog(Context context, GameEntity gameEntity, String titleName) {
|
||||
context = checkDialogContext(context);
|
||||
Dialog dialog = new Dialog(context, R.style.full_dialog);
|
||||
View inflate = LayoutInflater.from(context).inflate(R.layout.imprint_dialog, null);
|
||||
dialog.setContentView(inflate);
|
||||
dialog.show();
|
||||
|
||||
Window window = dialog.getWindow();
|
||||
WindowManager.LayoutParams params;
|
||||
if (window != null) {
|
||||
params = window.getAttributes();
|
||||
params.width = (int) (context.getResources().getDisplayMetrics().widthPixels * 0.9);
|
||||
window.setAttributes(params);
|
||||
window.setBackgroundDrawableResource(R.drawable.full_dialog_background);
|
||||
}
|
||||
|
||||
inflate.findViewById(R.id.imprint_close).setOnClickListener(v -> dialog.dismiss());
|
||||
LinearLayout content = inflate.findViewById(R.id.imprint_content);
|
||||
((TextView) inflate.findViewById(R.id.imprint_title)).setText(titleName);
|
||||
View head = LayoutInflater.from(context).inflate(R.layout.imprint_content_item, null);
|
||||
content.addView(head, LinearLayout.LayoutParams.MATCH_PARENT, DisplayUtils.dip2px(30));
|
||||
LimitHeightLinearLayout imprintContainer = inflate.findViewById(R.id.imprint_container);
|
||||
imprintContainer.setLimitHeight((int) (context.getResources().getDisplayMetrics().heightPixels * 0.8));
|
||||
|
||||
ArrayList<ApkEntity> list = gameEntity.getApk();
|
||||
SettingsEntity settings = Config.getSettings();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
ApkEntity apk = gameEntity.getApk().get(i);
|
||||
if (settings != null && settings.getGameDownloadBlackList().contains(apk.getPackageName())) {
|
||||
continue;
|
||||
}
|
||||
View item = LayoutInflater.from(context).inflate(R.layout.imprint_content_item, null);
|
||||
ImprintContentItemBinding bind = DataBindingUtil.bind(item);
|
||||
bind.setApk(apk);
|
||||
bind.setPlatformName(PlatformUtils.getInstance(context).getPlatformName(apk.getPlatform()));
|
||||
content.addView(item, LinearLayout.LayoutParams.MATCH_PARENT, DisplayUtils.dip2px(40));
|
||||
}
|
||||
|
||||
|
||||
// close line
|
||||
View view = new View(context);
|
||||
view.setBackgroundColor(context.getResources().getColor(R.color.text_5d5d5d));
|
||||
view.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, DisplayUtils.dip2px(1)));
|
||||
content.addView(view);
|
||||
}
|
||||
|
||||
public static void showKaifuRemindDialog(Context context, String content, String gameName) {
|
||||
context = checkDialogContext(context);
|
||||
|
||||
final Dialog dialog = new TrackableDialog(context, R.style.GhAlertDialog, "开服说明弹窗", "弹窗", gameName, null, null, true);
|
||||
|
||||
View contentView = LayoutInflater.from(context).inflate(R.layout.dialog_kaifu_remind, null);
|
||||
|
||||
TextView contentTv = contentView.findViewById(R.id.contentTv);
|
||||
MaxHeightNestedScrollView scrollView = contentView.findViewById(R.id.scrollView);
|
||||
contentTv.setText(Html.fromHtml(content));
|
||||
TextView ok = contentView.findViewById(R.id.dialog_ok);
|
||||
scrollView.setScrollChangedListener((l, t, oldl, oldt) -> {
|
||||
MtaHelper.onEvent("开服说明弹窗", "滑动内容", gameName);
|
||||
});
|
||||
ok.setOnClickListener(v -> {
|
||||
MtaHelper.onEvent("开服说明弹窗", "弹窗", "点击我知道了");
|
||||
MtaHelper.onEvent("开服说明弹窗", "点击我知道了", gameName);
|
||||
dialog.dismiss();
|
||||
});
|
||||
|
||||
Window window = dialog.getWindow();
|
||||
if (window != null) {
|
||||
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||
}
|
||||
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
dialog.setContentView(contentView);
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context may be is application context
|
||||
* @return activity context
|
||||
@ -1237,6 +1356,7 @@ public class DialogUtils {
|
||||
return context;
|
||||
}
|
||||
|
||||
// currentActivity 是否存在 isDestroyed 的情况?
|
||||
return AppManager.getInstance().currentActivity();
|
||||
}
|
||||
|
||||
|
||||
@ -188,7 +188,7 @@ object DirectUtils {
|
||||
|
||||
"column_collection", "专题合集" -> directToColumnCollection(context, linkEntity.link!!, -1, entrance)
|
||||
|
||||
"server" -> {
|
||||
"server", "game_server" -> {
|
||||
context.startActivity(GameServersActivity.getIntent(context, entrance, path))
|
||||
}
|
||||
|
||||
@ -204,6 +204,20 @@ object DirectUtils {
|
||||
|
||||
"game_video" -> directToGameVideo(context, linkEntity.link ?: "", entrance, path)
|
||||
|
||||
"libao" -> directToGiftDetail(context, linkEntity.link ?: "", entrance)
|
||||
|
||||
"feedback" -> directToFeedback(context, linkEntity.name, linkEntity.text, entrance)
|
||||
|
||||
"qa" -> directToQa(context, linkEntity.text ?: "", linkEntity.link ?: "")
|
||||
|
||||
"qa_collection" -> directToQaCollection(context, linkEntity.text ?: "", linkEntity.link
|
||||
?: "")
|
||||
|
||||
"anliwall" -> directToAmway(context, fixedTopAmwayCommentId = null, entrance = entrance, path = path)
|
||||
|
||||
"" -> {
|
||||
// do nothing
|
||||
}
|
||||
else -> {
|
||||
if (unknownCallback != null) {
|
||||
unknownCallback.invoke()
|
||||
@ -214,11 +228,25 @@ object DirectUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转至QA
|
||||
*/
|
||||
fun directToQa(context: Context, text: String, id: String) {
|
||||
context.startActivity(QaActivity.getIntent(context, navigationTitle = text, qaId = id))
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转至QA合集
|
||||
*/
|
||||
fun directToQaCollection(context: Context, text: String, id: String) {
|
||||
context.startActivity(QaActivity.getIntent(context, navigationTitle = text, qaCollectionId = id))
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转至专题合集
|
||||
*/
|
||||
fun directToColumnCollection(context: Context, id: String, position: Int = -1, entrance: String) {
|
||||
context.startActivity(ColumnCollectionDetailActivity.getIntent(context, id, position, entrance))
|
||||
fun directToColumnCollection(context: Context, id: String, position: Int = -1, entrance: String, columnName: String = "") {
|
||||
context.startActivity(ColumnCollectionDetailActivity.getIntent(context, id, position, entrance, columnName))
|
||||
}
|
||||
|
||||
/**
|
||||
@ -315,12 +343,21 @@ object DirectUtils {
|
||||
// 反馈
|
||||
@JvmStatic
|
||||
fun directToFeedback(context: Context, content: String? = null, entrance: String? = null) {
|
||||
directToFeedback(context, content, null, entrance)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun directToFeedback(context: Context, content: String? = null, hintType: String? = null, entrance: String? = null) {
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
|
||||
bundle.putString(KEY_TO, SuggestionActivity::class.java.simpleName)
|
||||
bundle.putString(KEY_CONTENT, content)
|
||||
bundle.putString(KEY_SUGGEST_HINT_TYPE, KEY_PLUGIN)
|
||||
bundle.putSerializable(EntranceUtils.KEY_SUGGESTTYPE, SuggestType.gameQuestion)
|
||||
if (TextUtils.isEmpty(hintType)) {
|
||||
bundle.putString(KEY_SUGGEST_HINT_TYPE, KEY_PLUGIN)
|
||||
} else {
|
||||
bundle.putString(KEY_SUGGEST_HINT_TYPE, hintType)
|
||||
}
|
||||
bundle.putSerializable(KEY_SUGGESTTYPE, SuggestType.gameQuestion)
|
||||
jumpActivity(context, bundle)
|
||||
}
|
||||
|
||||
@ -449,6 +486,7 @@ object DirectUtils {
|
||||
return true
|
||||
} catch (e: Exception) {
|
||||
// 未安装手Q或安装的版本不支持
|
||||
Utils.toast(context, "请安装QQ客户端")
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -528,7 +566,7 @@ object DirectUtils {
|
||||
* @param fromLocation 可见 [VideoDetailContainerViewModel.Location]
|
||||
*/
|
||||
@JvmStatic
|
||||
fun directToVideoDetail(context: Context, videoId: String, fromLocation: String, showComment: Boolean = false, gameId: String = "", entrance: String? = null, path: String? = "", referer: String = "") {
|
||||
fun directToVideoDetail(context: Context, videoId: String, fromLocation: String, showComment: Boolean = false, gameId: String = "", entrance: String? = null, path: String? = "", referer: String = "", type: String = "", act: String = "") {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
|
||||
@ -539,12 +577,19 @@ object DirectUtils {
|
||||
bundle.putString(KEY_LOCATION, fromLocation)
|
||||
bundle.putBoolean(KEY_SHOW_COMMENT, showComment)
|
||||
bundle.putString(KEY_REFERER, referer)
|
||||
bundle.putString(KEY_TYPE, type)
|
||||
bundle.putString(KEY_ACTIVITY_NAME, act)
|
||||
jumpActivity(context, bundle)
|
||||
} else {
|
||||
DialogUtils.showLowSystemVersionDialog(context)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun directToVideoDetail(context: Context, videoId: String, fromLocation: String, showComment: Boolean = false, gameId: String = "", entrance: String? = null, path: String? = "", referer: String = "") {
|
||||
directToVideoDetail(context, videoId, fromLocation, showComment, gameId, entrance, path, referer, "", "")
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转至安利墙
|
||||
* @param fixedTopAmwayCommentId 需要置顶的安利Id
|
||||
|
||||
@ -8,13 +8,21 @@ import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.collection.ArrayMap;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.gh.common.constant.Config;
|
||||
import com.gh.common.dialog.CertificationDialog;
|
||||
import com.gh.common.dialog.DeviceRemindDialog;
|
||||
import com.gh.common.dialog.ReserveDialogFragment;
|
||||
import com.gh.common.exposure.ExposureEvent;
|
||||
import com.gh.common.exposure.ExposureUtils;
|
||||
import com.gh.common.repository.ReservationRepository;
|
||||
import com.gh.common.view.DownloadDialog;
|
||||
import com.gh.download.DownloadManager;
|
||||
import com.gh.download.dialog.DownloadDialog;
|
||||
import com.gh.gamecenter.DownloadManagerActivity;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.WebActivity;
|
||||
@ -32,12 +40,9 @@ import com.lightgame.utils.Utils;
|
||||
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.collection.ArrayMap;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
/**
|
||||
* todo 下载判断不能以按钮文案为判断条件,否则按钮文案修改时又要修改判断逻辑
|
||||
*/
|
||||
public class DownloadItemUtils {
|
||||
|
||||
// 更新下载进度条
|
||||
@ -125,20 +130,20 @@ public class DownloadItemUtils {
|
||||
holder.gameDownloadBtn.setTextColor(Color.WHITE);
|
||||
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_pause_dn);
|
||||
}
|
||||
if (gameEntity.isLibaoExists()) {
|
||||
holder.gameLibaoIcon.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.gameLibaoIcon.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
public static void updateItem(Context context, GameEntity gameEntity, GameViewHolder holder,
|
||||
boolean isShowPlatform) {
|
||||
updateItem(context, gameEntity, holder, isShowPlatform, PluginLocation.only_game);
|
||||
updateItem(context, gameEntity, holder, isShowPlatform, PluginLocation.only_game, false);
|
||||
}
|
||||
|
||||
public static void updateItem(Context context, GameEntity gameEntity, GameViewHolder holder,
|
||||
boolean isShowPlatform, PluginLocation pluginLocation) {
|
||||
boolean isShowPlatform, boolean hideDownloadBtnIfNoAvailableContent) {
|
||||
updateItem(context, gameEntity, holder, isShowPlatform, PluginLocation.only_game, hideDownloadBtnIfNoAvailableContent);
|
||||
}
|
||||
|
||||
public static void updateItem(Context context, GameEntity gameEntity, GameViewHolder holder,
|
||||
boolean isShowPlatform, PluginLocation pluginLocation, boolean hideDownloadBtnIfNoAvailableContent) {
|
||||
|
||||
// 控制是否显示下载按钮
|
||||
if (!Config.isShowDownload(gameEntity.getId()) || context.getString(R.string.app_name).equals(gameEntity.getName())) {
|
||||
@ -147,12 +152,6 @@ public class DownloadItemUtils {
|
||||
holder.gameDownloadBtn.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
if (gameEntity.isLibaoExists()) {
|
||||
holder.gameLibaoIcon.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.gameLibaoIcon.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// 显示预约
|
||||
if (gameEntity.isReservable()) {
|
||||
holder.gameDes.setVisibility(View.VISIBLE);
|
||||
@ -171,14 +170,14 @@ public class DownloadItemUtils {
|
||||
}
|
||||
|
||||
if (gameEntity.getApk().isEmpty()
|
||||
|| gameEntity.getDownloadOffStatus() != null) {
|
||||
|| gameEntity.getDownloadOffStatus() != null) {
|
||||
LinkEntity h5LinkEntity = gameEntity.getH5Link();
|
||||
String offStatus = gameEntity.getDownloadOffStatus();
|
||||
|
||||
|
||||
holder.gameDes.setVisibility(View.VISIBLE);
|
||||
holder.gameProgressbar.setVisibility(View.GONE);
|
||||
holder.gameInfo.setVisibility(View.GONE);
|
||||
|
||||
|
||||
if (h5LinkEntity != null) {
|
||||
if ("play".equals(h5LinkEntity.getType())) {
|
||||
holder.gameDownloadBtn.setText("开始玩");
|
||||
@ -195,6 +194,9 @@ public class DownloadItemUtils {
|
||||
holder.gameDownloadBtn.setText("暂无");
|
||||
holder.gameDownloadBtn.setTextColor(ContextCompat.getColor(context, R.color.button_gray));
|
||||
holder.gameDownloadBtn.setBackgroundResource(R.drawable.news_detail_comment);
|
||||
if (hideDownloadBtnIfNoAvailableContent) {
|
||||
holder.gameDownloadBtn.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
holder.gameDownloadBtn.setClickable(false);
|
||||
}
|
||||
@ -334,10 +336,10 @@ public class DownloadItemUtils {
|
||||
holder.gameDownloadBtn.setText(R.string.install);
|
||||
holder.gameDownloadBtn.setTextColor(Color.WHITE);
|
||||
if (downloadEntity.isPluggable()
|
||||
&& PackagesManager.INSTANCE.isInstalled(downloadEntity.getPackageName())) {
|
||||
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_plugin_style);
|
||||
&& PackagesManager.isInstalled(downloadEntity.getPackageName())) {
|
||||
holder.gameDownloadBtn.setBackgroundResource(R.drawable.download_button_pluggable_style);
|
||||
} else {
|
||||
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_download_style);
|
||||
holder.gameDownloadBtn.setBackgroundResource(R.drawable.download_button_normal_style);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -420,7 +422,7 @@ public class DownloadItemUtils {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (gameEntity.getApk().size() == 0 && gameEntity.getH5Link() != null) {
|
||||
downloadBtn.setOnClickListener(v -> {
|
||||
MtaHelper.onEvent("H5页面", "入口", "列表页_" + gameEntity.getName());
|
||||
@ -449,7 +451,15 @@ public class DownloadItemUtils {
|
||||
clickCallback.onCallback();
|
||||
}
|
||||
PermissionHelper.checkStoragePermissionBeforeAction(context, () -> {
|
||||
DownloadDialog.getInstance(context).showPopupWindow(v, gameEntity, entrance, location, traceEvent);
|
||||
CertificationDialog.showCertificationDialog(context, gameEntity, () -> {
|
||||
DialogUtils.showVersionNumberDialog(context, gameEntity, () -> {
|
||||
DownloadDialog.showDownloadDialog(
|
||||
v.getContext(),
|
||||
gameEntity,
|
||||
entrance,
|
||||
location);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -480,19 +490,38 @@ public class DownloadItemUtils {
|
||||
if (str.equals(context.getString(R.string.download))) {
|
||||
// 先弹下载弹窗(如果需要的话)
|
||||
DownloadDialogHelper.findAvailableDialogAndShow(context, gameEntity, apk, () -> {
|
||||
DialogUtils.checkDownload(context, apk.getSize(),
|
||||
isSubscribe -> download(context, gameEntity, downloadBtn, entrance, location, isSubscribe, traceEvent));
|
||||
CertificationDialog.showCertificationDialog(context, gameEntity, () -> {
|
||||
DialogUtils.checkDownload(context, apk.getSize(),
|
||||
isSubscribe -> download(context, gameEntity, downloadBtn, entrance, location, isSubscribe, traceEvent));
|
||||
});
|
||||
});
|
||||
|
||||
DataLogUtils.uploadGameLog(context, gameEntity.getId(), gameEntity.getName(), entrance);
|
||||
} else if (str.equals(context.getString(R.string.pluggable))) {
|
||||
} else if (str.equals(context.getString(R.string.attempt))) {
|
||||
DownloadDialogHelper.findAvailableDialogAndShow(context, gameEntity, apk, () -> {
|
||||
CertificationDialog.showCertificationDialog(context, gameEntity, () -> {
|
||||
DialogUtils.showVersionNumberDialog(context, gameEntity, () -> {
|
||||
DialogUtils.checkDownload(context, apk.getSize(),
|
||||
isSubscribe -> download(context, gameEntity, downloadBtn, entrance, location, isSubscribe, traceEvent));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
DataLogUtils.uploadGameLog(context, gameEntity.getId(), gameEntity.getName(), entrance);
|
||||
} else if (str.contains("化")) {
|
||||
if (entrance.contains("我的游戏")) {
|
||||
MtaHelper.onEvent("我的游戏_启动", "插件化", gameEntity.getName());
|
||||
}
|
||||
DownloadDialogHelper.findAvailableDialogAndShow(context, gameEntity, apk, () -> {
|
||||
DialogUtils.checkDownload(context, apk.getSize(),
|
||||
isSubscribe -> plugin(context, gameEntity, downloadBtn, entrance, location, isSubscribe, traceEvent));
|
||||
});
|
||||
if (gameEntity.getPluggableCollection() != null) {
|
||||
DownloadDialog.showDownloadDialog(context, gameEntity, entrance, location);
|
||||
} else {
|
||||
DownloadDialogHelper.findAvailableDialogAndShow(context, gameEntity, apk, () -> {
|
||||
CertificationDialog.showCertificationDialog(context, gameEntity, () -> {
|
||||
DialogUtils.checkDownload(context, apk.getSize(),
|
||||
isSubscribe -> plugin(context, gameEntity, downloadBtn, entrance, location, isSubscribe, traceEvent));
|
||||
});
|
||||
});
|
||||
}
|
||||
} else if (str.equals(context.getString(R.string.install))) {
|
||||
install(context, gameEntity, position, adapter);
|
||||
} else if (str.equals(context.getString(R.string.launch))) {
|
||||
@ -536,6 +565,7 @@ public class DownloadItemUtils {
|
||||
downloadBtn.setText(R.string.downloading);
|
||||
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_downloading_style);
|
||||
downloadBtn.setTextColor(ContextCompat.getColorStateList(context, R.color.text_downloading_style));
|
||||
DeviceRemindDialog.Companion.showDeviceRemindDialog(context, gameEntity);
|
||||
} else {
|
||||
Utils.toast(context, msg);
|
||||
}
|
||||
@ -575,6 +605,8 @@ public class DownloadItemUtils {
|
||||
gameEntity.getEntryMap().remove(apkEntity.getPlatform());
|
||||
}
|
||||
adapter.notifyItemChanged(position);
|
||||
} else if (PackageUtils.isCanPluggable(apkEntity)) {
|
||||
DialogUtils.showPluginDialog(context, () -> context.startActivity(PackageUtils.getUninstallIntent(context, path)));
|
||||
} else {
|
||||
PackageUtils.launchSetup(context, downloadEntity);
|
||||
}
|
||||
@ -584,12 +616,12 @@ public class DownloadItemUtils {
|
||||
//更新
|
||||
private static void update(Context context, GameEntity gameEntity, String entrance, String location, boolean isSubscribe, @Nullable ExposureEvent traceEvent) {
|
||||
ApkEntity apkEntity = gameEntity.getApk().get(0);
|
||||
|
||||
|
||||
ExposureUtils.DownloadType downloadType = ExposureUtils.getUpdateType(apkEntity);
|
||||
DataUtils.onGameUpdateEvent(context, gameEntity.getName(), apkEntity.getPlatform(), "下载开始");
|
||||
|
||||
|
||||
ExposureEvent downloadExposureEvent = ExposureUtils.logADownloadExposureEvent(gameEntity, apkEntity
|
||||
.getPlatform(), traceEvent, downloadType);
|
||||
.getPlatform(), traceEvent, downloadType);
|
||||
DownloadManager.createDownload(context, gameEntity, "更新", entrance, location, isSubscribe, downloadExposureEvent);
|
||||
}
|
||||
|
||||
|
||||
@ -32,16 +32,18 @@ import okhttp3.MediaType
|
||||
import okhttp3.RequestBody
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.json.JSONObject
|
||||
import java.util.HashMap
|
||||
import java.util.*
|
||||
|
||||
object DownloadObserver {
|
||||
|
||||
|
||||
private val mApplication = HaloApp.getInstance().application
|
||||
|
||||
@JvmStatic
|
||||
fun initObserver() {
|
||||
val dataWatcher = object : DataWatcher() {
|
||||
override fun onDataChanged(downloadEntity: DownloadEntity) {
|
||||
val gameId = downloadEntity.getRealGameId(Constants.GAME_ID_DIVIDER)
|
||||
|
||||
if (downloadEntity.status != DownloadStatus.downloading) {
|
||||
LogUtils.uploadDownloadEvent(downloadEntity)
|
||||
}
|
||||
@ -51,7 +53,7 @@ object DownloadObserver {
|
||||
processHijack(downloadEntity)
|
||||
val nameAndPlatform = (downloadEntity.name + ":"
|
||||
+ PlatformUtils.getInstance(mApplication).getPlatformName(downloadEntity.platform))
|
||||
MtaHelper.onEvent( "下载劫持",
|
||||
MtaHelper.onEvent("下载劫持",
|
||||
"游戏名字", nameAndPlatform,
|
||||
"网络状态", DeviceUtils.getNetwork(mApplication))
|
||||
return
|
||||
@ -70,7 +72,7 @@ object DownloadObserver {
|
||||
SuggestionActivity.startSuggestionActivity(AppManager.getInstance().currentActivity(),
|
||||
SuggestType.gameQuestion, "notfound",
|
||||
StringUtils.buildString(downloadEntity.name, ",问题反馈:下载链接失效"),
|
||||
SimpleGameEntity(downloadEntity.gameId, downloadEntity.name, ""))
|
||||
SimpleGameEntity(gameId, downloadEntity.name, ""))
|
||||
}, null)
|
||||
return
|
||||
} else if (DownloadStatus.neterror == downloadEntity.status || DownloadStatus.timeout == downloadEntity.status) {
|
||||
@ -85,7 +87,10 @@ object DownloadObserver {
|
||||
if (DownloadStatus.done == downloadEntity.status) {
|
||||
if (downloadEntity.name.contains(mApplication.getString(R.string.app_name))) {
|
||||
MtaHelper.onEvent("软件更新", "下载完成")
|
||||
mApplication.startActivity(PackageUtils.getInstallIntent(mApplication, downloadEntity.path, true))
|
||||
tryWithDefaultCatch {
|
||||
// 会有 ActivityNotFoundException 异常,catch 掉不管了
|
||||
mApplication.startActivity(PackageUtils.getInstallIntent(mApplication, downloadEntity.path, true))
|
||||
}
|
||||
DataLogUtils.uploadUpgradeLog(mApplication, "install") //上传更新安装数据
|
||||
} else {
|
||||
statDoneEvent(downloadEntity)
|
||||
@ -111,7 +116,9 @@ object DownloadObserver {
|
||||
} else {
|
||||
if (PackageUtils.isCanLaunchSetup(mApplication, downloadEntity.path)) {
|
||||
downloadEntity.meta[Constants.MARK_ALREADY_TRIGGERED_INSTALLATION] = "YES"
|
||||
mApplication.startActivity(PackageUtils.getInstallIntent(mApplication, downloadEntity.path))
|
||||
tryWithDefaultCatch {
|
||||
mApplication.startActivity(PackageUtils.getInstallIntent(mApplication, downloadEntity.path))
|
||||
}
|
||||
} else {
|
||||
// 弹出卸载提示框
|
||||
EventBus.getDefault().post(EBShowDialog(BaseActivity.PLUGGABLE, downloadEntity.path))
|
||||
@ -121,7 +128,7 @@ object DownloadObserver {
|
||||
}
|
||||
|
||||
// 统计下载完成
|
||||
uploadData(downloadEntity.gameId, downloadEntity.platform)
|
||||
uploadData(gameId, downloadEntity.platform)
|
||||
}
|
||||
|
||||
// 下载过程分析统计
|
||||
@ -137,7 +144,7 @@ object DownloadObserver {
|
||||
}
|
||||
|
||||
if (downloadEntity.status == DownloadStatus.done) {
|
||||
EventBus.getDefault().post(EBDownloadStatus("done", "", "", "", "", ""))
|
||||
EventBus.getDefault().post(EBDownloadStatus("done", "", "", "", downloadEntity.packageName, ""))
|
||||
}
|
||||
|
||||
DownloadNotificationHelper.addOrUpdateDownloadNotification(downloadEntity)
|
||||
@ -174,7 +181,7 @@ object DownloadObserver {
|
||||
val kv2 = HashMap<String, Any>()
|
||||
kv2["版本"] = downloadEntity.platform
|
||||
kv2["状态"] = "下载完成"
|
||||
kv2["位置"] = downloadEntity.entrance
|
||||
kv2["位置"] = downloadEntity.entrance ?: "null"
|
||||
kv2["游戏分平台"] = downloadEntity.name + "-" + platform
|
||||
kv2["光环助手版本"] = BuildConfig.VERSION_NAME
|
||||
DataUtils.onEvent(mApplication, "游戏下载位置", downloadEntity.name, kv2)
|
||||
@ -183,7 +190,7 @@ object DownloadObserver {
|
||||
val kv3 = HashMap<String, Any>()
|
||||
kv3["下载"] = "下载完成"
|
||||
kv3["版本"] = downloadEntity.platform
|
||||
kv3["位置"] = downloadEntity.entrance
|
||||
kv3["位置"] = downloadEntity.entrance ?: "null"
|
||||
type = ExposureUtils.DownloadType.PLUGIN_DOWNLOAD
|
||||
DataUtils.onEvent(mApplication, "插件化", downloadEntity.name, kv3)
|
||||
|
||||
@ -196,7 +203,7 @@ object DownloadObserver {
|
||||
}
|
||||
|
||||
ExposureUtils.logADownloadCompleteExposureEvent(
|
||||
GameEntity(downloadEntity.gameId, downloadEntity.name),
|
||||
GameEntity(downloadEntity.getRealGameId(Constants.GAME_ID_DIVIDER), downloadEntity.name),
|
||||
downloadEntity.platform,
|
||||
downloadEntity.exposureTrace,
|
||||
type)
|
||||
|
||||
@ -86,6 +86,7 @@ public class EntranceUtils {
|
||||
public static final String KEY_OLDERUSER = "isOldUser";
|
||||
public static final String KEY_SEARCHKEY = "searchKey";
|
||||
public static final String KEY_HINT = "hint";
|
||||
public static final String KEY_GAME = "game";
|
||||
public static final String KEY_GAME_ICON_URL = "gameIconUrl";
|
||||
public static final String KEY_SHARECONTENT = "shareContent";
|
||||
public static final String KEY_SUGGESTTYPE = "suggestType";
|
||||
@ -138,6 +139,7 @@ public class EntranceUtils {
|
||||
public static final String KEY_DIRECT_COMMENT = "directComment";
|
||||
public static final String KEY_SORT = "sort";
|
||||
public static final String KEY_AMWAY = "amway";
|
||||
public static final String KEY_SKIP_SUCCESS_PAGE = "skipSuccessPage";
|
||||
public static final String KEY_COLLECTION_ID = "collectionId";
|
||||
public static final String KEY_NAVIGATION_TITLE = "navigationTitle";
|
||||
public static final String KEY_IMAGE_CROP_RATIO = "imageCropRatio";
|
||||
@ -147,8 +149,14 @@ public class EntranceUtils {
|
||||
public static final String KEY_IS_HOME_VIDEO = "isHomeVideo";
|
||||
public static final String KEY_IS_HOME = "isHome";
|
||||
public static final String KEY_WEB_SHARE = "webShare";
|
||||
public static final String KEY_ACTIVITY_NAME = "activityName";//活动名称
|
||||
public static final String KEY_REQUIRE_REDIRECT = "require_redirect"; // 标记需要再跳转
|
||||
public static final String KEY_COLUMNNAME = "columnName";
|
||||
public static final String KEY_QA_ID = "qaId";
|
||||
public static final String KEY_QA_COLLECTION_ID = "qaCollectionId";
|
||||
|
||||
public static void jumpActivity(Context context, Bundle bundle) {
|
||||
bundle.putBoolean(KEY_REQUIRE_REDIRECT, true);
|
||||
|
||||
//TODO 把其他类似的跳转启动逻辑也处理掉
|
||||
if (RunningUtils.isRunning(context)
|
||||
@ -173,6 +181,7 @@ public class EntranceUtils {
|
||||
|
||||
|
||||
public static void jumpActivity(Context context, Bundle nextToBundle, Bundle bundle, Callback callback) {
|
||||
bundle.putBoolean(KEY_REQUIRE_REDIRECT, true);
|
||||
|
||||
//TODO 把其他类似的跳转启动逻辑也处理掉
|
||||
if (RunningUtils.isRunning(context)
|
||||
|
||||
@ -237,6 +237,15 @@ inline fun tryCatchInRelease(action: (() -> Unit)) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 debug 状态下抛出异常
|
||||
*/
|
||||
fun throwExceptionInDebug(message: String = "", predicate: Boolean = true) {
|
||||
if (predicate && BuildConfig.DEBUG) {
|
||||
throw RuntimeException(message)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* String related
|
||||
*/
|
||||
|
||||
@ -82,11 +82,70 @@ object GameRepositoryHelper {
|
||||
return null
|
||||
}
|
||||
|
||||
fun replaceInstalledApp(gameList: MutableList<GameEntity>,
|
||||
alreadyDisplayedGameIdSet: HashSet<String>,
|
||||
relatedCollectionId: String,
|
||||
shouldLogReplaceEvent: Boolean) {
|
||||
val positionOfTheGameToReplaceList = arrayListOf<Int>()
|
||||
|
||||
// 标记需要替换的已安装游戏
|
||||
for ((index, game) in gameList.withIndex()) {
|
||||
|
||||
// 是大图形式的游戏时不标记替换
|
||||
if ((index == 0 && !gameList[0].image.isNullOrEmpty())) {
|
||||
continue
|
||||
}
|
||||
|
||||
var isThisPositionAdded = false
|
||||
// 检查是否已安装该游戏里同包名的 APK
|
||||
for (apk in game.getApk()) {
|
||||
if (PackageHelper.validLocalPackageNameSet.contains(apk.packageName)) {
|
||||
// 将该位置的游戏标记为需要替换
|
||||
positionOfTheGameToReplaceList.add(index)
|
||||
isThisPositionAdded = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 若此游戏所包含的 apk 没有已安装,那么再检查是否已安装有预设相关包名
|
||||
if (!isThisPositionAdded) {
|
||||
var relatedPackageList = arrayListOf<String>()
|
||||
for (entity in PackageHelper.relatedPackageList) {
|
||||
if (entity.gameId == game.id) {
|
||||
relatedPackageList = ArrayList(entity.packages)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for (packageName in relatedPackageList) {
|
||||
if (PackageHelper.validLocalPackageNameSet.contains(packageName)) {
|
||||
positionOfTheGameToReplaceList.add(index)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (positionOfTheGameToReplaceList.isNotEmpty()) {
|
||||
if (shouldLogReplaceEvent) {
|
||||
MtaHelper.onEvent("首页_加载", "启动光环", "替换游戏")
|
||||
}
|
||||
for (position in positionOfTheGameToReplaceList) {
|
||||
val replacingGame = getOneUniqueGame(relatedCollectionId, alreadyDisplayedGameIdSet)
|
||||
replacingGame?.let {
|
||||
gameList[position] = replacingGame
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun isThisGameUnique(game: GameEntity, gameIdList: HashSet<String>): Boolean {
|
||||
// 若该补充游戏已经存在关联关系,判定为非唯一
|
||||
for (relatedId in game.relatedGameIds!!) {
|
||||
gameIdList.contains(relatedId)
|
||||
return false
|
||||
if (gameIdList.contains(relatedId)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for (apk in game.getApk()) {
|
||||
// 检查本地是否已安装该游戏,已过滤那部分框架服务的包名
|
||||
|
||||
@ -5,12 +5,12 @@ import android.graphics.Color;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.gh.common.constant.Config;
|
||||
import com.gh.download.DownloadManager;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.entity.ApkEntity;
|
||||
import com.gh.gamecenter.entity.ApkLink;
|
||||
import com.gh.gamecenter.entity.GameCollectionEntity;
|
||||
import com.gh.gamecenter.entity.GameEntity;
|
||||
import com.gh.gamecenter.entity.GameUpdateEntity;
|
||||
import com.gh.gamecenter.entity.PluginLocation;
|
||||
@ -19,8 +19,11 @@ import com.gh.gamecenter.manager.PackagesManager;
|
||||
import com.lightgame.download.DownloadEntity;
|
||||
import com.lightgame.download.DownloadStatus;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
|
||||
public class GameUtils {
|
||||
|
||||
@ -53,13 +56,13 @@ public class GameUtils {
|
||||
String status = getDownloadBtnText(context, gameEntity, pluginLocation);
|
||||
downloadBtn.setTextColor(Color.WHITE);
|
||||
downloadBtn.setText(status);
|
||||
if ("插件化".equals(status)) {
|
||||
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_plugin_style);
|
||||
} else if ("打开".equals(status) || "启动".equals(status)) {
|
||||
downloadBtn.setBackgroundResource(R.drawable.detail_download_open_style);
|
||||
downloadBtn.setTextColor(ContextCompat.getColor(context, R.color.theme_font));
|
||||
if (context.getString(R.string.pluggable).equals(status)) {
|
||||
downloadBtn.setBackgroundResource(R.drawable.download_button_pluggable_style);
|
||||
String pluginDesc = gameEntity.getPluginDesc();
|
||||
if (pluginDesc.length() > 3) pluginDesc = pluginDesc.substring(0, 3);
|
||||
downloadBtn.setText((pluginDesc + "化"));
|
||||
} else {
|
||||
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_download_style);
|
||||
downloadBtn.setBackgroundResource(R.drawable.download_button_normal_style);
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,6 +70,10 @@ public class GameUtils {
|
||||
* 获取下载按钮文案
|
||||
*/
|
||||
public static String getDownloadBtnText(Context context, GameEntity gameEntity, PluginLocation pluginLocation) {
|
||||
if (gameEntity.getApk().size() > 1) {
|
||||
return context.getString(R.string.expand);
|
||||
}
|
||||
|
||||
int doneCount = 0; // 下载完成数量
|
||||
int pluginCount = 0; // 可插件化数量
|
||||
int updateCount = 0; // 可更新数量
|
||||
@ -118,18 +125,59 @@ public class GameUtils {
|
||||
} else if (updateCount != 0) {
|
||||
return context.getString(R.string.update);
|
||||
} else if (installCount != 0) {
|
||||
if (gameEntity.getApk().size() == 1) {
|
||||
return context.getString(R.string.launch);
|
||||
} else {
|
||||
return context.getString(R.string.open);
|
||||
}
|
||||
return context.getString(R.string.launch);
|
||||
} else if (gameEntity.getVersionNumber().contains("无版号") && Config.isGameDomeSwitchOpen()) {
|
||||
return context.getString(R.string.attempt);
|
||||
} else {
|
||||
return context.getString(R.string.download);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取GameUpdateEntity
|
||||
* 获取简单的下载按钮文案,只需要知道是否已下载,是否已安装
|
||||
*/
|
||||
public static String getSimpleDownloadBtnText(Context context, GameEntity gameEntity) {
|
||||
int doneCount = 0; // 下载完成数量
|
||||
int installCount = 0; // 已安装数量
|
||||
|
||||
DownloadEntity downloadEntity;
|
||||
Object gh_id;
|
||||
apkFor:
|
||||
for (ApkEntity apkEntity : gameEntity.getApk()) {
|
||||
// filter by packageName
|
||||
SettingsEntity settings = Config.getSettings();
|
||||
if (settings != null && gameEntity.getApk().size() > 1) {
|
||||
for (String pkgName : settings.getGameDownloadBlackList()) {
|
||||
if (pkgName.equals(apkEntity.getPackageName())) {
|
||||
continue apkFor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
downloadEntity = DownloadManager.getInstance(context).getDownloadEntityByUrl(apkEntity.getUrl());
|
||||
if (downloadEntity != null) {
|
||||
if (downloadEntity.getStatus().equals(DownloadStatus.done)) {
|
||||
doneCount++;
|
||||
}
|
||||
}
|
||||
if (PackagesManager.INSTANCE.isInstalled(apkEntity.getPackageName())) {
|
||||
gh_id = PackageUtils.getMetaData(context, apkEntity.getPackageName(), "gh_id");
|
||||
if (gh_id == null || gh_id.equals(gameEntity.getId())) {
|
||||
installCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (doneCount != 0) {
|
||||
return context.getString(R.string.install);
|
||||
} else if (installCount != 0) {
|
||||
return context.getString(R.string.launch);
|
||||
} else {
|
||||
return context.getString(R.string.download);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取GameUpdateEntity(插件化)
|
||||
*/
|
||||
public static GameUpdateEntity getGameUpdateEntity(GameEntity gameEntity, ApkEntity apkEntity) {
|
||||
GameUpdateEntity gameUpdateEntity = new GameUpdateEntity();
|
||||
@ -150,7 +198,50 @@ public class GameUtils {
|
||||
gameUpdateEntity.setPlugin(apkEntity.getPlugin());
|
||||
gameUpdateEntity.setDownload(gameEntity.getDownload());
|
||||
gameUpdateEntity.setIndexPlugin(gameEntity.getIndexPlugin());
|
||||
gameUpdateEntity.setPluginDesc(gameEntity.getPluginDesc());
|
||||
|
||||
GameCollectionEntity pluggableCollection = getPluggableCollectionFromGameEntity(gameEntity, apkEntity.getPackageName());
|
||||
if (pluggableCollection != null) {
|
||||
gameUpdateEntity.setPluggableCollection(pluggableCollection);
|
||||
}
|
||||
return gameUpdateEntity;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static GameCollectionEntity getPluggableCollectionFromGameEntity(GameEntity gameEntity, String targetPkg) {
|
||||
// 添加插件化包所在的合集
|
||||
for (GameCollectionEntity collectionEntity : gameEntity.getCollection()) {
|
||||
if (collectionEntity.getPackages().contains(targetPkg)) {
|
||||
ArrayList<ApkEntity> saveApkEntity = new ArrayList<>();
|
||||
for (String pkg : collectionEntity.getPackages()) {
|
||||
for (ApkEntity apk : gameEntity.getApk()) {
|
||||
if (pkg.equals(apk.getPackageName())) {
|
||||
saveApkEntity.add(apk);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<ApkLink> apkLinks = gameEntity.getApkLink();
|
||||
if (apkLinks != null) {
|
||||
for (ApkLink apkLink : apkLinks) {
|
||||
if (apkLink.getCollection().equals(collectionEntity.getId())) {
|
||||
ApkEntity element = new ApkEntity();
|
||||
element.setApkLink(apkLink);
|
||||
if (saveApkEntity.size() > apkLink.getSort()) {
|
||||
saveApkEntity.add(apkLink.getSort(), element);
|
||||
} else {
|
||||
saveApkEntity.add(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
collectionEntity.setSaveApkEntity(saveApkEntity);
|
||||
return collectionEntity;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -10,6 +10,9 @@ import android.widget.LinearLayout;
|
||||
import android.widget.LinearLayout.LayoutParams;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.gh.common.view.DrawableView;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.entity.TagStyleEntity;
|
||||
|
||||
@ -21,8 +24,6 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
/**
|
||||
* @author 温冠超
|
||||
* @email 294299195@qq.com
|
||||
@ -32,21 +33,16 @@ import androidx.core.content.ContextCompat;
|
||||
*/
|
||||
public class GameViewUtils {
|
||||
|
||||
public static void setLabelList(Context context, LinearLayout labelLayout, List<String> tag, String tagType, List<TagStyleEntity> tagStyle) {
|
||||
public static void setLabelList(Context context, LinearLayout labelLayout, List<TagStyleEntity> tagStyle) {
|
||||
labelLayout.removeAllViews();
|
||||
if (tag == null || tag.isEmpty()) {
|
||||
labelLayout.addView(getGameTagView(context, "官方版", 0, tagType, null));
|
||||
if (tagStyle == null || tagStyle.isEmpty()) {
|
||||
TagStyleEntity tagEntity = new TagStyleEntity();
|
||||
tagEntity.setName("官方版");
|
||||
labelLayout.addView(getNewGameTagView(context, tagEntity, 0));
|
||||
} else {
|
||||
for (int i = 0, size = tag.size(); i < size; i++) {
|
||||
View view;
|
||||
if (i == size - 1) {
|
||||
view = getGameTagView(context, tag.get(i), 0, tagType, tagStyle.size() > i ? tagStyle.get(i) : null);
|
||||
} else {
|
||||
view = getGameTagView(context, tag.get(i), DisplayUtils.dip2px(context, 8), tagType, tagStyle.size() > i ? tagStyle.get(i) : null);
|
||||
}
|
||||
if (view != null) {
|
||||
labelLayout.addView(view);
|
||||
}
|
||||
for (int i = 0, size = tagStyle.size(); i < size; i++) {
|
||||
View view = getNewGameTagView(context, tagStyle.get(i), i == size - 1 ? 0 : DisplayUtils.dip2px(context, 8));
|
||||
labelLayout.addView(view);
|
||||
if (labelLayout.getChildCount() == 3) {
|
||||
break;
|
||||
}
|
||||
@ -76,6 +72,32 @@ public class GameViewUtils {
|
||||
}
|
||||
}
|
||||
|
||||
// 新的游戏标签样式 version>=4.0.0
|
||||
private static TextView getNewGameTagView(Context context, TagStyleEntity tagEntity, int rightMargin) {
|
||||
// 参数不全,用旧样式实现
|
||||
if (TextUtils.isEmpty(tagEntity.getBackground())) {
|
||||
return getGameTagView(context, tagEntity.getName(), rightMargin, "type", tagEntity);
|
||||
}
|
||||
|
||||
LinearLayout.LayoutParams lparams = new LinearLayout.LayoutParams(
|
||||
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||
lparams.rightMargin = rightMargin;
|
||||
TextView tag = new TextView(context);
|
||||
tag.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
|
||||
tag.setSingleLine(true);
|
||||
tag.setText(tagEntity.getName());
|
||||
tag.setLayoutParams(lparams);
|
||||
tag.setPadding(
|
||||
DisplayUtils.dip2px(context, 4),
|
||||
0,
|
||||
DisplayUtils.dip2px(context, 4),
|
||||
DisplayUtils.dip2px(context, 1));
|
||||
|
||||
tag.setTextColor(Color.parseColor("#" + tagEntity.getColor()));
|
||||
tag.setBackground(DrawableView.getServerDrawable(Color.parseColor("#" + tagEntity.getBackground())));
|
||||
return tag;
|
||||
}
|
||||
|
||||
private static TextView getGameTagView(Context context, String tagStr, int rightMargin, String tagType, TagStyleEntity tagEntity) {
|
||||
LinearLayout.LayoutParams lparams = new LinearLayout.LayoutParams(
|
||||
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||
|
||||
@ -22,6 +22,12 @@ object GsonUtils {
|
||||
return gson.fromJson(json.toString(), type)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun <T> fromJsonList(json: String): List<T> {
|
||||
val type = object : TypeToken<List<T>>() {}.type
|
||||
return gson.fromJson(json, type)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun toJson(any: Any?): String {
|
||||
return gson.toJson(any)
|
||||
|
||||
@ -16,12 +16,14 @@ import com.facebook.drawee.controller.ControllerListener
|
||||
import com.facebook.drawee.drawable.ScalingUtils
|
||||
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder
|
||||
import com.facebook.drawee.view.SimpleDraweeView
|
||||
import com.facebook.imagepipeline.core.ImagePipeline
|
||||
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber
|
||||
import com.facebook.imagepipeline.image.ImageInfo
|
||||
import com.facebook.imagepipeline.request.ImageRequest
|
||||
import com.facebook.imagepipeline.request.ImageRequestBuilder
|
||||
import com.gh.common.constant.Config
|
||||
import com.gh.gamecenter.R
|
||||
import com.halo.assistant.HaloApp
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
|
||||
@ -323,6 +325,14 @@ object ImageUtils {
|
||||
draweeView.setImageURI("res:///" + res)
|
||||
}
|
||||
|
||||
//预加载图片
|
||||
@JvmStatic
|
||||
fun prefetchToDiskCache(url: String){
|
||||
val imagePipeline = Fresco.getImagePipeline()
|
||||
val imageRequest = ImageRequest.fromUri(url)
|
||||
imagePipeline.prefetchToDiskCache(imageRequest, HaloApp.getInstance().application)
|
||||
}
|
||||
|
||||
public interface OnImageloadListener {
|
||||
fun onLoadFinal(imageInfo: ImageInfo?)
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ public class InstallUtils {
|
||||
if (!TextUtils.isEmpty(installVersion) && downloadEntity != null &&
|
||||
installVersion.equals(downloadEntity.getVersionName())) {
|
||||
if (!downloadEntity.isPluggable() || PackageUtils.isSignature(context, packageName)) {
|
||||
EventBus.getDefault().post(new EBPackage("安装", packageName));
|
||||
EventBus.getDefault().post(new EBPackage("安装", packageName, installVersion));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -85,7 +85,7 @@ public class InstallUtils {
|
||||
keys.add(packageName);
|
||||
} else if (!list.contains(packageName)) {
|
||||
keys.add(packageName);
|
||||
EventBus.getDefault().post(new EBPackage("卸载", packageName));
|
||||
EventBus.getDefault().post(new EBPackage("卸载", packageName, ""));
|
||||
}
|
||||
}
|
||||
for (String key : keys) {
|
||||
|
||||
@ -11,8 +11,6 @@ import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.gh.gamecenter.BuildConfig;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.adapter.LibaoDetailAdapter;
|
||||
@ -37,6 +35,7 @@ import org.json.JSONObject;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
@ -151,76 +150,76 @@ public class LibaoUtils {
|
||||
});
|
||||
}
|
||||
|
||||
public static void setLiBaoBtnStatus(final TextView libaoBtn, String status, Context context) {
|
||||
public static void setLiBaoBtnStatusRound(final TextView libaoBtn, String status, Context context) {
|
||||
libaoBtn.setTextColor(Color.WHITE);
|
||||
if (TextUtils.isEmpty(status)) return;
|
||||
switch (status) {
|
||||
case "ling":
|
||||
libaoBtn.setText(R.string.libao_ling);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_style);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_round_style);
|
||||
break;
|
||||
case "tao":
|
||||
libaoBtn.setText(R.string.libao_tao);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_style);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_round_style);
|
||||
break;
|
||||
case "coming":
|
||||
libaoBtn.setText(R.string.libao_coming);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_border);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_round_border);
|
||||
libaoBtn.setTextColor(context.getResources().getColor(R.color.theme_font));
|
||||
break;
|
||||
case "used_up":
|
||||
libaoBtn.setText(R.string.libao_used_up);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_border);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_round_border);
|
||||
libaoBtn.setTextColor(context.getResources().getColor(R.color.theme_font));
|
||||
break;
|
||||
case "finish":
|
||||
libaoBtn.setText(R.string.libao_finish);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_border_gray);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_border_round_gray);
|
||||
libaoBtn.setTextColor(context.getResources().getColor(R.color.button_gray));
|
||||
break;
|
||||
case "linged":
|
||||
libaoBtn.setText(R.string.libao_linged);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_border);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_round_border);
|
||||
libaoBtn.setTextColor(context.getResources().getColor(R.color.theme_font));
|
||||
break;
|
||||
case "taoed":
|
||||
libaoBtn.setText(R.string.libao_taoed);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_border);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_round_border);
|
||||
libaoBtn.setTextColor(context.getResources().getColor(R.color.theme_font));
|
||||
break;
|
||||
case "copy":
|
||||
libaoBtn.setText(R.string.libao_copy);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_style);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_round_style);
|
||||
break;
|
||||
case "repeatLing":
|
||||
libaoBtn.setText(R.string.libao_repeat_ling);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_border);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_round_border);
|
||||
libaoBtn.setTextColor(context.getResources().getColor(R.color.theme_font));
|
||||
break;
|
||||
case "repeatLinged":
|
||||
libaoBtn.setText(R.string.libao_repeat_ling);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_style);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_round_style);
|
||||
break;
|
||||
case "repeatTao":
|
||||
libaoBtn.setText(R.string.libao_repeat_tao);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_border);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_round_border);
|
||||
libaoBtn.setTextColor(context.getResources().getColor(R.color.theme_font));
|
||||
break;
|
||||
case "repeatTaoed":
|
||||
libaoBtn.setText(R.string.libao_repeat_tao);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_style);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_round_style);
|
||||
break;
|
||||
case "unshelve":
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_border_gray);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_border_round_gray);
|
||||
libaoBtn.setText(R.string.libao_unshelve);
|
||||
libaoBtn.setTextColor(context.getResources().getColor(R.color.button_gray));
|
||||
break;
|
||||
case "check":
|
||||
libaoBtn.setText(R.string.libao_check);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_style);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_round_style);
|
||||
break;
|
||||
default:
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_border_gray);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_border_round_gray);
|
||||
libaoBtn.setText("异常");
|
||||
libaoBtn.setTextColor(context.getResources().getColor(R.color.button_gray));
|
||||
break;
|
||||
@ -230,7 +229,7 @@ public class LibaoUtils {
|
||||
public static void initLibaoBtn(final Context context, final TextView libaoBtn, final LibaoEntity libaoEntity,
|
||||
final boolean isInstallRequired, final LibaoDetailAdapter adapter, final String entrance) {
|
||||
String status = libaoEntity.getStatus();
|
||||
setLiBaoBtnStatus(libaoBtn, status, context);
|
||||
setLiBaoBtnStatusRound(libaoBtn, status, context);
|
||||
|
||||
libaoBtn.setOnClickListener(v -> {
|
||||
String btnStatus = libaoBtn.getText().toString();
|
||||
|
||||
@ -46,7 +46,7 @@ public class NewsUtils {
|
||||
* 统计阅读量
|
||||
*/
|
||||
public static void statNewsViews(Context context, String news_id) {
|
||||
RetrofitManager.getInstance(context).getData().postNewsViews(news_id)
|
||||
RetrofitManager.getInstance(context).getApi().postArticleVisit(news_id)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Response<ResponseBody>());
|
||||
|
||||
@ -20,6 +20,7 @@ import com.gh.gamecenter.BuildConfig;
|
||||
import com.gh.gamecenter.entity.ApkEntity;
|
||||
import com.gh.gamecenter.entity.GameEntity;
|
||||
import com.gh.gamecenter.entity.GameUpdateEntity;
|
||||
import com.gh.gamecenter.manager.PackagesManager;
|
||||
import com.halo.assistant.HaloApp;
|
||||
import com.lightgame.download.DownloadEntity;
|
||||
import com.lightgame.utils.Utils;
|
||||
@ -30,12 +31,17 @@ import org.json.JSONObject;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
public class PackageUtils {
|
||||
@ -45,47 +51,39 @@ public class PackageUtils {
|
||||
/*
|
||||
* 判断是否可以更新,只判断gh_version的大小
|
||||
*/
|
||||
public static List<GameUpdateEntity> isCanUpdate(Context context, GameEntity gameEntity) {
|
||||
public static List<GameUpdateEntity> getUpdateData(Context context, GameEntity gameEntity) {
|
||||
|
||||
List<GameUpdateEntity> updateList = new ArrayList<>();
|
||||
|
||||
boolean isAPluginGame = false;
|
||||
// 插件更新
|
||||
for (ApkEntity apkEntity : gameEntity.getApk()) {
|
||||
// 判断是否gh_version是否存在
|
||||
String gh_version = (String) PackageUtils.getMetaData(context, apkEntity.getPackageName(), "gh_version");
|
||||
Object gh_id = PackageUtils.getMetaData(context, apkEntity.getPackageName(), "gh_id");
|
||||
if (gh_version != null && apkEntity.getGhVersion() != null && gh_id != null) {
|
||||
|
||||
// 确定这是一个插件游戏
|
||||
if (!isAPluginGame) isAPluginGame = true;
|
||||
gh_version = gh_version.substring(2);
|
||||
if (Long.parseLong(gh_version) < Long.parseLong(apkEntity.getGhVersion()) && apkEntity.getForce()
|
||||
&& gh_id.equals(gameEntity.getId())) {
|
||||
GameUpdateEntity updateEntity = new GameUpdateEntity();
|
||||
updateEntity.setId(gameEntity.getId());
|
||||
updateEntity.setName(gameEntity.getName());
|
||||
updateEntity.setIcon(gameEntity.getIcon());
|
||||
updateEntity.setPackageName(apkEntity.getPackageName());
|
||||
updateEntity.setSize(apkEntity.getSize());
|
||||
updateEntity.setVersion(apkEntity.getVersion());
|
||||
updateEntity.setGhVersion(apkEntity.getGhVersion());
|
||||
updateEntity.setUrl(apkEntity.getUrl());
|
||||
updateEntity.setPlatform(apkEntity.getPlatform());
|
||||
updateEntity.setEtag(apkEntity.getEtag());
|
||||
updateEntity.setBrief(gameEntity.getBrief());
|
||||
updateEntity.setTag(gameEntity.getTag());
|
||||
updateEntity.setTagStyle(gameEntity.getTagStyle());
|
||||
updateEntity.setDownload(gameEntity.getDownload());
|
||||
updateEntity.setIndexPlugin(gameEntity.getIndexPlugin());
|
||||
updateList.add(updateEntity);
|
||||
}
|
||||
if (isCanUpdate(apkEntity, gameEntity.getId())) {
|
||||
GameUpdateEntity updateEntity = new GameUpdateEntity();
|
||||
updateEntity.setId(gameEntity.getId());
|
||||
updateEntity.setName(gameEntity.getName());
|
||||
updateEntity.setIcon(gameEntity.getIcon());
|
||||
updateEntity.setPackageName(apkEntity.getPackageName());
|
||||
updateEntity.setSize(apkEntity.getSize());
|
||||
updateEntity.setVersion(apkEntity.getVersion());
|
||||
updateEntity.setGhVersion(apkEntity.getGhVersion());
|
||||
updateEntity.setUrl(apkEntity.getUrl());
|
||||
updateEntity.setPlatform(apkEntity.getPlatform());
|
||||
updateEntity.setEtag(apkEntity.getEtag());
|
||||
updateEntity.setBrief(gameEntity.getBrief());
|
||||
updateEntity.setTag(gameEntity.getTag());
|
||||
updateEntity.setTagStyle(gameEntity.getTagStyle());
|
||||
updateEntity.setDownload(gameEntity.getDownload());
|
||||
updateEntity.setIndexPlugin(gameEntity.getIndexPlugin());
|
||||
updateEntity.setPluginDesc(gameEntity.getPluginDesc());
|
||||
updateList.add(updateEntity);
|
||||
}
|
||||
}
|
||||
|
||||
// 不是插件游戏
|
||||
if (!isAPluginGame) {
|
||||
for (ApkEntity apkEntity : gameEntity.getApkNormal()) {
|
||||
// 非插件游戏更新
|
||||
for (ApkEntity apkEntity : gameEntity.getApkNormal()) {
|
||||
|
||||
// ghVersion 不存在即是非插件游戏
|
||||
if (TextUtils.isEmpty(apkEntity.getGhVersion())) {
|
||||
String versionFromRequest = apkEntity.getVersion();
|
||||
String versionFromInstalledApp = getVersionByPackage(apkEntity.getPackageName());
|
||||
|
||||
@ -113,6 +111,7 @@ public class PackageUtils {
|
||||
updateEntity.setTag(gameEntity.getTag());
|
||||
updateEntity.setTagStyle(gameEntity.getTagStyle());
|
||||
updateEntity.setIndexPlugin(gameEntity.getIndexPlugin());
|
||||
updateEntity.setPluginDesc(gameEntity.getPluginDesc());
|
||||
updateList.add(updateEntity);
|
||||
}
|
||||
}
|
||||
@ -138,6 +137,19 @@ public class PackageUtils {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取已安装游戏的光环ID(游戏ID)
|
||||
*
|
||||
* @param packageName
|
||||
* @return
|
||||
*/
|
||||
@Nullable
|
||||
public static Object getGhId(String packageName) {
|
||||
return getMetaData(HaloApp.getInstance().getApplication(), packageName, "gh_id");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 判断是否是插件包
|
||||
*/
|
||||
@ -170,6 +182,9 @@ public class PackageUtils {
|
||||
public static boolean compareSignatureBetweenInstalledAppWithApk(Context context, String packageName, String apkFilePath) {
|
||||
try {
|
||||
Signature sig = context.getPackageManager().getPackageInfo(packageName, PackageManager.GET_SIGNATURES).signatures[0];
|
||||
|
||||
// Fuck HUAWEI, 华为系统调用 getPackageArchiveInfo 获取魔羯 apk 的签名时会耗时超过5秒造成 ANR,没有找到解决方法
|
||||
// 如果可以的话尽量避免调用 getPackageArchiveInfo 方法
|
||||
Signature releaseSig = context.getPackageManager().getPackageArchiveInfo(apkFilePath, PackageManager.GET_SIGNATURES).signatures[0];
|
||||
return sig.hashCode() == releaseSig.hashCode();
|
||||
} catch (Exception e) {
|
||||
@ -178,6 +193,28 @@ public class PackageUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 从 APK 中获取公钥(只支持包含 V1 签名的 APK
|
||||
private static String getApkSignatureFromFile(String apkFilePath) {
|
||||
try {
|
||||
ZipFile apkFile = new ZipFile(apkFilePath);
|
||||
Enumeration<?> entries = apkFile.entries();
|
||||
|
||||
while (entries.hasMoreElements()) {
|
||||
ZipEntry entry = ((ZipEntry) entries.nextElement());
|
||||
String entryName = entry.getName();
|
||||
if (entryName.contains("META-INF") && entryName.endsWith(".RSA")) {
|
||||
InputStream is = apkFile.getInputStream(entry);
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
X509Certificate cert = (X509Certificate) cf.generateCertificates(is).toArray()[0];
|
||||
return cert.getPublicKey().toString();
|
||||
}
|
||||
}
|
||||
return "";
|
||||
} catch (Exception e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 解析签名
|
||||
*/
|
||||
@ -193,7 +230,7 @@ public class PackageUtils {
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 启动安装应用程序
|
||||
*/
|
||||
@ -217,8 +254,10 @@ public class PackageUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 根据apk路径,获取apk包名、签名 根据包名 判断 是否已安装游戏 根据签名 判断 是否一致
|
||||
/**
|
||||
* 根据 path 获取 apk 信息确定处理方式
|
||||
*
|
||||
* @return true 为直接唤起系统 PackageInstaller, false 为需要插件化
|
||||
*/
|
||||
public static boolean isCanLaunchSetup(Context context, String path) {
|
||||
String packageName = getPackageNameByPath(context, path);
|
||||
@ -226,7 +265,7 @@ public class PackageUtils {
|
||||
if (TextUtils.isEmpty(packageName)) {
|
||||
return true;
|
||||
}
|
||||
boolean isContain = com.gh.gamecenter.manager.PackagesManager.INSTANCE.isInstalled(packageName);
|
||||
boolean isContain = PackagesManager.isInstalled(packageName);
|
||||
if (!isContain) {
|
||||
return true;
|
||||
}
|
||||
@ -236,13 +275,19 @@ public class PackageUtils {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 由于新增了非插件的也能更新的功能,倘若两个签名一样的话就不走下面的光环签名判断了
|
||||
if (compareSignatureBetweenInstalledAppWithApk(context, packageName, path)) {
|
||||
// 判断当前已安装应用是否为光环签名
|
||||
if (publicKey.equals(getApkSignatureByPackageName(context, packageName))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String signature = getApkSignatureByPackageName(context, packageName);
|
||||
return publicKey.equals(signature);
|
||||
// 从 APK 文件里读取公钥,与已安装的公钥比较判断是否一样
|
||||
String signatureFromApk = getApkSignatureFromFile(path);
|
||||
if (!TextUtils.isEmpty(signatureFromApk)) {
|
||||
return signatureFromApk.equals(getApkSignatureByPackageName(context, packageName));
|
||||
}
|
||||
|
||||
// 若已安装的应用的签名与即将要安装的签名一致也返回 true
|
||||
return compareSignatureBetweenInstalledAppWithApk(context, packageName, path);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -453,4 +498,55 @@ public class PackageUtils {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* todo 统一判断
|
||||
* <p>
|
||||
* 判断游戏包是否可以更新
|
||||
*
|
||||
* @param apkEntity apkEntity 必须是已安装的游戏
|
||||
* @param gameId 游戏id
|
||||
* @return true:可以更新 false:不可以更新
|
||||
*/
|
||||
public static boolean isCanUpdate(ApkEntity apkEntity, String gameId) {
|
||||
// gh_version: gh + timestamp
|
||||
String gh_version = (String) PackageUtils.getMetaData(
|
||||
HaloApp.getInstance().getApplication(),
|
||||
apkEntity.getPackageName(),
|
||||
"gh_version");
|
||||
|
||||
// gh_version: game id
|
||||
Object gh_id = PackageUtils.getMetaData(
|
||||
HaloApp.getInstance().getApplication(),
|
||||
apkEntity.getPackageName(),
|
||||
"gh_id");
|
||||
|
||||
if (gh_version != null && apkEntity.getGhVersion() != null && gh_id != null) {
|
||||
gh_version = gh_version.substring(2);
|
||||
return Long.parseLong(gh_version) < Long.parseLong(apkEntity.getGhVersion())
|
||||
&& apkEntity.getForce()
|
||||
&& gh_id.equals(gameId);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* todo 统一判断
|
||||
* <p>
|
||||
* 判断游戏包是否可以插件化
|
||||
*
|
||||
* @param apkEntity apkEntity 必须是已安装的游戏
|
||||
* @return true:可以插件化 false:不可以插件化
|
||||
*/
|
||||
public static boolean isCanPluggable(ApkEntity apkEntity) {
|
||||
String gh_id = (String) PackageUtils.getMetaData(
|
||||
HaloApp.getInstance().getApplication(),
|
||||
apkEntity.getPackageName(),
|
||||
"gh_id");
|
||||
|
||||
return PackageUtils.isInstalled(HaloApp.getInstance().getApplication(), apkEntity.getPackageName())
|
||||
&& gh_id == null
|
||||
&& !TextUtils.isEmpty(apkEntity.getGhVersion())
|
||||
&& !PackageUtils.isSignature(HaloApp.getInstance().getApplication(), apkEntity.getPackageName());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,102 +0,0 @@
|
||||
package com.gh.common.util;
|
||||
|
||||
import android.util.Base64;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
public class RSEUtils {
|
||||
|
||||
/**
|
||||
* 公钥
|
||||
*/
|
||||
private static final String PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQConqikyOCl5f/VO/vZ+s6wVOhFJcI7o7hYvaLQ5Lzt5/HXbozTeRrGonKFPJurapf9hzjkg0F4/BNFMGXRVlReVdwh+Px5rbXU/ceikF8Ouf67qxiGAuDVC+4e3eIHh+mH68DlqWFJ78sB80iSLXelflGuHkbTOTtaC5F2l+AgxQIDAQAB";
|
||||
|
||||
/**
|
||||
* 加密算法RSA
|
||||
*/
|
||||
private static final String KEY_ALGORITHM = "RSA";
|
||||
|
||||
/**
|
||||
* 使用公钥加密
|
||||
*
|
||||
* @param content
|
||||
* @return
|
||||
*/
|
||||
public static String encryptByPublic(String content) {
|
||||
try {
|
||||
PublicKey pubkey = getPublicKeyFromX509();
|
||||
|
||||
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, pubkey);
|
||||
|
||||
byte[] data = content.getBytes("UTF-8");
|
||||
byte[] output = cipher.doFinal(data);
|
||||
|
||||
String s = new String(Base64.encode(output, Base64.DEFAULT));
|
||||
|
||||
return s;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到公钥
|
||||
*
|
||||
* @return PublicKey 公钥
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws InvalidKeySpecException
|
||||
*/
|
||||
private static PublicKey getPublicKeyFromX509() throws Exception {
|
||||
byte[] decodedKey = Base64.decode(PUBLIC_KEY, Base64.DEFAULT);
|
||||
X509EncodedKeySpec x509 = new X509EncodedKeySpec(decodedKey);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
|
||||
return keyFactory.generatePublic(x509);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用公钥解密
|
||||
*
|
||||
* @param content 密文
|
||||
* @return 解密后的字符串
|
||||
*/
|
||||
public static String decryptByPublic(String content) {
|
||||
try {
|
||||
PublicKey pubkey = getPublicKeyFromX509();
|
||||
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
|
||||
cipher.init(Cipher.DECRYPT_MODE, pubkey);
|
||||
InputStream ins = new ByteArrayInputStream(Base64.decode(content,
|
||||
Base64.DEFAULT));
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[128];
|
||||
int len;
|
||||
while ((len = ins.read(buffer)) != -1) {
|
||||
byte[] block;
|
||||
if (buffer.length == len) {
|
||||
block = buffer;
|
||||
} else {
|
||||
block = new byte[len];
|
||||
for (int i = 0; i < len; i++) {
|
||||
block[i] = buffer[i];
|
||||
}
|
||||
}
|
||||
baos.write(cipher.doFinal(block));
|
||||
}
|
||||
return new String(baos.toByteArray(), "utf-8");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@ -18,7 +18,6 @@ import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.PopupWindow;
|
||||
@ -32,6 +31,7 @@ import com.facebook.imagepipeline.image.CloseableImage;
|
||||
import com.gh.common.constant.Config;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.WeiBoShareActivity;
|
||||
import com.gh.gamecenter.eventbus.EBShare;
|
||||
import com.lightgame.utils.Utils;
|
||||
import com.sina.weibo.sdk.WbSdk;
|
||||
import com.sina.weibo.sdk.auth.AuthInfo;
|
||||
@ -46,6 +46,8 @@ import com.tencent.tauth.IUiListener;
|
||||
import com.tencent.tauth.Tencent;
|
||||
import com.tencent.tauth.UiError;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
@ -53,7 +55,6 @@ import java.util.List;
|
||||
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import kotlin.jvm.functions.Function0;
|
||||
|
||||
import static com.gh.common.util.LoginHelper.WEIBO_SCOPE;
|
||||
|
||||
@ -99,6 +100,7 @@ public class ShareUtils {
|
||||
private PopupWindow popupWindow;
|
||||
|
||||
private ShareType mShareType;
|
||||
public static ShareType shareType;//全局保存shareType,分享成功后判断分享的类型
|
||||
|
||||
private WeakReference<Activity> mActivity;
|
||||
|
||||
@ -108,6 +110,7 @@ public class ShareUtils {
|
||||
@Override
|
||||
public void onComplete(Object o) {
|
||||
Utils.toast(mContext, R.string.share_success_hint);
|
||||
EventBus.getDefault().post(new EBShare(ShareUtils.shareType));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -150,7 +153,7 @@ public class ShareUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void showShareWindowsCancelCallback(Activity activity, View view, String url, String icon, String shareTitle, String shareSummary, ShareType shareType, Function0 function) {
|
||||
public void showShareWindowsCallback(Activity activity, View view, String url, String icon, String shareTitle, String shareSummary, ShareType shareType, ShareCallBack callBack) {
|
||||
if (activity.isFinishing()) return;
|
||||
this.mActivity = new WeakReference<>(activity);
|
||||
this.shareIcon = icon;
|
||||
@ -158,6 +161,7 @@ public class ShareUtils {
|
||||
this.mSummary = shareSummary;
|
||||
this.mTitle = shareTitle;
|
||||
this.mShareType = shareType;
|
||||
ShareUtils.shareType = mShareType;
|
||||
|
||||
View contentView = View.inflate(activity, R.layout.share_popup_layout, null);
|
||||
contentView.setFocusable(true);
|
||||
@ -178,8 +182,12 @@ public class ShareUtils {
|
||||
ShareRecyclerViewAdapter shareRecyclerViewAdapter = new ShareRecyclerViewAdapter();
|
||||
shareRecyclerView.setAdapter(shareRecyclerViewAdapter);
|
||||
shareRecyclerViewAdapter.setOnItemClickListener(position -> {
|
||||
if ("取消".equals(arrLabel[position])) {
|
||||
function.invoke();
|
||||
if (callBack != null) {
|
||||
if (position == arrLabel.length - 1) {
|
||||
callBack.onCancel();
|
||||
} else {
|
||||
callBack.onSuccess(arrLabel[position]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -198,9 +206,9 @@ public class ShareUtils {
|
||||
arrLogo[7] = R.drawable.share_cancel_logo;
|
||||
}
|
||||
|
||||
popupWindow = new PopupWindow(contentView, LinearLayout.LayoutParams.MATCH_PARENT
|
||||
popupWindow = new SharePopupWindow(contentView, LinearLayout.LayoutParams.MATCH_PARENT
|
||||
, LinearLayout.LayoutParams.MATCH_PARENT, true);
|
||||
popupWindow.setAnimationStyle(R.style.mypopwindow_anim_style);
|
||||
popupWindow.setAnimationStyle(R.style.popwindow_exit_only_anim_style);
|
||||
//解决PopupWindow无法覆盖状态栏
|
||||
popupWindow.setClippingEnabled(false);
|
||||
|
||||
@ -221,7 +229,9 @@ public class ShareUtils {
|
||||
&& event.getRepeatCount() == 0
|
||||
&& popupWindow != null
|
||||
&& popupWindow.isShowing()) {
|
||||
function.invoke();
|
||||
if (callBack != null) {
|
||||
callBack.onCancel();
|
||||
}
|
||||
popupWindow.dismiss();
|
||||
}
|
||||
return false;
|
||||
@ -230,74 +240,7 @@ public class ShareUtils {
|
||||
|
||||
|
||||
public void showShareWindows(Activity activity, View view, String url, String icon, String shareTitle, String shareSummary, ShareType shareType) {
|
||||
if (activity.isFinishing()) return;
|
||||
this.mActivity = new WeakReference<>(activity);
|
||||
this.shareIcon = icon;
|
||||
this.shareUrl = url;
|
||||
this.mSummary = shareSummary;
|
||||
this.mTitle = shareTitle;
|
||||
this.mShareType = shareType;
|
||||
|
||||
View contentView = View.inflate(activity, R.layout.share_popup_layout, null);
|
||||
contentView.setFocusable(true);
|
||||
contentView.setFocusableInTouchMode(true);
|
||||
RecyclerView shareRecyclerView = contentView.findViewById(R.id.share_rv);
|
||||
|
||||
shareRecyclerView.setPadding(DisplayUtils.dip2px(mContext, 20), DisplayUtils.dip2px(mContext, 10), DisplayUtils.dip2px(mContext, 20), 0);
|
||||
|
||||
//RecyclerView禁止滑动
|
||||
GridLayoutManager gridLayoutManager = new GridLayoutManager(mContext, 4) {
|
||||
@Override
|
||||
public boolean canScrollVertically() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
shareRecyclerView.setLayoutManager(gridLayoutManager);
|
||||
shareRecyclerView.setAdapter(new ShareRecyclerViewAdapter());
|
||||
|
||||
if (mShareType == ShareType.shareGh) {
|
||||
RelativeLayout layout = (RelativeLayout) view;
|
||||
layout.addView(contentView);
|
||||
arrLabel[6] = "邮件";
|
||||
arrLogo[6] = R.drawable.share_email_logo;
|
||||
arrLabel[7] = "复制链接";
|
||||
arrLogo[7] = R.drawable.share_copyfont_logo;
|
||||
return;
|
||||
} else {
|
||||
arrLabel[6] = "复制链接";
|
||||
arrLogo[6] = R.drawable.share_copyfont_logo;
|
||||
arrLabel[7] = "取消";
|
||||
arrLogo[7] = R.drawable.share_cancel_logo;
|
||||
}
|
||||
|
||||
popupWindow = new PopupWindow(contentView, LinearLayout.LayoutParams.MATCH_PARENT
|
||||
, LinearLayout.LayoutParams.MATCH_PARENT, true);
|
||||
popupWindow.setAnimationStyle(R.style.mypopwindow_anim_style);
|
||||
//解决PopupWindow无法覆盖状态栏
|
||||
popupWindow.setClippingEnabled(false);
|
||||
int bottomLocation = -DisplayUtils.retrieveNavigationHeight(activity);
|
||||
if (!DisplayUtils.isNavigationBarShow(activity)) {
|
||||
bottomLocation = 0;
|
||||
}
|
||||
try {
|
||||
// popupWindow.showAtLocation(view, Gravity.BOTTOM, 0, 0);
|
||||
popupWindow.showAtLocation(view, Gravity.NO_GRAVITY, 0, bottomLocation);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
contentView.setOnClickListener(v -> popupWindow.dismiss());
|
||||
|
||||
contentView.setOnKeyListener((v, keyCode, event) -> {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK
|
||||
&& event.getRepeatCount() == 0
|
||||
&& popupWindow != null
|
||||
&& popupWindow.isShowing()) {
|
||||
popupWindow.dismiss();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
showShareWindowsCallback(activity, view, url, icon, shareTitle, shareSummary, shareType, null);
|
||||
}
|
||||
|
||||
//QQ分享
|
||||
@ -707,4 +650,25 @@ public class ShareUtils {
|
||||
void onItemClick(int position);
|
||||
}
|
||||
|
||||
}
|
||||
public interface ShareCallBack {
|
||||
void onSuccess(String label);
|
||||
|
||||
void onCancel();
|
||||
}
|
||||
|
||||
private static class SharePopupWindow extends PopupWindow {
|
||||
SharePopupWindow(View contentView, int width, int height, boolean focusable) {
|
||||
super(contentView, width, height, focusable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dismiss() {
|
||||
View backgroundView = getContentView().findViewById(R.id.share_container);
|
||||
if (backgroundView != null) {
|
||||
backgroundView.setBackgroundColor(Color.TRANSPARENT);
|
||||
}
|
||||
getContentView().postDelayed(super::dismiss, 0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
package com.gh.common.util
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.Typeface
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.Spanned
|
||||
@ -21,6 +22,14 @@ class SpanBuilder(content: String) {
|
||||
return this
|
||||
}
|
||||
|
||||
fun color(start: Int, end: Int, colorHexInString: String): SpanBuilder {
|
||||
tryWithDefaultCatch {
|
||||
val colorSpan = ForegroundColorSpan(Color.parseColor(colorHexInString))
|
||||
spannableString.setSpan(colorSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun size(start: Int, end: Int, dpSize: Int): SpanBuilder {
|
||||
val s = AbsoluteSizeSpan(dpSize, true)
|
||||
spannableString.setSpan(s, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
|
||||
@ -5,6 +5,7 @@ import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.text.*
|
||||
import android.text.style.ClickableSpan
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.text.style.RelativeSizeSpan
|
||||
import android.view.View
|
||||
import android.widget.EditText
|
||||
@ -92,7 +93,8 @@ object TextHelper {
|
||||
|
||||
@JvmStatic
|
||||
fun highlightTextThatIsWrappedInsideWrapperByDefault(textView: TextView, text: String?) {
|
||||
textView.text = getHighlightedSpannableStringThatIsWrappedInsideWrapper(textView.context, text ?: "", "###", R.color.theme_font, object : SimpleCallback<String> {
|
||||
textView.text = getHighlightedSpannableStringThatIsWrappedInsideWrapper(textView.context, text
|
||||
?: "", "###", R.color.theme_font, object : SimpleCallback<String> {
|
||||
override fun onCallback(arg: String) {
|
||||
val application = HaloApp.getInstance().application
|
||||
val cmb = application.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
@ -185,8 +187,8 @@ object TextHelper {
|
||||
*/
|
||||
fun getCommentLabelSpannableStringBuilder(comment: CharSequence,
|
||||
amwayStyle: Boolean = false,
|
||||
tagStrokeColor: String = "#FFDAA2",
|
||||
tagTextColor: String = "#FF9B06"): SpannableStringBuilder {
|
||||
tagStrokeColor: String = "#FA8E00",
|
||||
tagTextColor: String = "#FA8E00"): SpannableStringBuilder {
|
||||
val contents = SpannableStringBuilder()
|
||||
var count = 0
|
||||
val splits = comment.split("<tag>")
|
||||
@ -220,6 +222,35 @@ object TextHelper {
|
||||
return contents
|
||||
}
|
||||
|
||||
fun getCommentLabelSpannableStringBuilder(comment: CharSequence, colorRes: Int): SpannableStringBuilder {
|
||||
val contents = SpannableStringBuilder()
|
||||
var count = 0
|
||||
val splits = comment.split("<tag>")
|
||||
splits.forEachIndexed { index, s ->
|
||||
if (index != 0) {
|
||||
val item = "<tag>$s"
|
||||
val pattern = Pattern.compile("<tag>(\\S+)</tag>([\\S\\s\n]+)")
|
||||
val matcher = pattern.matcher(item)
|
||||
if (matcher.find()) {
|
||||
val label = matcher.group(1)
|
||||
var content = item.substring(item.indexOf("</tag>") + 6)
|
||||
if (index == splits.size - 1) content += " "
|
||||
val newLabel = "#$label# "
|
||||
contents.append(newLabel)
|
||||
contents.setSpan(ForegroundColorSpan(ContextCompat.getColor(HaloApp.getInstance().application, colorRes)),
|
||||
count, count + newLabel.length - 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
count += newLabel.length
|
||||
contents.append(content)
|
||||
count += content.length
|
||||
}
|
||||
} else {
|
||||
contents.append(s)
|
||||
count += s.length
|
||||
}
|
||||
}
|
||||
return contents
|
||||
}
|
||||
|
||||
private fun getRoundStrokeBackgroundColorSpan(amwayStyle: Boolean,
|
||||
tagStrokeColor: String,
|
||||
tagTextColor: String): RoundStrokeBackgroundColorSpan {
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.gh.common.util
|
||||
|
||||
import java.text.ParseException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
@ -19,4 +20,57 @@ object TimeUtils {
|
||||
val formatter = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA)
|
||||
return formatter.format(date)
|
||||
}
|
||||
|
||||
fun getFormatTime(time: Long, pattern: String = "yyyy-MM-dd"): String {
|
||||
val f = SimpleDateFormat(pattern, Locale.CHINA)
|
||||
return f.format(Date(time * 1000))
|
||||
}
|
||||
|
||||
fun getFormatDate(timestamp: Long): String {
|
||||
val format = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA)
|
||||
format.timeZone = TimeZone.getTimeZone("Asia/Shanghai")
|
||||
val testDate: String
|
||||
val today = format.parse(format.format(Date())).time
|
||||
val day = timestamp * 1000
|
||||
val timeFormat = SimpleDateFormat("HH:mm", Locale.CHINA)
|
||||
val time = timeFormat.format(day)
|
||||
|
||||
testDate = if (day >= today && day < today + 86400 * 1000) {
|
||||
"今天 $time"
|
||||
} else if (day >= today + 86400 * 1000 && day < today + 86400 * 1000 * 2) {
|
||||
"明天 $time"
|
||||
} else {
|
||||
SimpleDateFormat("MM-dd HH:mm", Locale.CHINA).format(day)
|
||||
}
|
||||
return testDate
|
||||
}
|
||||
|
||||
//判断是不是今天
|
||||
fun isToday(timestamp: Long): Boolean {
|
||||
val format = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA)
|
||||
format.timeZone = TimeZone.getTimeZone("Asia/Shanghai")
|
||||
val today = format.parse(format.format(Date())).time
|
||||
val day = timestamp * 1000
|
||||
if (day >= today && day < today + 86400 * 1000) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断时间戳是多少天前
|
||||
*/
|
||||
fun getBeforeDays(timestamp: Long):Int{
|
||||
var days: Long = 0
|
||||
val format = SimpleDateFormat("yyyyMMdd HH:mm", Locale.getDefault())
|
||||
try {
|
||||
val today = format.parse(format.format(Date())).time
|
||||
val day = timestamp * 1000
|
||||
days = (today - day) / 86400000
|
||||
} catch (e: ParseException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
return days.toInt()
|
||||
}
|
||||
}
|
||||
@ -1,188 +0,0 @@
|
||||
package com.gh.common.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.net.TrafficStats;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
|
||||
public class TrafficUtils {
|
||||
|
||||
static TrafficUtils instance;
|
||||
DB db;
|
||||
Context context;
|
||||
|
||||
private TrafficUtils(Context context) {
|
||||
this.context = context.getApplicationContext();
|
||||
db = new DB(this.context);
|
||||
}
|
||||
|
||||
public static TrafficUtils getInstance(Context context) {
|
||||
return getInstance(context, false);
|
||||
}
|
||||
|
||||
public static TrafficUtils getInstance(Context context, boolean update) {
|
||||
if (instance == null) {
|
||||
synchronized (TrafficUtils.class) {
|
||||
if (instance == null) {
|
||||
instance = new TrafficUtils(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (update) {
|
||||
instance.update();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
|
||||
// 获取所有的安装在手机上的应用软件的信息,并且获取这些软件里面的权限信息
|
||||
PackageManager pm = context.getPackageManager();// 获取系统应用包管理
|
||||
// 获取每个包内的androidmanifest.xml信息,它的权限等等
|
||||
List<PackageInfo> pinfos = pm.getInstalledPackages(PackageManager.GET_PERMISSIONS);
|
||||
// 遍历每个应用包信息
|
||||
for (PackageInfo info : pinfos) {
|
||||
// 请求每个程序包对应的androidManifest.xml里面的权限
|
||||
String[] premissions = info.requestedPermissions;
|
||||
if (premissions != null && premissions.length > 0) {
|
||||
// 找出需要网络服务的应用程序
|
||||
for (String premission : premissions) {
|
||||
if ("android.permission.INTERNET".equals(premission)) {
|
||||
// 获取每个应用程序在操作系统内的进程id
|
||||
int uId = info.applicationInfo.uid;
|
||||
// 如果返回-1,代表不支持使用该方法,注意必须是2.2以上的
|
||||
long rx = TrafficStats.getUidRxBytes(uId);
|
||||
// 如果返回-1,代表不支持使用该方法,注意必须是2.2以上的
|
||||
long tx = TrafficStats.getUidTxBytes(uId);
|
||||
if (rx >= 0 && tx >= 0) {
|
||||
db.update(info.packageName, rx + tx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
db.clean(Calendar.getInstance().getTimeInMillis() - 3600 * 24 * 30);
|
||||
}
|
||||
|
||||
public long getTraffic(List<String> packageNameList) {
|
||||
return this.getTraffic(packageNameList, Calendar.getInstance()
|
||||
.getTimeInMillis() - 3600 * 24 * 7);
|
||||
}
|
||||
|
||||
public long getTraffic(List<String> packageNameList, long from) {
|
||||
return this.getTraffic(packageNameList, from, Calendar.getInstance()
|
||||
.getTimeInMillis());
|
||||
}
|
||||
|
||||
public long getTraffic(List<String> packageNameList, long from, long to) {
|
||||
long traffic = 0;
|
||||
for (String packageName : packageNameList) {
|
||||
traffic += db.getTraffic(packageName, from, to);
|
||||
}
|
||||
return traffic;
|
||||
}
|
||||
|
||||
public long getTraffice(String packageName) {
|
||||
return db.getTraffic(packageName, Calendar.getInstance()
|
||||
.getTimeInMillis() - 3600 * 24 * 7);
|
||||
}
|
||||
|
||||
public long getTraffice(String packageName, long from) {
|
||||
return db.getTraffic(packageName, from);
|
||||
}
|
||||
|
||||
class DB extends SQLiteOpenHelper {
|
||||
|
||||
static final String name = "gh_traffic.db";
|
||||
static final int version = 1;
|
||||
String traffic = "CREATE TABLE traffic(" + "package text,"
|
||||
+ "traffic integer not null," + "time integer not null" + ");";
|
||||
|
||||
public DB(Context context) {
|
||||
super(context, name, null, version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
db.execSQL(traffic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
}
|
||||
|
||||
public void update(String packageName, long traffic) {
|
||||
String sql = "INSERT INTO traffic (package, traffic, time) VALUES (?, ?, ?)";
|
||||
Object[] bindArgs = new Object[]{packageName, traffic,
|
||||
Calendar.getInstance().getTimeInMillis()};
|
||||
this.getWritableDatabase().execSQL(sql, bindArgs);
|
||||
}
|
||||
|
||||
public void clean(long time) {
|
||||
String sql = "DELETE FROM traffic WHERE time < ? ";
|
||||
Object[] bindArgs = new Object[]{time};
|
||||
this.getWritableDatabase().execSQL(sql, bindArgs);
|
||||
}
|
||||
|
||||
public long getTraffic(String packageName, long from) {
|
||||
return getTraffic(packageName, from, Calendar.getInstance()
|
||||
.getTimeInMillis());
|
||||
}
|
||||
|
||||
public long getTraffic(String packageName, long from, long to) {
|
||||
long traffic = 0;
|
||||
String sql = "SELECT * FROM traffic WHERE package = ? AND time >= ? AND time <= ? ORDER BY time DESC";
|
||||
String selectionArgs[] = new String[]{packageName,
|
||||
String.valueOf(from), String.valueOf(to)};
|
||||
Cursor cursor = this.getReadableDatabase().rawQuery(sql,
|
||||
selectionArgs);
|
||||
if (cursor.getCount() > 1) {
|
||||
cursor.moveToFirst();
|
||||
long traffic1 = cursor
|
||||
.getLong(cursor.getColumnIndex("traffic"));
|
||||
// long time1 = cursor.getLong(cursor.getColumnIndex("time"));
|
||||
cursor.moveToLast();
|
||||
long traffic2 = cursor
|
||||
.getLong(cursor.getColumnIndex("traffic"));
|
||||
// long time2 = cursor.getLong(cursor.getColumnIndex("time"));
|
||||
traffic = traffic1 - traffic2;
|
||||
// long cha = traffic1 - traffic2;
|
||||
// if(cha > 0){
|
||||
// traffic = cha / (time2 - time1);
|
||||
// }
|
||||
} else if (cursor.getCount() == 1) {
|
||||
sql = "SELECT * FROM traffic WHERE package = ? AND time < ? ORDER BY time DESC";
|
||||
selectionArgs = new String[]{packageName,
|
||||
String.valueOf(from)};
|
||||
Cursor cursor2 = this.getReadableDatabase().rawQuery(sql,
|
||||
selectionArgs);
|
||||
if (cursor2.moveToNext()) {
|
||||
cursor.moveToFirst();
|
||||
long traffic1 = cursor.getLong(cursor
|
||||
.getColumnIndex("traffic"));
|
||||
// long time1 =
|
||||
// cursor.getLong(cursor.getColumnIndex("time"));
|
||||
long traffic2 = cursor2.getLong(cursor
|
||||
.getColumnIndex("traffic"));
|
||||
// long time2 =
|
||||
// cursor2.getLong(cursor.getColumnIndex("time"));
|
||||
traffic = traffic1 - traffic2;
|
||||
// long cha = traffic1 - traffic2;
|
||||
// if(cha > 0){
|
||||
// traffic = cha / (time2 - time1);
|
||||
// }
|
||||
}
|
||||
cursor2.close();
|
||||
}
|
||||
cursor.close();
|
||||
return traffic;
|
||||
}
|
||||
}
|
||||
}
|
||||
209
app/src/main/java/com/gh/common/view/AdBannerView.kt
Normal file
209
app/src/main/java/com/gh/common/view/AdBannerView.kt
Normal file
@ -0,0 +1,209 @@
|
||||
package com.gh.common.view
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.*
|
||||
import android.widget.LinearLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.facebook.drawee.view.SimpleDraweeView
|
||||
import com.facebook.imagepipeline.image.ImageInfo
|
||||
import com.gh.common.util.DisplayUtils
|
||||
import com.gh.common.util.ImageUtils
|
||||
import com.gh.common.util.rxTimer
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.entity.SettingsEntity
|
||||
import io.reactivex.disposables.Disposable
|
||||
import kotlin.math.abs
|
||||
|
||||
|
||||
/**
|
||||
* 视频流广告轮播控件
|
||||
*/
|
||||
class AdBannerView : LinearLayout {
|
||||
|
||||
private lateinit var mViewPager2: ViewPager2
|
||||
private var mDatas = ArrayList<SettingsEntity.Advertisement>()
|
||||
var isAutoPlay = true
|
||||
private var currentPage = 0
|
||||
private var autoDurationTime = 6000L
|
||||
private var mRxTimer: Disposable? = null
|
||||
private var lastX: Float = 0F
|
||||
private var lastY: Float = 0F
|
||||
private var startX: Float = 0F
|
||||
private var startY: Float = 0F
|
||||
private val mTouchSlop = ViewConfiguration.get(context).scaledTouchSlop
|
||||
private val bannerWidth = DisplayUtils.dip2px(52f)
|
||||
var onItemClick: ((position: Int) -> Unit)? = null
|
||||
private lateinit var mIndicatorLayout: LinearLayout
|
||||
|
||||
constructor(context: Context) : super(context)
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
|
||||
|
||||
init {
|
||||
initViews()
|
||||
}
|
||||
|
||||
private fun initViews() {
|
||||
orientation = VERTICAL
|
||||
mViewPager2 = ViewPager2(context).apply {
|
||||
layoutParams = ViewGroup.LayoutParams(bannerWidth, bannerWidth)
|
||||
orientation = ViewPager2.ORIENTATION_HORIZONTAL
|
||||
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
super.onPageSelected(position)
|
||||
currentPage = position
|
||||
slideIndicator(currentPage % mDatas.size)
|
||||
}
|
||||
})
|
||||
}
|
||||
val recyclerView = mViewPager2.getChildAt(0) as RecyclerView
|
||||
recyclerView.overScrollMode = RecyclerView.OVER_SCROLL_NEVER
|
||||
addView(mViewPager2)
|
||||
mIndicatorLayout = LinearLayout(context).apply {
|
||||
orientation = HORIZONTAL
|
||||
gravity = Gravity.CENTER
|
||||
val params = LayoutParams(LayoutParams.MATCH_PARENT, DisplayUtils.dip2px(5F))
|
||||
params.topMargin = DisplayUtils.dip2px(8f)
|
||||
layoutParams = params
|
||||
}
|
||||
addView(mIndicatorLayout)
|
||||
}
|
||||
|
||||
|
||||
fun start(datas: ArrayList<SettingsEntity.Advertisement>) {
|
||||
if (datas.isNullOrEmpty()) throw IllegalArgumentException("广告列表不能为空")
|
||||
val adapter = mViewPager2.adapter
|
||||
mDatas = datas
|
||||
if (adapter == null) {
|
||||
val bannerAdapter = AdBannerAdapter()
|
||||
mViewPager2.adapter = bannerAdapter
|
||||
} else {
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
currentPage = (mViewPager2.adapter as AdBannerAdapter).getActualFirstPositionInCenter()
|
||||
mViewPager2.setCurrentItem(currentPage, false)
|
||||
if (mDatas.size > 1) {
|
||||
addIndicator()
|
||||
slideIndicator(currentPage % mDatas.size)
|
||||
autoPlay()
|
||||
}
|
||||
}
|
||||
|
||||
private fun autoPlay() {
|
||||
if (isAutoPlay && mDatas.size > 1) {
|
||||
if (mRxTimer != null && !mRxTimer!!.isDisposed) {
|
||||
mRxTimer?.dispose()
|
||||
mRxTimer = null
|
||||
}
|
||||
mRxTimer = rxTimer(autoDurationTime) {
|
||||
slidePage()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun slidePage() {
|
||||
currentPage++
|
||||
mViewPager2.setCurrentItem(currentPage, true)
|
||||
}
|
||||
|
||||
fun resumePage() {
|
||||
autoPlay()
|
||||
}
|
||||
|
||||
fun pausePage() {
|
||||
if (mRxTimer != null && !mRxTimer!!.isDisposed) {
|
||||
mRxTimer?.dispose()
|
||||
mRxTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
private fun addIndicator() {
|
||||
mIndicatorLayout.removeAllViews()
|
||||
mDatas.forEach { _ ->
|
||||
val view = View(context).apply {
|
||||
background = ContextCompat.getDrawable(context, R.drawable.selector_video_detail_ad_indicator)
|
||||
val params = LayoutParams(DisplayUtils.dip2px(4F), DisplayUtils.dip2px(4F))
|
||||
params.leftMargin = DisplayUtils.dip2px(2F)
|
||||
params.rightMargin = DisplayUtils.dip2px(2F)
|
||||
layoutParams = params
|
||||
}
|
||||
mIndicatorLayout.addView(view)
|
||||
}
|
||||
}
|
||||
|
||||
private fun slideIndicator(position: Int) {
|
||||
for (i in 0 until mIndicatorLayout.childCount) {
|
||||
val childAt = mIndicatorLayout.getChildAt(i)
|
||||
childAt.isSelected = i == position
|
||||
}
|
||||
}
|
||||
|
||||
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
|
||||
val action = ev.action
|
||||
if (action == MotionEvent.ACTION_DOWN) {
|
||||
lastX = ev.rawX
|
||||
startX = lastX
|
||||
lastY = ev.rawY
|
||||
startY = lastY
|
||||
} else if (action == MotionEvent.ACTION_MOVE) {
|
||||
lastX = ev.rawX
|
||||
lastY = ev.rawY
|
||||
val distanceX = abs(lastX - startX)
|
||||
val distanceY = abs(lastY - startY)
|
||||
val disallowIntercept: Boolean
|
||||
disallowIntercept = if (mViewPager2.orientation == ViewPager2.ORIENTATION_HORIZONTAL) {
|
||||
distanceX > mTouchSlop && distanceX > distanceY
|
||||
} else {
|
||||
distanceY > mTouchSlop && distanceY > distanceX
|
||||
}
|
||||
parent.requestDisallowInterceptTouchEvent(disallowIntercept)
|
||||
} else if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
|
||||
return abs(lastX - startX) > mTouchSlop || abs(lastY - startY) > mTouchSlop
|
||||
}
|
||||
return super.onInterceptTouchEvent(ev)
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
if (mRxTimer != null && !mRxTimer!!.isDisposed) {
|
||||
mRxTimer?.dispose()
|
||||
mRxTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
fun getAdDatas(): ArrayList<SettingsEntity.Advertisement> {
|
||||
return mDatas
|
||||
}
|
||||
|
||||
|
||||
private inner class AdBannerAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return object : RecyclerView.ViewHolder(LayoutInflater.from(context).inflate(R.layout.item_ad_banner, parent, false)) {}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = if (mDatas.size == 1) mDatas.size else Int.MAX_VALUE
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
val ad = mDatas[position % mDatas.size]
|
||||
val view = holder.itemView as SimpleDraweeView
|
||||
ImageUtils.display(view, ad.image)
|
||||
holder.itemView.setOnClickListener {
|
||||
onItemClick?.invoke(position % mDatas.size)
|
||||
}
|
||||
}
|
||||
|
||||
fun getActualFirstPositionInCenter(): Int {
|
||||
if (mDatas.size == 1) return 0
|
||||
var index = itemCount / 2
|
||||
if (index % mDatas.size != 0) {
|
||||
index -= (index % mDatas.size)
|
||||
}
|
||||
return index - 1
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -14,26 +14,26 @@ public class CenterImageSpan extends ImageSpan {
|
||||
public CenterImageSpan(Context context, int resourceId) {
|
||||
super(context, resourceId);
|
||||
}
|
||||
|
||||
public CenterImageSpan(Drawable drawable) {
|
||||
super( drawable);
|
||||
super(drawable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize(Paint paint, CharSequence text, int start, int end,
|
||||
Paint.FontMetricsInt fm) {
|
||||
Drawable d = getDrawable();
|
||||
Rect rect = d.getBounds();
|
||||
if (fm != null) {
|
||||
Paint.FontMetricsInt fontMetricsInt) {
|
||||
Drawable drawable = getDrawable();
|
||||
Rect rect = drawable.getBounds();
|
||||
if (fontMetricsInt != null) {
|
||||
Paint.FontMetricsInt fmPaint = paint.getFontMetricsInt();
|
||||
int fontHeight = fmPaint.bottom - fmPaint.top;
|
||||
int fontHeight = fmPaint.descent - fmPaint.ascent;
|
||||
int drHeight = rect.bottom - rect.top;
|
||||
int centerY = fmPaint.ascent + fontHeight / 2;
|
||||
|
||||
int top = drHeight / 2 - fontHeight / 4;
|
||||
int bottom = drHeight / 2 + fontHeight / 4;
|
||||
|
||||
fm.ascent = -bottom;
|
||||
fm.top = -bottom;
|
||||
fm.bottom = top;
|
||||
fm.descent = top;
|
||||
fontMetricsInt.ascent = centerY - drHeight / 2;
|
||||
fontMetricsInt.top = fontMetricsInt.ascent;
|
||||
fontMetricsInt.bottom = centerY + drHeight / 2 + 1; // fuck 这里不加 1px 会导致图片底部被截掉 1px
|
||||
fontMetricsInt.descent = fontMetricsInt.bottom;
|
||||
}
|
||||
return rect.right;
|
||||
}
|
||||
@ -41,14 +41,14 @@ public class CenterImageSpan extends ImageSpan {
|
||||
@Override
|
||||
public void draw(Canvas canvas, CharSequence text, int start, int end,
|
||||
float x, int top, int y, int bottom, Paint paint) {
|
||||
Drawable b = getDrawable();
|
||||
Drawable drawable = getDrawable();
|
||||
canvas.save();
|
||||
int transY;
|
||||
transY = ((bottom - top) - b.getBounds().bottom) / 2 + top;
|
||||
Paint.FontMetricsInt fmPaint = paint.getFontMetricsInt();
|
||||
int fontHeight = fmPaint.descent - fmPaint.ascent;
|
||||
int centerY = y + fmPaint.descent - fontHeight / 2;
|
||||
int transY = centerY - (drawable.getBounds().bottom - drawable.getBounds().top) / 2;
|
||||
canvas.translate(x, transY);
|
||||
if (b.isVisible()) {
|
||||
b.draw(canvas);
|
||||
}
|
||||
drawable.draw(canvas);
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
package com.gh.common.view
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Rect
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
class CustomDividerItemDecoration(
|
||||
context: Context,
|
||||
var onlyDecorateTheFirstItem: Boolean = false,
|
||||
var notDecorateTheFirstItem: Boolean = false,
|
||||
var notDecorateTheLastItem: Boolean = false,
|
||||
var notDecorateTheFirstTwoItems: Boolean = false) : DividerItemDecoration(context, LinearLayout.VERTICAL) {
|
||||
|
||||
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
|
||||
if (onlyDecorateTheFirstItem) {
|
||||
if (parent.getChildAdapterPosition(view) == 0) outRect[0, 0, 0] = drawable!!.intrinsicHeight
|
||||
} else {
|
||||
if (parent.getChildAdapterPosition(view) == 0
|
||||
&& (notDecorateTheFirstItem || notDecorateTheFirstTwoItems)) {
|
||||
outRect.set(0, 0, 0, 0)
|
||||
} else if (parent.getChildAdapterPosition(view) == 1 && notDecorateTheFirstTwoItems) {
|
||||
outRect.set(0, 0, 0, 0)
|
||||
} else if (parent.getChildAdapterPosition(view) == parent.adapter!!.itemCount - 1 && notDecorateTheLastItem) {
|
||||
outRect.set(0, 0, 0, 0)
|
||||
} else {
|
||||
outRect[0, 0, 0] = drawable!!.intrinsicHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,25 +1,63 @@
|
||||
package com.gh.common.view;
|
||||
|
||||
import android.text.Layout;
|
||||
import android.text.Selection;
|
||||
import android.text.Spannable;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class CustomLinkMovementMethod extends LinkMovementMethod {
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
|
||||
boolean b = super.onTouchEvent(widget, buffer, event);
|
||||
int action = event.getAction();
|
||||
boolean isTouchEventConsumed = false;
|
||||
|
||||
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) {
|
||||
int x = (int) event.getX();
|
||||
int y = (int) event.getY();
|
||||
|
||||
x -= widget.getTotalPaddingLeft();
|
||||
y -= widget.getTotalPaddingTop();
|
||||
|
||||
x += widget.getScrollX();
|
||||
y += widget.getScrollY();
|
||||
|
||||
Layout layout = widget.getLayout();
|
||||
int line = layout.getLineForVertical(y);
|
||||
int off = layout.getOffsetForHorizontal(line, x);
|
||||
|
||||
ClickableSpan[] links = buffer.getSpans(off, off, ClickableSpan.class);
|
||||
|
||||
if (links.length != 0) {
|
||||
ClickableSpan link = links[0];
|
||||
if (action == MotionEvent.ACTION_UP) {
|
||||
link.onClick(widget);
|
||||
} else if (action == MotionEvent.ACTION_DOWN) {
|
||||
// 下面这行代码会造成触摸 clickbleSpan 时 RecyclerView 的 item 高度变更 : (
|
||||
// 看起来像是跟文字选择相关的东西,反正我们的 clickSpan 也不让选,直接屏蔽掉了
|
||||
// Selection.setSelection(buffer,
|
||||
// buffer.getSpanStart(link),
|
||||
// buffer.getSpanEnd(link));
|
||||
}
|
||||
isTouchEventConsumed = true;
|
||||
} else {
|
||||
Selection.removeSelection(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
//解决点击事件冲突问题
|
||||
if (!b && event.getAction() == MotionEvent.ACTION_UP) {
|
||||
if (!isTouchEventConsumed && event.getAction() == MotionEvent.ACTION_UP) {
|
||||
ViewParent parent = iterateViewParentForClicking(widget.getParent());//处理widget的父控件点击事件
|
||||
if (parent != null && parent instanceof ViewGroup) {
|
||||
if (parent instanceof ViewGroup) {
|
||||
return ((ViewGroup) parent).performClick();
|
||||
}
|
||||
}
|
||||
return b;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -33,14 +71,13 @@ public class CustomLinkMovementMethod extends LinkMovementMethod {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static CustomLinkMovementMethod getInstance() {
|
||||
if (sInstance == null) sInstance = new CustomLinkMovementMethod();
|
||||
|
||||
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static CustomLinkMovementMethod sInstance;
|
||||
|
||||
|
||||
}
|
||||
@ -1,662 +0,0 @@
|
||||
package com.gh.common.view;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
import androidx.viewpager.widget.ViewPager.OnPageChangeListener;
|
||||
import android.text.TextUtils;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.LinearLayout.LayoutParams;
|
||||
import android.widget.PopupWindow;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.gh.common.constant.Config;
|
||||
import com.gh.common.exposure.ExposureEvent;
|
||||
import com.gh.common.util.ClickUtils;
|
||||
import com.gh.common.util.DirectUtils;
|
||||
import com.gh.common.util.DisplayUtils;
|
||||
import com.gh.common.util.PackageUtils;
|
||||
import com.gh.common.util.PlatformUtils;
|
||||
import com.gh.common.util.SPUtils;
|
||||
import com.gh.download.DownloadManager;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.adapter.OnCollectionCallBackListener;
|
||||
import com.gh.gamecenter.adapter.PlatformPagerAdapter;
|
||||
import com.gh.gamecenter.databinding.ImprintContentItemBinding;
|
||||
import com.gh.gamecenter.entity.ApkEntity;
|
||||
import com.gh.gamecenter.entity.ApkLink;
|
||||
import com.gh.gamecenter.entity.GameCollectionEntity;
|
||||
import com.gh.gamecenter.entity.GameEntity;
|
||||
import com.gh.gamecenter.entity.LinkEntity;
|
||||
import com.gh.gamecenter.entity.SettingsEntity;
|
||||
import com.gh.gamecenter.eventbus.EBDownloadStatus;
|
||||
import com.gh.gamecenter.eventbus.EBPackage;
|
||||
import com.gh.gamecenter.eventbus.EBReuse;
|
||||
import com.gh.gamecenter.manager.PackagesManager;
|
||||
import com.lightgame.download.DataWatcher;
|
||||
import com.lightgame.download.DownloadEntity;
|
||||
import com.lightgame.download.DownloadStatus;
|
||||
import com.lightgame.utils.Utils;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
|
||||
/**
|
||||
* @author 温冠超
|
||||
* @email 294299195@qq.com
|
||||
* @date 2015-8-12
|
||||
* @update 2015-8-12
|
||||
* @des 弹出游戏版本下载按钮,点击并添加到下载任务中
|
||||
*/
|
||||
public class DownloadDialog implements OnCollectionCallBackListener {
|
||||
private Context mContext;
|
||||
|
||||
private PopupWindow popupWindow;
|
||||
|
||||
private ViewPager viewPager;
|
||||
private ViewPager collectionViewPager;
|
||||
private LinearLayout dialog_ll_collection_hint;
|
||||
private LinearLayout dialog_ll_hint;
|
||||
private LinearLayout dialog_ll_collection;
|
||||
private View mDialogMorePlatformHint;
|
||||
|
||||
private List<ApkEntity> gameApk;
|
||||
private GameEntity gameEntity;
|
||||
|
||||
private PlatformPagerAdapter adapter;
|
||||
private PlatformPagerAdapter collectionAdapter;
|
||||
|
||||
private ExposureEvent traceEvent;
|
||||
|
||||
private static final String DOWNLOAD_PLATFORM_HINT = "download_platform_hint";
|
||||
|
||||
private String entrance;
|
||||
private String location;
|
||||
private String mAutoDownloadPlatform;
|
||||
|
||||
private final int row = 3;
|
||||
private final int column = 3;
|
||||
private boolean isLoadPlatform;
|
||||
|
||||
private DataWatcher dataWatcher = new DataWatcher() {
|
||||
@Override
|
||||
public void onDataChanged(DownloadEntity downloadEntity) {
|
||||
if (downloadEntity.getName().equals(gameEntity.getName())
|
||||
&& !DownloadStatus.delete.equals(DownloadManager.getInstance(mContext).getStatus(downloadEntity.getUrl()))) {
|
||||
|
||||
adapter.putDownloadEntity(downloadEntity);
|
||||
|
||||
if (collectionAdapter != null) {
|
||||
collectionAdapter.putDownloadEntity(downloadEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private DownloadDialog(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public static DownloadDialog getInstance(Context context) {
|
||||
return new DownloadDialog(context);
|
||||
}
|
||||
|
||||
// 自动下载并翻到相应页面
|
||||
public void showPopupWindowAutoDownload(View view, GameEntity gameEntity, String autoDownloadPlatform,
|
||||
String entrance, String location, ExposureEvent traceEvent) {
|
||||
mAutoDownloadPlatform = autoDownloadPlatform;
|
||||
showPopupWindow(view, gameEntity, entrance, location, traceEvent);
|
||||
}
|
||||
|
||||
public void showPopupWindow(View view, GameEntity gameEntity, String entrance, String location) {
|
||||
showPopupWindow(view, gameEntity, entrance, location, null);
|
||||
}
|
||||
|
||||
public void showPopupWindow(View view, GameEntity gameEntity, String entrance, String location,
|
||||
@Nullable ExposureEvent traceEvent) {
|
||||
if (ClickUtils.isFastDoubleClick()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.gameEntity = gameEntity;
|
||||
this.entrance = entrance;
|
||||
this.location = location;
|
||||
this.traceEvent = traceEvent;
|
||||
|
||||
gameApk = gameEntity.getApk();
|
||||
if (gameEntity.getCollection() != null) {
|
||||
mergeApkCollection(gameEntity);
|
||||
}
|
||||
|
||||
if (gameEntity.getApkLink() != null) {
|
||||
mergeApkLink(gameEntity);
|
||||
}
|
||||
|
||||
sortApk();
|
||||
|
||||
// 一个自定义的布局,作为显示的内容
|
||||
View contentView = View.inflate(mContext, R.layout.game_download_dialog, null);
|
||||
|
||||
TextView title = contentView.findViewById(R.id.dialog_title);
|
||||
title.setText(gameEntity.getName());
|
||||
|
||||
viewPager = contentView.findViewById(R.id.dialog_viewPager);
|
||||
dialog_ll_hint = contentView.findViewById(R.id.dialog_ll_hint);
|
||||
dialog_ll_collection = contentView.findViewById(R.id.dialog_ll_collection);
|
||||
dialog_ll_collection_hint = contentView.findViewById(R.id.dialog_ll_collection_hint);
|
||||
collectionViewPager = contentView.findViewById(R.id.dialog_collection_viewPager);
|
||||
mDialogMorePlatformHint = contentView.findViewById(R.id.dialog_more_platform_hit);
|
||||
TextView dialogAd = contentView.findViewById(R.id.dialog_ad);
|
||||
TextView dialogAdMirror = contentView.findViewById(R.id.dialog_ad_mirror);
|
||||
mDialogMorePlatformHint.setOnClickListener(v -> {
|
||||
mDialogMorePlatformHint.setVisibility(View.GONE);
|
||||
SPUtils.setBoolean(DOWNLOAD_PLATFORM_HINT, false);
|
||||
});
|
||||
|
||||
LinkEntity downloadAd = gameEntity.getDownloadAd();
|
||||
if (downloadAd != null) {
|
||||
dialogAd.setVisibility(View.VISIBLE);
|
||||
dialogAd.setText(downloadAd.getTitle());
|
||||
dialogAdMirror.setVisibility(View.INVISIBLE);
|
||||
dialogAdMirror.setText(downloadAd.getTitle());
|
||||
dialogAd.setOnClickListener(v -> {
|
||||
if ("imprint".equals(downloadAd.getType())) {
|
||||
showImprintDialog(downloadAd.getTitle());
|
||||
} else {
|
||||
DirectUtils.directToLinkPage(
|
||||
mContext,
|
||||
downloadAd,
|
||||
entrance, "下载多平台弹窗");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
isLoadPlatform = false;
|
||||
|
||||
init(new ArrayList<>(gameApk));
|
||||
|
||||
viewPager.addOnPageChangeListener(new MyPageChangeListener(dialog_ll_hint));
|
||||
|
||||
popupWindow = new PopupWindow(contentView, LayoutParams.MATCH_PARENT,
|
||||
LayoutParams.MATCH_PARENT, true);
|
||||
|
||||
contentView.setOnClickListener(v -> popupWindow.dismiss());
|
||||
contentView.setFocusable(true);
|
||||
contentView.setFocusableInTouchMode(true);
|
||||
contentView.setOnKeyListener((v, keyCode, event) -> {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0
|
||||
&& popupWindow != null && popupWindow.isShowing()) {
|
||||
popupWindow.dismiss();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
popupWindow.setOnDismissListener(() -> {
|
||||
EventBus.getDefault().unregister(DownloadDialog.this);
|
||||
DownloadManager.getInstance(mContext).removeObserver(dataWatcher);
|
||||
});
|
||||
|
||||
popupWindow.setTouchable(true);
|
||||
popupWindow.setFocusable(true);
|
||||
|
||||
// 设置popWindow的显示和消失动画
|
||||
popupWindow.setAnimationStyle(R.style.mypopwindow_anim_style);
|
||||
|
||||
// 设置好参数之后再show
|
||||
popupWindow.showAtLocation(view, Gravity.BOTTOM, 0, 0);
|
||||
|
||||
if (!EventBus.getDefault().isRegistered(DownloadDialog.this)) {
|
||||
EventBus.getDefault().register(DownloadDialog.this);
|
||||
}
|
||||
|
||||
DownloadManager.getInstance(mContext).addObserver(dataWatcher);
|
||||
}
|
||||
|
||||
private void showImprintDialog(String titleName) {
|
||||
Dialog dialog = new Dialog(mContext, R.style.full_dialog);
|
||||
View inflate = LayoutInflater.from(mContext).inflate(R.layout.imprint_dialog, null);
|
||||
dialog.setContentView(inflate);
|
||||
dialog.show();
|
||||
|
||||
Window window = dialog.getWindow();
|
||||
WindowManager.LayoutParams params;
|
||||
if (window != null) {
|
||||
params = window.getAttributes();
|
||||
params.width = (int) (mContext.getResources().getDisplayMetrics().widthPixels * 0.9);
|
||||
window.setAttributes(params);
|
||||
window.setBackgroundDrawableResource(R.drawable.full_dialog_background);
|
||||
}
|
||||
|
||||
inflate.findViewById(R.id.imprint_close).setOnClickListener(v -> dialog.dismiss());
|
||||
LinearLayout content = inflate.findViewById(R.id.imprint_content);
|
||||
((TextView) inflate.findViewById(R.id.imprint_title)).setText(titleName);
|
||||
View head = LayoutInflater.from(mContext).inflate(R.layout.imprint_content_item, null);
|
||||
content.addView(head, LayoutParams.MATCH_PARENT, DisplayUtils.dip2px(30));
|
||||
LimitHeightLinearLayout imprintContainer = inflate.findViewById(R.id.imprint_container);
|
||||
imprintContainer.setLimitHeight((int) (mContext.getResources().getDisplayMetrics().heightPixels * 0.8));
|
||||
|
||||
ArrayList<ApkEntity> list = gameEntity.getApk();
|
||||
SettingsEntity settings = Config.getSettings();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
ApkEntity apk = gameEntity.getApk().get(i);
|
||||
if (settings != null && settings.getGameDownloadBlackList().contains(apk.getPackageName())) {
|
||||
continue;
|
||||
}
|
||||
View item = LayoutInflater.from(mContext).inflate(R.layout.imprint_content_item, null);
|
||||
ImprintContentItemBinding bind = DataBindingUtil.bind(item);
|
||||
bind.setApk(apk);
|
||||
bind.setPlatformName(PlatformUtils.getInstance(mContext).getPlatformName(apk.getPlatform()));
|
||||
content.addView(item, LayoutParams.MATCH_PARENT, DisplayUtils.dip2px(40));
|
||||
}
|
||||
|
||||
|
||||
// close line
|
||||
View view = new View(mContext);
|
||||
view.setBackgroundColor(mContext.getResources().getColor(R.color.text_5d5d5d));
|
||||
view.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, DisplayUtils.dip2px(1)));
|
||||
content.addView(view);
|
||||
}
|
||||
|
||||
private void sortApk() {
|
||||
for (ApkEntity apkEntity : gameApk) {
|
||||
GameCollectionEntity apkCollection = apkEntity.getApkCollection();
|
||||
if (apkCollection != null) {
|
||||
List<ApkEntity> saveApkEntity = apkCollection.getSaveApkEntity();
|
||||
if (saveApkEntity != null) {
|
||||
for (ApkEntity entity : saveApkEntity) {
|
||||
int sortValue = getSortValue(entity);
|
||||
if (apkEntity.getOrder() < sortValue) {
|
||||
apkEntity.setOrder(sortValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
apkEntity.setOrder(getSortValue(apkEntity));
|
||||
}
|
||||
|
||||
Comparator<ApkEntity> comparator = (lhs, rhs) -> rhs.getOrder() - lhs.getOrder();
|
||||
Collections.sort(gameApk, comparator);
|
||||
}
|
||||
|
||||
private int getSortValue(ApkEntity apkEntity) {
|
||||
/*
|
||||
* 安装插件 10
|
||||
* 插件化下载中 9
|
||||
* 插件化 8
|
||||
* 安装更新 7
|
||||
* 更新下载中 6
|
||||
* 更新 5
|
||||
* 安装 4
|
||||
* 下载中 3
|
||||
* 启动 2
|
||||
* 默认(有图片)1
|
||||
* 默认(无图片)0
|
||||
*/
|
||||
|
||||
Object gh_id;
|
||||
String packageName = apkEntity.getPackageName();
|
||||
DownloadEntity downloadEntity = DownloadManager.getInstance(mContext).getDownloadEntityByUrl(apkEntity.getUrl());
|
||||
if (downloadEntity == null) {
|
||||
if (!TextUtils.isEmpty(packageName) && PackagesManager.INSTANCE.isInstalled(packageName)) {
|
||||
gh_id = PackageUtils.getMetaData(mContext, packageName, "gh_id");
|
||||
if (gh_id == null || gh_id.equals(gameEntity.getId())) {
|
||||
if (!PackageUtils.isSignature(mContext, packageName)) {
|
||||
return 8;
|
||||
} else if (PackagesManager.INSTANCE.isCanUpdate(gameEntity.getId(), packageName)) {
|
||||
return 5;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (apkEntity.getOrder() < 1) apkEntity.setOrder(1);
|
||||
|
||||
String platform = apkEntity.getPlatform();
|
||||
int id = PlatformUtils.getInstance(mContext).getPlatformPic(platform);
|
||||
if (id == 0) {
|
||||
String path = PlatformUtils.getInstance(mContext).getPlatformPicUrl(platform);
|
||||
if (path == null) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (downloadEntity.getStatus().equals(DownloadStatus.done)) {
|
||||
if (downloadEntity.isPluggable()) {
|
||||
return 10;
|
||||
} else if (downloadEntity.isUpdate()) {
|
||||
return 7;
|
||||
} else {
|
||||
return 4;
|
||||
}
|
||||
} else {
|
||||
if (downloadEntity.isPluggable()) {
|
||||
return 9;
|
||||
} else if (downloadEntity.isUpdate()) {
|
||||
return 6;
|
||||
} else {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 合并ApkLink
|
||||
private void mergeApkLink(GameEntity gameEntity) {
|
||||
for (ApkLink apkLink : gameEntity.getApkLink()) {
|
||||
if (!TextUtils.isEmpty(apkLink.getCollection())) {
|
||||
for (int i = 0; i < gameApk.size(); i++) {
|
||||
ApkEntity apkEntity = gameApk.get(i);
|
||||
GameCollectionEntity apkCollection = apkEntity.getApkCollection();
|
||||
if (apkCollection != null && apkCollection.getSaveApkEntity() != null) {
|
||||
List<ApkEntity> saveApkEntity = apkCollection.getSaveApkEntity();
|
||||
if (apkLink.getCollection().equals(apkCollection.getId())) {
|
||||
ApkEntity element = new ApkEntity();
|
||||
element.setApkLink(apkLink);
|
||||
if (saveApkEntity.size() > apkLink.getSort()) {
|
||||
saveApkEntity.add(apkLink.getSort(), element);
|
||||
} else {
|
||||
saveApkEntity.add(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ApkEntity element = new ApkEntity();
|
||||
element.setApkLink(apkLink);
|
||||
if (gameApk.size() > apkLink.getSort()) {
|
||||
gameApk.add(apkLink.getSort(), element);
|
||||
} else {
|
||||
gameApk.add(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 合并ApkCollection
|
||||
private void mergeApkCollection(GameEntity gameEntity) {
|
||||
List<ApkEntity> gameCollectionApk = new ArrayList<>();
|
||||
ConcurrentHashMap<String, Integer> hashMap = new ConcurrentHashMap<>();
|
||||
boolean isCollection;
|
||||
|
||||
for (ApkEntity apkEntity : gameApk) {
|
||||
isCollection = false;
|
||||
for (GameCollectionEntity gameCollectionEntity : gameEntity.getCollection()) {
|
||||
for (String packageName : gameCollectionEntity.getPackage()) {
|
||||
if (packageName.equals(apkEntity.getPackageName())) {
|
||||
isCollection = true;
|
||||
if (hashMap.get(gameCollectionEntity.getName()) != null) {
|
||||
gameCollectionApk.get(hashMap.get(gameCollectionEntity.getName()))
|
||||
.getApkCollection().getSaveApkEntity().add(apkEntity);
|
||||
} else {
|
||||
ApkEntity newApkEntity = new ApkEntity();
|
||||
GameCollectionEntity collectionEntity = new GameCollectionEntity();
|
||||
List<ApkEntity> saveApkList = new ArrayList<>();
|
||||
saveApkList.add(apkEntity);
|
||||
collectionEntity.setSaveApkEntity(saveApkList);
|
||||
collectionEntity.setId(gameCollectionEntity.getId());
|
||||
collectionEntity.setName(gameCollectionEntity.getName());
|
||||
collectionEntity.setIcon(gameCollectionEntity.getIcon());
|
||||
collectionEntity.setColor(gameCollectionEntity.getColor());
|
||||
newApkEntity.setApkCollection(collectionEntity);
|
||||
gameCollectionApk.add(newApkEntity);
|
||||
|
||||
hashMap.put(gameCollectionEntity.getName(), gameCollectionApk.size() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isCollection) {
|
||||
gameCollectionApk.add(apkEntity);
|
||||
}
|
||||
}
|
||||
|
||||
gameApk = gameCollectionApk;
|
||||
}
|
||||
|
||||
private void init(List<ApkEntity> apkList) {
|
||||
for (int i = 0; i < apkList.size(); i++) {
|
||||
String platformName = PlatformUtils.getInstance(mContext)
|
||||
.getPlatformName(apkList.get(i).getPlatform());
|
||||
if (platformName == null) {
|
||||
apkList.remove(i);
|
||||
i--;
|
||||
if (!isLoadPlatform) {
|
||||
PlatformUtils.getInstance(mContext).getPlatform();
|
||||
isLoadPlatform = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ApkEntity skipEntiy = new ApkEntity();
|
||||
skipEntiy.setPackageName("求版本");
|
||||
apkList.add(skipEntiy); // 添加一个跳转投票
|
||||
|
||||
dialog_ll_hint.removeAllViews();
|
||||
int size = (int) Math.ceil(apkList.size() / (double) (row * column));
|
||||
addHintPoint(dialog_ll_hint, size);
|
||||
|
||||
if (size >= 2) {
|
||||
boolean isShowPlatformHint = SPUtils.getBoolean(DOWNLOAD_PLATFORM_HINT, true);
|
||||
if (isShowPlatformHint) {
|
||||
mDialogMorePlatformHint.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
int count = apkList.size();
|
||||
int vpHeight;
|
||||
if (count <= 3) {
|
||||
vpHeight = 90;
|
||||
} else if (count <= 6) {
|
||||
vpHeight = 150;
|
||||
} else {
|
||||
vpHeight = 210;
|
||||
}
|
||||
ViewGroup.LayoutParams layoutParams = viewPager.getLayoutParams();
|
||||
layoutParams.height = DisplayUtils.dip2px(mContext, vpHeight);
|
||||
viewPager.setLayoutParams(layoutParams);
|
||||
|
||||
int currentItem = 0;
|
||||
if (viewPager != null) {
|
||||
currentItem = viewPager.getCurrentItem();
|
||||
}
|
||||
if (!TextUtils.isEmpty(mAutoDownloadPlatform)) {
|
||||
for (int i = 0; i < apkList.size(); i++) {
|
||||
ApkEntity apkEntity = apkList.get(i);
|
||||
if (mAutoDownloadPlatform.equals(apkEntity.getPlatform())) {
|
||||
currentItem = (i / 9);
|
||||
break;
|
||||
}
|
||||
GameCollectionEntity apkCollection = apkEntity.getApkCollection();
|
||||
if (apkCollection != null) {
|
||||
List<ApkEntity> saveApkEntity = apkCollection.getSaveApkEntity();
|
||||
if (saveApkEntity != null) {
|
||||
for (ApkEntity entity : saveApkEntity) {
|
||||
if (mAutoDownloadPlatform.equals(entity.getPlatform())) {
|
||||
currentItem = (i / 9);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Utils.log("currentItem = " + currentItem);
|
||||
adapter = new PlatformPagerAdapter(mContext, this,
|
||||
gameEntity, apkList, mAutoDownloadPlatform, entrance, location, traceEvent);
|
||||
viewPager.setAdapter(adapter);
|
||||
viewPager.setCurrentItem(currentItem);
|
||||
if (currentItem != 0) setDownloadOvalHint(dialog_ll_hint, currentItem);
|
||||
}
|
||||
|
||||
private void addHintPoint(LinearLayout linearLayout, int size) {
|
||||
if (size <= 1) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < size; i++) {
|
||||
ImageView imageView = new ImageView(mContext);
|
||||
LayoutParams lparams = new LayoutParams(
|
||||
DisplayUtils.dip2px(mContext, 6), DisplayUtils.dip2px(mContext, 6));
|
||||
if (i == 0) {
|
||||
lparams.leftMargin = 0;
|
||||
imageView.setImageResource(R.drawable.download_oval_hint_up);
|
||||
} else {
|
||||
lparams.leftMargin = DisplayUtils.dip2px(mContext, 9);
|
||||
imageView.setImageResource(R.drawable.oval_hint_gray_bg);
|
||||
}
|
||||
imageView.setLayoutParams(lparams);
|
||||
linearLayout.addView(imageView);
|
||||
}
|
||||
}
|
||||
|
||||
// 接收安装成功的消息,更新界面
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEvent(EBPackage busFour) {
|
||||
if ("安装".equals(busFour.getType())) {
|
||||
adapter.removeDownloadEntityByPackageName(busFour.getPackageName());
|
||||
if (collectionAdapter != null) {
|
||||
collectionAdapter.removeDownloadEntityByPackageName(busFour.getPackageName());
|
||||
}
|
||||
} else if ("卸载".equals(busFour.getType())) {
|
||||
adapter.updateItem(busFour.getPackageName());
|
||||
if (collectionAdapter != null) {
|
||||
collectionAdapter.updateItem(busFour.getPackageName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//接收下载被删除消息
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEvent(EBDownloadStatus status) {
|
||||
if ("delete".equals(status.getStatus())) {
|
||||
String url = status.getUrl();
|
||||
adapter.removeDownloadEntityByUrl(url);
|
||||
if (collectionAdapter != null) {
|
||||
collectionAdapter.removeDownloadEntityByUrl(url);
|
||||
}
|
||||
// DownloadManager.getInstance(mContext).putStatus(url, "delete");
|
||||
}
|
||||
}
|
||||
|
||||
// 接收platform数据改变消息,更新界面
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEvent(EBReuse reuse) {
|
||||
if ("PlatformChanged".equals(reuse.getType())) {
|
||||
DownloadManager.getInstance(mContext).removeObserver(dataWatcher);
|
||||
init(new ArrayList<>(gameApk));
|
||||
DownloadManager.getInstance(mContext).addObserver(dataWatcher);
|
||||
}
|
||||
}
|
||||
|
||||
// 显示合集
|
||||
@Override
|
||||
public void showCollection(GameCollectionEntity gameCollectionEntity) {
|
||||
dialog_ll_collection.setVisibility(View.VISIBLE);
|
||||
|
||||
List<ApkEntity> saveApkList = gameCollectionEntity.getSaveApkEntity();
|
||||
int count = saveApkList.size();
|
||||
int vpHeight;
|
||||
if (count <= 3) {
|
||||
vpHeight = 82;
|
||||
} else if (count <= 6) {
|
||||
vpHeight = 142;
|
||||
} else {
|
||||
vpHeight = 202;
|
||||
}
|
||||
|
||||
ViewGroup.LayoutParams layoutParams = collectionViewPager.getLayoutParams();
|
||||
layoutParams.height = DisplayUtils.dip2px(mContext, vpHeight);
|
||||
collectionViewPager.setLayoutParams(layoutParams);
|
||||
|
||||
dialog_ll_collection_hint.removeAllViews();
|
||||
int size = (int) Math.ceil(count / (double) (row * column));
|
||||
addHintPoint(dialog_ll_collection_hint, size);
|
||||
|
||||
collectionAdapter = new PlatformPagerAdapter(
|
||||
mContext, null, gameEntity, saveApkList,
|
||||
mAutoDownloadPlatform, entrance, location, traceEvent);
|
||||
collectionViewPager.setAdapter(collectionAdapter);
|
||||
|
||||
collectionViewPager.addOnPageChangeListener(new MyPageChangeListener(dialog_ll_collection_hint));
|
||||
|
||||
int currentItem = 0;
|
||||
if (!TextUtils.isEmpty(mAutoDownloadPlatform)) {
|
||||
for (int i = 0; i < saveApkList.size(); i++) {
|
||||
ApkEntity apkEntity = saveApkList.get(i);
|
||||
if (mAutoDownloadPlatform.equals(apkEntity.getPlatform())) {
|
||||
currentItem = (i / 9);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currentItem != 0) {
|
||||
collectionViewPager.setCurrentItem(currentItem);
|
||||
setDownloadOvalHint(dialog_ll_collection_hint, currentItem);
|
||||
}
|
||||
mAutoDownloadPlatform = null;
|
||||
}
|
||||
|
||||
// 隐藏合集
|
||||
@Override
|
||||
public void hideCollection() {
|
||||
dialog_ll_collection.setVisibility(View.GONE);
|
||||
collectionAdapter = null;
|
||||
}
|
||||
|
||||
private class MyPageChangeListener implements OnPageChangeListener {
|
||||
|
||||
private LinearLayout linearLayout;
|
||||
|
||||
public MyPageChangeListener(LinearLayout linearLayout) {
|
||||
this.linearLayout = linearLayout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrolled(int i, float v, int i1) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
setDownloadOvalHint(linearLayout, position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int i) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void setDownloadOvalHint(LinearLayout linearLayout, int position) {
|
||||
for (int i = 0, size = linearLayout.getChildCount(); i < size; i++) {
|
||||
if (i == position % size) {
|
||||
((ImageView) linearLayout.getChildAt(i))
|
||||
.setImageResource(R.drawable.download_oval_hint_up);
|
||||
} else {
|
||||
((ImageView) linearLayout.getChildAt(i))
|
||||
.setImageResource(R.drawable.oval_hint_gray_bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -24,9 +24,7 @@ import androidx.core.content.ContextCompat;
|
||||
public class DownloadProgressBar extends ProgressBar {
|
||||
public static final int MAX_LENGTH = 1000;
|
||||
public static final int DOWNLOAD_NORMAL_STYLE = 0;
|
||||
public static final int DOWNLOAD_RECT_STYLE = 1;
|
||||
public static final int DOWNLOAD_IMAGE_STYLE = 2;
|
||||
public static final int DOWNLOAD_SLIDE_STYLE = 3;
|
||||
|
||||
public enum DownloadType {
|
||||
NORMAL,
|
||||
@ -115,8 +113,6 @@ public class DownloadProgressBar extends ProgressBar {
|
||||
int color = Color.WHITE;
|
||||
if (DOWNLOAD_IMAGE_STYLE == mDownloadStyle) {
|
||||
color = Color.BLACK;
|
||||
} else if (DOWNLOAD_SLIDE_STYLE == mDownloadStyle) {
|
||||
color = getResources().getColor(R.color.theme_font);
|
||||
}
|
||||
mPaint.setColor(color); // 反向颜色
|
||||
}
|
||||
@ -167,73 +163,48 @@ public class DownloadProgressBar extends ProgressBar {
|
||||
case NONE_WITH_HINT:
|
||||
case INSTALL_NORMAL:
|
||||
case H5_GAME:
|
||||
switch (mDownloadStyle) {
|
||||
case DOWNLOAD_RECT_STYLE:
|
||||
setProgressDrawable(getResources().getDrawable(R.drawable.detail_download_normal_rect_style));
|
||||
mDefaultColor = Color.WHITE;
|
||||
break;
|
||||
case DOWNLOAD_IMAGE_STYLE:
|
||||
setProgressDrawable(getResources().getDrawable(R.drawable.detail_download_normal_image_style));
|
||||
mDefaultColor = Color.BLACK;
|
||||
break;
|
||||
default:
|
||||
setProgressDrawable(getResources().getDrawable(R.drawable.game_item_btn_download_style));
|
||||
mDefaultColor = Color.WHITE;
|
||||
break;
|
||||
if (mDownloadStyle == DOWNLOAD_IMAGE_STYLE) {
|
||||
setProgressDrawable(getResources().getDrawable(R.drawable.detail_download_normal_image_style));
|
||||
mDefaultColor = Color.BLACK;
|
||||
} else {
|
||||
setProgressDrawable(getResources().getDrawable(R.drawable.download_button_normal_style));
|
||||
mDefaultColor = Color.WHITE;
|
||||
}
|
||||
setProgress(0);
|
||||
break;
|
||||
case PLUGIN:
|
||||
case INSTALL_PLUGIN:
|
||||
setProgressDrawable(getResources().getDrawable(mDownloadStyle == DOWNLOAD_RECT_STYLE
|
||||
? R.drawable.detail_download_plugin_install_rect_style : R.drawable.game_item_btn_plugin_style));
|
||||
setProgressDrawable(getResources().getDrawable(R.drawable.download_button_pluggable_style));
|
||||
mDefaultColor = Color.WHITE;
|
||||
setProgress(0);
|
||||
break;
|
||||
case NONE:
|
||||
setProgressDrawable(getResources().getDrawable(mDownloadStyle == DOWNLOAD_RECT_STYLE
|
||||
? R.drawable.detail_download_none_rect_style : R.drawable.news_detail_comment));
|
||||
setProgressDrawable(getResources().getDrawable(R.drawable.news_detail_comment));
|
||||
mDefaultColor = ContextCompat.getColor(getContext(), R.color.hint);
|
||||
setProgress(0);
|
||||
break;
|
||||
case LAUNCH_OR_OPEN:
|
||||
switch (mDownloadStyle) {
|
||||
case DOWNLOAD_RECT_STYLE:
|
||||
setProgressDrawable(getResources().getDrawable(R.drawable.detail_download_open_rect_style));
|
||||
mDefaultColor = ContextCompat.getColor(getContext(), R.color.theme_font);
|
||||
break;
|
||||
case DOWNLOAD_IMAGE_STYLE:
|
||||
setProgressDrawable(getResources().getDrawable(R.drawable.detail_download_open_image_style));
|
||||
mDefaultColor = Color.WHITE;
|
||||
break;
|
||||
default:
|
||||
setProgressDrawable(getResources().getDrawable(R.drawable.detail_download_open_style));
|
||||
mDefaultColor = ContextCompat.getColor(getContext(), R.color.theme_font);
|
||||
break;
|
||||
if (mDownloadStyle == DOWNLOAD_IMAGE_STYLE) {
|
||||
setProgressDrawable(getResources().getDrawable(R.drawable.detail_download_open_image_style));
|
||||
mDefaultColor = Color.WHITE;
|
||||
} else {
|
||||
setProgressDrawable(getResources().getDrawable(R.drawable.download_button_normal_style));
|
||||
mDefaultColor = Color.WHITE;
|
||||
}
|
||||
setProgress(0);
|
||||
break;
|
||||
case DOWNLOADING_NORMAL:
|
||||
switch (mDownloadStyle) {
|
||||
case DOWNLOAD_RECT_STYLE:
|
||||
setProgressDrawable(getResources().getDrawable(R.drawable.detail_downloading_normal_rect_style));
|
||||
mDefaultColor = ContextCompat.getColor(getContext(), R.color.theme_font);
|
||||
break;
|
||||
case DOWNLOAD_IMAGE_STYLE:
|
||||
case DOWNLOAD_SLIDE_STYLE:
|
||||
setProgressDrawable(getResources().getDrawable(R.drawable.detail_downloading_normal_image_style));
|
||||
mDefaultColor = Color.WHITE;
|
||||
break;
|
||||
default:
|
||||
setProgressDrawable(getResources().getDrawable(R.drawable.detail_downloading_normal_style));
|
||||
mDefaultColor = ContextCompat.getColor(getContext(), R.color.theme_font);
|
||||
break;
|
||||
if (mDownloadStyle == DOWNLOAD_IMAGE_STYLE) {
|
||||
setProgressDrawable(getResources().getDrawable(R.drawable.detail_downloading_normal_image_style));
|
||||
mDefaultColor = Color.WHITE;
|
||||
} else {
|
||||
setProgressDrawable(getResources().getDrawable(R.drawable.detail_downloading_normal_style));
|
||||
mDefaultColor = ContextCompat.getColor(getContext(), R.color.theme_font);
|
||||
}
|
||||
break;
|
||||
case DOWNLOADING_PLUGIN:
|
||||
setProgressDrawable(getResources().getDrawable(mDownloadStyle == DOWNLOAD_RECT_STYLE
|
||||
? R.drawable.detail_downloading_plugin_rect_style : R.drawable.detail_downloading_plugin_style));
|
||||
mDefaultColor = ContextCompat.getColor(getContext(), R.color.btn_plugin);
|
||||
setProgressDrawable(getResources().getDrawable(R.drawable.detail_downloading_plugin_style));
|
||||
mDefaultColor = ContextCompat.getColor(getContext(), R.color.text_00B8B8);
|
||||
break;
|
||||
case RESERVABLE:
|
||||
setProgressDrawable(getResources().getDrawable(R.drawable.button_reserve));
|
||||
|
||||
@ -97,6 +97,13 @@ object DrawableView {
|
||||
return drawable
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getCornerGradientDrawable(startColor: Int, endColor: Int, radius: Float = 999F): Drawable {
|
||||
val drawable = GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, intArrayOf(startColor, endColor))
|
||||
drawable.cornerRadius = DisplayUtils.dip2px(radius).toFloat()
|
||||
return drawable
|
||||
}
|
||||
|
||||
fun convertAlphaKey(percent: Int): String {
|
||||
val hexString = Integer.toHexString(Math.round((255 * percent / 100).toFloat()))
|
||||
return (if (hexString.length < 2) "0" else "") + hexString
|
||||
|
||||
@ -1,33 +1,36 @@
|
||||
package com.gh.common.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.Build;
|
||||
import android.text.Layout;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextPaint;
|
||||
import android.text.style.BackgroundColorSpan;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatTextView;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.gh.common.util.DisplayUtils;
|
||||
import com.gh.gamecenter.R;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatTextView;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
public class ExpandTextView extends AppCompatTextView {
|
||||
|
||||
private CharSequence mSnapshotText;
|
||||
|
||||
private String mExpendText = "...全文";
|
||||
private String mEndText = "...";
|
||||
private String mExpandText = mEndText + "全文";
|
||||
private boolean mUseGradientAlphaEndText = false;
|
||||
|
||||
private int mMaxLines = 3; // 由于sdk版本限制(getMaxLines) 这里设置默认值
|
||||
|
||||
private static int DEFAULT_ADDITIONAL_END_TEXT_COUNT = 2;
|
||||
|
||||
private boolean mInitLayout = false;
|
||||
private boolean mOpenLayout = false;
|
||||
private boolean mIsExpanded = false; // 位于 recyclerView 时需要自行在外层管理是否已展开
|
||||
|
||||
private ExpandCallback mExpandCallback;
|
||||
|
||||
@ -40,20 +43,25 @@ public class ExpandTextView extends AppCompatTextView {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
mMaxLines = getMaxLines();
|
||||
}
|
||||
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ExpandTextView);
|
||||
mUseGradientAlphaEndText = ta.getBoolean(R.styleable.ExpandTextView_useGradientAlphaEndText, false);
|
||||
mEndText = ta.getString(R.styleable.ExpandTextView_endText) == null ? mEndText : ta.getString(R.styleable.ExpandTextView_endText);
|
||||
mExpandText = ta.getString(R.styleable.ExpandTextView_expandText) == null ? mExpandText : ta.getString(R.styleable.ExpandTextView_expandText);
|
||||
ta.recycle();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
if (mInitLayout && !mOpenLayout && getLineCount() > mMaxLines) {
|
||||
if (mInitLayout && !mIsExpanded && getLineCount() > mMaxLines) {
|
||||
mSnapshotText = getText();
|
||||
mInitLayout = false;
|
||||
showExpendButton();
|
||||
showExpandButton();
|
||||
}
|
||||
}
|
||||
|
||||
public void setExpendText(String text) {
|
||||
this.mExpendText = text;
|
||||
this.mExpandText = text;
|
||||
}
|
||||
|
||||
public void setExpandCallback(ExpandCallback callback) {
|
||||
@ -66,7 +74,9 @@ public class ExpandTextView extends AppCompatTextView {
|
||||
super.setText(text, type);
|
||||
}
|
||||
|
||||
private void showExpendButton() {
|
||||
private void showExpandButton() {
|
||||
String finalEndText = "";
|
||||
|
||||
Layout layout = getLayout();
|
||||
int start = layout.getLineStart(0);
|
||||
int lastLineEnd = layout.getLineEnd(mMaxLines - 1);
|
||||
@ -74,28 +84,59 @@ public class ExpandTextView extends AppCompatTextView {
|
||||
float lastLineRight = layout.getLineRight(mMaxLines - 1);
|
||||
|
||||
int viewWidth = getWidth() - getPaddingRight() - getPaddingLeft();
|
||||
int additionalEndTextCount = 0;
|
||||
|
||||
TextPaint paint = getPaint();
|
||||
float expendTextWidth = paint.measureText(mExpendText);
|
||||
float expandTextWidth;
|
||||
if (mUseGradientAlphaEndText) {
|
||||
additionalEndTextCount = DEFAULT_ADDITIONAL_END_TEXT_COUNT;
|
||||
// 如果不加多个括号的话有可能算不对,惊了,明明是同样的 paint 同样的文字,长度却会略有不同
|
||||
expandTextWidth = paint.measureText(mEndText + mExpandText + " ");
|
||||
} else {
|
||||
expandTextWidth = paint.measureText(mExpandText);
|
||||
}
|
||||
|
||||
CharSequence content = mSnapshotText.subSequence(start, lastLineEnd);
|
||||
if (viewWidth - lastLineRight > expendTextWidth) {
|
||||
content = content.toString().trim() + mExpendText;
|
||||
|
||||
if (viewWidth - lastLineRight > expandTextWidth) {
|
||||
if (mUseGradientAlphaEndText) {
|
||||
finalEndText = content.toString().substring(content.length() - additionalEndTextCount, content.length()) + mEndText;
|
||||
finalEndText = finalEndText.replace("\n", "");
|
||||
|
||||
content = content.subSequence(0, content.length() - additionalEndTextCount) + finalEndText + mExpandText;
|
||||
} else {
|
||||
content = content.toString().trim() + mExpandText;
|
||||
}
|
||||
} else {
|
||||
CharSequence lastText = mSnapshotText.subSequence(lastLineStart, lastLineEnd);
|
||||
for (int i = lastText.length() - 1; i > 0; i--) {
|
||||
CharSequence sequence = lastText.subSequence(0, i);
|
||||
float w = paint.measureText(sequence.toString());
|
||||
if (viewWidth - w - DisplayUtils.dip2px(5) > expendTextWidth) {
|
||||
content = mSnapshotText.subSequence(start, lastLineStart + i) + mExpendText;
|
||||
if (viewWidth - w - DisplayUtils.dip2px(5) > expandTextWidth) {
|
||||
if (mUseGradientAlphaEndText) {
|
||||
finalEndText = lastText.subSequence(i - additionalEndTextCount, i) + mEndText;
|
||||
finalEndText = finalEndText.replace("\n", "");
|
||||
|
||||
content = mSnapshotText.subSequence(start, lastLineStart + i - additionalEndTextCount) + finalEndText + mExpandText;
|
||||
} else {
|
||||
content = mSnapshotText.subSequence(start, lastLineStart + i) + mExpandText;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SpannableStringBuilder msp = new SpannableStringBuilder(mSnapshotText);
|
||||
int length = msp.length();
|
||||
int startPosition = content.length() - mExpendText.length();
|
||||
startPosition = startPosition < 0 ? 0 : startPosition;
|
||||
msp.replace(startPosition, length, mExpendText);
|
||||
int startPosition = 0;
|
||||
startPosition = content.length() - finalEndText.length() - mExpandText.length();
|
||||
startPosition = Math.max(startPosition, 0);
|
||||
|
||||
// 避免越界
|
||||
if (startPosition >= length) return;
|
||||
|
||||
msp.replace(startPosition, length, finalEndText + mExpandText);
|
||||
|
||||
msp.setSpan(new ClickableSpan() {
|
||||
@Override
|
||||
public void updateDrawState(TextPaint ds) {
|
||||
@ -106,7 +147,7 @@ public class ExpandTextView extends AppCompatTextView {
|
||||
|
||||
@Override
|
||||
public void onClick(View widget) {
|
||||
mOpenLayout = true;
|
||||
mIsExpanded = true;
|
||||
setMaxLines(Integer.MAX_VALUE);
|
||||
setText(mSnapshotText);
|
||||
|
||||
@ -114,15 +155,21 @@ public class ExpandTextView extends AppCompatTextView {
|
||||
mExpandCallback.onExpand();
|
||||
}
|
||||
}
|
||||
}, startPosition + 3, msp.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
msp.setSpan(new BackgroundColorSpan(Color.WHITE), startPosition, msp.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}, startPosition + mEndText.length(), msp.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
msp.setSpan(new GradientAlphaTextSpan(), startPosition, startPosition + finalEndText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
setText(msp);
|
||||
setMovementMethod(CustomLinkMovementMethod.getInstance());
|
||||
}
|
||||
|
||||
/**
|
||||
* 此方法仅更改标记,不做实际展开的操作
|
||||
*/
|
||||
public void setIsExpanded(boolean isExpanded) {
|
||||
mIsExpanded = isExpanded;
|
||||
}
|
||||
|
||||
public void setExpendMaxLines(int maxLines) {
|
||||
public void setExpandMaxLines(int maxLines) {
|
||||
mMaxLines = maxLines;
|
||||
setMaxLines(maxLines);
|
||||
}
|
||||
|
||||
124
app/src/main/java/com/gh/common/view/FlexLinearLayout.kt
Normal file
124
app/src/main/java/com/gh/common/view/FlexLinearLayout.kt
Normal file
@ -0,0 +1,124 @@
|
||||
package com.gh.common.view
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.util.AttributeSet
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.gh.common.util.DisplayUtils
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.entity.TagStyleEntity
|
||||
import kotlin.math.ceil
|
||||
|
||||
/**
|
||||
* 标签最多显示一行,显示的个数由行宽决定
|
||||
*/
|
||||
class FlexLinearLayout @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : LinearLayout(context, attrs) {
|
||||
|
||||
private var mTotalCount = 0
|
||||
private var mTags = ArrayList<TagStyleEntity>()
|
||||
private var mItemHeight = DisplayUtils.dip2px(20F)
|
||||
private var mPadding = DisplayUtils.dip2px(5F)
|
||||
private var mMargin = DisplayUtils.dip2px(4F)
|
||||
private var mTextSize = 10F
|
||||
private var mLastItemWidth = DisplayUtils.dip2px(18F)//最后更多按钮宽度
|
||||
private var mTotalWidth = 0
|
||||
var onClickListener: OnItemClickListener? = null
|
||||
|
||||
init {
|
||||
gravity = Gravity.CENTER_VERTICAL
|
||||
}
|
||||
|
||||
fun setTags(tags: ArrayList<TagStyleEntity>) {
|
||||
mTags.clear()
|
||||
mTotalCount = tags.size
|
||||
mTotalWidth = measuredWidth
|
||||
val paint = Paint()
|
||||
paint.textSize = DisplayUtils.sp2px(context, mTextSize).toFloat()
|
||||
|
||||
var currentWidth = mLastItemWidth.toFloat()
|
||||
tags.forEach {
|
||||
val strWidth = getTextWidth(paint, it.name)
|
||||
if (currentWidth + strWidth + mPadding * 2 + mMargin <= mTotalWidth) {
|
||||
currentWidth += strWidth + mPadding * 2 + mMargin
|
||||
mTags.add(it)
|
||||
}
|
||||
}
|
||||
if (mTags.isNotEmpty()) {
|
||||
addTags()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getTextWidth(paint: Paint, str: String?): Int {
|
||||
var width = 0
|
||||
if (str != null && str.isNotEmpty()) {
|
||||
val len = str.length
|
||||
val widths = FloatArray(len)
|
||||
paint.getTextWidths(str, widths)
|
||||
for (i in 0 until len) {
|
||||
width += ceil(widths[i].toDouble()).toInt()
|
||||
}
|
||||
}
|
||||
return width
|
||||
}
|
||||
|
||||
private fun addTags() {
|
||||
removeAllViews()
|
||||
mTags.forEach {
|
||||
addView(createView(it))
|
||||
}
|
||||
if (mTotalCount != mTags.size) {
|
||||
val imageView = ImageView(context).apply {
|
||||
val params = LayoutParams(mLastItemWidth, mItemHeight)
|
||||
layoutParams = params
|
||||
setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_game_detail_label_more))
|
||||
background = createBackgroundDrawable()
|
||||
scaleType=ImageView.ScaleType.CENTER
|
||||
}
|
||||
imageView.setOnClickListener {
|
||||
onClickListener?.onMoreClickListener()
|
||||
}
|
||||
addView(imageView)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createView(tag: TagStyleEntity): View {
|
||||
return TextView(context).apply {
|
||||
text = tag.name
|
||||
includeFontPadding = false
|
||||
textSize = mTextSize
|
||||
gravity = Gravity.CENTER
|
||||
setTextColor(Color.parseColor("#333333"))
|
||||
|
||||
val params = LayoutParams(LayoutParams.WRAP_CONTENT, mItemHeight)
|
||||
params.setMargins(0, 0, mMargin, 0)
|
||||
setPadding(mPadding, 0, mPadding, 0)
|
||||
layoutParams = params
|
||||
|
||||
val gradientDrawable = createBackgroundDrawable()
|
||||
background = gradientDrawable
|
||||
setOnClickListener {
|
||||
onClickListener?.onItemClickListener(tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createBackgroundDrawable(): GradientDrawable {
|
||||
val gradientDrawable = GradientDrawable()
|
||||
gradientDrawable.setColor(Color.TRANSPARENT)
|
||||
gradientDrawable.setStroke(DisplayUtils.dip2px(context, 1f), Color.parseColor("#C2C6CC"))
|
||||
gradientDrawable.cornerRadius = DisplayUtils.dip2px(2f).toFloat()
|
||||
return gradientDrawable
|
||||
}
|
||||
|
||||
interface OnItemClickListener {
|
||||
fun onMoreClickListener()
|
||||
fun onItemClickListener(tag: TagStyleEntity)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package com.gh.common.view
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.LinearGradient
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Shader
|
||||
import android.text.style.ReplacementSpan
|
||||
import androidx.core.graphics.ColorUtils
|
||||
|
||||
class GradientAlphaTextSpan() : ReplacementSpan() {
|
||||
|
||||
override fun getSize(paint: Paint, text: CharSequence?, start: Int, end: Int, fm: Paint.FontMetricsInt?): Int {
|
||||
return paint.measureText(text, start, end).toInt()
|
||||
}
|
||||
|
||||
override fun draw(canvas: Canvas, text: CharSequence?, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) {
|
||||
val originalColor = paint.color
|
||||
val originalColorWithAlphaChanged = ColorUtils.setAlphaComponent(paint.color, 1)
|
||||
|
||||
val textWidth = paint.measureText(text, start, end).toInt()
|
||||
|
||||
val mShader = LinearGradient(x, 0F, x + textWidth - 10, 0F,
|
||||
originalColor, originalColorWithAlphaChanged, Shader.TileMode.CLAMP)
|
||||
paint.shader = mShader
|
||||
|
||||
canvas.drawText(text, start, end, x, y.toFloat(), paint)
|
||||
|
||||
paint.shader = null
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
package com.gh.common.view
|
||||
|
||||
import android.graphics.*
|
||||
import android.text.style.ReplacementSpan
|
||||
import androidx.annotation.Dimension
|
||||
import com.gh.common.util.DisplayUtils
|
||||
import com.halo.assistant.HaloApp
|
||||
|
||||
|
||||
class GradientRoundBackgroundColorSpan(private val startColor: String,
|
||||
private val endColor: String,
|
||||
private val textColor: String,
|
||||
@Dimension(unit = Dimension.SP)
|
||||
private val textSize: Int = 0) : ReplacementSpan() {
|
||||
|
||||
|
||||
override fun getSize(paint: Paint, text: CharSequence, start: Int, end: Int, fm: Paint.FontMetricsInt?): Int {
|
||||
return paint.measureText(text, start, end).toInt()
|
||||
}
|
||||
|
||||
override fun draw(canvas: Canvas, text: CharSequence, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) {
|
||||
val originalColor = paint.color
|
||||
val originalTextSize = paint.textSize
|
||||
|
||||
paint.style = Paint.Style.FILL
|
||||
|
||||
val fontMetrics = paint.fontMetrics
|
||||
|
||||
// 原文字宽度
|
||||
val originalTextWidth = paint.measureText(text, start, end).toInt()
|
||||
// 当目标字体大小与原字体大小不一样时,需要先用目标字体大小测量字体宽度
|
||||
if (textSize != 0) {
|
||||
paint.textSize = DisplayUtils.sp2px(HaloApp.getInstance().application, textSize.toFloat()).toFloat()
|
||||
}
|
||||
// 目标文字宽度
|
||||
val textWidth = paint.measureText(text, start, end).toInt()
|
||||
|
||||
// 用原字体大小画椭圆
|
||||
paint.textSize = originalTextSize
|
||||
//添加渐变Shader
|
||||
val mShader = LinearGradient(0F, top.toFloat(), x + originalTextWidth, top + fontMetrics.bottom - fontMetrics.top,
|
||||
Color.parseColor(startColor), Color.parseColor(endColor), Shader.TileMode.CLAMP)
|
||||
paint.shader = mShader
|
||||
canvas.drawRoundRect(RectF(
|
||||
x,
|
||||
top.toFloat(),
|
||||
x + originalTextWidth,
|
||||
top + fontMetrics.bottom - fontMetrics.top),
|
||||
DisplayUtils.dip2px(HaloApp.getInstance().application, 5f).toFloat(),
|
||||
DisplayUtils.dip2px(HaloApp.getInstance().application, 5f).toFloat(),
|
||||
paint)
|
||||
|
||||
//重置paint
|
||||
paint.color = Color.parseColor(this.textColor)
|
||||
//移除渐变Shader,否则绘制的文字也是渐变色
|
||||
paint.shader = null
|
||||
paint.style = Paint.Style.FILL
|
||||
|
||||
// 用目标字体大小画字
|
||||
if (textSize != 0) {
|
||||
paint.textSize = DisplayUtils.sp2px(HaloApp.getInstance().application, textSize.toFloat()).toFloat()
|
||||
}
|
||||
val offsetX = (originalTextWidth - textWidth) / 2
|
||||
canvas.drawText(text, start, end, x + offsetX, y.toFloat(), paint)
|
||||
|
||||
paint.color = originalColor
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
package com.gh.common.view
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import android.view.ViewConfiguration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlin.math.abs
|
||||
|
||||
class InterceptRecyclerView : RecyclerView {
|
||||
constructor(context: Context) : super(context)
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
|
||||
|
||||
private var startX = 0F
|
||||
private var startY = 0F
|
||||
|
||||
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
|
||||
if (ev.action == MotionEvent.ACTION_DOWN) {
|
||||
startX = ev.x
|
||||
startY = ev.y
|
||||
} else if (ev.action == MotionEvent.ACTION_MOVE) {
|
||||
val dx = ev.x - startX
|
||||
val dy = ev.y - startY
|
||||
val scaledTouchSlop = ViewConfiguration.get(context).scaledTouchSlop
|
||||
if (layoutManager != null && layoutManager is LinearLayoutManager) {
|
||||
val orientation = (layoutManager as LinearLayoutManager).orientation
|
||||
if (orientation == HORIZONTAL) {
|
||||
if (abs(dx) > scaledTouchSlop && abs(dx) > abs(dy)) {
|
||||
parent.requestDisallowInterceptTouchEvent(true)
|
||||
} else if (abs(dy) > scaledTouchSlop && abs(dy) > abs(dx)) {
|
||||
parent.requestDisallowInterceptTouchEvent(false)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return super.dispatchTouchEvent(ev)
|
||||
}
|
||||
}
|
||||
@ -30,6 +30,7 @@ public class MarqueeView extends ViewFlipper {
|
||||
private int animDuration = 500;
|
||||
private int textSize = 14;
|
||||
private int textColor = 0xffffffff;
|
||||
private boolean useSingleLineText = false;
|
||||
|
||||
public MarqueeView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
@ -51,6 +52,7 @@ public class MarqueeView extends ViewFlipper {
|
||||
textSize = DisplayUtils.px2sp(mContext, textSize);
|
||||
}
|
||||
textColor = typedArray.getColor(R.styleable.MarqueeViewStyle_mvTextColor, textColor);
|
||||
useSingleLineText = typedArray.getBoolean(R.styleable.MarqueeViewStyle_mvSingleLine, useSingleLineText);
|
||||
typedArray.recycle();
|
||||
|
||||
setFlipInterval(interval);
|
||||
@ -114,6 +116,10 @@ public class MarqueeView extends ViewFlipper {
|
||||
// 创建ViewFlipper下的TextView
|
||||
private TextView createTextView(String text) {
|
||||
TextView tv = new TextView(mContext);
|
||||
if (useSingleLineText) {
|
||||
tv.setSingleLine(true);
|
||||
tv.setEllipsize(TextUtils.TruncateAt.END);
|
||||
}
|
||||
tv.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
|
||||
tv.setText(text);
|
||||
tv.setTextColor(textColor);
|
||||
@ -127,6 +133,10 @@ public class MarqueeView extends ViewFlipper {
|
||||
start();
|
||||
}
|
||||
|
||||
public void enableSingleLineText() {
|
||||
useSingleLineText = true;
|
||||
}
|
||||
|
||||
public List<String> getNotices() {
|
||||
return notices;
|
||||
}
|
||||
|
||||
@ -4,9 +4,19 @@ import android.content.Context
|
||||
import android.os.Build
|
||||
import android.util.AttributeSet
|
||||
import android.widget.RelativeLayout
|
||||
import com.gh.gamecenter.R
|
||||
|
||||
class MaterializedRelativeLayout @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null)
|
||||
: RelativeLayout(context, attrs) {
|
||||
class MaterializedRelativeLayout : RelativeLayout {
|
||||
|
||||
private var mConsumeWindowInset: Boolean = false
|
||||
|
||||
constructor(context: Context) : super(context)
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
|
||||
val ta = context.obtainStyledAttributes(attrs, R.styleable.MaterializedRelativeLayout)
|
||||
mConsumeWindowInset = ta.getBoolean(R.styleable.MaterializedRelativeLayout_consumeWindowInsets, false)
|
||||
ta.recycle()
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 windowInsets 传递给子 view [https://medium.com/androiddevelopers/why-would-i-want-to-fitssystemwindows-4e26d9ce1eec#.raoa9t506]
|
||||
@ -16,9 +26,16 @@ class MaterializedRelativeLayout @JvmOverloads constructor(context: Context, att
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
setOnApplyWindowInsetsListener { _, insets ->
|
||||
val childCount = childCount
|
||||
for (index in 0 until childCount) {
|
||||
getChildAt(index).dispatchApplyWindowInsets(insets)
|
||||
if (mConsumeWindowInset) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
|
||||
insets.replaceSystemWindowInsets(insets.systemWindowInsetLeft, 0, insets.systemWindowInsetRight, insets.systemWindowInsetBottom)
|
||||
insets.consumeSystemWindowInsets()
|
||||
}
|
||||
} else {
|
||||
val childCount = childCount
|
||||
for (index in 0 until childCount) {
|
||||
getChildAt(index).dispatchApplyWindowInsets(insets)
|
||||
}
|
||||
}
|
||||
insets
|
||||
}
|
||||
|
||||
@ -0,0 +1,39 @@
|
||||
package com.gh.common.view
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import androidx.core.widget.NestedScrollView
|
||||
import com.gh.gamecenter.R
|
||||
|
||||
|
||||
class MaxHeightNestedScrollView(context: Context, attrs: AttributeSet? = null) : NestedScrollView(context, attrs) {
|
||||
|
||||
var maxHeight = 0F
|
||||
var scrollChangedListener: ScrollScrollChangedListener? = null
|
||||
|
||||
init {
|
||||
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.MaxHeightNestedScrollView)
|
||||
maxHeight = typedArray.getDimension(R.styleable.MaxHeightNestedScrollView_mMaxHeight, 0f)
|
||||
typedArray.recycle()
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
var newHeightMeasureSpec = 0
|
||||
try {
|
||||
newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight.toInt(), MeasureSpec.AT_MOST)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
//重新计算控件高、宽
|
||||
super.onMeasure(widthMeasureSpec, newHeightMeasureSpec)
|
||||
}
|
||||
|
||||
override fun onScrollChanged(l: Int, t: Int, oldl: Int, oldt: Int) {
|
||||
super.onScrollChanged(l, t, oldl, oldt)
|
||||
scrollChangedListener?.onScrollChanged(l, t, oldl, oldt)
|
||||
}
|
||||
|
||||
interface ScrollScrollChangedListener {
|
||||
fun onScrollChanged(l: Int, t: Int, oldl: Int, oldt: Int)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
package com.gh.common.view
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.TypedArray
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
import com.gh.gamecenter.R
|
||||
|
||||
class MaxHeightRecyclerView : RecyclerView {
|
||||
private var mMaxHeight: Int = 0
|
||||
|
||||
constructor(context: Context) : super(context) {}
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
|
||||
initialize(context, attrs)
|
||||
}
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
|
||||
initialize(context, attrs)
|
||||
}
|
||||
|
||||
private fun initialize(context: Context, attrs: AttributeSet) {
|
||||
val arr = context.obtainStyledAttributes(attrs, R.styleable.MaxHeightRecyclerView)
|
||||
mMaxHeight = arr.getLayoutDimension(R.styleable.MaxHeightRecyclerView_mRvMaxHeight, mMaxHeight)
|
||||
arr.recycle()
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
var heightMeasureSpec = heightMeasureSpec
|
||||
if (mMaxHeight > 0) {
|
||||
heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(mMaxHeight, MeasureSpec.AT_MOST)
|
||||
}
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||
}
|
||||
}
|
||||
164
app/src/main/java/com/gh/common/view/NestedScrollWebView.java
Normal file
164
app/src/main/java/com/gh/common/view/NestedScrollWebView.java
Normal file
@ -0,0 +1,164 @@
|
||||
package com.gh.common.view;
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import androidx.core.view.MotionEventCompat;
|
||||
import androidx.core.view.NestedScrollingChild;
|
||||
import androidx.core.view.NestedScrollingChildHelper;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import wendu.dsbridge.DWebView;
|
||||
|
||||
public class NestedScrollWebView extends DWebView implements NestedScrollingChild {
|
||||
|
||||
public static final String TAG = NestedScrollWebView.class.getSimpleName();
|
||||
|
||||
private int mLastMotionY;
|
||||
|
||||
private final int[] mScrollOffset = new int[2];
|
||||
private final int[] mScrollConsumed = new int[2];
|
||||
|
||||
private int mNestedYOffset;
|
||||
private boolean mChange;
|
||||
|
||||
private NestedScrollingChildHelper mChildHelper;
|
||||
|
||||
public NestedScrollWebView(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public NestedScrollWebView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
private void init() {
|
||||
mChildHelper = new NestedScrollingChildHelper(this);
|
||||
setNestedScrollingEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent ev) {
|
||||
getParent().requestDisallowInterceptTouchEvent(true);
|
||||
return super.dispatchTouchEvent(ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
boolean result = false;
|
||||
|
||||
MotionEvent trackedEvent = MotionEvent.obtain(event);
|
||||
|
||||
final int action = MotionEventCompat.getActionMasked(event);
|
||||
|
||||
if (action == MotionEvent.ACTION_DOWN) {
|
||||
mNestedYOffset = 0;
|
||||
}
|
||||
|
||||
int y = (int) event.getY();
|
||||
|
||||
event.offsetLocation(0, mNestedYOffset);
|
||||
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mLastMotionY = y;
|
||||
startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
|
||||
result = super.onTouchEvent(event);
|
||||
mChange = false;
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
int deltaY = mLastMotionY - y;
|
||||
|
||||
if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset)) {
|
||||
deltaY -= mScrollConsumed[1];
|
||||
trackedEvent.offsetLocation(0, mScrollOffset[1]);
|
||||
mNestedYOffset += mScrollOffset[1];
|
||||
}
|
||||
|
||||
int oldY = getScrollY();
|
||||
mLastMotionY = y - mScrollOffset[1];
|
||||
int newScrollY = Math.max(0, oldY + deltaY);
|
||||
deltaY -= newScrollY - oldY;
|
||||
if (dispatchNestedScroll(0, newScrollY - deltaY, 0, deltaY, mScrollOffset)) {
|
||||
mLastMotionY -= mScrollOffset[1];
|
||||
trackedEvent.offsetLocation(0, mScrollOffset[1]);
|
||||
mNestedYOffset += mScrollOffset[1];
|
||||
}
|
||||
if(mScrollConsumed[1]==0 && mScrollOffset[1]==0) {
|
||||
if(mChange){
|
||||
mChange =false;
|
||||
trackedEvent.setAction(MotionEvent.ACTION_DOWN);
|
||||
super.onTouchEvent(trackedEvent);
|
||||
}else {
|
||||
result = super.onTouchEvent(trackedEvent);
|
||||
}
|
||||
trackedEvent.recycle();
|
||||
}else{
|
||||
if(Math.abs(mLastMotionY - y) >= 10) {
|
||||
if (!mChange) {
|
||||
mChange = true;
|
||||
super.onTouchEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_POINTER_DOWN:
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
stopNestedScroll();
|
||||
result = super.onTouchEvent(event);
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// NestedScrollingChild
|
||||
@Override
|
||||
public void setNestedScrollingEnabled(boolean enabled) {
|
||||
mChildHelper.setNestedScrollingEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNestedScrollingEnabled() {
|
||||
return mChildHelper.isNestedScrollingEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startNestedScroll(int axes) {
|
||||
return mChildHelper.startNestedScroll(axes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopNestedScroll() {
|
||||
mChildHelper.stopNestedScroll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNestedScrollingParent() {
|
||||
return mChildHelper.hasNestedScrollingParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
|
||||
return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
|
||||
return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
|
||||
return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
|
||||
return mChildHelper.dispatchNestedPreFling(velocityX, velocityY);
|
||||
}
|
||||
|
||||
}
|
||||
@ -14,17 +14,24 @@ import android.webkit.WebChromeClient;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
|
||||
import com.gh.common.DefaultJsApi;
|
||||
import com.gh.common.constant.Config;
|
||||
import com.gh.common.util.DeviceUtils;
|
||||
import com.gh.common.util.DialogUtils;
|
||||
import com.gh.common.util.DisplayUtils;
|
||||
import com.gh.common.util.GsonUtils;
|
||||
import com.gh.common.util.HtmlUtils;
|
||||
import com.gh.common.util.ImageUtils;
|
||||
import com.gh.common.util.MtaHelper;
|
||||
import com.gh.common.util.NetworkUtils;
|
||||
import com.gh.common.util.RichEditorUtils;
|
||||
import com.gh.common.util.SPUtils;
|
||||
import com.gh.gamecenter.BuildConfig;
|
||||
import com.gh.gamecenter.entity.MtaEvent;
|
||||
import com.gh.gamecenter.entity.MyVideoEntity;
|
||||
import com.gh.gamecenter.qa.entity.EditorInsertEntity;
|
||||
import com.halo.assistant.HaloApp;
|
||||
import com.lightgame.utils.Utils;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
@ -509,6 +516,10 @@ public class RichEditor extends WebView {
|
||||
return HtmlUtils.stripHtml(mContents);
|
||||
}
|
||||
|
||||
// 在web页面播放视频
|
||||
// public void initArticleVideo() {
|
||||
// exec("javascript:RE.initArticleVideo();");
|
||||
// }
|
||||
|
||||
private String convertHexColorString(int color) {
|
||||
return String.format("#%06X", (0xFFFFFF & color));
|
||||
@ -615,5 +626,98 @@ public class RichEditor extends WebView {
|
||||
public boolean isInputEnabled() {
|
||||
return mInputEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取网络状态
|
||||
*
|
||||
* @return "WIFI", "2G", "3G", "4G","无网络","未知"
|
||||
*/
|
||||
@JavascriptInterface
|
||||
public String getNetworkStatus() {
|
||||
return DeviceUtils.getNetwork(HaloApp.getInstance().getApplication());
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示toast
|
||||
*
|
||||
* @param text toast 内容
|
||||
*/
|
||||
@JavascriptInterface
|
||||
public void toast(String text) {
|
||||
Utils.toast(HaloApp.getInstance().getApplication(), text);
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示Dialog弹窗
|
||||
*
|
||||
* @param title 标题
|
||||
* @param message 内容
|
||||
* @param positive 确认按钮文本
|
||||
* @param negative 取消按钮文本
|
||||
*/
|
||||
@JavascriptInterface
|
||||
public void showDialog(String title, String message, String positive, String negative, String jsCallbackCode) {
|
||||
DialogUtils.showAlertDialog(getContext(), title, message, positive, negative,
|
||||
() -> post(() -> loadUrl("javascript:" + jsCallbackCode + "(" + true + ")")),
|
||||
() -> post(() -> loadUrl("javascript:" + jsCallbackCode + "(" + false + ")")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Javascript向App存储数据
|
||||
*
|
||||
* @param isPermanent true:永久存储 false:临时存储于内存中(注意:数据是不互通的)
|
||||
*/
|
||||
@JavascriptInterface
|
||||
public void setDataToNative(String key, String value, boolean isPermanent) {
|
||||
key += "-RichEditor";
|
||||
if (!TextUtils.isEmpty(key)) {
|
||||
if (isPermanent) {
|
||||
if (value == null) {
|
||||
SPUtils.remove(key);
|
||||
} else {
|
||||
SPUtils.setString(key, value);
|
||||
}
|
||||
} else {
|
||||
if (value == null) {
|
||||
HaloApp.remove(key);
|
||||
} else {
|
||||
HaloApp.put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Javascript向App获取数据
|
||||
*
|
||||
* @param isPermanent 获取永久数据还是临时数据(注意:数据是不互通的)
|
||||
*/
|
||||
@JavascriptInterface
|
||||
public String getDataFromNative(String key, boolean isPermanent) {
|
||||
key += "-RichEditor";
|
||||
if (!TextUtils.isEmpty(key)) {
|
||||
if (isPermanent) {
|
||||
return SPUtils.getString(key);
|
||||
} else {
|
||||
Object object = HaloApp.get(key, false);
|
||||
if (object != null) return object.toString();
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public void logMtaEvent(String event) {
|
||||
try {
|
||||
MtaEvent mtaEvent = GsonUtils.fromJson(event, MtaEvent.class);
|
||||
MtaHelper.onEvent(mtaEvent.getName(), mtaEvent.getKey(), mtaEvent.getValue());
|
||||
} catch (Exception e) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
throw e;
|
||||
} else {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4,13 +4,14 @@ import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
|
||||
import com.gh.common.util.DisplayUtils;
|
||||
import com.gh.gamecenter.R;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public class VerticalItemDecoration extends RecyclerView.ItemDecoration {
|
||||
|
||||
private Paint paint;
|
||||
@ -18,13 +19,21 @@ public class VerticalItemDecoration extends RecyclerView.ItemDecoration {
|
||||
private int mIntervalLR;
|
||||
private boolean mIsShowIntervalTop;
|
||||
|
||||
public VerticalItemDecoration(Context context, float interval, boolean isShowIntervalTop) {
|
||||
public VerticalItemDecoration(Context context, float interval, boolean isShowIntervalTop, int colorRes) {
|
||||
paint = new Paint();
|
||||
paint.setColor(ContextCompat.getColor(context, R.color.background));
|
||||
if (colorRes != 0) {
|
||||
paint.setColor(ContextCompat.getColor(context, colorRes));
|
||||
} else {
|
||||
paint.setColor(ContextCompat.getColor(context, R.color.background));
|
||||
}
|
||||
mInterval = DisplayUtils.dip2px(context, interval);
|
||||
mIsShowIntervalTop = isShowIntervalTop;
|
||||
}
|
||||
|
||||
public VerticalItemDecoration(Context context, float interval, boolean isShowIntervalTop) {
|
||||
this(context, interval, isShowIntervalTop, 0);
|
||||
}
|
||||
|
||||
public VerticalItemDecoration(Context context, int interval, boolean isShowIntervalTop, int intervalLR) {
|
||||
paint = new Paint();
|
||||
paint.setColor(ContextCompat.getColor(context, R.color.background));
|
||||
|
||||
@ -42,28 +42,28 @@ class WelcomeDialog : BaseDialogFragment() {
|
||||
binding.ivOpeningCover.setOnClickListener {
|
||||
when (mWelcomeEntity?.type) {
|
||||
EntranceUtils.HOST_ARTICLE -> {
|
||||
DirectUtils.directToArticle(context!!, mWelcomeEntity?.link!!, EntranceUtils.ENTRANCE_WELCOME)
|
||||
DirectUtils.directToArticle(requireContext(), mWelcomeEntity?.link!!, EntranceUtils.ENTRANCE_WELCOME)
|
||||
}
|
||||
EntranceUtils.HOST_GAME -> {
|
||||
DirectUtils.directToGameDetail(context!!, mWelcomeEntity?.link!!, EntranceUtils.ENTRANCE_WELCOME)
|
||||
DirectUtils.directToGameDetail(requireContext(), mWelcomeEntity?.link!!, EntranceUtils.ENTRANCE_WELCOME)
|
||||
}
|
||||
EntranceUtils.HOST_COLUMN -> {
|
||||
DirectUtils.directToSubject(context!!, mWelcomeEntity?.link!!, null, EntranceUtils.ENTRANCE_WELCOME)
|
||||
DirectUtils.directToSubject(requireContext(), mWelcomeEntity?.link!!, null, EntranceUtils.ENTRANCE_WELCOME)
|
||||
}
|
||||
EntranceUtils.HOST_QUESTION -> {
|
||||
DirectUtils.directToQuestionDetail(context!!, mWelcomeEntity?.link!!, EntranceUtils.ENTRANCE_WELCOME, "首页弹窗")
|
||||
DirectUtils.directToQuestionDetail(requireContext(), mWelcomeEntity?.link!!, EntranceUtils.ENTRANCE_WELCOME, "首页弹窗")
|
||||
}
|
||||
EntranceUtils.HOST_ANSWER -> {
|
||||
DirectUtils.directToAnswerDetail(context!!, mWelcomeEntity?.link!!, EntranceUtils.ENTRANCE_WELCOME, "首页弹窗")
|
||||
DirectUtils.directToAnswerDetail(requireContext(), mWelcomeEntity?.link!!, EntranceUtils.ENTRANCE_WELCOME, "首页弹窗")
|
||||
}
|
||||
EntranceUtils.HOST_WEB -> {
|
||||
DirectUtils.directToWebView(context!!, mWelcomeEntity?.link!!, EntranceUtils.ENTRANCE_WELCOME)
|
||||
DirectUtils.directToWebView(requireContext(), mWelcomeEntity?.link!!, EntranceUtils.ENTRANCE_WELCOME)
|
||||
}
|
||||
EntranceUtils.HOST_QQ -> {
|
||||
DirectUtils.directToQqConversation(context!!, mWelcomeEntity?.link!!)
|
||||
DirectUtils.directToQqConversation(requireContext(), mWelcomeEntity?.link!!)
|
||||
}
|
||||
EntranceUtils.HOST_COMMUNITY -> {
|
||||
DirectUtils.directToCommunity(context!!, CommunityEntity(mWelcomeEntity?.link!!, mWelcomeEntity?.text!!))
|
||||
DirectUtils.directToCommunity(requireContext(), CommunityEntity(mWelcomeEntity?.link!!, mWelcomeEntity?.text!!))
|
||||
}
|
||||
else -> DialogUtils.showLowVersionDialog(context)
|
||||
}
|
||||
@ -75,10 +75,19 @@ class WelcomeDialog : BaseDialogFragment() {
|
||||
|
||||
binding.ivOpeningCover.loadingCallback = object : WrapContentDraweeView.LoadingCallback {
|
||||
override fun loaded() {
|
||||
binding.ivClose.visibility = View.VISIBLE
|
||||
binding.root.post {
|
||||
if (binding.ivOpeningCover.measuredHeight > binding.root.resources.displayMetrics.heightPixels * .8) {
|
||||
binding.ivCloseBackup.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.ivClose.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binding.ivCloseBackup.setOnClickListener {
|
||||
dismiss()
|
||||
}
|
||||
binding.ivClose.setOnClickListener {
|
||||
dismiss()
|
||||
}
|
||||
|
||||
@ -3,8 +3,8 @@ package com.gh.common.view
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Animatable
|
||||
import android.net.Uri
|
||||
import androidx.annotation.Nullable
|
||||
import android.util.AttributeSet
|
||||
import androidx.annotation.Nullable
|
||||
import com.facebook.drawee.backends.pipeline.PipelineDraweeControllerBuilder
|
||||
import com.facebook.drawee.controller.BaseControllerListener
|
||||
import com.facebook.drawee.view.SimpleDraweeView
|
||||
|
||||
@ -0,0 +1,436 @@
|
||||
package com.gh.common.view.divider;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import androidx.annotation.ColorRes;
|
||||
import androidx.annotation.DimenRes;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
|
||||
public abstract class FlexibleDividerDecoration extends RecyclerView.ItemDecoration {
|
||||
|
||||
private static final int DEFAULT_SIZE = 2;
|
||||
private static final int[] ATTRS = new int[]{
|
||||
android.R.attr.listDivider
|
||||
};
|
||||
|
||||
protected enum DividerType {
|
||||
DRAWABLE, PAINT, COLOR
|
||||
}
|
||||
|
||||
protected DividerType mDividerType = DividerType.DRAWABLE;
|
||||
protected VisibilityProvider mVisibilityProvider;
|
||||
protected PaintProvider mPaintProvider;
|
||||
protected ColorProvider mColorProvider;
|
||||
protected DrawableProvider mDrawableProvider;
|
||||
protected SizeProvider mSizeProvider;
|
||||
protected boolean mShowLastDivider;
|
||||
protected boolean mPositionInsideItem;
|
||||
private Paint mPaint;
|
||||
|
||||
protected FlexibleDividerDecoration(Builder builder) {
|
||||
if (builder.mPaintProvider != null) {
|
||||
mDividerType = DividerType.PAINT;
|
||||
mPaintProvider = builder.mPaintProvider;
|
||||
} else if (builder.mColorProvider != null) {
|
||||
mDividerType = DividerType.COLOR;
|
||||
mColorProvider = builder.mColorProvider;
|
||||
mPaint = new Paint();
|
||||
setSizeProvider(builder);
|
||||
} else {
|
||||
mDividerType = DividerType.DRAWABLE;
|
||||
if (builder.mDrawableProvider == null) {
|
||||
TypedArray a = builder.mContext.obtainStyledAttributes(ATTRS);
|
||||
final Drawable divider = a.getDrawable(0);
|
||||
a.recycle();
|
||||
mDrawableProvider = new DrawableProvider() {
|
||||
@Override
|
||||
public Drawable drawableProvider(int position, RecyclerView parent) {
|
||||
return divider;
|
||||
}
|
||||
};
|
||||
} else {
|
||||
mDrawableProvider = builder.mDrawableProvider;
|
||||
}
|
||||
mSizeProvider = builder.mSizeProvider;
|
||||
}
|
||||
|
||||
mVisibilityProvider = builder.mVisibilityProvider;
|
||||
mShowLastDivider = builder.mShowLastDivider;
|
||||
mPositionInsideItem = builder.mPositionInsideItem;
|
||||
}
|
||||
|
||||
private void setSizeProvider(Builder builder) {
|
||||
mSizeProvider = builder.mSizeProvider;
|
||||
if (mSizeProvider == null) {
|
||||
mSizeProvider = new SizeProvider() {
|
||||
@Override
|
||||
public int dividerSize(int position, RecyclerView parent) {
|
||||
return DEFAULT_SIZE;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
|
||||
RecyclerView.Adapter adapter = parent.getAdapter();
|
||||
if (adapter == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int itemCount = adapter.getItemCount();
|
||||
int lastDividerOffset = getLastDividerOffset(parent);
|
||||
int validChildCount = parent.getChildCount();
|
||||
int lastChildPosition = -1;
|
||||
for (int i = 0; i < validChildCount; i++) {
|
||||
View child = parent.getChildAt(i);
|
||||
int childPosition = parent.getChildAdapterPosition(child);
|
||||
|
||||
if (childPosition < lastChildPosition) {
|
||||
// Avoid remaining divider when animation starts
|
||||
continue;
|
||||
}
|
||||
lastChildPosition = childPosition;
|
||||
|
||||
if (!mShowLastDivider && childPosition >= itemCount - lastDividerOffset) {
|
||||
// Don't draw divider for last line if mShowLastDivider = false
|
||||
continue;
|
||||
}
|
||||
|
||||
if (wasDividerAlreadyDrawn(childPosition, parent)) {
|
||||
// No need to draw divider again as it was drawn already by previous column
|
||||
continue;
|
||||
}
|
||||
|
||||
int groupIndex = getGroupIndex(childPosition, parent);
|
||||
if (mVisibilityProvider.shouldHideDivider(groupIndex, parent)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Rect bounds = getDividerBound(groupIndex, parent, child);
|
||||
switch (mDividerType) {
|
||||
case DRAWABLE:
|
||||
Drawable drawable = mDrawableProvider.drawableProvider(groupIndex, parent);
|
||||
drawable.setBounds(bounds);
|
||||
drawable.draw(c);
|
||||
break;
|
||||
case PAINT:
|
||||
mPaint = mPaintProvider.dividerPaint(groupIndex, parent);
|
||||
c.drawLine(bounds.left, bounds.top, bounds.right, bounds.bottom, mPaint);
|
||||
break;
|
||||
case COLOR:
|
||||
mPaint.setColor(mColorProvider.dividerColor(groupIndex, parent));
|
||||
mPaint.setStrokeWidth(mSizeProvider.dividerSize(groupIndex, parent));
|
||||
c.drawLine(bounds.left, bounds.top, bounds.right, bounds.bottom, mPaint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getItemOffsets(Rect rect, View v, RecyclerView parent, RecyclerView.State state) {
|
||||
int position = parent.getChildAdapterPosition(v);
|
||||
int itemCount = parent.getAdapter().getItemCount();
|
||||
int lastDividerOffset = getLastDividerOffset(parent);
|
||||
if (!mShowLastDivider && position >= itemCount - lastDividerOffset) {
|
||||
// Don't set item offset for last line if mShowLastDivider = false
|
||||
return;
|
||||
}
|
||||
|
||||
int groupIndex = getGroupIndex(position, parent);
|
||||
if (mVisibilityProvider.shouldHideDivider(groupIndex, parent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setItemOffsets(rect, groupIndex, parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if recyclerview is reverse layout
|
||||
*
|
||||
* @param parent RecyclerView
|
||||
* @return true if recyclerview is reverse layout
|
||||
*/
|
||||
protected boolean isReverseLayout(RecyclerView parent) {
|
||||
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
|
||||
if (layoutManager instanceof LinearLayoutManager) {
|
||||
return ((LinearLayoutManager) layoutManager).getReverseLayout();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In the case mShowLastDivider = false,
|
||||
* Returns offset for how many views we don't have to draw a divider for,
|
||||
* for LinearLayoutManager it is as simple as not drawing the last child divider,
|
||||
* but for a GridLayoutManager it needs to take the span count for the last items into account
|
||||
* until we use the span count configured for the grid.
|
||||
*
|
||||
* @param parent RecyclerView
|
||||
* @return offset for how many views we don't have to draw a divider or 1 if its a
|
||||
* LinearLayoutManager
|
||||
*/
|
||||
private int getLastDividerOffset(RecyclerView parent) {
|
||||
if (parent.getLayoutManager() instanceof GridLayoutManager) {
|
||||
GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
|
||||
GridLayoutManager.SpanSizeLookup spanSizeLookup = layoutManager.getSpanSizeLookup();
|
||||
int spanCount = layoutManager.getSpanCount();
|
||||
int itemCount = parent.getAdapter().getItemCount();
|
||||
for (int i = itemCount - 1; i >= 0; i--) {
|
||||
if (spanSizeLookup.getSpanIndex(i, spanCount) == 0) {
|
||||
return itemCount - i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether divider was already drawn for the row the item is in,
|
||||
* effectively only makes sense for a grid
|
||||
*
|
||||
* @param position current view position to draw divider
|
||||
* @param parent RecyclerView
|
||||
* @return true if the divider can be skipped as it is in the same row as the previous one.
|
||||
*/
|
||||
private boolean wasDividerAlreadyDrawn(int position, RecyclerView parent) {
|
||||
if (parent.getLayoutManager() instanceof GridLayoutManager) {
|
||||
GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
|
||||
GridLayoutManager.SpanSizeLookup spanSizeLookup = layoutManager.getSpanSizeLookup();
|
||||
int spanCount = layoutManager.getSpanCount();
|
||||
return spanSizeLookup.getSpanIndex(position, spanCount) > 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a group index for GridLayoutManager.
|
||||
* for LinearLayoutManager, always returns position.
|
||||
*
|
||||
* @param position current view position to draw divider
|
||||
* @param parent RecyclerView
|
||||
* @return group index of items
|
||||
*/
|
||||
private int getGroupIndex(int position, RecyclerView parent) {
|
||||
if (parent.getLayoutManager() instanceof GridLayoutManager) {
|
||||
GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
|
||||
GridLayoutManager.SpanSizeLookup spanSizeLookup = layoutManager.getSpanSizeLookup();
|
||||
int spanCount = layoutManager.getSpanCount();
|
||||
return spanSizeLookup.getSpanGroupIndex(position, spanCount);
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
protected abstract Rect getDividerBound(int position, RecyclerView parent, View child);
|
||||
|
||||
protected abstract void setItemOffsets(Rect outRect, int position, RecyclerView parent);
|
||||
|
||||
/**
|
||||
* Interface for controlling divider visibility
|
||||
*/
|
||||
public interface VisibilityProvider {
|
||||
|
||||
/**
|
||||
* Returns true if divider should be hidden.
|
||||
*
|
||||
* @param position Divider position (or group index for GridLayoutManager)
|
||||
* @param parent RecyclerView
|
||||
* @return True if the divider at position should be hidden
|
||||
*/
|
||||
boolean shouldHideDivider(int position, RecyclerView parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for controlling paint instance for divider drawing
|
||||
*/
|
||||
public interface PaintProvider {
|
||||
|
||||
/**
|
||||
* Returns {@link Paint} for divider
|
||||
*
|
||||
* @param position Divider position (or group index for GridLayoutManager)
|
||||
* @param parent RecyclerView
|
||||
* @return Paint instance
|
||||
*/
|
||||
Paint dividerPaint(int position, RecyclerView parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for controlling divider color
|
||||
*/
|
||||
public interface ColorProvider {
|
||||
|
||||
/**
|
||||
* Returns {@link android.graphics.Color} value of divider
|
||||
*
|
||||
* @param position Divider position (or group index for GridLayoutManager)
|
||||
* @param parent RecyclerView
|
||||
* @return Color value
|
||||
*/
|
||||
int dividerColor(int position, RecyclerView parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for controlling drawable object for divider drawing
|
||||
*/
|
||||
public interface DrawableProvider {
|
||||
|
||||
/**
|
||||
* Returns drawable instance for divider
|
||||
*
|
||||
* @param position Divider position (or group index for GridLayoutManager)
|
||||
* @param parent RecyclerView
|
||||
* @return Drawable instance
|
||||
*/
|
||||
Drawable drawableProvider(int position, RecyclerView parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for controlling divider size
|
||||
*/
|
||||
public interface SizeProvider {
|
||||
|
||||
/**
|
||||
* Returns size value of divider.
|
||||
* Height for horizontal divider, width for vertical divider
|
||||
*
|
||||
* @param position Divider position (or group index for GridLayoutManager)
|
||||
* @param parent RecyclerView
|
||||
* @return Size of divider
|
||||
*/
|
||||
int dividerSize(int position, RecyclerView parent);
|
||||
}
|
||||
|
||||
public static class Builder<T extends Builder> {
|
||||
|
||||
private Context mContext;
|
||||
protected Resources mResources;
|
||||
private PaintProvider mPaintProvider;
|
||||
private ColorProvider mColorProvider;
|
||||
private DrawableProvider mDrawableProvider;
|
||||
private SizeProvider mSizeProvider;
|
||||
private VisibilityProvider mVisibilityProvider = new VisibilityProvider() {
|
||||
@Override
|
||||
public boolean shouldHideDivider(int position, RecyclerView parent) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
private boolean mShowLastDivider = false;
|
||||
private boolean mPositionInsideItem = false;
|
||||
|
||||
public Builder(Context context) {
|
||||
mContext = context;
|
||||
mResources = context.getResources();
|
||||
}
|
||||
|
||||
public T paint(final Paint paint) {
|
||||
return paintProvider(new PaintProvider() {
|
||||
@Override
|
||||
public Paint dividerPaint(int position, RecyclerView parent) {
|
||||
return paint;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public T paintProvider(PaintProvider provider) {
|
||||
mPaintProvider = provider;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public T color(final int color) {
|
||||
return colorProvider(new ColorProvider() {
|
||||
@Override
|
||||
public int dividerColor(int position, RecyclerView parent) {
|
||||
return color;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public T colorResId(@ColorRes int colorId) {
|
||||
return color(ContextCompat.getColor(mContext, colorId));
|
||||
}
|
||||
|
||||
public T colorProvider(ColorProvider provider) {
|
||||
mColorProvider = provider;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public T drawable(@DrawableRes int id) {
|
||||
return drawable(ContextCompat.getDrawable(mContext, id));
|
||||
}
|
||||
|
||||
public T drawable(final Drawable drawable) {
|
||||
return drawableProvider(new DrawableProvider() {
|
||||
@Override
|
||||
public Drawable drawableProvider(int position, RecyclerView parent) {
|
||||
return drawable;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public T drawableProvider(DrawableProvider provider) {
|
||||
mDrawableProvider = provider;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public T size(final int size) {
|
||||
return sizeProvider(new SizeProvider() {
|
||||
@Override
|
||||
public int dividerSize(int position, RecyclerView parent) {
|
||||
return size;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public T sizeResId(@DimenRes int sizeId) {
|
||||
return size(mResources.getDimensionPixelSize(sizeId));
|
||||
}
|
||||
|
||||
public T sizeProvider(SizeProvider provider) {
|
||||
mSizeProvider = provider;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public T visibilityProvider(VisibilityProvider provider) {
|
||||
mVisibilityProvider = provider;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public T showLastDivider() {
|
||||
mShowLastDivider = true;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public T positionInsideItem(boolean positionInsideItem) {
|
||||
mPositionInsideItem = positionInsideItem;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
protected void checkBuilderParams() {
|
||||
if (mPaintProvider != null) {
|
||||
if (mColorProvider != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Use setColor method of Paint class to specify line color. Do not provider ColorProvider if you set PaintProvider.");
|
||||
}
|
||||
if (mSizeProvider != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Use setStrokeWidth method of Paint class to specify line size. Do not provider SizeProvider if you set PaintProvider.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,171 @@
|
||||
package com.gh.common.view.divider;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import androidx.annotation.DimenRes;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
|
||||
public class HorizontalDividerItemDecoration extends FlexibleDividerDecoration {
|
||||
|
||||
private MarginProvider mMarginProvider;
|
||||
|
||||
protected HorizontalDividerItemDecoration(Builder builder) {
|
||||
super(builder);
|
||||
mMarginProvider = builder.mMarginProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Rect getDividerBound(int position, RecyclerView parent, View child) {
|
||||
Rect bounds = new Rect(0, 0, 0, 0);
|
||||
int transitionX = (int) ViewCompat.getTranslationX(child);
|
||||
int transitionY = (int) ViewCompat.getTranslationY(child);
|
||||
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
|
||||
bounds.left = parent.getPaddingLeft() +
|
||||
mMarginProvider.dividerLeftMargin(position, parent) + transitionX;
|
||||
bounds.right = parent.getWidth() - parent.getPaddingRight() -
|
||||
mMarginProvider.dividerRightMargin(position, parent) + transitionX;
|
||||
|
||||
int dividerSize = getDividerSize(position, parent);
|
||||
boolean isReverseLayout = isReverseLayout(parent);
|
||||
if (mDividerType == DividerType.DRAWABLE) {
|
||||
// set top and bottom position of divider
|
||||
if (isReverseLayout) {
|
||||
bounds.bottom = child.getTop() - params.topMargin + transitionY;
|
||||
bounds.top = bounds.bottom - dividerSize;
|
||||
} else {
|
||||
bounds.top = child.getBottom() + params.bottomMargin + transitionY;
|
||||
bounds.bottom = bounds.top + dividerSize;
|
||||
}
|
||||
} else {
|
||||
// set center point of divider
|
||||
int halfSize = dividerSize / 2;
|
||||
if (isReverseLayout) {
|
||||
bounds.top = child.getTop() - params.topMargin - halfSize + transitionY;
|
||||
} else {
|
||||
bounds.top = child.getBottom() + params.bottomMargin + halfSize + transitionY;
|
||||
}
|
||||
bounds.bottom = bounds.top;
|
||||
}
|
||||
|
||||
if (mPositionInsideItem) {
|
||||
if (isReverseLayout) {
|
||||
bounds.top += dividerSize;
|
||||
bounds.bottom += dividerSize;
|
||||
} else {
|
||||
bounds.top -= dividerSize;
|
||||
bounds.bottom -= dividerSize;
|
||||
}
|
||||
}
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setItemOffsets(Rect outRect, int position, RecyclerView parent) {
|
||||
if (mPositionInsideItem) {
|
||||
outRect.set(0, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isReverseLayout(parent)) {
|
||||
outRect.set(0, getDividerSize(position, parent), 0, 0);
|
||||
} else {
|
||||
outRect.set(0, 0, 0, getDividerSize(position, parent));
|
||||
}
|
||||
}
|
||||
|
||||
private int getDividerSize(int position, RecyclerView parent) {
|
||||
if (mPaintProvider != null) {
|
||||
return (int) mPaintProvider.dividerPaint(position, parent).getStrokeWidth();
|
||||
} else if (mSizeProvider != null) {
|
||||
return mSizeProvider.dividerSize(position, parent);
|
||||
} else if (mDrawableProvider != null) {
|
||||
Drawable drawable = mDrawableProvider.drawableProvider(position, parent);
|
||||
return drawable.getIntrinsicHeight();
|
||||
}
|
||||
throw new RuntimeException("failed to get size");
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for controlling divider margin
|
||||
*/
|
||||
public interface MarginProvider {
|
||||
|
||||
/**
|
||||
* Returns left margin of divider.
|
||||
*
|
||||
* @param position Divider position (or group index for GridLayoutManager)
|
||||
* @param parent RecyclerView
|
||||
* @return left margin
|
||||
*/
|
||||
int dividerLeftMargin(int position, RecyclerView parent);
|
||||
|
||||
/**
|
||||
* Returns right margin of divider.
|
||||
*
|
||||
* @param position Divider position (or group index for GridLayoutManager)
|
||||
* @param parent RecyclerView
|
||||
* @return right margin
|
||||
*/
|
||||
int dividerRightMargin(int position, RecyclerView parent);
|
||||
}
|
||||
|
||||
public static class Builder extends FlexibleDividerDecoration.Builder<Builder> {
|
||||
|
||||
private MarginProvider mMarginProvider = new MarginProvider() {
|
||||
@Override
|
||||
public int dividerLeftMargin(int position, RecyclerView parent) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int dividerRightMargin(int position, RecyclerView parent) {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
public Builder(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public Builder margin(final int leftMargin, final int rightMargin) {
|
||||
return marginProvider(new MarginProvider() {
|
||||
@Override
|
||||
public int dividerLeftMargin(int position, RecyclerView parent) {
|
||||
return leftMargin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int dividerRightMargin(int position, RecyclerView parent) {
|
||||
return rightMargin;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Builder margin(int horizontalMargin) {
|
||||
return margin(horizontalMargin, horizontalMargin);
|
||||
}
|
||||
|
||||
public Builder marginResId(@DimenRes int leftMarginId, @DimenRes int rightMarginId) {
|
||||
return margin(mResources.getDimensionPixelSize(leftMarginId),
|
||||
mResources.getDimensionPixelSize(rightMarginId));
|
||||
}
|
||||
|
||||
public Builder marginResId(@DimenRes int horizontalMarginId) {
|
||||
return marginResId(horizontalMarginId, horizontalMarginId);
|
||||
}
|
||||
|
||||
public Builder marginProvider(MarginProvider provider) {
|
||||
mMarginProvider = provider;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HorizontalDividerItemDecoration build() {
|
||||
checkBuilderParams();
|
||||
return new HorizontalDividerItemDecoration(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,172 @@
|
||||
package com.gh.common.view.divider;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import androidx.annotation.DimenRes;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
|
||||
|
||||
public class VerticalDividerItemDecoration extends FlexibleDividerDecoration {
|
||||
|
||||
private MarginProvider mMarginProvider;
|
||||
|
||||
protected VerticalDividerItemDecoration(Builder builder) {
|
||||
super(builder);
|
||||
mMarginProvider = builder.mMarginProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Rect getDividerBound(int position, RecyclerView parent, View child) {
|
||||
Rect bounds = new Rect(0, 0, 0, 0);
|
||||
int transitionX = (int) ViewCompat.getTranslationX(child);
|
||||
int transitionY = (int) ViewCompat.getTranslationY(child);
|
||||
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
|
||||
bounds.top = parent.getPaddingTop() +
|
||||
mMarginProvider.dividerTopMargin(position, parent) + transitionY;
|
||||
bounds.bottom = parent.getHeight() - parent.getPaddingBottom() -
|
||||
mMarginProvider.dividerBottomMargin(position, parent) + transitionY;
|
||||
|
||||
int dividerSize = getDividerSize(position, parent);
|
||||
boolean isReverseLayout = isReverseLayout(parent);
|
||||
if (mDividerType == DividerType.DRAWABLE) {
|
||||
// set left and right position of divider
|
||||
if (isReverseLayout) {
|
||||
bounds.right = child.getLeft() - params.leftMargin + transitionX;
|
||||
bounds.left = bounds.right - dividerSize;
|
||||
} else {
|
||||
bounds.left = child.getRight() + params.rightMargin + transitionX;
|
||||
bounds.right = bounds.left + dividerSize;
|
||||
}
|
||||
} else {
|
||||
// set center point of divider
|
||||
int halfSize = dividerSize / 2;
|
||||
if (isReverseLayout) {
|
||||
bounds.left = child.getLeft() - params.leftMargin - halfSize + transitionX;
|
||||
} else {
|
||||
bounds.left = child.getRight() + params.rightMargin + halfSize + transitionX;
|
||||
}
|
||||
bounds.right = bounds.left;
|
||||
}
|
||||
|
||||
if (mPositionInsideItem) {
|
||||
if (isReverseLayout) {
|
||||
bounds.left += dividerSize;
|
||||
bounds.right += dividerSize;
|
||||
} else {
|
||||
bounds.left -= dividerSize;
|
||||
bounds.right -= dividerSize;
|
||||
}
|
||||
}
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setItemOffsets(Rect outRect, int position, RecyclerView parent) {
|
||||
if (mPositionInsideItem) {
|
||||
outRect.set(0, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isReverseLayout(parent)) {
|
||||
outRect.set(getDividerSize(position, parent), 0, 0, 0);
|
||||
} else {
|
||||
outRect.set(0, 0, getDividerSize(position, parent), 0);
|
||||
}
|
||||
}
|
||||
|
||||
private int getDividerSize(int position, RecyclerView parent) {
|
||||
if (mPaintProvider != null) {
|
||||
return (int) mPaintProvider.dividerPaint(position, parent).getStrokeWidth();
|
||||
} else if (mSizeProvider != null) {
|
||||
return mSizeProvider.dividerSize(position, parent);
|
||||
} else if (mDrawableProvider != null) {
|
||||
Drawable drawable = mDrawableProvider.drawableProvider(position, parent);
|
||||
return drawable.getIntrinsicWidth();
|
||||
}
|
||||
throw new RuntimeException("failed to get size");
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for controlling divider margin
|
||||
*/
|
||||
public interface MarginProvider {
|
||||
|
||||
/**
|
||||
* Returns top margin of divider.
|
||||
*
|
||||
* @param position Divider position (or group index for GridLayoutManager)
|
||||
* @param parent RecyclerView
|
||||
* @return top margin
|
||||
*/
|
||||
int dividerTopMargin(int position, RecyclerView parent);
|
||||
|
||||
/**
|
||||
* Returns bottom margin of divider.
|
||||
*
|
||||
* @param position Divider position (or group index for GridLayoutManager)
|
||||
* @param parent RecyclerView
|
||||
* @return bottom margin
|
||||
*/
|
||||
int dividerBottomMargin(int position, RecyclerView parent);
|
||||
}
|
||||
|
||||
public static class Builder extends FlexibleDividerDecoration.Builder<Builder> {
|
||||
|
||||
private MarginProvider mMarginProvider = new MarginProvider() {
|
||||
@Override
|
||||
public int dividerTopMargin(int position, RecyclerView parent) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int dividerBottomMargin(int position, RecyclerView parent) {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
public Builder(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public Builder margin(final int topMargin, final int bottomMargin) {
|
||||
return marginProvider(new MarginProvider() {
|
||||
@Override
|
||||
public int dividerTopMargin(int position, RecyclerView parent) {
|
||||
return topMargin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int dividerBottomMargin(int position, RecyclerView parent) {
|
||||
return bottomMargin;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Builder margin(int verticalMargin) {
|
||||
return margin(verticalMargin, verticalMargin);
|
||||
}
|
||||
|
||||
public Builder marginResId(@DimenRes int topMarginId, @DimenRes int bottomMarginId) {
|
||||
return margin(mResources.getDimensionPixelSize(topMarginId),
|
||||
mResources.getDimensionPixelSize(bottomMarginId));
|
||||
}
|
||||
|
||||
public Builder marginResId(@DimenRes int verticalMarginId) {
|
||||
return marginResId(verticalMarginId, verticalMarginId);
|
||||
}
|
||||
|
||||
public Builder marginProvider(MarginProvider provider) {
|
||||
mMarginProvider = provider;
|
||||
return this;
|
||||
}
|
||||
|
||||
public VerticalDividerItemDecoration build() {
|
||||
checkBuilderParams();
|
||||
return new VerticalDividerItemDecoration(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -23,15 +23,15 @@ public class MaterialRatingDrawable extends LayerDrawable {
|
||||
public MaterialRatingDrawable(Context context, boolean fillBackgroundStars) {
|
||||
super(new Drawable[]{
|
||||
createLayerDrawableWithTintAttrRes(fillBackgroundStars ?
|
||||
R.drawable.ic_star
|
||||
: R.drawable.ic_star_border, fillBackgroundStars ?
|
||||
R.drawable.ic_icon_star_ratting
|
||||
: R.drawable.ic_icon_star_ratting_gray, fillBackgroundStars ?
|
||||
R.attr.colorControlHighlight : R.attr.colorControlNormal, context),
|
||||
fillBackgroundStars ? createClippedLayerDrawableWithTintColor(
|
||||
R.drawable.ic_star, Color.TRANSPARENT, context)
|
||||
R.drawable.ic_icon_star_ratting, Color.TRANSPARENT, context)
|
||||
: createClippedLayerDrawableWithTintAttrRes(
|
||||
R.drawable.ic_star_border, R.attr.colorControlActivated,
|
||||
R.drawable.ic_icon_star_ratting_gray, R.attr.colorControlActivated,
|
||||
context),
|
||||
createClippedLayerDrawableWithTintAttrRes(R.drawable.ic_star,
|
||||
createClippedLayerDrawableWithTintAttrRes(R.drawable.ic_icon_star_ratting,
|
||||
R.attr.colorControlActivated, context)
|
||||
});
|
||||
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
package com.gh.common.view.vertical_recycler
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
/**
|
||||
* 列表在滑动过程中拦截事件
|
||||
*/
|
||||
class CustomRecyclerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :RecyclerView(context, attrs) {
|
||||
|
||||
override fun onInterceptTouchEvent(e: MotionEvent?): Boolean {
|
||||
if(isScrolling()){
|
||||
return true
|
||||
}
|
||||
return super.onInterceptTouchEvent(e)
|
||||
}
|
||||
|
||||
private fun isScrolling():Boolean{
|
||||
return scrollState != SCROLL_STATE_IDLE
|
||||
}
|
||||
}
|
||||
|
||||
@ -179,6 +179,10 @@ public class PagerLayoutManager extends LinearLayoutManager {
|
||||
return super.scrollHorizontallyBy(dx, recycler, state);
|
||||
}
|
||||
|
||||
public boolean isSlideDown() {
|
||||
return mDrift >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置监听
|
||||
*
|
||||
|
||||
@ -2,14 +2,15 @@ package com.gh.download;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.collection.ArrayMap;
|
||||
|
||||
import com.gh.common.AppExecutor;
|
||||
import com.gh.common.exposure.ExposureEvent;
|
||||
import com.gh.common.util.AppDebugConfig;
|
||||
@ -55,11 +56,6 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.collection.ArrayMap;
|
||||
|
||||
import static android.os.Build.MANUFACTURER;
|
||||
|
||||
public class DownloadManager implements DownloadStatusListener {
|
||||
|
||||
private static DownloadManager mInstance;
|
||||
@ -229,7 +225,7 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
@Nullable ExposureEvent traceEvent) {
|
||||
|
||||
// 安装指引
|
||||
if ("Huawei".equalsIgnoreCase(MANUFACTURER) || "Oppo".equalsIgnoreCase(MANUFACTURER)) {
|
||||
/*if ("Huawei".equalsIgnoreCase(MANUFACTURER) || "Oppo".equalsIgnoreCase(MANUFACTURER)) {
|
||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
final SharedPreferences.Editor edit = sp.edit();
|
||||
if (sp.getBoolean("InstallHint" + PackageUtils.getVersionName(), true)) {
|
||||
@ -241,7 +237,7 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
edit.putBoolean("InstallHint" + PackageUtils.getVersionName(), false).apply();
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
// 插件版本下载互斥弹窗
|
||||
List<String> mutexPackage = gameEntity.getMutexPackage();
|
||||
@ -275,12 +271,15 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
}
|
||||
}
|
||||
downloadEntity.setInstalled(installed);
|
||||
if ("更新".equals(method)) {
|
||||
downloadEntity.setUpdate(true);
|
||||
} else if ("插件化".equals(method)) {
|
||||
|
||||
// todo 不是应该实时判断吗?
|
||||
if (PackageUtils.isCanPluggable(apkEntity)) {
|
||||
downloadEntity.setPluggable(true);
|
||||
} else if (PackageUtils.isCanUpdate(apkEntity, gameEntity.getId())) {
|
||||
downloadEntity.setUpdate(true);
|
||||
}
|
||||
downloadEntity.setPlugin(gameEntity.getTag() != null && gameEntity.getTag().size() != 0);
|
||||
|
||||
downloadEntity.setPlugin(!TextUtils.isEmpty(apkEntity.getGhVersion()));
|
||||
|
||||
if (isSubscribe) {
|
||||
DownloadManager.getInstance(context).subscribe(downloadEntity);
|
||||
@ -634,6 +633,7 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
|
||||
// 开启下载服务, Fuck me, 即便是在启动页调用的方法,依然有可能触发 `unable is in background`
|
||||
startDownloadService();
|
||||
checkRetryDownload();
|
||||
}
|
||||
|
||||
public void addObserver(DataWatcher dataWatcher) {
|
||||
@ -648,9 +648,10 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
|
||||
private void startDownloadService() {
|
||||
Intent serviceIntent = new Intent(mContext, DownloadService.class);
|
||||
// 当满足系统版本大于 8.0 、应用在后台运行时以前台服务开启
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
||||
&& !HaloApp.getInstance().isRunningForeground) {
|
||||
// 当满足系统版本大于 8.0 就以前台服务开启 DownloadService
|
||||
// (因为即便在 SplashActivity 里初始化,也有可能报 not allowed to start service, app is in background 的错误)
|
||||
// DownloadService 会调用 stopForeground 方法,理论上会去掉 `光环助手正在运行中` 的通知
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
serviceIntent.putExtra(DownloadService.KEY_SERVICE_ACTION, DownloadService.START_FOREGROUND);
|
||||
mContext.startForegroundService(serviceIntent);
|
||||
} else {
|
||||
|
||||
@ -2,6 +2,7 @@ package com.gh.download.cache
|
||||
|
||||
import android.net.Uri
|
||||
import com.gh.common.runOnIoThread
|
||||
import com.gh.common.util.DeviceUtils
|
||||
import com.gh.common.util.NetworkUtils
|
||||
import com.gh.gamecenter.BuildConfig
|
||||
import com.google.android.exoplayer2.upstream.DataSpec
|
||||
@ -20,11 +21,18 @@ import tv.danmaku.ijk.media.exo2.source.GSYExoHttpDataSourceFactory
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
|
||||
object ExoCacheManager {
|
||||
|
||||
private val threads = ConcurrentHashMap<String, AtomicBoolean>()
|
||||
private const val preLength = 50 * 1024 * 1024L //预加载视频大小
|
||||
|
||||
private val preLength by lazy {
|
||||
val totalRamSizeOfDevice = DeviceUtils.getTotalRamSizeOfDevice(HaloApp.getInstance().application)
|
||||
if (totalRamSizeOfDevice <= 2 * 1024) {
|
||||
10 * 1024 * 1024L
|
||||
} else {
|
||||
50 * 1024 * 1024L
|
||||
}
|
||||
}
|
||||
|
||||
fun preload(videoUri: String) {
|
||||
if (NetworkUtils.isWifiConnected(HaloApp.getInstance().application)) {
|
||||
@ -48,7 +56,7 @@ object ExoCacheManager {
|
||||
Utils.log("$requestLength--$bytesCached--$newBytesCached")
|
||||
}
|
||||
}, threads[videoUri])
|
||||
} catch (e: Exception) {
|
||||
} catch (e: Throwable) {
|
||||
threads.remove(videoUri)
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
423
app/src/main/java/com/gh/download/dialog/DownloadDialog.kt
Normal file
423
app/src/main/java/com/gh/download/dialog/DownloadDialog.kt
Normal file
@ -0,0 +1,423 @@
|
||||
package com.gh.download.dialog
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.*
|
||||
import android.view.animation.Animation
|
||||
import android.view.animation.AnimationUtils
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import androidx.recyclerview.widget.*
|
||||
import androidx.recyclerview.widget.RecyclerView.SmoothScroller
|
||||
import com.gh.base.fragment.BaseDialogFragment
|
||||
import com.gh.common.TimeElapsedHelper
|
||||
import com.gh.common.util.*
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.BuildConfig
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.databinding.DialogDownloadBinding
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import com.gh.gamecenter.eventbus.EBPackage
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.download.DataWatcher
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import com.lightgame.download.DownloadStatus
|
||||
import com.lightgame.utils.AppManager
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import java.util.*
|
||||
|
||||
|
||||
class DownloadDialog : BaseDialogFragment(), View.OnTouchListener {
|
||||
|
||||
private lateinit var mGameEntity: GameEntity
|
||||
private lateinit var mViewModel: DownloadViewModel
|
||||
private lateinit var mBinding: DialogDownloadBinding
|
||||
|
||||
private lateinit var mElapsedHelper: TimeElapsedHelper
|
||||
|
||||
private lateinit var mGestureDetector: GestureDetector
|
||||
|
||||
private var mAdapter: DownloadDialogAdapter? = null
|
||||
|
||||
// 合集页面保持和后台一样的顺序
|
||||
private var mCollectionAdapter: DownloadDialogAdapter? = null
|
||||
|
||||
/**
|
||||
* 例子:在首页跳转至专题详情点击下载
|
||||
* mEntrance-> (首页)+(专题详情)
|
||||
* mLocation-> 专题名称:游戏名称
|
||||
*
|
||||
* 注意:location 只用于下载统计,具体的 entrance = entrance + path
|
||||
*/
|
||||
private var mEntrance: String = "" // 入口位置
|
||||
private var mLocation: String = "" // 最终位置
|
||||
|
||||
private var mInitPositionY = 0f
|
||||
|
||||
private val mDataWatcher = object : DataWatcher() {
|
||||
override fun onDataChanged(downloadEntity: DownloadEntity) {
|
||||
if (downloadEntity.gameId == mGameEntity?.id &&
|
||||
DownloadStatus.delete != DownloadManager.getInstance(requireContext()).getStatus(downloadEntity.url)) {
|
||||
mAdapter?.listData?.forEachIndexed { index, entity ->
|
||||
if (entity.normal?.packageName == downloadEntity.packageName
|
||||
|| entity.installed?.packageName == downloadEntity.packageName) {
|
||||
mAdapter?.notifyItemChanged(index)
|
||||
}
|
||||
}
|
||||
|
||||
mCollectionAdapter?.listData?.forEachIndexed { index, entity ->
|
||||
if (entity.normal?.packageName == downloadEntity.packageName
|
||||
|| entity.installed?.packageName == downloadEntity.packageName) {
|
||||
mCollectionAdapter?.notifyItemChanged(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val createDialog = super.onCreateDialog(savedInstanceState)
|
||||
createDialog.setCanceledOnTouchOutside(true)
|
||||
|
||||
val window = createDialog.window
|
||||
window?.setGravity(Gravity.BOTTOM)
|
||||
window?.setWindowAnimations(R.style.community_publication_animation)
|
||||
return createDialog
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
EventBus.getDefault().register(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
mGameEntity = requireArguments().getParcelable(GameEntity::class.java.simpleName)!!
|
||||
mEntrance = requireArguments().getString(EntranceUtils.KEY_ENTRANCE) ?: ""
|
||||
mLocation = requireArguments().getString(EntranceUtils.KEY_LOCATION) ?: ""
|
||||
|
||||
val factory = DownloadViewModel.Factory(HaloApp.getInstance().application, mGameEntity)
|
||||
mViewModel = ViewModelProviders.of(this, factory).get(DownloadViewModel::class.java)
|
||||
mViewModel.listLiveData.observeNonNull(this, callback = { itemList ->
|
||||
mAdapter = DownloadDialogAdapter(requireContext(), mViewModel, itemList, false, mEntrance, mLocation)
|
||||
mBinding.contentList.layoutManager = createLayoutManager(itemList)
|
||||
mBinding.contentList.adapter = mAdapter
|
||||
})
|
||||
|
||||
mViewModel.collectionLiveData.observe(this, Observer { collection ->
|
||||
if (collection != null) {
|
||||
postBrowseMta()
|
||||
|
||||
mBinding.title.text = collection.name
|
||||
val saveApkList = collection.saveApkEntity
|
||||
throwExceptionInDebug("collection content must not be empty", saveApkList.isNullOrEmpty())
|
||||
|
||||
val itemList: MutableList<DownloadDialogItemData> = ArrayList()
|
||||
saveApkList?.forEach { apk ->
|
||||
itemList.add(DownloadDialogItemData(normal = apk))
|
||||
}
|
||||
if (collection.downloadInstruction.isNotEmpty()) {
|
||||
itemList.add(DownloadDialogItemData(instruction = collection.downloadInstruction))
|
||||
}
|
||||
|
||||
mCollectionAdapter = DownloadDialogAdapter(requireContext(), mViewModel, itemList, true, mEntrance, mLocation)
|
||||
mBinding.collectionList.layoutManager = createLayoutManager(itemList)
|
||||
mBinding.collectionList.adapter = mCollectionAdapter
|
||||
|
||||
if (mAdapter != null) collectionEnterAnimation()
|
||||
} else {
|
||||
mBinding.title.text = ("选择下载" + mGameEntity?.pluginDesc + "版本")
|
||||
|
||||
mCollectionAdapter = null
|
||||
collectionExitAnimation()
|
||||
}
|
||||
})
|
||||
mViewModel.dismissLiveData.observe(this, Observer {
|
||||
dismiss()
|
||||
})
|
||||
|
||||
mElapsedHelper = TimeElapsedHelper()
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
mBinding = DataBindingUtil.inflate(inflater, R.layout.dialog_download, container, false)
|
||||
mBinding.title.text = ("选择下载" + mGameEntity.pluginDesc + "版本")
|
||||
|
||||
val downloadNotice = mGameEntity.downloadAd
|
||||
if (downloadNotice != null) {
|
||||
mBinding.downloadNotice.visibility = View.VISIBLE
|
||||
mBinding.downloadNotice.text = downloadNotice.title
|
||||
mBinding.downloadNotice.setOnClickListener {
|
||||
if ("imprint" == downloadNotice.type) {
|
||||
DialogUtils.showImprintDialog(requireContext(), mGameEntity, downloadNotice.title)
|
||||
} else {
|
||||
DirectUtils.directToLinkPage(
|
||||
requireContext(),
|
||||
downloadNotice,
|
||||
mEntrance, "下载多平台弹窗")
|
||||
}
|
||||
MtaHelper.onEvent(MTA_KEY, "点击", mViewModel.gameEntity.name + "_" + downloadNotice.title)
|
||||
}
|
||||
}
|
||||
|
||||
mBinding.back.setOnClickListener {
|
||||
if (mCollectionAdapter != null && mAdapter != null) {
|
||||
postBrowseMta()
|
||||
mViewModel.collectionLiveData.postValue(null)
|
||||
} else {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
mGestureDetector = GestureDetector(requireContext(), SingleTapConfirm())
|
||||
mBinding.dragClose.setOnTouchListener(this)
|
||||
mBinding.back.setOnTouchListener(this)
|
||||
mBinding.downloadNotice.setOnTouchListener(this)
|
||||
|
||||
(mBinding.contentList.itemAnimator as DefaultItemAnimator).supportsChangeAnimations = false
|
||||
(mBinding.collectionList.itemAnimator as DefaultItemAnimator).supportsChangeAnimations = false
|
||||
mBinding.contentList.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
val layoutManager = mBinding.contentList.layoutManager
|
||||
if (layoutManager is LinearLayoutManager) {
|
||||
val position = layoutManager.findLastVisibleItemPosition()
|
||||
mBinding.otherVersionButtonContainer.goneIf(position > mViewModel.otherSectionPosition - 1)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
val smoothScroller: SmoothScroller = object : LinearSmoothScroller(requireContext()) {
|
||||
override fun getVerticalSnapPreference(): Int {
|
||||
return SNAP_TO_START
|
||||
}
|
||||
}
|
||||
mBinding.otherVersionButtonContainer.setOnClickListener {
|
||||
smoothScroller.targetPosition = mViewModel.otherSectionPosition
|
||||
mBinding.contentList.layoutManager?.startSmoothScroll(smoothScroller)
|
||||
}
|
||||
return mBinding.root
|
||||
}
|
||||
|
||||
private fun collectionEnterAnimation() {
|
||||
val backAnimation = AnimationUtils.loadAnimation(requireContext(), R.anim.download_dialog_collection_back_enter)
|
||||
backAnimation.fillAfter = true
|
||||
mBinding.back.startAnimation(backAnimation)
|
||||
|
||||
val contentAnimation = AnimationUtils.loadAnimation(requireContext(), R.anim.download_dialog_content_enter)
|
||||
contentAnimation.setAnimationListener(object : Animation.AnimationListener {
|
||||
override fun onAnimationRepeat(animation: Animation?) {
|
||||
}
|
||||
|
||||
override fun onAnimationEnd(animation: Animation?) {
|
||||
mBinding.contentListContainer.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun onAnimationStart(animation: Animation?) {
|
||||
}
|
||||
})
|
||||
mBinding.contentListContainer.startAnimation(contentAnimation)
|
||||
|
||||
val collectionAnimation = AnimationUtils.loadAnimation(requireContext(), R.anim.download_dialog_collection_content_enter)
|
||||
mBinding.collectionList.startAnimation(collectionAnimation)
|
||||
mBinding.collectionList.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
private fun collectionExitAnimation() {
|
||||
val backAnimation = AnimationUtils.loadAnimation(requireContext(), R.anim.download_dialog_collection_back_exit)
|
||||
backAnimation.fillAfter = true
|
||||
mBinding.back.startAnimation(backAnimation)
|
||||
|
||||
val contentAnimation = AnimationUtils.loadAnimation(requireContext(), R.anim.download_dialog_content_exit)
|
||||
mBinding.contentListContainer.startAnimation(contentAnimation)
|
||||
mBinding.contentListContainer.visibility = View.VISIBLE
|
||||
|
||||
val collectionAnimation = AnimationUtils.loadAnimation(requireContext(), R.anim.download_dialog_collection_content_exit)
|
||||
collectionAnimation.setAnimationListener(object : Animation.AnimationListener {
|
||||
override fun onAnimationRepeat(animation: Animation?) {
|
||||
}
|
||||
|
||||
override fun onAnimationEnd(animation: Animation?) {
|
||||
mBinding.collectionList.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun onAnimationStart(animation: Animation?) {
|
||||
}
|
||||
})
|
||||
mBinding.collectionList.startAnimation(collectionAnimation)
|
||||
}
|
||||
|
||||
private fun createLayoutManager(listData: List<DownloadDialogItemData>): RecyclerView.LayoutManager {
|
||||
val layoutManager = GridLayoutManager(requireContext(), 2)
|
||||
layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
||||
override fun getSpanSize(position: Int): Int {
|
||||
return if (listData[position].normal == null) 2 else 1
|
||||
}
|
||||
}
|
||||
return layoutManager
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
mAdapter?.notifyDataSetChanged()
|
||||
mCollectionAdapter?.notifyDataSetChanged()
|
||||
super.onResume()
|
||||
DownloadManager.getInstance(requireContext()).addObserver(mDataWatcher)
|
||||
mElapsedHelper.resetCounting()
|
||||
mElapsedHelper.resumeCounting()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
DownloadManager.getInstance(requireContext()).removeObserver(mDataWatcher)
|
||||
mElapsedHelper.pauseCounting()
|
||||
postBrowseMta()
|
||||
}
|
||||
|
||||
private fun postBrowseMta() {
|
||||
if (mCollectionAdapter == null) {
|
||||
MtaHelper.onEventWithTime(MTA_KEY, mElapsedHelper.elapsedTime, "浏览", mGameEntity?.name)
|
||||
} else {
|
||||
val collectionData = mViewModel.collectionLiveData.value
|
||||
MtaHelper.onEventWithTime(MTA_KEY, mElapsedHelper.elapsedTime, "浏览", mGameEntity?.name + "_" + collectionData?.name)
|
||||
throwExceptionInDebug("collectionData must be not null", collectionData == null)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
val width = HaloApp.getInstance().application.resources.displayMetrics.widthPixels
|
||||
val height = dialog?.window?.attributes?.height ?: ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
dialog?.window?.setLayout(width, height)
|
||||
}
|
||||
|
||||
override fun onBack(): Boolean {
|
||||
if (mCollectionAdapter != null && mAdapter != null) {
|
||||
postBrowseMta()
|
||||
mViewModel.collectionLiveData.postValue(null)
|
||||
return true
|
||||
}
|
||||
return super.onBack()
|
||||
}
|
||||
|
||||
// dialog drag animation
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onTouch(v: View, event: MotionEvent): Boolean {
|
||||
if (mGestureDetector.onTouchEvent(event) && mBinding.root.y == 0F) {
|
||||
v.performClick()
|
||||
return true
|
||||
}
|
||||
when (event.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
mInitPositionY = mBinding.root.y - event.rawY
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
val offsetY = event.rawY + mInitPositionY
|
||||
val dialogY = mBinding.root.y
|
||||
if (dialogY + offsetY > 0) {
|
||||
mBinding.root.animate()
|
||||
.y(offsetY)
|
||||
.setDuration(0)
|
||||
.start()
|
||||
} else {
|
||||
resetDialogPosition()
|
||||
}
|
||||
}
|
||||
MotionEvent.ACTION_CANCEL,
|
||||
MotionEvent.ACTION_UP,
|
||||
MotionEvent.ACTION_OUTSIDE -> {
|
||||
if (mBinding.root.y >= mBinding.root.height / 2) {
|
||||
dismiss()
|
||||
} else {
|
||||
resetDialogPosition(300)
|
||||
}
|
||||
}
|
||||
else -> return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun resetDialogPosition(duration: Long = 0) {
|
||||
mBinding.root.animate()
|
||||
.y(0F)
|
||||
.setDuration(duration)
|
||||
.start()
|
||||
}
|
||||
|
||||
private class SingleTapConfirm : GestureDetector.SimpleOnGestureListener() {
|
||||
override fun onSingleTapUp(event: MotionEvent): Boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
//安装、卸载事件
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(busFour: EBPackage) {
|
||||
if (::mViewModel.isInitialized && mViewModel.gameEntity.pluggableCollection == null) {
|
||||
val listData = mViewModel.listLiveData.value ?: return
|
||||
listData.forEach { itemData ->
|
||||
if (itemData.installed != null) {
|
||||
if (itemData.installed?.packageName == busFour.packageName) {
|
||||
mViewModel.initListData()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (itemData.normal != null) {
|
||||
if (itemData.normal?.packageName == busFour.packageName) {
|
||||
mViewModel.initListData()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
EventBus.getDefault().unregister(this)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun showDownloadDialog(context: Context?, gameEntity: GameEntity, entrance: String?, location: String?) {
|
||||
val fragmentActivity: FragmentActivity = if (context is FragmentActivity) {
|
||||
context
|
||||
} else if (BuildConfig.DEBUG) {
|
||||
throw IllegalStateException("DownloadDialog context must be FragmentActivity")
|
||||
} else {
|
||||
val currentActivity = AppManager.getInstance().currentActivity()
|
||||
if (currentActivity is FragmentActivity) {
|
||||
currentActivity
|
||||
} else {
|
||||
throw IllegalStateException("current activity context must be FragmentActivity")
|
||||
}
|
||||
}
|
||||
|
||||
// 防止重复弹出
|
||||
if (hasDownloadDialogInCurrentActivity(fragmentActivity)) return
|
||||
|
||||
val downloadDialog = DownloadDialog().apply {
|
||||
val bundle = Bundle()
|
||||
bundle.putString(EntranceUtils.KEY_ENTRANCE, entrance)
|
||||
bundle.putString(EntranceUtils.KEY_LOCATION, location)
|
||||
bundle.putParcelable(GameEntity::class.java.simpleName, gameEntity)
|
||||
arguments = bundle
|
||||
}
|
||||
downloadDialog.show(fragmentActivity.supportFragmentManager, DownloadDialog::class.java.name)
|
||||
}
|
||||
|
||||
private fun hasDownloadDialogInCurrentActivity(fragmentActivity: FragmentActivity): Boolean {
|
||||
val fragments: List<Fragment> = fragmentActivity.supportFragmentManager.fragments
|
||||
fragments.forEach { fragment ->
|
||||
if (fragment is DownloadDialog) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const val MTA_KEY = "多版本下载"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,130 @@
|
||||
package com.gh.download.dialog
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.base.OnViewClickListener
|
||||
import com.gh.common.util.MtaHelper
|
||||
import com.gh.common.util.throwExceptionInDebug
|
||||
import com.gh.gamecenter.NewsDetailActivity
|
||||
import com.gh.gamecenter.QaActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.databinding.*
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import com.lightgame.adapter.BaseRecyclerAdapter
|
||||
import com.lightgame.utils.Utils
|
||||
|
||||
class DownloadDialogAdapter(context: Context,
|
||||
val viewModel: DownloadViewModel,
|
||||
val listData: List<DownloadDialogItemData>,
|
||||
val isCollectionPage: Boolean,
|
||||
private val mEntrance: String,
|
||||
private val mLocation: String) : BaseRecyclerAdapter<RecyclerView.ViewHolder>(context) {
|
||||
|
||||
private val mPath = if (isCollectionPage) {
|
||||
"下载弹窗-二级页"
|
||||
} else {
|
||||
"下载弹窗"
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
val itemData = listData[position]
|
||||
if (itemData.links != null) return ITEM_LINK
|
||||
if (itemData.section != null) return ITEM_SECTION
|
||||
if (itemData.installed != null) return ITEM_INSTALLED
|
||||
if (itemData.instruction != null) return ITEM_INSTRUCTION
|
||||
if (itemData.platformRequest != null) return ITEM_PLATFORM_REQUEST
|
||||
return ITEM_NORMAL
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val view: View
|
||||
return when (viewType) {
|
||||
ITEM_LINK -> {
|
||||
view = mLayoutInflater.inflate(R.layout.download_dialog_link_item, parent, false)
|
||||
DownloadDialogLinkItemViewHolder(DownloadDialogLinkItemBinding.bind(view))
|
||||
}
|
||||
ITEM_SECTION -> {
|
||||
view = mLayoutInflater.inflate(R.layout.download_dialog_section_item, parent, false)
|
||||
DownloadDialogSectionItemViewHolder(DownloadDialogSectionItemBinding.bind(view))
|
||||
}
|
||||
ITEM_INSTALLED -> {
|
||||
view = mLayoutInflater.inflate(R.layout.download_dialog_installed_item, parent, false)
|
||||
DownloadDialogInstalledItemViewHolder(DownloadDialogInstalledItemBinding.bind(view))
|
||||
}
|
||||
ITEM_INSTRUCTION -> {
|
||||
view = mLayoutInflater.inflate(R.layout.download_dialog_instruction_item, parent, false)
|
||||
DownloadDialogInstructionItemViewHolder(DownloadDialogInstructionItemBinding.bind(view))
|
||||
}
|
||||
ITEM_PLATFORM_REQUEST -> {
|
||||
view = mLayoutInflater.inflate(R.layout.download_dialog_platform_request_item, parent, false)
|
||||
DownloadDialogPlatformRequestItemViewHolder(DownloadDialogPlatformRequestItemBinding.bind(view))
|
||||
}
|
||||
else -> {
|
||||
view = mLayoutInflater.inflate(R.layout.download_dialog_item, parent, false)
|
||||
DownloadDialogItemViewHolder(DownloadDialogItemBinding.bind(view))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (holder) {
|
||||
is DownloadDialogSectionItemViewHolder -> {
|
||||
holder.binding.type = listData[position].section
|
||||
}
|
||||
is DownloadDialogLinkItemViewHolder -> {
|
||||
holder.binding.links = listData[position].links
|
||||
holder.binding.clickListener = OnViewClickListener<GameEntity.PluginLink> { _, data ->
|
||||
when (data.linkType) {
|
||||
"dialog" -> {
|
||||
DownloadLinkDialog.showDownloadDialog(mContext, data)
|
||||
}
|
||||
"article" -> {
|
||||
mContext.startActivity(NewsDetailActivity.getIntentById(mContext, data.linkId, mEntrance))
|
||||
}
|
||||
"qa" -> {
|
||||
mContext.startActivity(QaActivity.getIntent(mContext, data.linkText, qaId = data.linkId))
|
||||
}
|
||||
"qa_collection" -> {
|
||||
mContext.startActivity(QaActivity.getIntent(mContext, data.linkText, qaCollectionId = data.linkId))
|
||||
}
|
||||
else -> {
|
||||
Utils.toast(mContext, "暂不支持类型:" + data.linkType)
|
||||
}
|
||||
}
|
||||
|
||||
MtaHelper.onEvent(DownloadDialog.MTA_KEY, "点击", viewModel.gameEntity.name + "_" + data.title)
|
||||
}
|
||||
}
|
||||
is DownloadDialogInstructionItemViewHolder -> {
|
||||
holder.bindItem(listData[position].instruction!!)
|
||||
}
|
||||
is DownloadDialogPlatformRequestItemViewHolder -> {
|
||||
holder.bindItem(viewModel.gameEntity)
|
||||
}
|
||||
is DownloadDialogInstalledItemViewHolder -> {
|
||||
holder.bindInstalledItem(listData[position].installed!!, viewModel, mEntrance, mPath, mLocation)
|
||||
throwExceptionInDebug("合集页面不应该存在该条数据", isCollectionPage)
|
||||
}
|
||||
is DownloadDialogItemViewHolder -> {
|
||||
holder.bindItem(listData[position].normal!!, viewModel, isCollectionPage, mEntrance, mPath, mLocation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return listData.size
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ITEM_LINK = 200
|
||||
private const val ITEM_SECTION = 201
|
||||
private const val ITEM_INSTALLED = 202
|
||||
private const val ITEM_NORMAL = 203
|
||||
private const val ITEM_INSTRUCTION = 204
|
||||
private const val ITEM_PLATFORM_REQUEST = 205
|
||||
|
||||
const val ITEM_TAG_KEY = R.id.download_item_type
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,128 @@
|
||||
package com.gh.download.dialog
|
||||
|
||||
import android.view.View
|
||||
import com.gh.base.BaseRecyclerViewHolder
|
||||
import com.gh.common.constant.Config
|
||||
import com.gh.common.util.PackageUtils
|
||||
import com.gh.common.util.goneIf
|
||||
import com.gh.common.util.throwExceptionInDebug
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.databinding.DownloadDialogInstalledItemBinding
|
||||
import com.gh.gamecenter.entity.ApkEntity
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.download.DownloadStatus
|
||||
|
||||
class DownloadDialogInstalledItemViewHolder(val binding: DownloadDialogInstalledItemBinding) : BaseRecyclerViewHolder<Any>(binding.root) {
|
||||
|
||||
fun bindInstalledItem(apkEntity: ApkEntity, viewModel: DownloadViewModel, entrance: String, path: String, location: String) {
|
||||
|
||||
val gameEntity = viewModel.gameEntity
|
||||
val apkCollection = apkEntity.apkCollection
|
||||
binding.pluginDesc = gameEntity.pluginDesc
|
||||
binding.apk = if (apkCollection != null) {
|
||||
ApkEntity(platformIcon = apkCollection.newIcon, platformName = apkCollection.name, remark = apkCollection.remark)
|
||||
} else apkEntity
|
||||
binding.root.setBackgroundResource(R.drawable.download_dialog_item_background)
|
||||
|
||||
if (apkEntity.apkLink != null) {
|
||||
throwExceptionInDebug("apkLink 不应该出现在这里")
|
||||
} else if (apkCollection != null || apkEntity.downloadInstruction.isNotEmpty()) {
|
||||
binding.collection.visibility = View.VISIBLE
|
||||
binding.downloadStatusIcon.visibility = View.GONE
|
||||
binding.progressbar.visibility = View.GONE
|
||||
binding.pluggable.visibility = View.GONE
|
||||
binding.install.visibility = View.GONE
|
||||
binding.update.visibility = View.GONE
|
||||
binding.status.visibility = View.GONE
|
||||
binding.launch.visibility = View.GONE
|
||||
if (apkCollection != null) {
|
||||
binding.remark.goneIf(apkCollection.remark.isEmpty())
|
||||
binding.root.setBackgroundResource(R.drawable.download_dialog_item_collection_background)
|
||||
} else {
|
||||
binding.remark.goneIf(apkEntity.remark.isEmpty())
|
||||
}
|
||||
|
||||
binding.collection.text = if (apkCollection != null) {
|
||||
"查看合集"
|
||||
} else "查看详情"
|
||||
itemView.setTag(DownloadDialogAdapter.ITEM_TAG_KEY, DownloadDialogItemStatus.COLLECTION)
|
||||
} else {
|
||||
binding.collection.visibility = View.GONE
|
||||
|
||||
val downloadEntity = DownloadManager.getInstance(HaloApp.getInstance().application).getDownloadEntityByUrl(apkEntity.url)
|
||||
if (downloadEntity != null) {
|
||||
binding.downloadStatusIcon.visibility = View.VISIBLE
|
||||
binding.progressbar.visibility = View.VISIBLE
|
||||
binding.status.visibility = View.VISIBLE
|
||||
binding.remark.visibility = View.GONE
|
||||
binding.launch.visibility = View.GONE
|
||||
binding.pluggable.visibility = View.GONE
|
||||
binding.update.visibility = View.GONE
|
||||
binding.install.goneIf(DownloadStatus.done != downloadEntity.status)
|
||||
|
||||
binding.downloadStatusIcon.setImageResource(R.drawable.download_dialog_status_pause)
|
||||
binding.progressbar.progress = downloadEntity.percent.toInt()
|
||||
binding.status.text = DownloadDialogItemViewHolder.getDownloadingStatusText(downloadEntity)
|
||||
|
||||
if (DownloadStatus.done == downloadEntity.status) {
|
||||
itemView.setTag(DownloadDialogAdapter.ITEM_TAG_KEY, DownloadDialogItemStatus.INSTALL)
|
||||
} else {
|
||||
itemView.setTag(DownloadDialogAdapter.ITEM_TAG_KEY, DownloadDialogItemStatus.DOWNLOADING)
|
||||
}
|
||||
} else {
|
||||
binding.downloadStatusIcon.visibility = View.GONE
|
||||
binding.progressbar.visibility = View.GONE
|
||||
binding.status.visibility = View.GONE
|
||||
binding.install.visibility = View.GONE
|
||||
binding.remark.goneIf(apkEntity.remark.isEmpty())
|
||||
|
||||
if (PackageUtils.isCanPluggable(apkEntity)) {
|
||||
binding.pluggable.visibility = View.VISIBLE
|
||||
binding.update.visibility = View.GONE
|
||||
binding.launch.visibility = View.GONE
|
||||
|
||||
itemView.setTag(DownloadDialogAdapter.ITEM_TAG_KEY, DownloadDialogItemStatus.PLUGGABLE)
|
||||
} else if (PackageUtils.isCanUpdate(apkEntity, gameEntity.id)) {
|
||||
binding.update.visibility = View.VISIBLE
|
||||
binding.pluggable.visibility = View.GONE
|
||||
binding.launch.visibility = View.GONE
|
||||
|
||||
itemView.setTag(DownloadDialogAdapter.ITEM_TAG_KEY, DownloadDialogItemStatus.UPDATE)
|
||||
} else if (PackageUtils.getGhId(apkEntity.packageName) == gameEntity.id) {
|
||||
binding.status.visibility = View.VISIBLE
|
||||
binding.pluggable.visibility = View.GONE
|
||||
binding.update.visibility = View.GONE
|
||||
|
||||
var isFilter = false
|
||||
Config.getSettings()?.gameDownloadBlackList?.forEach { pkgName ->
|
||||
if (pkgName == apkEntity.packageName) {
|
||||
isFilter = true
|
||||
}
|
||||
}
|
||||
|
||||
if (isFilter) {
|
||||
// 已安装
|
||||
binding.status.visibility = View.VISIBLE
|
||||
binding.launch.visibility = View.GONE
|
||||
|
||||
binding.status.text = "已安装"
|
||||
itemView.setTag(DownloadDialogAdapter.ITEM_TAG_KEY, DownloadDialogItemStatus.INSTALLED)
|
||||
} else {
|
||||
// 点击启动
|
||||
binding.launch.visibility = View.VISIBLE
|
||||
binding.status.visibility = View.GONE
|
||||
|
||||
binding.status.text = "点击启动"
|
||||
itemView.setTag(DownloadDialogAdapter.ITEM_TAG_KEY, DownloadDialogItemStatus.LAUNCH)
|
||||
}
|
||||
} else {
|
||||
throwExceptionInDebug("下载弹窗-我的版本出现未知类型")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DownloadDialogItemViewHolder.setDownloadClickListener(itemView, apkEntity, viewModel, entrance, path, location)
|
||||
binding.executePendingBindings()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package com.gh.download.dialog
|
||||
|
||||
import android.graphics.Color
|
||||
import com.gh.base.BaseRecyclerViewHolder
|
||||
import com.gh.gamecenter.databinding.DownloadDialogInstructionItemBinding
|
||||
|
||||
class DownloadDialogInstructionItemViewHolder(val binding: DownloadDialogInstructionItemBinding) : BaseRecyclerViewHolder<Any>(binding.root) {
|
||||
|
||||
fun bindItem(instruction: String) {
|
||||
binding.webView.loadDataWithBaseURL(
|
||||
null,
|
||||
"<body style='margin:0;padding:0;'>$instruction</body>",
|
||||
"text/html",
|
||||
"utf-8", null)
|
||||
binding.webView.settings
|
||||
binding.webView.setBackgroundColor(Color.TRANSPARENT)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package com.gh.download.dialog
|
||||
|
||||
import com.gh.gamecenter.entity.ApkEntity
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
|
||||
data class DownloadDialogItemData(var links: List<GameEntity.PluginLink>? = null,
|
||||
var section: DownloadDialogSectionType? = null,
|
||||
var installed: ApkEntity? = null,
|
||||
var normal: ApkEntity? = null,
|
||||
var instruction: String? = null,
|
||||
var platformRequest: Any? = null)
|
||||
|
||||
enum class DownloadDialogSectionType {
|
||||
INSTALLED,
|
||||
OTHER
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package com.gh.download.dialog
|
||||
|
||||
enum class DownloadDialogItemStatus {
|
||||
DOWNLOAD,
|
||||
LAUNCH,
|
||||
DOWNLOADING,
|
||||
INSTALL,
|
||||
PLUGGABLE,
|
||||
UPDATE,
|
||||
COLLECTION,
|
||||
LINK,
|
||||
INSTALLED,
|
||||
IGNORE
|
||||
}
|
||||
@ -0,0 +1,288 @@
|
||||
package com.gh.download.dialog
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import com.gh.base.BaseActivity
|
||||
import com.gh.base.BaseRecyclerViewHolder
|
||||
import com.gh.common.constant.Config
|
||||
import com.gh.common.dialog.CertificationDialog
|
||||
import com.gh.common.dialog.DeviceRemindDialog
|
||||
import com.gh.common.util.*
|
||||
import com.gh.common.util.DirectUtils.directToLinkPage
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.DownloadManagerActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.databinding.DownloadDialogItemBinding
|
||||
import com.gh.gamecenter.entity.ApkEntity
|
||||
import com.gh.gamecenter.entity.GameCollectionEntity
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import com.lightgame.download.DownloadStatus
|
||||
import com.lightgame.download.FileUtils
|
||||
import com.lightgame.utils.AppManager
|
||||
import com.lightgame.utils.Utils
|
||||
|
||||
class DownloadDialogItemViewHolder(val binding: DownloadDialogItemBinding) : BaseRecyclerViewHolder<Any>(binding.root) {
|
||||
|
||||
fun bindItem(apkEntity: ApkEntity,
|
||||
viewModel: DownloadViewModel,
|
||||
isCollectionPage: Boolean,
|
||||
entrance: String,
|
||||
path: String,
|
||||
location: String) {
|
||||
|
||||
val gameEntity = viewModel.gameEntity
|
||||
val apkLink = apkEntity.apkLink
|
||||
val apkCollection = apkEntity.apkCollection
|
||||
|
||||
binding.apk = when {
|
||||
apkLink != null -> ApkEntity(
|
||||
platformIcon = apkLink.icon,
|
||||
platformName = apkLink.name,
|
||||
remark = apkLink.remark)
|
||||
apkCollection != null -> ApkEntity(
|
||||
platformIcon = apkCollection.newIcon,
|
||||
platformName = apkCollection.name,
|
||||
remark = apkCollection.remark)
|
||||
else -> apkEntity
|
||||
}
|
||||
binding.root.setBackgroundResource(R.drawable.download_dialog_item_background)
|
||||
|
||||
if (apkLink != null) {
|
||||
binding.downloadStatusIcon.visibility = View.VISIBLE
|
||||
binding.status.visibility = View.VISIBLE
|
||||
binding.progressbar.visibility = View.GONE
|
||||
binding.install.visibility = View.GONE
|
||||
binding.remark.goneIf(apkLink.remark.isEmpty())
|
||||
|
||||
binding.status.text = "点击查看"
|
||||
binding.downloadStatusIcon.setImageResource(R.drawable.download_dialog_collection_status_link)
|
||||
binding.root.setBackgroundResource(R.drawable.download_dialog_installed_background)
|
||||
itemView.setTag(DownloadDialogAdapter.ITEM_TAG_KEY, DownloadDialogItemStatus.LINK)
|
||||
} else if (apkCollection != null || (!isCollectionPage && apkEntity.downloadInstruction.isNotEmpty())) {
|
||||
binding.downloadStatusIcon.visibility = View.VISIBLE
|
||||
binding.progressbar.visibility = View.GONE
|
||||
binding.install.visibility = View.GONE
|
||||
binding.status.visibility = View.GONE
|
||||
if (apkCollection != null) {
|
||||
binding.remark.goneIf(apkCollection.remark.isEmpty())
|
||||
binding.root.setBackgroundResource(R.drawable.download_dialog_item_collection_background)
|
||||
} else {
|
||||
binding.remark.goneIf(apkEntity.remark.isEmpty())
|
||||
}
|
||||
|
||||
binding.downloadStatusIcon.setImageResource(R.drawable.download_dialog_status_collection)
|
||||
itemView.setTag(DownloadDialogAdapter.ITEM_TAG_KEY, DownloadDialogItemStatus.COLLECTION)
|
||||
} else {
|
||||
|
||||
val downloadEntity = DownloadManager.getInstance(HaloApp.getInstance().application).getDownloadEntityByUrl(apkEntity.url)
|
||||
if (downloadEntity != null) {
|
||||
binding.downloadStatusIcon.visibility = View.VISIBLE
|
||||
binding.progressbar.visibility = View.VISIBLE
|
||||
binding.status.visibility = View.VISIBLE
|
||||
binding.remark.visibility = View.GONE
|
||||
binding.install.goneIf(DownloadStatus.done != downloadEntity.status)
|
||||
|
||||
binding.downloadStatusIcon.setImageResource(R.drawable.download_dialog_status_pause)
|
||||
binding.progressbar.progress = downloadEntity.percent.toInt()
|
||||
binding.status.text = getDownloadingStatusText(downloadEntity)
|
||||
|
||||
if (DownloadStatus.done == downloadEntity.status) {
|
||||
itemView.setTag(DownloadDialogAdapter.ITEM_TAG_KEY, DownloadDialogItemStatus.INSTALL)
|
||||
} else {
|
||||
itemView.setTag(DownloadDialogAdapter.ITEM_TAG_KEY, DownloadDialogItemStatus.DOWNLOADING)
|
||||
}
|
||||
} else {
|
||||
binding.progressbar.visibility = View.GONE
|
||||
binding.install.visibility = View.GONE
|
||||
binding.remark.goneIf(apkEntity.remark.isEmpty())
|
||||
|
||||
if (PackageUtils.isCanUpdate(apkEntity, gameEntity.id)) {
|
||||
binding.downloadStatusIcon.visibility = View.VISIBLE
|
||||
binding.status.visibility = View.VISIBLE
|
||||
|
||||
binding.status.text = "可更新"
|
||||
binding.downloadStatusIcon.setImageResource(R.drawable.download_dialog_collection_status_update)
|
||||
itemView.setTag(DownloadDialogAdapter.ITEM_TAG_KEY, DownloadDialogItemStatus.UPDATE)
|
||||
} else if (PackageUtils.getGhId(apkEntity.packageName) == gameEntity.id) {
|
||||
binding.downloadStatusIcon.visibility = View.GONE
|
||||
binding.status.visibility = View.VISIBLE
|
||||
binding.root.setBackgroundResource(R.drawable.download_dialog_installed_background)
|
||||
|
||||
var isFilter = false
|
||||
Config.getSettings()?.gameDownloadBlackList?.forEach { pkgName ->
|
||||
if (pkgName == apkEntity.packageName) {
|
||||
isFilter = true
|
||||
}
|
||||
}
|
||||
|
||||
if (isFilter) {
|
||||
// 已安装
|
||||
binding.status.text = "已安装"
|
||||
itemView.setTag(DownloadDialogAdapter.ITEM_TAG_KEY, DownloadDialogItemStatus.INSTALLED)
|
||||
} else {
|
||||
// 点击启动
|
||||
binding.status.text = "点击启动"
|
||||
itemView.setTag(DownloadDialogAdapter.ITEM_TAG_KEY, DownloadDialogItemStatus.LAUNCH)
|
||||
}
|
||||
} else {
|
||||
binding.downloadStatusIcon.visibility = View.VISIBLE
|
||||
binding.status.visibility = View.GONE
|
||||
|
||||
binding.downloadStatusIcon.setImageResource(R.drawable.download_dialog_status_download)
|
||||
if (PackageUtils.isCanPluggable(apkEntity)) {
|
||||
itemView.setTag(DownloadDialogAdapter.ITEM_TAG_KEY, DownloadDialogItemStatus.PLUGGABLE)
|
||||
} else {
|
||||
itemView.setTag(DownloadDialogAdapter.ITEM_TAG_KEY, DownloadDialogItemStatus.DOWNLOAD)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setDownloadClickListener(itemView, apkEntity, viewModel, entrance, path, location)
|
||||
binding.executePendingBindings()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getDownloadingStatusText(downloadEntity: DownloadEntity): String {
|
||||
return when (downloadEntity.status) {
|
||||
DownloadStatus.waiting -> "等待中"
|
||||
DownloadStatus.done -> "等待安装"
|
||||
DownloadStatus.downloading -> SpeedUtils.getSpeed(downloadEntity.speed)
|
||||
else -> "已暂停"
|
||||
}
|
||||
}
|
||||
|
||||
fun setDownloadClickListener(itemView: View,
|
||||
apkEntity: ApkEntity,
|
||||
viewModel: DownloadViewModel,
|
||||
entrance: String,
|
||||
path: String,
|
||||
location: String) {
|
||||
|
||||
val gameEntity = viewModel.gameEntity
|
||||
itemView.setOnClickListener {
|
||||
var mtaValue = "未知"
|
||||
when (itemView.getTag(DownloadDialogAdapter.ITEM_TAG_KEY)) {
|
||||
DownloadDialogItemStatus.DOWNLOAD -> {
|
||||
createDownloadTask(it.context, apkEntity, gameEntity, "下载", entrance, location)
|
||||
mtaValue = gameEntity.name + "_" + apkEntity.getPlatformName() + "_下载"
|
||||
}
|
||||
DownloadDialogItemStatus.LAUNCH -> {
|
||||
PackageUtils.launchApplicationByPackageName(it.context, apkEntity.packageName)
|
||||
|
||||
mtaValue = gameEntity.name + "_" + apkEntity.getPlatformName() + "_启动"
|
||||
}
|
||||
DownloadDialogItemStatus.DOWNLOADING -> {
|
||||
// 打开下载管理界面
|
||||
it.context.startActivity(DownloadManagerActivity.getDownloadMangerIntent(
|
||||
it.context,
|
||||
apkEntity.url,
|
||||
BaseActivity.mergeEntranceAndPath(entrance, path)))
|
||||
if (AppManager.getInstance().currentActivity() is DownloadManagerActivity) {
|
||||
viewModel.dismissLiveData.postValue(Any())
|
||||
}
|
||||
|
||||
val downloadEntity = DownloadManager.getInstance(it.context).getDownloadEntityByUrl(apkEntity.url)
|
||||
if (downloadEntity.status == DownloadStatus.pause) {
|
||||
mtaValue = gameEntity.name + "_" + apkEntity.getPlatformName() + "_暂停中"
|
||||
} else if (downloadEntity.status == DownloadStatus.waiting) {
|
||||
mtaValue = gameEntity.name + "_" + apkEntity.getPlatformName() + "_等待中"
|
||||
} else if (downloadEntity.status == DownloadStatus.subscribe) {
|
||||
mtaValue = gameEntity.name + "_" + apkEntity.getPlatformName() + "_排队中"
|
||||
} else {
|
||||
mtaValue = gameEntity.name + "_" + apkEntity.getPlatformName() + "_下载中"
|
||||
}
|
||||
|
||||
}
|
||||
DownloadDialogItemStatus.INSTALL -> {
|
||||
val downloadEntity = DownloadManager.getInstance(it.context).getDownloadEntityByUrl(apkEntity.url)
|
||||
if (FileUtils.isEmptyFile(downloadEntity.path)) {
|
||||
Utils.toast(it.context, R.string.install_failure_hint)
|
||||
DownloadManager.getInstance(it.context).cancel(apkEntity.url)
|
||||
} else {
|
||||
if (PackageUtils.isCanPluggable(apkEntity)) {
|
||||
DialogUtils.showPluginDialog(it.context) {
|
||||
it.context.startActivity(PackageUtils.getUninstallIntent(it.context, downloadEntity.path))
|
||||
}
|
||||
} else {
|
||||
PackageUtils.launchSetup(it.context, downloadEntity)
|
||||
}
|
||||
}
|
||||
|
||||
if (downloadEntity.isPluggable) {
|
||||
mtaValue = gameEntity.name + "_" + apkEntity.getPlatformName() + "_插件化安装"
|
||||
} else {
|
||||
mtaValue = gameEntity.name + "_" + apkEntity.getPlatformName() + "_安装"
|
||||
}
|
||||
}
|
||||
DownloadDialogItemStatus.PLUGGABLE -> {
|
||||
createDownloadTask(it.context, apkEntity, gameEntity, "插件化", entrance, location)
|
||||
|
||||
mtaValue = gameEntity.name + "_" + apkEntity.getPlatformName() + "_插件化"
|
||||
}
|
||||
DownloadDialogItemStatus.UPDATE -> {
|
||||
createDownloadTask(it.context, apkEntity, gameEntity, "更新", entrance, location)
|
||||
|
||||
mtaValue = gameEntity.name + "_" + apkEntity.getPlatformName() + "_更新"
|
||||
}
|
||||
DownloadDialogItemStatus.COLLECTION -> {
|
||||
val apkCollection = apkEntity.apkCollection
|
||||
if (apkCollection != null) {
|
||||
viewModel.collectionLiveData.postValue(apkCollection)
|
||||
|
||||
mtaValue = gameEntity.name + "_" + apkCollection.name + "_查看合集"
|
||||
} else if (apkEntity.downloadInstruction.isNotEmpty()) {
|
||||
val fakeCollection = GameCollectionEntity()
|
||||
fakeCollection.saveApkEntity = arrayListOf(apkEntity)
|
||||
fakeCollection.name = apkEntity.getPlatformName()
|
||||
fakeCollection.downloadInstruction = apkEntity.downloadInstruction
|
||||
viewModel.collectionLiveData.postValue(fakeCollection)
|
||||
|
||||
mtaValue = gameEntity.name + "_" + apkEntity.getPlatformName() + "_查看详情"
|
||||
} else {
|
||||
throwExceptionInDebug("合集和下载说明至少一个不为空")
|
||||
}
|
||||
}
|
||||
DownloadDialogItemStatus.LINK -> {
|
||||
directToLinkPage(it.context, apkEntity.apkLink?.getLinkEntity()!!, entrance, path)
|
||||
|
||||
mtaValue = gameEntity.name + "_" + apkEntity.apkLink?.name
|
||||
}
|
||||
DownloadDialogItemStatus.INSTALLED -> {
|
||||
mtaValue = gameEntity.name + "_" + apkEntity.getPlatformName() + "_已安装"
|
||||
}
|
||||
}
|
||||
throwExceptionInDebug("无法识别当前状态", mtaValue == "未知")
|
||||
MtaHelper.onEvent(DownloadDialog.MTA_KEY, "点击", mtaValue)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createDownloadTask(context: Context,
|
||||
apkEntity: ApkEntity,
|
||||
gameEntity: GameEntity,
|
||||
downloadMethod: String,
|
||||
entrance: String,
|
||||
location: String) {
|
||||
DownloadDialogHelper.findAvailableDialogAndShow(context, gameEntity, apkEntity, object : EmptyCallback {
|
||||
override fun onCallback() {
|
||||
CertificationDialog.showCertificationDialog(context, gameEntity, DialogUtils.ConfirmListener {
|
||||
DialogUtils.checkDownload(context, apkEntity.size) { isSubscribe ->
|
||||
DownloadManager.createDownload(
|
||||
context,
|
||||
apkEntity,
|
||||
gameEntity,
|
||||
downloadMethod,
|
||||
entrance,
|
||||
location,
|
||||
isSubscribe, null)
|
||||
|
||||
DeviceRemindDialog.showDeviceRemindDialog(context, gameEntity)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
package com.gh.download.dialog
|
||||
|
||||
import com.gh.base.BaseRecyclerViewHolder
|
||||
import com.gh.gamecenter.databinding.DownloadDialogLinkItemBinding
|
||||
|
||||
class DownloadDialogLinkItemViewHolder(val binding: DownloadDialogLinkItemBinding) : BaseRecyclerViewHolder<Any>(binding.root) {
|
||||
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package com.gh.download.dialog
|
||||
|
||||
import com.gh.base.BaseRecyclerViewHolder
|
||||
import com.gh.common.util.MtaHelper
|
||||
import com.gh.gamecenter.VoteActivity
|
||||
import com.gh.gamecenter.databinding.DownloadDialogPlatformRequestItemBinding
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
|
||||
class DownloadDialogPlatformRequestItemViewHolder(val binding: DownloadDialogPlatformRequestItemBinding)
|
||||
: BaseRecyclerViewHolder<Any>(binding.root) {
|
||||
|
||||
fun bindItem(gameEntity: GameEntity) {
|
||||
binding.content.setOnClickListener {
|
||||
val intent = VoteActivity.getIntent(it.context, gameEntity.name, gameEntity.id)
|
||||
it.context.startActivity(intent)
|
||||
MtaHelper.onEvent(DownloadDialog.MTA_KEY, "点击", gameEntity.name + "_求版本")
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user